1. getById 的执行

前面一篇 提到过, Mapper.java 创建的时候, 会通过 jdk 代理的方式来创建, 且代理处理类为: MapperProxy .

所以当执行 UserMapper 的 getById 方法的时候, 就会去 MapperProxy 中执行 invoke 方法.

  //MapperProxy.java
@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);
}
cachedMapperMethod 方法是做了一层缓存处理. 先从缓存中获取, 如果获取不到, 再创建 MapperMethod 对象.
  private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}

接下来看一下 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);
}
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;
}

getById 在这里会调用 SqlSessionTemplate 的 selectOne 方法.

  //SqlSessionTemplate.java
@Override
public <T> T selectOne(String statement, Object parameter) {
return this.sqlSessionProxy.<T> selectOne(statement, parameter);
}

前面解析过, sqlSessionProxy.selectOne 的时候, 会去  SqlSessionInterceptor 中执行 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);
}
}
}
}

这里有两个方法需要重点关注, 一个是  getSqlSession() , 另一个是 invoke() 方法.

 1.1 getSqlSession()

  public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED); SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); SqlSession session = sessionHolder(executorType, holder);
if (session != null) {
return session;
} if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Creating a new SqlSession");
} session = sessionFactory.openSession(executorType); registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session); return session;
}

这里的 SqlSessionFactory 就是配置类中创建的 默认的实现类: DefaultSqlSessionFactory

  //DefaultSqlSessionFactory.java
@Override
public SqlSession openSession(ExecutorType execType) {
return openSessionFromDataSource(execType, null, false);
} |
|
\|/ private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
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();
}
}

这里先不管别的代码, 直接看返回值, 创建了一个 DefaultSqlSession 的实例返回.

1.2 method.invoke()

回到  SqlSessionInterceptor 中来, 继续看下面的执行代码:

Object result = method.invoke(sqlSession, args);

sqlSession 就是上面创建的 DefaultSqlSesion 实例. 我们知道 Method.invoke(obj, args) 是反射调用方法的一种方式.

此处的Method是 getById 的方法反射类型. 所以, 会调用 obj 的 getById() 方法, 即 DefaultSqlSession 的 getById() 方法.

  @Override
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
List<T> list = this.<T>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;
}
} @Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
} @Override
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();
}
}

此例中, statement = "com.study.demo.mybatis.mapper.UserMapper.getById"

这里是通过statement 拿 MappedStatement, 而 MappedStatement 中, 就有 mapper.xml 中对应的 sql 语句.

后续过程中, 就可以拿着 sql 去数据库执行了.

到这里, 主体流程其实已经走通了.

最新文章

  1. SQL执行效率和性能测试方法总结
  2. MySQL集群在断网后再启动报&quot;Unable to start missing node group&quot;问题处理
  3. Ext.Net TreePanel 修改Icon图标
  4. Linux文件管理命令
  5. [转]Android WebView播放视频(包括全屏播放),androidwebview
  6. 透过浏览器看HTTP缓存(转)
  7. HAProxy 代理负载均衡
  8. C# 汉字转拼音 将中文转换成拼音
  9. Mybatis传参方式
  10. 1.1 为什么要使用lambda 表达式
  11. 01.pandas
  12. 物理层PHY 和 网络层MAC
  13. php遍历数组7种方式(严格说是五种)
  14. JSF中run项目时候Tomcat8启动不了的一种方法
  15. 迅为4412开发板QtE系统源码-屏幕横竖屏切换修改方法
  16. python 实现求一个集合的子集
  17. 从Tomcat的处理web请求分析Java的内存模型
  18. idea/ecipse中使用maven集成springmvc相关jar包时候,出错:java.lang.ClassNotFoundException: org.springframework.web.servlet.DispatcherServlet
  19. Kubenets 调试cronjob
  20. YII2表单中上传单个文件

热门文章

  1. 2.2测试赛AC代码临时保存
  2. python面试的100题(15)
  3. View --&gt;Controller传值的几种方法
  4. 02 Django虚拟环境搭建
  5. .htaccess详解
  6. TP5和TP3.2的使用区别
  7. 复制文件或目录命令 - cp
  8. JVM的前世今生
  9. Thinkcmf对接支付宝支付和获取用户信息
  10. object转为string