1. SqlSessionFactory
java
/**
* 工厂类,主要负责创建SqlSession
*/
public interface SqlSessionFactory {
/**
* openSession()方法的参数用于指定生成的SqlSession的性质
* 1. autoCommit:是否自动提交
* 2. level: 事务的隔离级别
* 3. execType: 底层执行器的类型,这个之后再详细说明
*/
SqlSession openSession();
SqlSession openSession(boolean autoCommit);
SqlSession openSession(Connection connection);
SqlSession openSession(TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType);
SqlSession openSession(ExecutorType execType, boolean autoCommit);
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType, Connection connection);
/**
* 获取Configuration对象
* Configuration中包含MyBatis的各种配置,比如:是否开启驼峰命名(mapUnderscoreToCamelCase)等
*/
Configuration getConfiguration();
}
SqlSessionFactory
接口有两个实现类:SqlSessionManager
和DefaultSqlSessionFactory
1.1. SqlSessionManager
java
/**
* SqlSession管理器;底层依赖另一个SqlSessionFactory来创建SqlSession
*/
public class SqlSessionManager implements SqlSessionFactory, SqlSession {
private final SqlSessionFactory sqlSessionFactory;
@Override
public SqlSession openSession() {
return sqlSessionFactory.openSession();
}
// 其它重载的openSession()方法和上面的openSession()方法类似,因此省略
@Override
public Configuration getConfiguration() {
return sqlSessionFactory.getConfiguration();
}
}
1.2. DefaultSqlSessionFactory
java
/**
* 默认的SqlSessionFactory;本类可以看作是SqlSessionFactory的唯一实现
*/
public class DefaultSqlSessionFactory implements SqlSessionFactory {
/**
* 底层维护一个Configuration对象,该对象需要在构造时指定
*/
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
/**
* 获取environment底层的TransactionFactory;如果获取不到,则返回一个新的ManagedTransactionFactory
*/
private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
if (environment == null || environment.getTransactionFactory() == null) {
return new ManagedTransactionFactory();
}
return environment.getTransactionFactory();
}
/**
* 有6个openSession()方法的底层都是在调用本方法
*
* @param execType 执行器的类型;如果用户没有指定,则取默认值configuration.getDefaultExecutorType()
* @param level 事务隔离级别;如果用户没有指定,则为null,此时采用数据库的默认隔离级别
* @param autoCommit 是否自动提交;如果用户没有指定,则取默认值false
*/
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level,
boolean autoCommit) {
Transaction tx = null;
try {
// 通过Configuration获取到环境信息,并获取到其中的TransactionFactory
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 通过数据源来创建一个Transaction对象(即懒初始化的Connection对象)
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 通过Configuration来创建相应类型的执行器,并将该Transaction绑定到该执行器上
// 由于Configuration中包含Mybatis拦截器的配置,因此Configuration在创建了执行器之后,还会用拦截器对其进行包装
final Executor executor = configuration.newExecutor(tx, execType);
// 创建DefaultSqlSession,并把执行器绑定到该SqlSession上
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();
}
}
/**
* 另外2个openSession()方法底层都是在调用本方法;本方法和上面的方法类似
*/
private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
try {
// 读取JDBC连接的autoCommit属性
// 如果出现了异常,说明JDBC驱动或数据库不支持事务,此时认为autoCommit为true
boolean autoCommit;
try {
autoCommit = connection.getAutoCommit();
} catch (SQLException e) {
autoCommit = true;
}
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 直接通过用户自己指定的JDBC连接来创建对应的Transaction对象
final Transaction tx = transactionFactory.newTransaction(connection);
// 创建相应类型的执行器,再创建DefaultSqlSession,并把执行器绑定到该SqlSession上
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();
}
}
@Override
public Configuration getConfiguration() {
return configuration;
}
}
1.3. 小结
- 一个
DefaultSqlSession
底层绑定了一个Executor
- 一个
Executor
底层绑定了一个Transaction
- 而
Transaction
其实就相当于原生的JDBC连接 - 因此,一个
SqlSession
就对应了一个原生的JDBC连接
text
+------------------------------------+
| DefaultSqlSession |
| +--------------------------------+ |
| | Executor | |
| | +----------------------------+ | |
| | | Transaction | | |
| | | +------------------------+ | | |
| | | | Connection | | | |
| | | +------------------------+ | | |
| | +----------------------------+ | |
| +--------------------------------+ |
+------------------------------------+
2. SqlSession
java
/**
* 与MyBatis进行交互的主要接口,我们可以通过该接口来执行SQL命令、获取Mapper和管理事务
*/
public interface SqlSession extends Closeable {
/**
* 与CRUD相关的操作;部分参数的含义如下:
* 1. statement:select等标签的id(比如"com.xxx.UserMapper.getUser"),而不是具体的SQL语句
* 2. rowBounds:分页参数;如果指定了分页参数,则MyBatis在查询出所有数据后,会截取出属于当前页的数据
* 3. handler:结果处理器;主要负责对解析到的实体类对象进行处理,比如将其保存到List集合中
*
* 注意,selectCursor()方法返回的是结果集的游标
* 游标相当于一个迭代器,每次获取下一个元素时,MyBatis就会读取结果集的下一行数据并将其封装成实体类对象
* 因此,当查询的数据量比较大时,使用游标可以有效减少内存的占用(内存中不会出现大量的实体类对象)
* 而且,当只需要在查询结果中获取到第一个符合要求的记录时,使用游标也可以减少不必要的结果集映射操作
*
* 还要注意的是,select()方法竟然是没有返回值的
* 这是因为select()方法允许用户自己处理实体类对象,因此要求用户自己传入一个ResultHandler实例
* 由于最终的处理结果是存放在ResultHandler实例中的,且ResultHandler接口并没有定义获取最终结果的方法
* 因此,就只能让用户自己通过ResultHandler实例来获取最终的处理结果了
*/
<T> T selectOne(String statement);
<T> T selectOne(String statement, Object parameter);
<E> List<E> selectList(String statement);
<E> List<E> selectList(String statement, Object parameter);
<E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);
<K, V> Map<K, V> selectMap(String statement, String mapKey);
<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey);
<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);
<T> Cursor<T> selectCursor(String statement);
<T> Cursor<T> selectCursor(String statement, Object parameter);
<T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds);
void select(String statement, Object parameter, ResultHandler handler);
void select(String statement, ResultHandler handler);
void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);
int insert(String statement);
int insert(String statement, Object parameter);
int update(String statement);
int update(String statement, Object parameter);
int delete(String statement);
int delete(String statement, Object parameter);
/**
* 我们知道,JDBC的Statement及其子类都可以执行批处理操作,因此SqlSession自然也需要支持批处理功能
* SqlSession会将待执行executeBatch()方法的Statement放在一个集合中(注意,批处理语句只能是更新语句)
* 当调用本方法时,SqlSession会逐个调用这些Statement的executeBatch()方法,并返回相应的执行结果
* 注意,只有当SqlSession底层的执行器是BatchExecutor时,该方法才有意义;因为只有BatchExecutor才支持批处理操作
*/
List<BatchResult> flushStatements();
/**
* 提交事务、回滚事务、关闭连接
*/
void commit();
void commit(boolean force);
void rollback();
void rollback(boolean force);
void close();
/**
* 其它方法
*/
void clearCache();
Configuration getConfiguration();
<T> T getMapper(Class<T> type);
Connection getConnection();
}
SqlSession
有3个实现类:DefaultSqlSession
、SqlSessionManager
和SqlSessionTemplate
其中的
SqlSessionTemplate
可以先不管,它是在Spring和MyBatis整合时才会用到的
3. SqlSessionManager
3.1. 简单方法
java
/**
* SqlSession管理器;负责为每个线程维护它们各自的SqlSession;作用类似于ThreadLocal
*/
public class SqlSessionManager implements SqlSessionFactory, SqlSession {
/**
* 底层真正干活的SqlSessionFactory;这个前面有说过,因此这里省略与它相关的代码
*/
private final SqlSessionFactory sqlSessionFactory;
// =======================================================================================
/**
* 底层真正干活的SqlSession;注意它是个代理,并且只代理CRUD操作;代理的具体逻辑等一下再说
*/
private final SqlSession sqlSessionProxy;
/**
* 所有CRUD方法底层都是在调用sqlSessionProxy的相应方法;这里贴出selectOne()方法作为示例
*/
@Override
public <T> T selectOne(String statement) {
return sqlSessionProxy.<T>selectOne(statement);
}
// =======================================================================================
/**
* 私有的构造方法,用于初始化底层的sqlSessionFactory和sqlSessionProxy字段
* 此外,本类有多个静态的重载的newInstance()工厂方法,其底层都是在调用本构造方法,因此也省略
*/
private SqlSessionManager(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[]{SqlSession.class},
new SqlSessionInterceptor()); // 代理的增强逻辑都在SqlSessionInterceptor类中
}
// =======================================================================================
/**
* 保存各个线程各自对应的SqlSession
*/
private final ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<SqlSession>();
/**
* 为当前线程创建一个SqlSession,并将其存到ThreadLocal中
* 该方法还有其它重载的方法,功能是一样的,因此省略
*/
public void startManagedSession() {
this.localSqlSession.set(openSession());
}
/**
* 判断当前线程是否调用过startManagedSession()方法且未关闭它的SqlSession
*/
public boolean isManagedSessionStarted() {
return this.localSqlSession.get() != null;
}
@Override
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
}
}
3.2. SqlSession代理
java
public class SqlSessionManager implements SqlSessionFactory, SqlSession {
/**
* 接下来就可以来看SqlSession代理的具体增强逻辑了;再说明一下,这里只代理SqlSession的CRUD操作
*/
private class SqlSessionInterceptor implements InvocationHandler {
public SqlSessionInterceptor() {
// Prevent Synthetic Access
}
/**
* 拦截目标方法(即CRUD方法)
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 拿到当前线程对应的SqlSession
final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get();
// 如果当前线程确实开启了SqlSession,则调用该SqlSession对应的CRUD方法
if (sqlSession != null) {
try {
return method.invoke(sqlSession, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
// 否则,先为当前线程创建一个临时的SqlSession,然后调用它对应的CRUD方法并提交,最后关闭该SqlSession
} else {
final SqlSession autoSqlSession = openSession();
try {
final Object result = method.invoke(autoSqlSession, args);
autoSqlSession.commit();
return result;
} catch (Throwable t) {
autoSqlSession.rollback();
throw ExceptionUtil.unwrapThrowable(t);
} finally {
autoSqlSession.close();
}
}
}
}
/**
* 获取当前线程的SqlSession中的JDBC连接
* 注意,以下方法的实现逻辑和本方法类似,因此这里就不贴出来了:
* clearCache()、commit()、commit(force)、rollback()、rollback(force)、flushStatements()
*/
@Override
public Connection getConnection() {
final SqlSession sqlSession = localSqlSession.get();
if (sqlSession == null) {
throw new SqlSessionException("Error: Cannot get connection. No managed session is started.");
}
return sqlSession.getConnection();
}
/**
* 关闭当前线程的SqlSession,并将其从ThreadLocal中移除
*/
@Override
public void close() {
final SqlSession sqlSession = localSqlSession.get();
if (sqlSession == null) {
throw new SqlSessionException("Error: Cannot close. No managed session is started.");
}
try {
sqlSession.close();
} finally {
localSqlSession.set(null);
}
}
}
4. DefaultSqlSession
4.1. 简单方法
java
/**
* 默认的SqlSession;本类可以看作是SqlSession的唯一实现
*/
public class DefaultSqlSession implements SqlSession {
private final Configuration configuration;
private final Executor executor; // 底层的执行器
private final boolean autoCommit; // 底层的JDBC连接是否为自动提交
private boolean dirty; // 当前是否有未提交的修改语句
private List<Cursor<?>> cursorList; // 游标列表;selectCursor()方法返回的游标会被存放到该列表中
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}
public DefaultSqlSession(Configuration configuration, Executor executor) {
this(configuration, executor, false);
}
@Override
public Configuration getConfiguration() {
return configuration;
}
@Override
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
/**
* 判断是否需要提交/回滚;参数force代表是否求强制提交/回滚
* 如果强制提交/回滚,则返回true;否则,如果不是自动提交且执行了update语句,则也返回true
*/
private boolean isCommitOrRollbackRequired(boolean force) {
return (!autoCommit && dirty) || force;
}
@Override
public void commit() {
commit(false);
}
/**
* 提交;底层是在调用执行器的commit()方法
*/
@Override
public void commit(boolean force) {
try {
executor.commit(isCommitOrRollbackRequired(force));
dirty = false; // 将dirty置为false
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error committing transaction. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
@Override
public void rollback() {
rollback(false);
}
/**
* 回滚;底层是在调用执行器的rollback()方法
*/
@Override
public void rollback(boolean force) {
try {
executor.rollback(isCommitOrRollbackRequired(force));
dirty = false; // 将dirty置为false
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error rolling back transaction. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
/**
* 执行批处理语句;底层是在调用执行器的flushStatements()方法
*/
@Override
public List<BatchResult> flushStatements() {
try {
return executor.flushStatements();
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error flushing statements. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
/**
* 清除缓存;底层是在调用执行器的clearLocalCache()方法
*/
@Override
public void clearCache() {
executor.clearLocalCache();
}
/**
* 获取JDBC连接;这里返回的是执行器绑定的Transaction底层的JDBC连接
*/
@Override
public Connection getConnection() {
try {
return executor.getTransaction().getConnection();
} catch (SQLException e) {
throw ExceptionFactory.wrapException("Error getting a new connection. Cause: " + e, e);
}
}
}
4.2. CRUD操作
java
public class DefaultSqlSession implements SqlSession {
/**
* selectOne()方法和另外两个selectList()方法底层都是在调用本方法
*
* @param statement select语句的id
* @param parameter select语句的参数;如果用户未指定,则为null
* @param rowBounds 分页信息;如果用户未指定,则为RowBounds.DEFAULT(即不分页)
*/
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// 根据MappedStatement的id获取到MappedStatement,然后对用户传的参数进行包装,然后调用执行器的query()方法
MappedStatement ms = configuration.getMappedStatement(statement);
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();
}
}
/**
* 另外两个selectMap()方法底层都是在调用本方法
*/
@Override
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
// 这里也是在调用selectList()方法来进行查询
final List<? extends V> list = selectList(statement, parameter, rowBounds);
// 将查询出来的List集合封装成Map集合;这里涉及到DefaultMapResultHandler和DefaultResultContext组件,先不用管
final DefaultMapResultHandler<K, V> mapResultHandler = new DefaultMapResultHandler<K, V>(mapKey,
configuration.getObjectFactory(), configuration.getObjectWrapperFactory(),
configuration.getReflectorFactory());
final DefaultResultContext<V> context = new DefaultResultContext<V>();
for (V o : list) {
context.nextResultObject(o);
mapResultHandler.handleResult(context);
}
return mapResultHandler.getMappedResults();
}
/**
* 另外两个selectCursor()方法底层都是在调用本方法
*/
@Override
public <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds) {
try {
// 调用执行器的queryCursor()方法获取游标,并把游标保存到this.cursorList中,方便管理
MappedStatement ms = configuration.getMappedStatement(statement);
Cursor<T> cursor = executor.queryCursor(ms, wrapCollection(parameter), rowBounds);
registerCursor(cursor);
return cursor;
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
/**
* 另外两个select()方法底层都是在调用本方法
*/
@Override
public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
try {
// 调用执行器的query()方法;这里和selectList()方法的不同之处在于后者的结果处理器是Executor.NO_RESULT_HANDLER
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();
}
}
/**
* 所有更新数据库的方法(增删改)底层都是在调用本方法
*
* @param statement 更新语句的id
* @param parameter 更新语句的参数;如果用户未指定,则为null
*/
@Override
public int update(String statement, Object parameter) {
try {
// 将dirty置为true,然后调用执行器的update()方法
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();
}
}
/**
* 关闭SqlSession;底层会调用执行器的close()方法,并且将注册的游标也关闭掉
*/
@Override
public void close() {
try {
executor.close(isCommitOrRollbackRequired(false));
closeCursors();
dirty = false;
} finally {
ErrorContext.instance().reset();
}
}
/**
* 对集合/数组类型的参数进行封装,这是因为执行器无法直接处理集合/数组类型的参数
*/
private Object wrapCollection(final Object object) {
// 如果是集合类型,则以"collection"作为key,集合本身作为value存入Map中,然后返回该Map
// 特别地,如果同时是List类型,则还会以"list"作为key,集合本身作为value存入Map中
// StrictMap继承自HashMap,其get()方法会先判断键是否存在,不存在则抛异常;否则调用父类的get()方法
if (object instanceof Collection) {
StrictMap<Object> map = new StrictMap<>();
map.put("collection", object);
if (object instanceof List) {
map.put("list", object);
}
return map;
// 如果是数组类型,则以"array"作为key,数组本身作为value存入Map中,然后返回该Map
} else if (object != null && object.getClass().isArray()) {
StrictMap<Object> map = new StrictMap<>();
map.put("array", object);
return map;
}
// 否则,说明object为null,或者是简单类型,或者是实体类型,或者本身已经是Map类型了,此时无需包装
return object;
}
}
4.3. 小结
DefaultSqlSession
中的方法基本上都是在调用Executor
的相应方法- 因此,
SqlSession
其实就是在对Executor
进行封装,方便用户使用