Mybatis的使用只需要创建一个DAO层接口,配合相应Mapper.xml的sql文件以及配置即可使用。那么到底是什么力量让没有手动实现的接口完成了对数据库的链接、查询、数据组装等工作了,这篇文章主要就是沿着Mybatis的源码分析一下这个问题。
从Mybatis的入门开始,最简单Mybatis初始化代码需要一个mybatis-config.xml的配置文件来构建一个叫做SqlSessionFactory的对象,构建代码如下:
String resource = "mybatis-config.xml"; //配置文件路径
InputStream inputStream = Resources.getResourceAsStream(resource); //文件转为流
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //build构建
获取到了SqlSessionFactory便可以从中拿到SqlSession的对象对mapper接口中的方法在数据库的层面进行操作了,可见这个SqlSessionFactoryBuilder().build(inputStream)就是我们这次分析的重点。
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(Reader reader) {
return build(reader, null, null);
}
public SqlSessionFactory build(Reader reader, String environment) {
return build(reader, environment, null);
}
public SqlSessionFactory build(Reader reader, Properties properties) {
return build(reader, null, properties);
}
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
public SqlSessionFactory build(InputStream inputStream) { //入口1
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment) {
return build(inputStream, environment, null);
}
public SqlSessionFactory build(InputStream inputStream, Properties properties) {
return build(inputStream, null, properties);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { //重载2
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
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) { //重载3 最终将config也就是配置文件内容作为构造参数创建了一个DefaultSqlSessionFactory的对象
return new DefaultSqlSessionFactory(config);
}
}
SqlSessionFactoryBuilder的build方法经过多次重载,对配置文件内容进行分析转化为Configuration对象,最终创建了一个叫做DefaultSqlSessionFactory的对象,接下来继续看:
public class DefaultSqlSessionFactory implements SqlSessionFactory { //注意。DefaultSqlSessionFactory是SqlSessionFactory的实现类
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) { //配置类构造
this.configuration = configuration;
}
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
@Override
public SqlSession openSession(boolean autoCommit) {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
}
@Override
public SqlSession openSession(ExecutorType execType) {
return openSessionFromDataSource(execType, null, false);
}
@Override
public SqlSession openSession(TransactionIsolationLevel level) {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), level, false);
}
@Override
public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) {
return openSessionFromDataSource(execType, level, false);
}
@Override
public SqlSession openSession(ExecutorType execType, boolean autoCommit) {
return openSessionFromDataSource(execType, null, autoCommit);
}
@Override
public SqlSession openSession(Connection connection) {
return openSessionFromConnection(configuration.getDefaultExecutorType(), connection);
}
@Override
public SqlSession openSession(ExecutorType execType, Connection connection) {
return openSessionFromConnection(execType, connection);
}
@Override
public Configuration getConfiguration() {
return configuration;
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
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 SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
try {
boolean autoCommit;
try {
autoCommit = connection.getAutoCommit();
} catch (SQLException e) {
// Failover to true, as most poor drivers
// or databases won't support transactions
autoCommit = true;
}
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
final Transaction tx = transactionFactory.newTransaction(connection);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
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();
}
private void closeTransaction(Transaction tx) {
if (tx != null) {
try {
tx.close();
} catch (SQLException ignore) {
// Intentionally ignore. Prefer previous error.
}
}
}
}
这个类的方法比较多,我单独贴一下类结构的图解:
之前的Mybatis运用简单案例中,获取到了SqlSessionFactory之后,就可以通过openSession的方法获取到一个SqlSession来操作mapper中的语句,那我们再通过上面的一些源码分析也可以知道,实际调用的openSession方法也是DefaultSqlSessionFactory类的方法,因为重载的次数比较多,我们挑一个来仔细看看:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null; //方法接收一个执行器,一个事务隔离级别,一个是否自动提交作为参数
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit); //所有的重载方法最后都是这样的new一个
} 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();
}
}
所有的重载方法openSession最后都是返回了一个DefaultSqlSession的对象,只不过在执行器、事务级别上面可能有些区别:
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) { //需要一个配置类、一个执行器以及是否自动提交作为构造参数
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}
DefaultSqlSession对象方法比较多,贴一下OutLine不贴源码了:
下面还有一点,但是通过outLine我们也可以知道,DefaultSqlSession就是根据mapper中不同的语句对数据库进行操作的类。
至此,我们就可以通过:
String resource = "mybatis-config.xml"; //配置文件路径
InputStream inputStream = Resources.getResourceAsStream(resource); //文件转为流
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //build构建
SqlSession session = sqlSessionFactory.openSession();
Object student = session.selectOne("com.gao.runtest.dao.mapper.StudentMapper.getOne","s1005");
这样的方法对数据库进行操作了。