一、简介
SqlSession 是 MyBatis 核心接口之一,也是 MyBatis 接口层的主要组成部分,对外提供 MyBatis 常用 API。 MyBatis 提供了两个 SqlSession 接口的实现,下图所示,这里使用了工 厂方法模式,其中开发人员最常用的是 DefaultSq!Session 实现。
SqLSessionFactory 负责创建 SqlSession 对象,其中只包含了多个 openSession()方法的重载, 可以通过其参数指定事务的隔离级别、底层使用 Executor 的类型以及是否自动提交事务等方面 的配置。 SqLSessionFactory 接口的定义比较简单,代码就不再展示了。
在SqlSession 中定义了常用的数据库操作以及事务的相关操作,为了方便用户使用,每种 类型的操作都提供了多种重载。
二 、源码分析
2.1 创建SqlSession的流程
代码如下:
@Test
public void testManyParamQuery(){
String resource = "mybatis-config.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
// 1.读取mybatis配置文件创SqlSessionFactory
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 2.获取sqlSession 方法级别的
SqlSession sqlSession = sqlSessionFactory.openSession();
3.获取对应mapper
TUserMapper mapper = sqlSession.getMapper(TUserMapper.class);
String email = "qq.com";
Byte sex = 1;
// 第三种方式用对象
EmailSexBean esb = new EmailSexBean();
esb.setEmail(email);
esb.setSex(sex);
List<TUser> list3 = mapper.selectByEmailAndSex3(esb);
System.out.println("javabean:"+list3.size());
}
2.2 SqlSessionFactory
SqlSessionFactory 是创建 SqlSession对象工厂接口,其中默认实现类是DefaultSqlSessionFactory。
DefaultSqlSessionFactory 中提供的所有 openSession()方法重载都是基于上述两种方式创建 DefaultSqlSession 对象的, 这里不再赘述。
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
DefaultSqlSessionFactory 主要提供了两种创建 SqlSession 对象的方式。
一种方式是通过数据源获取数据库连接, 并创建 Executor 对象以及 DefaultSqlSession 对象, 该方式的具体实现如下:
private SqlSession openSessionFromDataSource(ExecutorType execType,
TransactionIsolationLevel level,
boolean autoCommit) {
Transaction tx = null;
try {
//获取mybatis配置文件中的environment对象
final Environment environment = configuration.getEnvironment();
//获取的TransactionFactory对象
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//创建事务对象
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//根据配置创建Executor 对象
final Executor executor = configuration.newExecutor(tx, execType);
//创建DefaultSqlSession
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();
}
}
另一种方式是用户提供数据库连接对象, DefaultSqlSessionFactory 会使用该数据库连接对象创建 Executor 对象以及 DefaultSq!Session 对象,具体实现如下:
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;
}
//获取mybatis配置文件中的environment对象
final Environment environment = configuration.getEnvironment();
//从environment获取transactionFactory对象
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//创建事务对象
final Transaction tx = transactionFactory.newTransaction(connection);
//根据配置创建executor
final Executor executor = configuration.newExecutor(tx, execType);
//创建DefaultSqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
2.3 DefaultSqlSession
SqlSession 对象实现 DefaultSqlSession 类型,它也是单独使用 MyBatis 进行开发时最常用的 SqI Session 接口实现。 DefaultSqlSession 中 核心字段的含义如下:
private final Configuration configuration;//configuration对象,全局唯一
private final Executor executor;//底层依赖的Executor对象
private final boolean autoCommit;//是否自动提交
private boolean dirty;//当前缓存是否有脏数据
// 为防止用户忘记关闭已打开的游标对象,会通过 cursorList 字段记录由该 SqlSession 对象生成的游标
//对象,在 DefaultSqlSession.close ()方法中会统一关闭这些游标对象
private List<Cursor<?>> cursorList;
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}
在 DefaultSqlSession 中使用到了策略模式, DefaultSqlSession 扮演了 Context 的角色,而将 所有数据库相关的操作全部封装到 Executor 接口实现中,并通过 executor 字段选择不同的 Executor 实现。
DefaultSqlSession 中实现了 SqISession 接口中定义的方法, 并且为每种数据库操作提供了多 个重载。 图 3-55 为 select()方法、 selectOne()方法、 selectList()方法以及 selectMap()方法的各个 重载方法之间的调用关系。
上述重载方法最终都是通过调用 Executor.query(MappedStatement, Object, RowBounds, ResultHandler)方法实现数据库查询操作的,但各自对结果对象进行 了 相应的调整 ,例如 :
- selectOne()方法是从结果对象集合中获取了第一个元素返回;
- selectMap()方法会将 List 类型的结 果对象集合转换成 Map 类型集合返回:
- select()方法是将结果对象集合交 由用户 指定的 ResultHandler 对象处理,且没有返回值:
- selectList()方法则是直接返回结果对象集合。
所有select方法最终都会掉线下面两个方法
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//从configuration中获取要执行的sql语句的配置信息
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();
}
}
@Override
public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
executor.query(ms, wrapCollection(parameter), rowBounds, handler);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
DefaultSqlSession.insert()方法、 update()方法、 delete()方法也有多个重载,它们最后都是通 过调用的DefaultSqlSession.update(String, Object)方法实现的,该重载首先会将dirty字段置为true, 然后再通过 Executor.update()方法完成数据库修改操作。
@Override
public int update(String statement, Object parameter) {
try {
dirty = true;
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.update(ms, wrapCollection(parameter));
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
DefaultSqlSession.commit()方法、 rollback()方法以及 close()方法都会调用 Executor 中相应的 方法,其中就会涉及清空缓存的操作(具体实现请读者参考 Executor 小节〉, 之后就会将 dirty 字段设置为 false。
上述的 dirty 宇段主要在 isCommitOrRollbackRequired()方法中, 与 autoCommit 字段以及用 户传入的 force 参数共同决定是否提交/回滚事务,具体实现如下所示。 该方法的返回值将作为 Executor.commit()方法和 rollback()方法的参数。
@Override
public void commit(boolean force) {
try {
executor.commit(isCommitOrRollbackRequired(force));
dirty = false;
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error committing transaction. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
private boolean isCommitOrRollbackRequired(boolean force) {
return (!autoCommit && dirty) || force;
}
所以归根结底,SqlSession的所有查询接口最后都归结位Exector的方法调用。