简介
SqlSession接口提供了查询,插入,更新,删除方法,Mybatis中所有的数据库交互都由SqlSession来完成。SqlSession 对象完全包含以数据库为背景的所有执行 SQL 操作的方法,它的底层封装了 JDBC 连接,可以用 SqlSession 实例来直接执行已映射的 SQL 语句。每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不能被共享,也是线程不安全的,使用完成后需要及时关闭
SqlSession的创建
SqlSession 的创建需要借助于 SqlSessionFactory,SqlSessionFactory 是 Mybatis 的关键对象,每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心。创建代码示例如下:
SqlSession sqlSession = sqlSessionFactory.openSession();
SqlSessionFactory创建SqlSession的列表如下:
public interface SqlSessionFactory {
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 getConfiguration();
}
直接分析默认使用的openSession()无参的方法,在DefaultSqlSessionFactory#openSession中,可以看到其调用的是openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit)
这个方法:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
//1. 从配置中获取对应的环境
final Environment environment = configuration.getEnvironment();
// 获取事务工厂
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//2. 根据数据源,事物隔离级别,是否自动提交创建事物管理器
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//3. 创建Executor执行器
final Executor executor = configuration.newExecutor(tx, execType);
//4. 返回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();
}
}
该方法首先是根据事物工厂以及数据源工厂构造了一个事物管理器,之后创建一个Executor对象存入到DefaultSqlSession,后续DefaultSqlSession执行的时候便是通过Executor对象实例执行的。
Executor执行器的创建
Executor 接口定义了数据库操作的基本方法,其中 query*() 方法、update() 方法、flushStatement() 方法是执行 SQL 语句的基础方法,commit() 方法、rollback() 方法以及 getTransaction() 方法与事务的提交/回滚相关。Executor的是根据传入的executorType来创建的,有多个 Executor 接口的实现类,如下图所示:
创建Executor的代码如下:
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
// 是否开启二级缓存
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
// 插件的扩展
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
SqlSession的执行流程
创建Mapper
在获取到SqlSession后,根据SqlSession获取对应Mapper
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
SqlSession创建的Mapper是从Configuration中的mapperRegistry获取的:
// Configuration#getMapper
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
// MapperRegistry#getMapper
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
根据MapperProxyFactory创建代理对象
MapperProxyFactory 的核心功能就是创建 Mapper 接口的代理对象,在 MapperRegistry 中会依赖 MapperProxyFactory 的 newInstance() 方法创建代理对象,底层则是通过 JDK 动态代理的方式生成代理对象的
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
Mapper的执行
由上面的流程可知,通过SqlSession获取到的mapper对象是一个代理对象,其执行逻辑在InvocationHandler的invoke方法中
MapperProxy
通过分析 MapperProxyFactory 这个工厂类,我们可以清晰地看到MapperProxy 是生成 Mapper 接口代理对象的关键,它实现了 InvocationHandler 接口,接下来分析一下它的invoke方法
public class MapperProxy<T> implements InvocationHandler, Serializable {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 如果是Object的方法,直接调用
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
// 尝试从methodCache缓存中查询方法对应的MapperMethodInvoker
MapperMethodInvoker invoker = methodCache.get(method);
if (invoker != null) {
return invoker;
}
// 如果方法在缓存中没有对应的MapperMethodInvoker,则进行创建
return methodCache.computeIfAbsent(method, m -> {
if (m.isDefault()) {//针对默认方法
try {
// 这里根据JDK版本的不同,获取方法对应的MethodHandle的方式也有所不同
// 在JDK 8中使用的是lookupConstructor字段,而在JDK 9中使用的是
// privateLookupInMethod字段。获取到MethodHandle之后,会使用
// DefaultMethodInvoker进行封装
if (privateLookupInMethod == null) {
return new DefaultMethodInvoker(getMethodHandleJava8(method));
} else {
return new DefaultMethodInvoker(getMethodHandleJava9(method));
}
} catch (IllegalAccessException | InstantiationException | InvocationTargetException
| NoSuchMethodException e) {
throw new RuntimeException(e);
}
} else {
// 对于其他方法,会创建MapperMethod并使用PlainMethodInvoker封装
return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
});
} catch (RuntimeException re) {
Throwable cause = re.getCause();
throw cause == null ? re : cause;
}
}
}
MapperMethod
通过对 MapperProxy的invoke分析我们知道,MapperMethod 是最终执行 SQL 语句的地方,同时也记录了 Mapper 接口中的对应方法,其核心字段也围绕这两方面的内容展开
MapperMethod 的第一个核心字段是 command(SqlCommand 类型),其中维护了关联 SQL 语句的相关信息。在 MapperMethod$SqlCommand 这个内部类中,通过 name 字段记录了关联 SQL 语句的唯一标识,通过 type 字段(SqlCommandType 类型)维护了 SQL 语句的操作类型,这里 SQL 语句的操作类型分为 INSERT、UPDATE、DELETE、SELECT 和 FLUSH 五种。
SqlCommand
public static class SqlCommand {
//记录了关联 SQL 语句的唯一标识
private final String name;
//维护了 SQL 语句的操作类型
private final SqlCommandType type;
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
获取Mapper接口中对应的方法名称
final String methodName = method.getName();
// 获取Mapper接口的类型
final Class<?> declaringClass = method.getDeclaringClass();
// 将Mapper接口名称和方法名称拼接起来作为SQL语句唯一标识,
// 到Configuration这个全局配置对象中查找SQL语句
// MappedStatement对象就是Mapper.xml配置文件中一条SQL语句解析之后得到的对象
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
configuration);
if (ms == null) {
// 针对@Flush注解的处理
if (method.getAnnotation(Flush.class) != null) {
name = null;
type = SqlCommandType.FLUSH;
} else {
throw new BindingException("Invalid bound statement (not found): "
+ mapperInterface.getName() + "." + methodName);
}
} else {
// 记录SQL语句唯一标识
name = ms.getId();
// 记录SQL语句的操作类型
type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
}
public String getName() {
return name;
}
public SqlCommandType getType() {
return type;
}
private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
Class<?> declaringClass, Configuration configuration) {
//将Mapper接口名称和方法名称拼接起来作为SQL语句唯一标识
String statementId = mapperInterface.getName() + "." + methodName;
if (configuration.hasStatement(statementId)) {
return configuration.getMappedStatement(statementId);
// 如果方法就定义在当前接口中,则证明没有对应的SQL语句,返回null
} else if (mapperInterface.equals(declaringClass)) {
return null;
}
// 如果当前检查的Mapper接口(mapperInterface)中不是定义该方法的接口(declaringClass),
// 则会从mapperInterface开始,沿着继承关系向上查找递归每个接口,
// 查找该方法对应的MappedStatement对象
for (Class<?> superInterface : mapperInterface.getInterfaces()) {
if (declaringClass.isAssignableFrom(superInterface)) {
MappedStatement ms = resolveMappedStatement(superInterface, methodName,
declaringClass, configuration);
if (ms != null) {
return ms;
}
}
}
return null;
}
}
MethodSignature
MapperMethod 的第二个核心字段是 method 字段(MethodSignature 类型),其中维护了 Mapper 接口中方法的相关信息。
public static class MethodSignature {
//表示方法返回值是否为 Collection 集合或数组、Map 集合、void、Cursor、Optional 类型
private final boolean returnsMany;
private final boolean returnsMap;
private final boolean returnsVoid;
private final boolean returnsCursor;
private final boolean returnsOptional;
//方法返回值的具体类型
private final Class<?> returnType;
//如果方法的返回值为 Map 集合,则通过 mapKey 字段记录了作为 key 的列名。mapKey 字段的值是通过解析方法上的 @MapKey 注解得到的。
private final String mapKey;
//记录了 Mapper 接口方法的参数列表中 ResultHandler 类型参数的位置。
private final Integer resultHandlerIndex;
//记录了 Mapper 接口方法的参数列表中 RowBounds 类型参数的位置。
private final Integer rowBoundsIndex;
//用来解析方法参数列表的工具类
private final ParamNameResolver paramNameResolver;
public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
if (resolvedReturnType instanceof Class<?>) {
this.returnType = (Class<?>) resolvedReturnType;
} else if (resolvedReturnType instanceof ParameterizedType) {
this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
} else {
this.returnType = method.getReturnType();
}
// 根据返回值类型,初始化returnsVoid、returnsMany、returnsCursor、
// returnsMap、returnsOptional这五个与方法返回值类型相关的字段
this.returnsVoid = void.class.equals(this.returnType);
this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
this.returnsCursor = Cursor.class.equals(this.returnType);
this.returnsOptional = Optional.class.equals(this.returnType);
// 如果返回值为Map类型,则从方法的@MapKey注解中获取Map中为key的字段名称
this.mapKey = getMapKey(method);
this.returnsMap = this.mapKey != null;
// 解析方法中RowBounds类型参数以及ResultHandler类型参数的下标索引位置,
// 初始化rowBoundsIndex和resultHandlerIndex字段
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
// 创建ParamNameResolver工具对象,在创建ParamNameResolver对象的时候,
// 会解析方法的参数列表信息
this.paramNameResolver = new ParamNameResolver(configuration, method);
}
}
MapperMethod#execute
MapperMethod实例拥有了SqlCommand,MethodSignature对象之后,就可以开始执行Sql的逻辑了;根据SqlCommand不同的执行类型以及MethodSignature返回值类型调用SqlSession执行Sql语句获取到执行结果
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);
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
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#selectList
在MapperMethod中调用SqlSession的查询方法selectList,在SqlSession中最终是通过Executor执行sql逻辑,代码如下:
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();
}
}
总结
- SqlSession是通过SqlSessionFactory创建的,封装了Execcutor对象
- 获取Mapper接口是通过动态代理完成的,使用MapperProxyFactory创建Mapper代理对象,执行的时候通过MapperMethod中封装的SqlCommand获取绑定的sql,通过MethodSignature确定接口的返回值,最终统一调用Execcutor的逻辑完成整个数据库的操作