Mybatis源码分析

Mybatis是一个持久层框架,通过对底层的封装,使得我们通过一些配置以及SQL语句就可以很容易就可以对数据库进行操作。

下面就是Mybatis的一个HelloWorld的使用(基于Mybatis 3.4.6版本):

下面是一个java bean对象,一个实体类Student

public class Student {
	private Integer id;
	private String name;
	private int age;
	public Student() {
	}
	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "student [id=" + id + ", name=" + name + ", age=" + age + "]";
	}
	
}

IStudentDao接口

public interface IStudentDao {
	//按id查询
	Student selById(int id);
}

Mapper.xml配置

<mapper namespace="cn.xiongyu.dao.IStudentDao">
	<select id="selById" resultType="Student">
		select * from stu where id=#{id}
	</select>
</mapper>

主配置文件Mybatis.xml配置信息

<configuration>
	<properties resource="jdbc_mysql.properties"></properties>
	<typeAliases>
		<package name="cn.xiongyu.bean"/>
	</typeAliases>
	<environments default="testEM">
		<environment id="testEM">
			<transactionManager type="JDBC"/>
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driver}"/>
				<property name="url" value="${jdbc.url}"/>
				<property name="username" value="${jdbc.username}"/>
				<property name="password" value="${jdbc.password}"/>
			</dataSource>
		</environment>
	</environments>
	<mappers>
		<mapper resource="cn/xiongyu/dao/mapper.xml"/>
	</mappers>
</configuration>

测试类

public class MyBatisTest{
	@Test
	public void test01(){
		//通过主配置文件的路径获取主配置文件的字节流,实际上是一个BufferedInputStream
		InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
		//获取SqlSessionFactory,用于得到SqlSession对象
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		SqlSession sqlSession = sqlSessionFactory.openSession();
		//通过SqlSession对象获取代理mapper,用于实现IStudentDao接口中的方法
		IStudentDao studentDao = sqlSession.getMapper(IStudentDao.class);
		//具体的查询语句
		Student stu = studentDao.selById(10);
		sqlSession.close();
	}
}

首选通过SqlSessionFactoryBuilder来构建SqlSessionFactory对象,传入的是一个mybatis.xml配置文件流,下面看看SqlSessionFactoryBuilder源码:

public class SqlSessionFactoryBuilder {
	//通过调用该build方法,实际调用的是下面的重载方法
	public SqlSessionFactory build(InputStream inputStream) {
        return build(inputStream, null, null);
    }
	//实际构造SqlSessionFactory 的代码
	public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
        	//对Mybatis.xml进行解析得到XMLConfigBuilder 对象
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            //真正对主配置文件进行解析是parser.parse()操作,该源码部分如下。
            return build(parser.parse());
        } catch (Exception e) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
            ErrorContext.instance().reset();
            try {
                inputStream.close();
            } catch (IOException e) {
                // Intentionally ignore. Prefer previous error.
            }
        }
    }
	  
	public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
    }

}

XMLConfigBuilder 的parse()方法

public Configuration parse() {
        if (parsed) {
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        }
        parsed = true;
        //实际调用下面方法进行解析XML
        parseConfiguration(parser.evalNode("/configuration"));
        return configuration;
    }
	//可以知道主配置文件中只能配置下面节点,下面只介绍了一下主要用到的
    private void parseConfiguration(XNode root) {
        try {
            //解析properties属性文件节点
            propertiesElement(root.evalNode("properties"));
            Properties settings = settingsAsProperties(root.evalNode("settings"));
            loadCustomVfs(settings);
            //解析typeAliases节点,设置别名,实际上底层是一个HashMap用于映射别名
            typeAliasesElement(root.evalNode("typeAliases"));
            pluginElement(root.evalNode("plugins"));
            objectFactoryElement(root.evalNode("objectFactory"));
            objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            reflectorFactoryElement(root.evalNode("reflectorFactory"));
            settingsElement(settings);
            //环境节点,可以配置多个环境
            environmentsElement(root.evalNode("environments"));
            databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            //类型处理节点,就是用于映射jdbc_type与java类型
            typeHandlerElement(root.evalNode("typeHandlers"));
            //用于解析mapper节点,后面用于解析mapper文件
            mapperElement(root.evalNode("mappers"));
        } catch (Exception e) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
    }

以上解析的结果都放于Configuration对象内,Configuration就像一个大仓库,放有所有的配置信息,解析出Configuration之后,就调用SqlSessionFactory build(Configuration config)方法,返回一个DefaultSqlSessionFactory对象。
得到了SqlSessionFactory 之后调用openSession()方法得到SqlSession对象。

下面是DefaultSqlSessionFactory部分源代码

public class DefaultSqlSessionFactory implements SqlSessionFactory {

    private final Configuration configuration;

    public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public SqlSession openSession() {
    	//直接调用openSessionFromDataSource方法,事务设置手动提交
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
    }
	//调用该方法返回SqlSession对象
    private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;
        try {
        	//获取环境对象
            final Environment environment = configuration.getEnvironment();
            //获取TransactionFactory ,用于生产Transaction对象,默认是JdbcTransaction
            final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
            //得到事务对象,默认实现是JdbcTransaction,隔离级别采用默认的
            tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
            //获取Executor 对象,用于实际执行SQL语句
            final Executor executor = configuration.newExecutor(tx, execType);
            return new DefaultSqlSession(configuration, executor, autoCommit);
        } catch (Exception e) {
            closeTransaction(tx); // may have fetched a connection so lets call close()
            throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
        } finally {
            ErrorContext.instance().reset();
        }
    }
    private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
        if (environment == null || environment.getTransactionFactory() == null) {
            return new ManagedTransactionFactory();
        }
        return environment.getTransactionFactory();
    }
}

得到SqlSession对象之后,然后就可以获取Mapper动态代理对象,用于调用接口中相应的方法。
下面看看SqlSession.getMapper()方法具体实现:

public <T> T getMapper(Class<T> type) {
    return configuration.<T>getMapper(type, this);
  }

getMapper()把具体实现抛给了Configuration,再来看Configuration类中的getMapper方法

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }

Configuration把问题抛给了MapperRegistry,再来看MapperRegistry类中的getMapper方法

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
	//得到Mapper代理工厂,用于生产Mapper代理对象
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
        throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
    	//返回Mapper对象,具体实现看下面
        return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
        throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
}

下面再看MapperProxyFactory类

public class MapperProxyFactory<T> {

    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();

    public MapperProxyFactory(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    public Class<T> getMapperInterface() {
        return mapperInterface;
    }

    public Map<Method, MapperMethod> getMethodCache() {
        return methodCache;
    }

    @SuppressWarnings("unchecked")
    //使用JDK动态代理生成代理对象
    protected T newInstance(MapperProxy<T> mapperProxy) {
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
    }
	//调用该方法,实际执行的是上面方法
    public T newInstance(SqlSession sqlSession) {
    	//生成MapperProxy对象
        final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
        return newInstance(mapperProxy);
    }
}

下面看看MapperProxy对象部分实现:

//实现了InvocationHandler对象
public class MapperProxy<T> implements InvocationHandler, Serializable {

    private static final long serialVersionUID = -6424540398559729838L;
    private final SqlSession sqlSession;
    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethod> methodCache;

    public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
        this.methodCache = methodCache;
    }

    @Override
    //通过代理调用Dao接口方法后,会执行该方法
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            } else if (isDefaultMethod(method)) {
                return invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
        }
        //该方法会创建MapperMethod 对象
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        return mapperMethod.execute(sqlSession, args);
    }

    private MapperMethod cachedMapperMethod(Method method) {
        MapperMethod mapperMethod = methodCache.get(method);
        if (mapperMethod == null) {
            mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
            methodCache.put(method, mapperMethod);
        }
        return mapperMethod;
    }
}

下面是MapperMethod 类中的execute方法的实现

//可以看到该方法的执行是首先判断是执行CURD哪种操作,绕后就调用SqlSession中具体的方法执行
public Object execute(SqlSession sqlSession, Object[] args) {
        Object result;
        switch (command.getType()) {
            case INSERT: {
                Object param = method.convertArgsToSqlCommandParam(args);
                result = rowCountResult(sqlSession.insert(command.getName(), param));
                break;
            }
            case UPDATE: {
                Object param = method.convertArgsToSqlCommandParam(args);
                result = rowCountResult(sqlSession.update(command.getName(), param));
                break;
            }
            case DELETE: {
                Object param = method.convertArgsToSqlCommandParam(args);
                result = rowCountResult(sqlSession.delete(command.getName(), param));
                break;
            }
            case SELECT:
                if (method.returnsVoid() && method.hasResultHandler()) {
                    executeWithResultHandler(sqlSession, args);
                    result = null;
                } else if (method.returnsMany()) {
                    result = executeForMany(sqlSession, args);
                } else if (method.returnsMap()) {
                    result = executeForMap(sqlSession, args);
                } else if (method.returnsCursor()) {
                    result = executeForCursor(sqlSession, args);
                } else {
                    Object param = method.convertArgsToSqlCommandParam(args);
                    result = sqlSession.selectOne(command.getName(), param);
                }
                break;
            case FLUSH:
                result = sqlSession.flushStatements();
                break;
            default:
                throw new BindingException("Unknown execution method for: " + command.getName());
        }
        if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
            throw new BindingException("Mapper method '" + command.getName()
                    + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
        }
        return result;
    }

接下来看SqlSession中的某个查询方法

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
        MappedStatement ms = configuration.getMappedStatement(statement);
        //最终交给executor执行
        return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
        throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
        ErrorContext.instance().reset();
    }
}

经过一层层的调用,会来到SimpleExecutor的doQuery方法

public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Statement stmt = null;
        try {
            Configuration configuration = ms.getConfiguration();
            StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
            stmt = prepareStatement(handler, ms.getStatementLog());
            //StatementHandler封装了Statement, 让 StatementHandler 去处理
            return handler.<E>query(stmt, resultHandler);
        } finally {
            closeStatement(stmt);
        }
    }

看到这里,mybatis的最基本的流程应该大致了解了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值