mybatis - 执行 getById
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 去数据库执行了.
到这里, 主体流程其实已经走通了.
mybatis - 执行 getById的更多相关文章
- mybatis执行批量更新update
Mybatis的批量插入这里有http://ljhzzyx.blog.163.com/blog/static/38380312201353536375/.目前想批量更新,如果update的值是相同的话 ...
- Mybatis执行CachingExecutor(六)
前面几篇博客我们介绍了Excutor及抽象类BaseExecutor和实现类SimpleExecutor.BatchExecutor和ReuseExecutor: 博客列表: Mybatis执行Exe ...
- 一图看懂mybatis执行过程
一图看懂mybatis执行过程,不再懵B了
- 【mybatis】mybatis执行一个update方法,返回值为1,但是数据库中数据并未更新,粘贴sql语句直接在数据库执行,等待好久报错:Lock wait timeout exceeded; try restarting transaction
今天使用mybatis和jpa的过程中,发现这样一个问题: mybatis执行一个update方法,返回值为1,但是数据库中数据并未更新,粘贴sql语句直接在数据库执行,等待好久报错:Lock wai ...
- mybatis执行多条sql语句
1,mybatis执行多条sql语句,有以下几种思路, a,存储过程 b,修改jdbc的参数,允许执行多条语句,如下所示: sqlserver可以直接使用begin,end来执行多条语句, mysql ...
- mybatis执行过程及经典面试题
Mybatis执行流程 mybatis中xml解析是通过SqlSessionFactoryBuilder.build()方法. 初始化mybatis(解析xml文件构建成Configuration对象 ...
- mybatis 逆向工程(通过数据库表针对单表自动生成mybatis执行所需要的代码)
mybatis需要程序员自己编写sql语句,mybatis官方提供逆向工程,可以针对单表自动生成mybatis执行所需要的代码(mapper.java.mapper.xml.pojo…),可以让程序员 ...
- mybatis 执行流程以及初用错误总结
mappper 配置文件 头文件: 1. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" &q ...
- MyBatis执行流程的各阶段介绍
目录 一.mybatis极简示例 1.1 创建mybatis配置文件 1.2 创建数据库表 1.3 创建javabean 1.4 创建mapper映射文件 1.5 运行测试 二.mybatis的几大“ ...
随机推荐
- 在多租户(容器)数据库中如何创建PDB:方法2 克隆本地PDB
基于版本:19c (12.2.0.3) AskScuti 创建方法:克隆本地PDB(从本地其他PDB创建新的PDB) 对应路径:Creating a PDB --> Cloning --> ...
- 零Web知识个性化Blog
需要使用到的工具 Chrome Pycharm 自定主题的CSS 申请博客的Js权限 设置博客选项 打开Chrome修改查看CSS样式,Windows(F12),MacOS(Command+Optio ...
- c#窗体进度条
c#窗体进度条 //进度条的最大值 ; progressBar1.Maximum = Convert.ToInt32(a); ;i<progressBar1.Maximum;i++) { //进 ...
- StringBuilder与String的区别
String 在进行运算时(如赋值.拼接等)会产生一个新的实例,而 StringBuilder 则不会.所以在大量字符串拼接或频繁对某一字符串进行操作时最好使用 StringBuilder,不要使用 ...
- IMM设置静态ip,安装系统
IMM设置静态ip,安装系统 设置ip: System Setting->Integrated Management Module->Network configuration 修改后选择 ...
- L1-7 谁是赢家
思路 这题好简单,可以分析一下,没有别的情况了. 代码 #include <bits/stdc++.h> using namespace std; int main() { int p1, ...
- Go操作Elasticsearch
文章转自 Elasticsearch Elasticsearch 下载 https://www.elastic.co/cn/start 运行 解压后cd到解压目录 ./bin/elasticsea ...
- 【C语言】用指针作为形参完成数据的升序排列
#include<stdio.h> void sort(int *x,int n); int main() { ] = { ,,,,,,,,, },i; sort(arr, ); prin ...
- 购物单 && 动态规划 && 背包问题
题目叙述的言语倒是蛮多的: 王强今天很开心,公司发给N元的年终奖.王强决定把年终奖用于购物,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子: 主件 附件 电脑 ...
- Mysql 函数定义及批量数据脚本
零.说在前面 在定义函数之前 需要先将 log_bin_trust_function_creators 值设为开启,原因如下 在主从复制的两台Mysql服务器中,slaver会从master复制数据, ...