MyBatis update、insert、delete方法调用 - litter-fish/ReadSource GitHub Wiki
// SqlSessionFactoryBuilder
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
// 通过XMLConfigBuilder解析配置文件,解析的配置相关信息都会封装为一个Configuration对象
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
// 创建DefaultSessionFactory对象
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.
}
}
}
// org/apache/ibatis/session/defaults/DefaultSqlSessionFactory.java
public SqlSession openSession() {
// 使用默认的执行器类型(默认是SIMPLE),默认隔离级别,非自动提交 委托给openSessionFromDataSource方法
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
获取session通过数据源
获取session通过数据源时序图:
// org/apache/ibatis/session/defaults/DefaultSqlSessionFactory.java
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 通过Confuguration对象去获取Mybatis相关配置信息, Environment对象包含了数据源和事务的配置
final Environment environment = configuration.getEnvironment();
// 获取事务管理器, 支持从数据源或者直接获取
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 从数据源创建一个事务, 同样,数据源必须配置, mybatis内置了JNDI、POOLED、UNPOOLED三种类型的数据源,
// 其中POOLED对应的实现为org.apache.ibatis.datasource.pooled.PooledDataSource,它是mybatis自带实现的一个同步、
// 线程安全的数据库连接池 一般在生产中,我们会使用dbcp或者druid连接池
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();
}
}
获取事务管理器
// org/apache/ibatis/session/defaults/DefaultSqlSessionFactory.java
private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
// 如果没有配置environment或者environment的事务管理器为空,则使用受管的事务管理器
// 除非什么都没有配置,否则在mybatis-config里面,至少要配置一个environment,此时事务工厂不允许为空
// 对于jdbc类型的事务管理器,则返回JdbcTransactionFactory,其内部操作mybatis的JdbcTransaction实现(采用了Facade模式),后者对jdbc连接操作
if (environment == null || environment.getTransactionFactory() == null) {
return new ManagedTransactionFactory();
}
return environment.getTransactionFactory();
}
使用事务管理器创建事务
// org/apache/ibatis/transaction/jdbc/JdbcTransactionFactory.java
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
return new JdbcTransaction(ds, level, autoCommit);
}
创建执行器实例
// org/apache/ibatis/session/Configuration.java
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) { // 预处理语句(prepared statements)
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
// 如果启用了全局的缓存(一级缓存),则使用缓存执行器。
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
// 植入插件
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
获取getMapper的时序图:
public class DefaultSqlSession implements SqlSession {
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
}
public class Configuration {
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
}
public class MapperRegistry {
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// knownMappers 由解析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 {
// 调用 MapperProxyFactory 实例化 T
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
}
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")
protected T newInstance(MapperProxy<T> mapperProxy) {
// 创建 DAO 接口的 Proxy, mapperInterface 代表我们自己定义的DAO 接口
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
sql的执行过程时序图:
getMapper 最终通过 MapperProxyFactory 创建代理对象,而 MapperProxy 实现了InvocationHandler接口,因此当调用Proxy.newProxyInstance方法创建代理是会调用 MapperProxy 的 invoke 方法。
// org/apache/ibatis/binding/MapperProxy.java
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 if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
// 获取 MapperMethod
final MapperMethod mapperMethod = cachedMapperMethod(method);
// 执行sql语句
return mapperMethod.execute(sqlSession, args);
}
首先获取 MapperMethod,接着执行sql语句
获取 MapperMethod
// org/apache/ibatis/binding/MapperProxy.java
private MapperMethod cachedMapperMethod(Method method) {
// 根据方法从缓存中获取
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
// 不存在则创建一个MapperMethod
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
// 放入缓存
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
创建 MapperMethod 对象
// org/apache/ibatis/binding/MapperMethod.java
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
// SqlCommand一个内部类 封装了SQL标签的类型 insert update delete select
this.command = new SqlCommand(config, mapperInterface, method);
// MethodSignature一个内部类 封装了方法的参数信息 返回类型信息等
this.method = new MethodSignature(config, mapperInterface, method);
}
创建 SqlCommand 对象,封装了具体执行的动作
// org/apache/ibatis/binding/MapperMethod.SqlCommand
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
final String methodName = method.getName();
final Class<?> declaringClass = method.getDeclaringClass();
// 解析 MappedStatement
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
configuration);
// 检测当前方法是否有对应的 MappedStatement
if (ms == null) {
// 检测当前方法是否有 @Flush 注解
if (method.getAnnotation(Flush.class) != null) {
// 设置 name 和 type
name = null;
type = SqlCommandType.FLUSH;
} else {
/*
* 若 ms == null 且方法无 @Flush 注解,此时抛出异常。
*/
throw new BindingException("Invalid bound statement (not found): "
+ mapperInterface.getName() + "." + methodName);
}
} else {
// 设置 name 和 type 变量
name = ms.getId();
type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
}
创建 MethodSignature 对象, 封装方法签名,封装了接口当中方法的 参数类型 返回值类型 等信息对象
// org/apache/ibatis/binding/MapperMethod.MethodSignature
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();
}
// 检测返回值类型是否是 void、集合或数组、Cursor、Map 等
this.returnsVoid = void.class.equals(this.returnType);
this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
this.returnsCursor = Cursor.class.equals(this.returnType);
// 解析 @MapKey 注解,获取注解内容
this.mapKey = getMapKey(method);
this.returnsMap = this.mapKey != null;
/*
* 获取 RowBounds 参数在参数列表中的位置,如果参数列表中
* 包含多个 RowBounds 参数,此方法会抛出异常
*/
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
// 获取 ResultHandler 参数在参数列表中的位置
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
// 解析参数列表
this.paramNameResolver = new ParamNameResolver(configuration, method);
}
创建解析参数对象
// org/apache/ibatis/reflection/ParamNameResolver.java
public ParamNameResolver(Configuration config, Method method) {
// 获取参数类型列表
final Class<?>[] paramTypes = method.getParameterTypes();
// 获取参数注解
final Annotation[][] paramAnnotations = method.getParameterAnnotations();
final SortedMap<Integer, String> map = new TreeMap<Integer, String>();
int paramCount = paramAnnotations.length;
// get names from @Param annotations
for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
// 检测当前的参数类型是否为 RowBounds 或 ResultHandler
if (isSpecialParameter(paramTypes[paramIndex])) {
// skip special parameters
continue;
}
String name = null;
for (Annotation annotation : paramAnnotations[paramIndex]) {
if (annotation instanceof Param) {
hasParamAnnotation = true;
// 获取 @Param 注解内容
name = ((Param) annotation).value();
break;
}
}
// name 为空,表明未给参数配置 @Param 注解
if (name == null) {
// @Param was not specified.
// 检测是否设置了 useActualParamName 全局配置
if (config.isUseActualParamName()) {
/*
* 通过反射获取参数名称。此种方式要求 JDK 版本为 1.8+,
* 且要求编译时加入 -parameters 参数,否则获取到的参数名
* 仍然是 arg1, arg2, ..., argN
*/
name = getActualParamName(method, paramIndex);
}
if (name == null) {
// use the parameter index as the name ("0", "1", ...)
// gcode issue #71
/*
* 使用 map.size() 返回值作为名称,思考一下为什么不这样写:
* name = String.valueOf(paramIndex);
* 因为如果参数列表中包含 RowBounds 或 ResultHandler,这两个参数
* 会被忽略掉,这样将导致名称不连续。
*
* 比如参数列表 (int p1, int p2, RowBounds rb, int p3)
* - 期望得到名称列表为 ["0", "1", "2"]
* - 实际得到名称列表为 ["0", "1", "3"]
*/
name = String.valueOf(map.size());
}
}
// 存储 paramIndex 到 name 的映射
map.put(paramIndex, name);
}
names = Collections.unmodifiableSortedMap(map);
}
执行 execute 方法
public class MapperMethod {
// 根据 SQL 类型执行相应的数据库操作
public Object execute(SqlSession sqlSession, Object[] args) {
Object result; // 返回结果
switch (command.getType()) {
// INSERT操作
case INSERT: {
// 对用户传入的参数进行转换
Object param = method.convertArgsToSqlCommandParam(args);
// 执行插入操作,rowCountResult 方法用于处理返回值
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()) {
/*
* 如果方法返回值为 void,但参数列表中包含 ResultHandler,表明使用者
* 想通过 ResultHandler 的方式获取查询结果,而非通过返回值获取结果
*/
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
// 执行查询操作,并返回多个结果
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
// 执行查询操作,并将结果封装在 Map 中返回
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
// 执行查询操作,并返回一个 Cursor 对象
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());
}
// 如果方法的返回值为基本类型,而返回值却为 null,此种情况下应抛出异常
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;
}
}
传入参数转换
// org/apache/ibatis/binding/MapperMethod.java
public Object convertArgsToSqlCommandParam(Object[] args) {
return paramNameResolver.getNamedParams(args);
}
委托paramNameResolver进程参数转换
// org/apache/ibatis/reflection/ParamNameResolver.java
public Object getNamedParams(Object[] args) {
final int paramCount = names.size();
if (args == null || paramCount == 0) {
// 如果没参数
return null;
} else if (!hasParamAnnotation && paramCount == 1) {
/*
* 如果方法参数列表无 @Param 注解,且仅有一个非特别参数,则返回该参数的值。
* 比如如下方法:
* List findList(RowBounds rb, String name)
* names 如下:
* names = {1 : "0"}
* 此种情况下,返回 args[names.firstKey()],即 args[1] -> name
*/
return args[names.firstKey()];
} else {
// 否则,返回一个ParamMap,修改参数名,参数名就是其位置
final Map<String, Object> param = new ParamMap<Object>();
int i = 0;
for (Map.Entry<Integer, String> entry : names.entrySet()) {
// 添加 <参数名, 参数值> 键值对到 param 中
param.put(entry.getValue(), args[entry.getKey()]);
// genericParamName = param + index。比如 param1, param2, ... paramN
final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
/*
* 检测 names 中是否包含 genericParamName,什么情况下会包含?答案如下:
*
* 使用者显式将参数名称配置为 param1,即 @Param("param1")
*/
if (!names.containsValue(genericParamName)) {
// 添加 <param*, value> 到 param 中
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}
调用DefaultSqlSession的 insert、update等
// 调用sqlSession的insert方法, 对返回值的类型进行了一些检查
result = rowCountResult(sqlSession.insert(command.getName(), param));
处理返回结果
// org/apache/ibatis/binding/MapperMethod.java
private Object rowCountResult(int rowCount) {
final Object result;
/*
* 这里的 method 类型为 MethodSignature,即方法签名,包含了某个方法较为详细的信息。Cache
* 某个方法指的是 Mapper 或 Dao 接口中的方法,比如上一节示例 AuthorDao 中的
* insertMany 方法。
*/
if (method.returnsVoid()) {
// 方法返回类型为 void,则不用返回结果,这里将结果置空
result = null;
} else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {
// 方法返回类型为 Integer 或 int,直接赋值返回即可
result = rowCount;
} else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {
// 如果返回值类型为 Long 或者 long,这里强转一下即可
result = (long)rowCount;
} else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {
// 方法返回类型为布尔类型,若 rowCount > 0,则返回 ture,否则返回 false
result = rowCount > 0;
} else {
throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: " + method.getReturnType());
}
return result;
}
方式一 SqlSession.getMapper实现最终都会调用方式二的逻辑:
update执行的时序图:
插入、更新以及删除操作最终都调用了 SqlSession 接口中的方法。这三个方法返回值均是受影响行数,是一个整型值
// org/apache/ibatis/session/defaults/DefaultSqlSession.java
public int update(String statement, Object parameter) {
try {
dirty = true;
// 获取 MappedStatement
MappedStatement ms = configuration.getMappedStatement(statement);
// 调用 Executor 的 update 方法
return executor.update(ms, wrapCollection(parameter));
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
首先获取 MappedStatement,接着调用 Executor 的 update 方法
获取 MappedStatement
// org/apache/ibatis/session/Configuration.java
public MappedStatement getMappedStatement(String id) {
return this.getMappedStatement(id, true);
}
public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
if (validateIncompleteStatements) {
buildAllStatements();
}
return mappedStatements.get(id);
}
调用 Executor 的 update 方法
public int update(MappedStatement ms, Object parameterObject) throws SQLException {
// 刷新二级缓存
flushCacheIfRequired(ms);
// 执行具体构建类的逻辑
return delegate.update(ms, parameterObject);
}
首先刷新二级缓存,接着执行具体构建类的逻辑。
刷新二级缓存
// org/apache/ibatis/executor/CachingExecutor.java
private void flushCacheIfRequired(MappedStatement ms) {
Cache cache = ms.getCache();
if (cache != null && ms.isFlushCacheRequired()) {
// 委托事务缓存管理器进行缓存清理
tcm.clear(cache);
}
}
委托事务缓存管理器进行缓存清理
// org/apache/ibatis/cache/TransactionalCacheManager.java
public void clear(Cache cache) {
// 获取 TransactionalCache 对象,并调用该对象的 clear 方法
getTransactionalCache(cache).clear();
}
首先获取 TransactionalCache,然后调用其clear 方法
获取 TransactionalCache
// org/apache/ibatis/cache/TransactionalCacheManager.java
private TransactionalCache getTransactionalCache(Cache cache) {
// 从映射表中获取 TransactionalCache
TransactionalCache txCache = transactionalCaches.get(cache);
if (txCache == null) {
// TransactionalCache 也是一种装饰类,为 Cache 增加事务功能
txCache = new TransactionalCache(cache);
transactionalCaches.put(cache, txCache);
}
return txCache;
}
事务缓存的一些重要属性
// TransactionalCache
private boolean clearOnCommit;
// 在事务被提交前,所有从数据库中查询的结果将缓存在此集合中
private final Map<Object, Object> entriesToAddOnCommit;
// 在事务被提交前,当缓存未命中时,CacheKey 将会被存储在此集合中
private final Set<Object> entriesMissedInCache;
清空缓存
// org/apache/ibatis/cache/decorators/TransactionalCache.java
public void clear() {
clearOnCommit = true;
// 清空 entriesToAddOnCommit,但不清空 delegate 缓存
entriesToAddOnCommit.clear();
}
Executor 的 update 方法总结: 首先委托缓存装饰类TransactionalCache,清空缓存数据,接着执行具体构建类的update方法。
执行具体构建类的逻辑
// org/apache/ibatis/executor/BaseExecutor.java
public int update(MappedStatement ms, Object parameter) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
// 刷新一级缓存
clearLocalCache();
// 调用具体执行器实现的doUpdate方法
return doUpdate(ms, parameter);
}
首先刷新一级缓存,接着调用具体执行器实现的doUpdate方法。
刷新一级缓存
// org/apache/ibatis/executor/BaseExecutor.java
public void clearLocalCache() {
if (!closed) {
// 清空 缓存查询结果
localCache.clear();
// 清空 存储过程类型的查询参数
localOutputParameterCache.clear();
}
}
上面的两个缓存都是通过PerpetualCache持有的
public class PerpetualCache implements Cache {
private Map<Object, Object> cache = new HashMap<Object, Object>();
}
从上可知对于一级缓存直接使用了HashMap进行存储的。
接着看简单的执行器执行更新操作
// org/apache/ibatis/executor/ReuseExecutor.java
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Configuration configuration = ms.getConfiguration();
// 创建 StatementHandler
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
// 创建 Statement
Statement stmt = prepareStatement(handler, ms.getStatementLog());
// 调用 StatementHandler 的 update 方法
return handler.update(stmt);
}
首先创建 StatementHandler,然后创建Statement,最后调用 StatementHandler 的 update 方法。
创建 StatementHandler
// org/apache/ibatis/session/Configuration.java
public StatementHandler newStatementHandler(Executor executor,
MappedStatement mappedStatement, Object parameterObject,
RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 创建具有路由功能的 StatementHandler
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
// 应用插件到 StatementHandler 上
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
StatementHandler的适配器设计模式:
创建RoutingStatementHandler对象适配器类
// org/apache/ibatis/executor/statement/RoutingStatementHandler.java
public RoutingStatementHandler(Executor executor, MappedStatement ms,
Object parameter, RowBounds rowBounds,
ResultHandler resultHandler, BoundSql boundSql) {
// 根据 StatementType 创建不同的 StatementHandler
switch (ms.getStatementType()) {
case STATEMENT:
// 简单SQL的处理
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
// 预编译SQL的处理
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
// 存储过程的处理
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
为StatementHandler加入插件的执行逻辑
// org/apache/ibatis/plugin/InterceptorChain.java
public Object pluginAll(Object target) {
// 遍历拦截器集合
for (Interceptor interceptor : interceptors) {
// 调用拦截器的 plugin 方法植入相应的插件逻辑
target = interceptor.plugin(target);
}
return target;
}
创建 Statement
对于 ReuseExecutor 可以通过缓存Statement以便再次使用。
// org/apache/ibatis/executor/ReuseExecutor.java
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
// 获取
BoundSql boundSql = handler.getBoundSql();
String sql = boundSql.getSql();
// 先取缓存
if (hasStatementFor(sql)) {
stmt = getStatement(sql);
applyTransactionTimeout(stmt);
} else {
// 获取JDBC连接
Connection connection = getConnection(statementLog);
// 调用语句处理器的prepare方法
stmt = handler.prepare(connection, transaction.getTimeout());
// 缓存 Statement
putStatement(sql, stmt);
}
// 为 Statement 设置 IN 参数
handler.parameterize(stmt);
return stmt;
}
首先获取JDBC连接、接着调用语句处理器的prepare方法、最后为 Statement 设置 IN 参数。
获取JDBC连接
// org/apache/ibatis/executor/BaseExecutor.java
protected Connection getConnection(Log statementLog) throws SQLException {
// 通过jdbc获取数据库连接
Connection connection = transaction.getConnection();
if (statementLog.isDebugEnabled()) {
return ConnectionLogger.newInstance(connection, statementLog, queryStack);
} else {
return connection;
}
}
通过jdbc获取数据库连接
// org/apache/ibatis/transaction/jdbc/JdbcTransaction.java
public Connection getConnection() throws SQLException {
if (connection == null) {
openConnection();
}
return connection;
}
protected void openConnection() throws SQLException {
if (log.isDebugEnabled()) {
log.debug("Opening JDBC Connection");
}
// 通过连接池获取连接
connection = dataSource.getConnection();
if (level != null) {
// 设置隔离级别
connection.setTransactionIsolation(level.getLevel());
}
// 设置是否自动提交
setDesiredAutoCommit(autoCommmit);
}
调用语句处理器的prepare方法
// org/apache/ibatis/executor/statement/BaseStatementHandler.java
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
// 首先实例化语句,因为PREPARE和非PREPARE不同,所以留给具体子类实现
statement = instantiateStatement(connection);
// 设置语句超时时间
setStatementTimeout(statement, transactionTimeout);
// 设置fetch大小
setFetchSize(statement);
return statement;
} catch (SQLException e) {
closeStatement(statement);
throw e;
} catch (Exception e) {
closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}
首先实例化Statement,接着设置语句超时时间与fetch大小。
实例化Statement
// org/apache/ibatis/executor/statement/SimpleStatementHandler.java
protected Statement instantiateStatement(Connection connection) throws SQLException {
// 如果存在resultSetType设置
if (mappedStatement.getResultSetType() != null) {
return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
return connection.createStatement();
}
}
设置Statement超时时间
// org/apache/ibatis/executor/statement/BaseStatementHandler.java
protected void setStatementTimeout(Statement stmt, Integer transactionTimeout) throws SQLException {
Integer queryTimeout = null;
// 获取mapper语句设置的默认超时时间
if (mappedStatement.getTimeout() != null) {
queryTimeout = mappedStatement.getTimeout();
// 如果mapper语句未设置则尝试从configuration配置中获取
} else if (configuration.getDefaultStatementTimeout() != null) {
queryTimeout = configuration.getDefaultStatementTimeout();
}
// 设置超时时间
if (queryTimeout != null) {
stmt.setQueryTimeout(queryTimeout);
}
// 处理事务中设置的超时时间
StatementUtil.applyTransactionTimeout(stmt, queryTimeout, transactionTimeout);
}
处理事务中设置的超时时间
// org/apache/ibatis/executor/statement/StatementUtil.java
public static void applyTransactionTimeout(Statement statement, Integer queryTimeout, Integer transactionTimeout) throws SQLException {
// 未设置事务超时时间,直接return
if (transactionTimeout == null){
return;
}
Integer timeToLiveOfQuery = null;
// 如果未设置查询超时时间,则取事务实例中设置的超时时间
if (queryTimeout == null || queryTimeout == 0) {
timeToLiveOfQuery = transactionTimeout;
// 如果事务实例中设置的超时时间 小于 查询设置的超时时间则以 事务实例中设置的超时时间的时间为准。
} else if (transactionTimeout < queryTimeout) {
timeToLiveOfQuery = transactionTimeout;
}
if (timeToLiveOfQuery != null) {
// 重新设置statement超时时间
statement.setQueryTimeout(timeToLiveOfQuery);
}
}
设置fetch大小
protected void setFetchSize(Statement stmt) throws SQLException {
// 同样先取mapper中设置的fetchSize,如果设置了就以此为值进行设置
Integer fetchSize = mappedStatement.getFetchSize();
if (fetchSize != null) {
stmt.setFetchSize(fetchSize);
return;
}
// 如果mapper中未进行设置,这尝试从configuration中取默认的fetchSize
Integer defaultFetchSize = configuration.getDefaultFetchSize();
if (defaultFetchSize != null) {
stmt.setFetchSize(defaultFetchSize);
}
}
为 Statement 设置 IN 参数
对于SimpleStatementHandler parameterize 方法是一个空实现, 查看PreparedStatementHandler的实现
// org/apache/ibatis/executor/statement/PreparedStatementHandler.java
public void parameterize(Statement statement) throws SQLException {
// 通过参数处理器 ParameterHandler 设置运行时参数到 PreparedStatement 中
parameterHandler.setParameters((PreparedStatement) statement);
}
通过参数处理器 ParameterHandler 设置运行时参数到 PreparedStatement 中
// org/apache/ibatis/scripting/defaults/DefaultParameterHandler.java
public void setParameters(PreparedStatement ps) {
/*
* 从 BoundSql 中获取 ParameterMapping 列表,每个 ParameterMapping
* 与原始 SQL 中的 #{xxx} 占位符一一对应
*/
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
// 检测参数类型,排除掉 mode 为 OUT 类型的 parameterMapping
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
// 获取属性名
String propertyName = parameterMapping.getProperty();
// 检测 BoundSql 的 additionalParameters 是否包含 propertyName
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
// 检测运行时参数是否有相应的类型解析器
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
/*
* 若运行时参数的类型有相应的类型处理器 TypeHandler,则将
* parameterObject 设为当前属性的值。
*/
value = parameterObject;
} else {
// 为用户传入的参数 parameterObject 创建元信息对象
MetaObject metaObject = configuration.newMetaObject(parameterObject);
// 从用户传入的参数中获取 propertyName 对应的值
value = metaObject.getValue(propertyName);
}
/////// 以上代码用于获取 #{xxx} 占位符属性所对应的运行时参数
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
// 此处 jdbcType = JdbcType.OTHER
jdbcType = configuration.getJdbcTypeForNull();
}
/////// 以上取 #{xxx} 占位符属性对应的 TypeHandler
try {
// jdbc下标从1开始,由具体的类型处理器进行参数的设置, 对于每个jdbcType, mybatis都提供了一个对应的Handler,
// 具体可参考上文TypeHandler详解, 其内部调用的是PrepareStatement.setXXX进行设置。
// 由类型处理器 typeHandler 向 ParameterHandler 设置参数
////// 通过 TypeHandler 将运行时参数值设置到 PreparedStatement 中
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
} catch (SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
首先获取 #{xxx} 占位符属性所对应的运行时参数, 接着获取 #{xxx} 占位符属性对应的 TypeHandler, 然后通过 TypeHandler 将运行时参数值设置到 PreparedStatement 中。
经过前面的准备操作,可以调用 StatementHandler 的 update 方法执行 SQL
// org/apache/ibatis/executor/statement/PreparedStatementHandler.java
public int update(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// 执行 SQL
ps.execute();
// 返回受影响行数
int rows = ps.getUpdateCount();
// 获取用户传入的参数值,参数值类型可能是普通的实体类,也可能是 Map
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
// 获取自增主键的值,并将值填入到参数对象中
keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
return rows;
}
KeyGenerator 的实现类
Jdbc3KeyGenerator 用于获取插入数据后的自增主键数值。 SelectKeyGenerator 某些数据库不支持自增主键,需要手动填写主键字段,此时需要借助此获取主键值。 NoKeyGenerator,这是一个空实现
KeyGenerator接口
// before key generator 主要用于oracle等使用序列机制的ID生成方式
void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter);
// after key generator 主要用于mysql等使用自增机制的ID生成方式
void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter);
使用 Jdbc3KeyGenerator 获取自增主键
public class Jdbc3KeyGenerator implements KeyGenerator {
public static final Jdbc3KeyGenerator INSTANCE = new Jdbc3KeyGenerator();
@Override
public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
// do nothing
}
@Override
public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
processBatch(ms, stmt, getParameters(parameter));
}
public void processBatch(MappedStatement ms, Statement stmt, Collection<Object> parameters) {
ResultSet rs = null;
try {
rs = stmt.getGeneratedKeys();
final Configuration configuration = ms.getConfiguration();
final TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
// 获取主键字段
final String[] keyProperties = ms.getKeyProperties();
// 获取结果集 ResultSet 的元数据
final ResultSetMetaData rsmd = rs.getMetaData();
TypeHandler<?>[] typeHandlers = null;
// ResultSet 中数据的列数要大于等于主键的数量
if (keyProperties != null && rsmd.getColumnCount() >= keyProperties.length) {
// 遍历 parameters
for (Object parameter : parameters) {
// there should be one row for each statement (also one for each parameter)
// 对于批量插入,ResultSet 会返回多行数据
if (!rs.next()) {
break;
}
final MetaObject metaParam = configuration.newMetaObject(parameter);
if (typeHandlers == null) {
// 为每个主键属性获取 TypeHandler
typeHandlers = getTypeHandlers(typeHandlerRegistry, metaParam, keyProperties, rsmd);
}
// 填充结果到运行时参数中
populateKeys(rs, metaParam, keyProperties, typeHandlers);
}
}
} catch (Exception e) {
throw new ExecutorException("Error getting generated key or setting result to parameter object. Cause: " + e, e);
} finally {
if (rs != null) {
try {
rs.close();
} catch (Exception e) {
// ignore
}
}
}
}
private Collection<Object> getParameters(Object parameter) {
Collection<Object> parameters = null;
if (parameter instanceof Collection) {
parameters = (Collection) parameter;
} else if (parameter instanceof Map) {
/*
* 如果 parameter 是 Map 类型,则从其中提取指定 key 对应的值。
* 至于 Map 中为什么会出现 collection/list/array 等键。大家
* 可以参考 DefaultSqlSession 的 wrapCollection 方法
*/
Map parameterMap = (Map) parameter;
if (parameterMap.containsKey("collection")) {
parameters = (Collection) parameterMap.get("collection");
} else if (parameterMap.containsKey("list")) {
parameters = (List) parameterMap.get("list");
} else if (parameterMap.containsKey("array")) {
parameters = Arrays.asList((Object[]) parameterMap.get("array"));
}
}
if (parameters == null) {
parameters = new ArrayList<Object>();
// 将普通的对象添加到 parameters 中
parameters.add(parameter);
}
return parameters;
}
private TypeHandler<?>[] getTypeHandlers(TypeHandlerRegistry typeHandlerRegistry, MetaObject metaParam,
String[] keyProperties, ResultSetMetaData rsmd) throws SQLException {
TypeHandler<?>[] typeHandlers = new TypeHandler<?>[keyProperties.length];
for (int i = 0; i < keyProperties.length; i++) {
if (metaParam.hasSetter(keyProperties[i])) {
TypeHandler<?> th;
try {
Class<?> keyPropertyType = metaParam.getSetterType(keyProperties[i]);
th = typeHandlerRegistry.getTypeHandler(keyPropertyType, JdbcType.forCode(rsmd.getColumnType(i + 1)));
} catch (BindingException e) {
th = null;
}
typeHandlers[i] = th;
}
}
return typeHandlers;
}
private void populateKeys(ResultSet rs, MetaObject metaParam, String[] keyProperties, TypeHandler<?>[] typeHandlers) throws SQLException {
// 遍历 keyProperties
for (int i = 0; i < keyProperties.length; i++) {
// 获取主键属性
String property = keyProperties[i];
TypeHandler<?> th = typeHandlers[i];
if (th != null) {
// 从 ResultSet 中获取某列的值
Object value = th.getResult(rs, i + 1);
// 设置结果值到运行时参数中
metaParam.setValue(property, value);
}
}
}
}
事务提交时序图:
事务提交
// org/apache/ibatis/session/defaults/DefaultSqlSession.java
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();
}
}
缓存的提交操作
// org/apache/ibatis/executor/CachingExecutor.java
public void commit(boolean required) throws SQLException {
// 清除一级缓存,执行事务提交
delegate.commit(required);
// 将存放在TransactionCache中的数据对象提交到PerpetualCache中
tcm.commit();
}
清除一级缓存,执行事务提交
// org/apache/ibatis/executor/BaseExecutor.java
public void commit(boolean required) throws SQLException {
if (closed) {
throw new ExecutorException("Cannot commit, transaction is already closed");
}
// 清除本地缓存
clearLocalCache();
// 刷新Statements
flushStatements();
if (required) {
// 提交事务
transaction.commit();
}
}
清除本地缓存
public void clearLocalCache() {
if (!closed) {
// 清空 缓存查询结果
localCache.clear();
// 清空 存储过程类型的查询参数
localOutputParameterCache.clear();
}
}
刷新Statements
// org/apache/ibatis/executor/BaseExecutor.java
public List<BatchResult> flushStatements() throws SQLException {
return flushStatements(false);
}
public List<BatchResult> flushStatements(boolean isRollBack) throws SQLException {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
return doFlushStatements(isRollBack);
}
// org/apache/ibatis/executor/ReuseExecutor.java
public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
for (Statement stmt : statementMap.values()) {
// 关闭statement
closeStatement(stmt);
}
// 清空statement 缓存
statementMap.clear();
return Collections.emptyList();
}
调用底层连接提交事务
// org/apache/ibatis/transaction/jdbc/JdbcTransaction.java
public void commit() throws SQLException {
if (connection != null && !connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Committing JDBC Connection [" + connection + "]");
}
connection.commit();
}
}
事务提交后,将存放在TransactionCache中的数据对象提交到PerpetualCache中
// org/apache/ibatis/cache/TransactionalCacheManager.java
public void commit() {
// 把涉及到的TransactionCache都进行处理:提交到二级缓存,并清空数据
for (TransactionalCache txCache : transactionalCaches.values()) {
txCache.commit();
}
}
二级缓存的处理
// org/apache/ibatis/cache/decorators/TransactionalCache.java
public void commit() {
// 根据 clearOnCommit 的值决定是否清空 delegate
if (clearOnCommit) {
// 清空二级缓存
delegate.clear();
}
// 刷新未缓存的结果到 delegate 缓存中
flushPendingEntries();
// 重置 entriesToAddOnCommit 和 entriesMissedInCache
reset();
}
刷新未缓存的结果到 delegate 缓存中
// org/apache/ibatis/cache/decorators/TransactionalCache.java
private void flushPendingEntries() {
// 把之前存放到 entriesToAddOnCommit 中的数据提交到二级缓存中,具体的说是存放到PerpetualCache类的一个HashMap中
for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
// 将 entriesToAddOnCommit 中的内容转存到 delegate 中
delegate.putObject(entry.getKey(), entry.getValue());
}
for (Object entry : entriesMissedInCache) {
if (!entriesToAddOnCommit.containsKey(entry)) {
// 存入空值
delegate.putObject(entry, null);
}
}
}
重置属性
// org/apache/ibatis/cache/decorators/TransactionalCache.java
private void reset() {
clearOnCommit = false;
// 清空集合
entriesToAddOnCommit.clear();
entriesMissedInCache.clear();
}
回滚时序图:
回滚操作
// org/apache/ibatis/session/defaults/DefaultSqlSession.java
public void rollback(boolean force) {
try {
executor.rollback(isCommitOrRollbackRequired(force));
dirty = false;
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error rolling back transaction. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
缓存的回滚操作
// org/apache/ibatis/executor/CachingExecutor.java
public void rollback(boolean required) throws SQLException {
try {
// 清空一级缓存,执行事务回滚
delegate.rollback(required);
} finally {
if (required) {
tcm.rollback();
}
}
}
清空一级缓存,执行事务回滚
// org/apache/ibatis/executor/BaseExecutor.java
public void rollback(boolean required) throws SQLException {
if (!closed) {
try {
// 清空一级缓存
clearLocalCache();
// 刷新Statement
flushStatements(true);
} finally {
if (required) {
// 执行事务回滚
transaction.rollback();
}
}
}
}
执行事务回滚
// org/apache/ibatis/transaction/jdbc/JdbcTransaction.java
public void rollback() throws SQLException {
if (connection != null && !connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Rolling back JDBC Connection [" + connection + "]");
}
connection.rollback();
}
}