一、简单说一下mapper初始化
mybatis在应用启动的时候,会默认扫描指定路径的mapper,然后生成代理类。入口在@MapperScan注解上,有个@Import(MapperScannerRegistrar.class)。我们重点看下MapperScannerRegistrar类,它会注册mapper的BeanDef,这里就不介绍了。重点介绍mapper接口封装后的类MapperFactoryBean,它的getObject就是咱们的mapper之后的代理类。如下:
protected T newInstance(MapperProxy mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
发现最终通过jdk动态代理生成实现类,注意这里的sqlSession是传进来的。这个sqlSession并不是真正sql执行的session,它只是一个session代理,初始化阶段并不会创建sqlSession。这里下节就会介绍。我们继续mapper代理类的初始化,看下MapperProxy类:
public class MapperProxy implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class mapperInterface;
private final Map methodCache;
public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
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);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
上面只截取了代码片段。这里看到MapperProxy实现了InvocationHandler,说明以后mapper的每次sql调用都会调用到这里的invoke方法。启动工程,抓取mapper的代理类的实现:
public final class $Proxy159 extends Proxy implements JournalLineMapper {
private static Method m1;
private static Method m7;
private static Method m6;
private static Method m2;
private static Method m5;
private static Method m3;
private static Method m4;
private static Method m8;
private static Method m9;
private static Method m0;
public $Proxy159(InvocationHandler var1) {
super(var1);
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m7 = Class.forName("com.xxxx.query.mapper.JournalLineMapper").getMethod("getJournalLinePOBySearchTxt", Class.forName("java.lang.Long"), Class.forName("java.lang.String"), Class.forName("java.sql.Date"), Class.forName("java.sql.Date"), Class.forName("java.lang.Long"), Class.forName("java.lang.Integer"), Class.forName("org.apache.ibatis.session.RowBounds"));
m6 = Class.forName("com.xxxx.query.mapper.JournalLineMapper").getMethod("sequenceJournal", Class.forName("java.lang.Long"), Class.forName("java.util.List"), Class.forName("java.math.BigDecimal"), Class.forName("java.math.BigDecimal"), Class.forName("java.lang.String"), Class.forName("java.lang.String"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m5 = Class.forName("com.xxxx.query.mapper.JournalLineMapper").getMethod("getAccountSummaryRTWithChildrenValue", Class.forName("java.lang.Long"), Class.forName("java.lang.Long"), Class.forName("java.util.List"), Class.forName("java.lang.Integer"));
m3 = Class.forName("com.xxxx.query.mapper.JournalLineMapper").getMethod("getSummaryBySobIdAndLedgerId", Class.forName("java.lang.Long"), Class.forName("java.lang.Long"));
m4 = Class.forName("com.xxxx.query.mapper.JournalLineMapper").getMethod("getSummaryWithChildren", Class.forName("java.lang.Long"), Class.forName("java.lang.Long"), Class.forName("java.lang.String"));
m8 = Class.forName("com.xxxx.query.mapper.JournalLineMapper").getMethod("getJournalLinePOCount", Class.forName("java.lang.Long"), Class.forName("java.lang.String"), Class.forName("java.sql.Date"), Class.forName("java.sql.Date"), Class.forName("java.lang.Long"), Class.forName("java.lang.Integer"));
m9 = Class.forName("com.xxxx.query.mapper.JournalLineMapper").getMethod("getMultiLedgerAccountSummaryRTWithChildrenValue", Class.forName("java.lang.Long"), Class.forName("java.util.List"), Class.forName("java.util.List"), Class.forName("java.lang.Integer"));
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
public final boolean equals(Object var1) {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final List getSummaryBySobIdAndLedgerId(Long var1, Long var2) {
try {
return (List)super.h.invoke(this, m3, new Object[]{var1, var2});
} catch (RuntimeException | Error var4) {
throw var4;
} catch (Throwable var5) {
throw new UndeclaredThrowableException(var5);
}
}
public final JournalLineSummaryDTO getSummaryWithChildren(Long var1, Long var2, String var3) {
try {
return (JournalLineSummaryDTO)super.h.invoke(this, m4, new Object[]{var1, var2, var3});
} catch (RuntimeException | Error var5) {
throw var5;
} catch (Throwable var6) {
throw new UndeclaredThrowableException(var6);
}
}
public final JournalLineAccountSummaryDTO getAccountSummaryRTWithChildrenValue(Long var1, Long var2, List var3, Integer var4) {
try {
return (JournalLineAccountSummaryDTO)super.h.invoke(this, m5, new Object[]{var1, var2, var3, var4});
} catch (RuntimeException | Error var6) {
throw var6;
} catch (Throwable var7) {
throw new UndeclaredThrowableException(var7);
}
}
上面反编译的源码,也能看到每个mapper接口方法的实现,其实调用的都是super.h.invoke方法。验证上面说的结论。
二、sqlSession在mapper代理类初始化阶段,是怎么注入的?
上面也提到了在MapperFactoryBean.getObject方法实现里面,有段代码:
@Override
public T getMapper(Class type) {
return getConfiguration().getMapper(type, this);
}
把this传递进去了,这个this是SqlSessionTemplate对象,看下这个类:
public class SqlSessionTemplate implements SqlSession, DisposableBean {
private final SqlSessionFactory sqlSessionFactory;
private final ExecutorType executorType;
private final SqlSession sqlSessionProxy;
private final PersistenceExceptionTranslator exceptionTranslator;
发现没,成员有个叫SqlSession类型,看下它的构造函数,看下SqlSession创建的是啥?
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
notNull(executorType, "Property 'executorType' is required");
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy = (SqlSession) newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class },
new SqlSessionInterceptor());
}
sqlSessionProxy是通过jdk动态代理生成的SqlSession接口实现类。说明传递给mapper代理类的这个sqlSession并不是真正的session,而是一个代理类。后面sql在执行的时候,所有SqlSession接口的方法调用,都会被委托给这个SqlSessionInterceptor的invoke方法。SqlSessionInterceptor的源码如下:
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = getSqlSession(
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
可以在invoke里面看到真正调用执行的时候,sqlSession是在invoke方法里面通过getSqlSession得到。
三、mapper方法执行过程
入口在mapper的代理类
return (Long)super.h.invoke(this, m8, new Object[]{var1, var2, var3, var4, var5, var6});
看下org.apache.ibatis.binding.MapperProxy#invoke源码:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
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);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
进入mapperMethod.execute(sqlSession, args),如下:
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);
}
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.selectXXX方法,而这个sqlSession就是初始化的时候传递进去的sqlSessionProxy并不是真正的session,它没有执行能力。它会委托给org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor类的invoke方法,上面已经讲过了。看下它的invoke方法:
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = getSqlSession(
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
获取真正的sqlSession,然后反射调用。
method.invoke(sqlSession, args) 这句话就是 sqlSession.method(args),只不过这里要用反射来调用,所以必须得用前面那种写法。
我们继续看session.method方法,比如随机选择一个session的方法,selectOne好了。
org.apache.ibatis.session.defaults.DefaultSqlSession#selectOne(java.lang.String, java.lang.Object)的源码:
@Override
public T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
List list = this.selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
到这里,终于看到了session维度了。继续追踪:
@Override
public List 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了吧,它是session的属性,而session是每次调用的时候创建。说明executor也是每次sql调用都会重新创建新的对象。进入executor看看:
@Override
public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@SuppressWarnings("unchecked")
@Override
public List 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 list;
try {
queryStack++;
list = resultHandler == null ? (List) 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;
}
选择BaseExecutor,看到里面有localcache,这里就不在展开了,上一篇已经讲过了。
到这里,我们可以后面再执行,就是拿Connection去真正执行了。。。以后有时间在补充