Mapper方法执行流程
在不结合spring的时候,一般都会通过类似如下的方式来执行Mapper的指定方法
try (Reader reader = Resources.getResourceAsReader("org/apache/ibatis/submitted/collectionparameters/mybatis-config.xml")) {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession sqlSession = sqlSessionFactory.openSession();
Mapper mapper = sqlSession.getMapper(Mapper.class);
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
List<User> users = mapper.getUsersFromList(list);
Assertions.assertEquals(2, users.size());
}
获取SqlSession
SqlSession的功能主要有下面三个:
- 执行命令
- 获取mapper
- 管理事务
这里首先看下如何获取SqlSession
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 从全局配置文件中取出Environment
final Environment environment = configuration.getEnvironment();
// 从Environment中取出TransactionFactory
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 使用TransactionFactory创建一个事务
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 根据executor的类型,创建对应类型的Executor实例
final Executor executor = configuration.newExecutor(tx, execType);
// 使用前面创建的Executor,来创建一个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();
}
}
获取Transaction
上面的TransactionFactory对应mybatis配置文件中下面这个部分的配置
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="" value="" />
</transactionManager>
<dataSource type="UNPOOLED">
<property name="driver" value="org.hsqldb.jdbcDriver" />
<property name="url" value="jdbc:hsqldb:mem:collectionparameters" />
<property name="username" value="sa" />
</dataSource>
</environment>
</environments>
在mybatis解析阶段,会根据transactionManager标签的type属性来创建对应的TransactionFactory
这里看下实现类JdbcTransactionFactory的newTransaction
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
return new JdbcTransaction(ds, level, autoCommit);
}
这里就是简单的使用数据源,事务隔离级别和是否自动提交创建了一个JdbcTransaction
获取Executor
Executor是SqlSession的核心属性,用来执行sql
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
// 这里获取Executor的类型,如果指定了exectorType,那么使用指定的
// 如果没有指定,那么使用默认的,defaultExectorType默认是SIMPLE,可以手动设置
// 如果defaultExecutorType没有设置,那么使用SIMPLE
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
// 创建相应类型的Executor
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);
}
// 这里之后介绍interceptor的时候再说
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
获取Mapper
下面看下DefaultSqlSession的getMapper方法
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 在解析Mapper接口的时候,会将当前Mapper接口的MapperProxyFactory加入到knownMapper中
// 具体可以看MapperRegistry的addMapper
// 这里根据Mapper类型,从knownMapper中取出对应的MapperProxyFactory
// 从名字也可以看出,是Mapper接口代理的工厂,主要用来生成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 {
// 使用工厂创建代理类的实例
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
下面看下如何生成代理类的实例以及这层代理做了哪些事情
public T newInstance(SqlSession sqlSession) {
// 这里的mapperInterface就是对应的Mapper接口
// methodCache,则是一个方法缓存,Method -> MapperMethodInvoker
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);
}
主要的代理逻辑都在MapperProxy中,MapperProxy实现了InvocationHandler,因此mybatis使用的是jdk中的动态代理
public class MapperProxy<T> implements InvocationHandler, Serializable {
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethodInvoker> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethodInvoker> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
// 略
}
Mapper方法执行
因为我们获取到的实际是Mapper的代理类,而代理类的逻辑主要在MapperProxy中,所以下面看下MapperProxy的invoke方法
@Override
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 {
// 这里调用的是Mapper接口中声明的方法
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
获取MapperMethodInvoker
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
// A workaround for https://bugs.openjdk.java.net/browse/JDK-8161372
// It should be removed once the fix is backported to Java 8 or
// MyBatis drops Java 8 support. See gh-1929
// 首先从缓存中来尝试获取当前执行的方法对应的MapperMethodInvoker
MapperMethodInvoker invoker = methodCache.get(method);
if (invoker != null) {
return invoker;
}
// 下面是计算MapperMethodInvoker的逻辑
// 得到MapperMethodInvoker之后,会将其放入缓存
return methodCache.computeIfAbsent(method, m -> {
if (m.isDefault()) {
try {
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;
}
}
执行MapperMethodInvoker
下面看下PlainMethodInvoker的invoke
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
// 可以看到就是简单地将参数透传给了MapperMethod
return mapperMethod.execute(sqlSession, args);
}
在看MapperMethod的execute方法之前,首先看下这个类,这个类比较重要,执行逻辑都在这个方法中
MapperMethod
构造函数
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
可以看到构造函数中主要做的就是两件事:创建SqlCommand和创建MethodSignature
解析SqlCommand
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
final String methodName = method.getName();
final Class<?> declaringClass = method.getDeclaringClass();
// 从全局配置中寻找当前方法对应的MappedStatement,从之前的源码我们知道,在Mapper xml文件中配置的或者是在Mapper接口的方法上使用注解配置的select update insert等最终都会转换为MappedStatement
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
configuration);
if (ms == null) {
if (method.getAnnotation(Flush.class) != null) {
name = null;
type = SqlCommandType.FLUSH;
} else {
throw new BindingException("Invalid bound statement (not found): "
+ mapperInterface.getName() + "." + methodName);
}
} else {
// 返回全局配置文件中找到的MappedStatement的sqlCommandType
name = ms.getId();
type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
}
下面看下如何从全局配置中获取当前执行方法的MappedStatement
private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
Class<?> declaringClass, Configuration configuration) {
// 首先会使用接口的名称和当前调用方法的拼接作为statement的唯一标志
String statementId = mapperInterface.getName() + "." + methodName;
// 然后从全局配置中使用唯一标志进行寻找,如果找到直接返回
if (configuration.hasStatement(statementId)) {
return configuration.getMappedStatement(statementId);
} else if (mapperInterface.equals(declaringClass)) {
// 如果没有找到,会判断当前执行的方法是否是在当前的Mapper接口中声明的,如果是,直接返回null,
return null;
}
// 最后,走到这里,代表当前执行的方法并不是在当前的Mapper接口中声明的,尝试使用当前接口的父接口的名称和当前方法的名称作为唯一标志来进行寻找
for (Class<?> superInterface : mapperInterface.getInterfaces()) {
if (declaringClass.isAssignableFrom(superInterface)) {
MappedStatement ms = resolveMappedStatement(superInterface, methodName,
declaringClass, configuration);
if (ms != null) {
return ms;
}
}
}
return null;
}
解析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();
}
// 解析其他属性
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);
// 判断方法上使用使用了@MapKey注解,如果使用了MapKey注解,那么返回该注解的值
this.mapKey = getMapKey(method);
this.returnsMap = this.mapKey != null;
// 记录RowBounds类型在方法参数中是第几个参数
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
// 记录ResultHandler类型在方法参数中是第几个参数
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
this.paramNameResolver = new ParamNameResolver(configuration, method);
}
private Integer getUniqueParamIndex(Method method, Class<?> paramType) {
Integer index = null;
// 遍历方法的入参,找到指定类型参数出现的位置
final Class<?>[] argTypes = method.getParameterTypes();
for (int i = 0; i < argTypes.length; i++) {
if (paramType.isAssignableFrom(argTypes[i])) {
if (index == null) {
index = i;
} else {
throw new BindingException(method.getName() + " cannot have multiple " + paramType.getSimpleName() + " parameters");
}
}
}
return index;
}
public ParamNameResolver(Configuration config, Method method) {
this.useActualParamName = config.isUseActualParamName();
final Class<?>[] paramTypes = method.getParameterTypes();
// 是一个二维数组,用来存放每个参数上的多个注解
final Annotation[][] paramAnnotations = method.getParameterAnnotations();
// map存放的是参数在方法中出现的顺序->解析出来的参数名称
final SortedMap<Integer, String> map = new TreeMap<>();
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]) {
// 如果使用了@Param注解,那么使用注解的值作为当前参数的名称
if (annotation instanceof Param) {
hasParamAnnotation = true;
name = ((Param) annotation).value();
break;
}
}
if (name == null) {
// @Param was not specified.
// 如果在全局配置中设置了useActualParamName,那么会将arg0 arg1等作为名称
if (useActualParamName) {
name = getActualParamName(method, paramIndex);
}
// 如果既没有使用@Param注解来指定当前参数的名称,也没有开启useActualParamName
// 那么会使用当前有效解析了的参数的总个数作为名称
if (name == null) {
// use the parameter index as the name ("0", "1", ...)
// gcode issue #71
name = String.valueOf(map.size());
}
}
map.put(paramIndex, name);
}
names = Collections.unmodifiableSortedMap(map);
}
这里总结下参数名称的解析过程:
- 首先过滤掉特殊类型的参数,RowBound和ResultHandler
- 如果当前参数上使用了@Param注解,那么使用注解上的值作为名称
- 配置中开启了useActualParamName,那么使用参数的名称(arg0 arg1)作为名称
- 使用当前已经解析的有效参数的个数作为名称
execute
下面重点看下MapperMethod的execute方法
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;
}
参数解析
首先看下convertArgsToSqlCommandParam
public Object convertArgsToSqlCommandParam(Object[] args) {
return paramNameResolver.getNamedParams(args);
}
public Object getNamedParams(Object[] args) {
final int paramCount = names.size();
if (args == null || paramCount == 0) {
// 如果方法本身没有入参,或者是除了特殊参数之外没有其他参数,那么直接返回null
return null;
} else if (!hasParamAnnotation && paramCount == 1) {
// 如果没有使用@Param注解,并且除了特殊参数之外的参数只有一个
// names中存放的是name -> index的映射,获取有效参数的值
Object value = args[names.firstKey()];
return wrapToMapIfCollection(value, useActualParamName ? names.get(0) : null);
} else {
// 当前方法使用了@Param注解或者有效参数不止一个
// 这里最终会返回一个Map,其中包含了参数名称和参数值之间的映射
final Map<String, Object> param = new ParamMap<>();
int i = 0;
for (Map.Entry<Integer, String> entry : names.entrySet()) {
param.put(entry.getValue(), args[entry.getKey()]);
// add generic param names (param1, param2, ...)
// 另外添加param1 param2这样的参数
final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);
// ensure not to overwrite parameter named with @Param
if (!names.containsValue(genericParamName)) {
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}
下面看下对没有使用@Param注解,并且方法的参数只有一个有效参数时的处理
public static Object wrapToMapIfCollection(Object object, String actualParamName) {
// 判断参数是否是集合类
if (object instanceof Collection) {
// 创建一个ParamMap对象
ParamMap<Object> map = new ParamMap<>();
map.put("collection", object);
// 如果参数是List的子类,额外多存放一个名为list的参数
if (object instanceof List) {
map.put("list", object);
}
// 如果通过useActualParamName指定了使用方法的实际名称
Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
return map;
} else if (object != null && object.getClass().isArray()) {
// 如果参数是数组
ParamMap<Object> map = new ParamMap<>();
map.put("array", object);
Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
return map;
}
return object;
}
总结下这里对参数的处理:
- 如果方法没有入参,或者没有有效的参数(除了RowBounds和ResultHandler之外的其他参数),那么解析后的参数为null
- 如果方法只有一个有效参数,但是没有使用@Param,那么会判断该参数的类型是否是集合类型或者数组类型,如果不是,直接返回该参数;如果是会创建一个ParamMap,并向其中添加默认的参数,比如collection,list,array等,并返回
- 如果方法有多个参数或者使用了@Param注解,那么会创建一个ParamMap,并将之前解析方法参数列表得到的参数映射关系添加进去,另外会添加param1 param2这种参数
更新类操作
更新类操作包括insert update delete
@Override
public int insert(String statement, Object parameter) {
return update(statement, parameter);
}
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();
}
}
public int delete(String statement, Object parameter) {
return update(statement, parameter);
}
从源码可以看到,不管是insert,update还是delete最终执行的都是update
清空本地缓存
因为SimpleExecutor继承了BaseExecutor,所以首先看下BaseExecutor的update方法
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.");
}
// 在执行update之前,首先会清空本地的缓存
clearLocalCache();
return doUpdate(ms, parameter);
}
public void clearLocalCache() {
if (!closed) {
localCache.clear();
localOutputParameterCache.clear();
}
}
执行update
下面看下SimpleExecutor的doUpdate
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
} finally {
closeStatement(stmt);
}
}
创建StatementHandler
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 根据MappedStatement的statementType来创建不同的实现类
// 一般是PREPARED
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
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());
}
}
下面重点看下PreparedStatementHandler,其构造函数只是简单的对属性进行了赋值,并调用了父类BaseStatementHandler的构造函数,下面看下BaseStatementHandler的构造函数
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
this.configuration = mappedStatement.getConfiguration();
this.executor = executor;
this.mappedStatement = mappedStatement;
this.rowBounds = rowBounds;
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.objectFactory = configuration.getObjectFactory();
// 从上面的源码知道,当执行更新类操作时,boundSql和resultHandler都是null
if (boundSql == null) { // issue #435, get the key before calculating the statement
// 这里会使用创建MappedStatement时的KeyGenerator来进行查询,然后将查询结果设置到参数中
generateKeys(parameterObject);
// 获取最终解析之后的结果,包括已经使用?替换后的sql,参数对象,参数映射parameterMapping
boundSql = mappedStatement.getBoundSql(parameterObject);
}
this.boundSql = boundSql;
// 分别创建ParameterHandler和resultSetHandler
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
下面看下generateKeys做了什么,这里看下SelectKeyGenerator
rotected void generateKeys(Object parameter) {
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
ErrorContext.instance().store();
keyGenerator.processBefore(executor, mappedStatement, null, parameter);
ErrorContext.instance().recall();
}
public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
if (executeBefore) {
processGeneratedKeys(executor, ms, parameter);
}
}
private void processGeneratedKeys(Executor executor, MappedStatement ms, Object parameter) {
try {
// 参数parameter不为null,因为查询结果最终会设置到parameter中
// keyStatement不为null,因为需要通过keyStatement来查询用来设置到parameter中的值
// keyStatement.getKeyProperties不为null,这个属性指定了参数中哪些属性需要赋值
if (parameter != null && keyStatement != null && keyStatement.getKeyProperties() != null) {
String[] keyProperties = keyStatement.getKeyProperties();
final Configuration configuration = ms.getConfiguration();
// 使用MetaObject包裹参数对象,方便对参数对象的属性进行操作
final MetaObject metaParam = configuration.newMetaObject(parameter);
// Do not close keyExecutor.
// The transaction will be closed by parent executor.
Executor keyExecutor = configuration.newExecutor(executor.getTransaction(), ExecutorType.SIMPLE);
// 执行查询
List<Object> values = keyExecutor.query(keyStatement, parameter, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
// 查询结果个数只能是一个
if (values.size() == 0) {
throw new ExecutorException("SelectKey returned no data.");
} else if (values.size() > 1) {
throw new ExecutorException("SelectKey returned more than one value.");
} else {
// 使用MetaObject对查询的结果进行包裹
MetaObject metaResult = configuration.newMetaObject(values.get(0));
// 如果需要赋值的参数只有一个,那么直接调用setter
if (keyProperties.length == 1) {
if (metaResult.hasGetter(keyProperties[0])) {
setValue(metaParam, keyProperties[0], metaResult.getValue(keyProperties[0]));
} else {
// no getter for the property - maybe just a single value object
// so try that
setValue(metaParam, keyProperties[0], values.get(0));
}
} else {
// 解决有多个属性需要设置的情况
// 这里的代码比较简单,就是判断keyGenerator是否制定了keyColumns
// 如果没有指定,直接从查询结果中按照属性名称进行设值,即认为查询结果中的字段和参数中的属性的名称相同
// 如果指定了,那么就将keyProperty和keyColumn按照出现的顺序进行映射
handleMultipleProperties(keyProperties, metaParam, metaResult);
}
}
}
} catch (ExecutorException e) {
throw e;
} catch (Exception e) {
throw new ExecutorException("Error selecting key or setting result to parameter object. Cause: " + e, e);
}
}
prepareStatement
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
// 通过Transaction从数据源获取一个连接
Connection connection = getConnection(statementLog);
// 处理Jdbc3KeyGenerator,设置ResultSetType,timeout,fetchSize
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
下面看下prepare
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
// 这里会返回一个PreparedStatement,并且包含对Jdbc3KeyGenerator的处理
statement = instantiateStatement(connection);
// 设置执行的超时事件
setStatementTimeout(statement, transactionTimeout);
// 当调用ResultSet.next时,如果当前内存缓存中没有更多的结果记录能够消费,那么会从数据库中读取fetchSize条记录,然后放到内存中,通过设置这个参数,可以避免一次性读取大量记录到内存中,从而造成oom
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);
}
}
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
// 这里会判断是否使用了Jdbc3keyGenerator,可以通过useGeneratedKey来启用
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
String[] keyColumnNames = mappedStatement.getKeyColumns();
// 判断使用useGeneratedKey的时候,是否同时指定了keyColumns,
if (keyColumnNames == null) {
// 如果没有指定,那么使用主键作为当前需要自动产生值的列
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
} else {
// 如果指定了,那么使用keyColumnNames指定的列作为当前需要自动产生值的列
return connection.prepareStatement(sql, keyColumnNames);
}
// 后续当通过这个statement执行了更新操作之后,可以通过statement.getGeneratedKeys来获得本次操作产生的自增值
} else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
// 默认只能向前遍历ResultSet,并且ResultSet无法修改
return connection.prepareStatement(sql);
} else {
// 使用指定的resultSetType,指定不可以更新ResultSet
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}
下面接着看下parameterize
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
// 遍历ParameterMapping
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
// 存储过程有三种类型的参数
// in 输入参数,可以有多个
// out 输出参数,只能有一个
// inout 既是输入也是输出,只能有一个
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
// 下面从各个地方来获取当前parameterMapping包含的property的值
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())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
// 使用对应的typeHandler来对参数值进行转换,然后设置到PreparedStatement中
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException | SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
statementHandler.update
创建StatementHandler时返回的是RoutingStatementHandler,当调用update方法时,会调用底层的PreparedStatementHandler
public int update(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
int rows = ps.getUpdateCount();
// 使用KeyGenerator对参数进行后置处理,比如设置插入的自增主键等
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
// 这里和processBefore类似,就不细看了
keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
return rows;
}
查询类操作
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;
executeForMany
这里以executeForMany为例,当方法的返回值是集合或者是数组类型时,会判断方法returnMany
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
// 这个方法之前介绍过
Object param = method.convertArgsToSqlCommandParam(args);
// 判断方法的参数中是否有RowBounds类型的参数
if (method.hasRowBounds()) {
// 对返回结果数做限制
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.selectList(command.getName(), param, rowBounds);
} else {
// 对返回结果数不做限制
result = sqlSession.selectList(command.getName(), param);
}
// issue #510 Collections & arrays support
// 这里根据方法返回类型,将查询结果转换为数组或者是集合,代码比较简单
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
return result;
}
从上面的代码了解到,会判断当前执行的方法中是否有RowBound类型的参数,从而执行不同版本的selectList,下面分别看下这两个不同的版本
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
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();
}
}
可以看到最终都会执行带有参数的版本,接着看Executor的query方法
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
// 生成当前方法的缓存key
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
// 执行查询
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
下面看下生成缓存key的方法
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
CacheKey cacheKey = new CacheKey();
cacheKey.update(ms.getId());
cacheKey.update(rowBounds.getOffset());
cacheKey.update(rowBounds.getLimit());
cacheKey.update(boundSql.getSql());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
// mimic DefaultParameterHandler logic
for (ParameterMapping parameterMapping : parameterMappings) {
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
cacheKey.update(value);
}
}
if (configuration.getEnvironment() != null) {
// issue #176
cacheKey.update(configuration.getEnvironment().getId());
}
return cacheKey;
}
从上面的源码了解到,主要就是使用MappedStatement,rowbounds,sql,参数值的hash值来生成CacheKey的唯一标志
下面接着看query
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
// 对缓存的处理,这里先暂时不看缓存相关的,后面会有一篇文章来分析mybatis中的缓存
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
接着看下queryFromDatabase
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
// 前面的代码在分析更新操作时都分析过,主要看query
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
// 下面是对从数据库中查询的到的结果的处理,主要逻辑在ResultHandler,在下一篇分析ResultHandler的文章里再详细分析
return resultSetHandler.handleResultSets(ps);
}