<!-- index-menu -->
Mybatis整体结构
├─annotations ->注解相关 @select @insert
├─binding -> mapper 相关
├─builder -> 解析 xml 相关
├─cache -> 缓存
├─cursor -> 返回结果 resultset
├─datasourcer -> 数据管理
├─exceptionsr -> 异常
├─executorr -> 执行器
├─io -> classloader
├─jdbc -> jdbc
├─lang -> jdk7 jdk8
├─logging -> 日志相关
├─mapping -> mapper 相关的封装
├─parsing -> xml 相关解析
├─plugin -> 拦截器
├─reflection -> 反射相关
├─scripting -> 数据厂家
├─session -> sessiomn
├─transaction -> 事务
└─type -> 返回类型对应
Mybatis核心概念
Configuration 、SqlSessionFactory 、Session 、Executor 、MappedStatement、StatementHandler 、ResultSetHandler
名称 | 意义 |
---|---|
Configuration | 管理 mybatis-config.xml 全局配置关系类 |
SqlSessionFactory | Session 管理工厂接口 |
Session | SqlSession 是一个面向用户(程序员)的接口。SqlSession 中提供了很多操作数据库的方法 |
Executor | 执行器是一个接口(基本执行器、缓存执行器)作用:SqlSession 内部通过执行器操作数据库 |
MapperStatement | 底层封装对象 作用:对操作数据库存储封装,包括 sql 语句、输入输出参数 |
StatementHandler | 具体操作数据库相关的 handler 接口 |
ResultSetHandler | 具体操作数据库返回结果的 handler 接口 |
Mybatis的使用
/**
* 步骤分析:
* 第一步:读取配置文件(此时读取的是主配置文件SqlMapConfig.xml,它里面指定了映射配置文件位置)
* 第二步:创建SqlSessionFactory对象(它是一个工厂对象,通过类名就可以得知,它是生产SqlSession)
* 第三步:使用SqlSession对象,创建一个IUserDao接口的代理实现类。
* 第四步:使用代理实现类对象,调用findAll方法实现查询所有
* 第五步:释放资源
mybatis框架主要是围绕着SqlSessionFactory进行的
(1)、定义一个Configuration对象,其中包含数据源、事务、mapper文件资源以及影响数据库行为属性设置settings
(2)、通过配置对象,则可以创建一个SqlSessionFactoryBuilder对象
(3)、通过 SqlSessionFactoryBuilder 获得SqlSessionFactory 的实例。
(4)、SqlSessionFactory 的实例可以获得操作数据的SqlSession实例,通过这个实例对数据库进行操作
*/
public class MyBatisTest {
public static void main(String[] args) throws IOException {
// 读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 创建SQLSessionFactory工厂的构建对象
/**
* 定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行
* 为什么使用工厂模式:
* 1、一个调用者想创建一个对象,只要知道其名称就可以了。
* 2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
* 3、屏蔽产品的具体实现,调用者只关心产品的接口。
* 理解:你需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现
**/
// SqlSessionFactoryBuilder:负责构建SqlSessionFactory,并且提供了多个build的重载方法
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// 创建创建SqlSessionFactory 它是一个创建SqlSession的工厂 是一个接口
SqlSessionFactory factory = builder.build(in);
// 使用工厂生产一个SqlSession
// SqlSession是MyBatis的关键对象,是执行持久化操作的独享,类似于JDBC中的Connection.
// 它是应用程序与持久层之间执行交互操作的一个单线程对象,也是MyBatis执行持久化操作的关键对象.
// SqlSession对象完全包含以数据库为背景的所有执行SQL操作的方法,它的底层封装了JDBC连接,
// 可以用SqlSession实例来直接执行被映射的SQL语句.每个线程都应该有它自己的SqlSession实例.
// SqlSession的实例不能被共享,同时SqlSession也是线程不安全的,
// 绝对不能将SqlSeesion实例的引用放在一个类的静态字段甚至是实例字段中.
// 也绝不能将SqlSession实例的引用放在任何类型的管理范围中,比如Servlet当中的HttpSession对象中.
// 使用完SqlSeesion之后关闭Session很重要,应该确保使用finally块来关闭它.
SqlSession session = factory.openSession();
// 使用SqlSession创建一个dao接口的代理实现类
// SqlSession.getMapper()就是绕一大圈去动态代理一个对象。这个对象根据返回类型和参数类型,最后用SqlSession的方法。
IUserDao proxyUserDao = session.getMapper(IUserDao.class);
// 使用代理dao调用方法
List<User> all = proxyUserDao.findAll();
for (User user : all) {
System.out.println(user);
}
// 释放资源
session.close();
}
配置文件SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--引入jdbc配置文件-->
<properties resource="jdbc.properties"/>
<!--设置别名 实体类-->
<typeAliases>
<!-- 单个
<typeAlias type="fun.chenqi.ssm.dao.IUserDao"></typeAlias>
-->
<package name="fun.chenqi.ssm.domain"></package>
</typeAliases>
<!--配置mybatis的环境-->
<!--配置MySQL的环境-->
<environments default="mysql">
<environment id="mysql">
<!--配置事务的类型-->
<transactionManager type="JDBC"></transactionManager>
<!--配置是否使用连接池,以及使用哪种连接池
使用:POOLED 不使用:UNPOOLED 以上都是使用mybatis内置的
非Mybatis内置的:JDNI(
-->
<dataSource type="POOLED">
<!--配置连接数据库的四个要素-->
<property name="driver" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</dataSource>
</environment>
</environments>
<!-- 指定写有sql语句的映射配置文件位置-->
<mappers>
<!-- 单个映射
<mapper resource="fun/chenqi/ssm/dao/IUserDao.xml"></mapper>
<mapper resource="fun/chenqi/ssm/dao/IAccountDao.xml"></mapper>
-->
<!--整个包映射-->
<package name="fun.chenqi.ssm.dao"/>
<!-- <mapper resource="fun/chenqi/ssm/dao/IUserDao.xml"/> -->
<!--<mapper class="fun.chenqi.ssm.dao.IUserDao"/>-->
</mappers>
</configuration>
源码解析 Configuration
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in); // 第三行
以上代码是读取配置文件,然后通过SqlSessionFactoryBuilder传入配置信息(输入流)创建一个SqlSessionFactory对象。
1、从上面第三行代码跟进去 :
/**
这块代码是根据传入的配置信息输入流创建一个XMLConfigBuilder对象,这个对象是专门生成 ConfigBuilder对象。
*/
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse()); // 第六行
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
2、上面的代码,先跟进第六行parser.parse():
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration")); // 第六行
return configuration;
}
3、 再跟进这块代码的第六行parser.evalNode("/configuration"):
public XNode evalNode(Object root, String expression) {
Node node = (Node) evaluate(expression, root, XPathConstants.NODE);
if (node == null) {
return null;
}
return new XNode(this, node, variables);
}
这块代码是干嘛的,其实就是读取配置文件SqlMapConfig.xml的根标签</configuration>然后返回一个XNode对象!XNode对象里面存的是配置文件的信息。
读取完了根标签是不是接下来要读取根标签下面的其它标签了,回到 2、处这块代码
parseConfiguration(parser.evalNode("/configuration"));
其实就是读取根标签下面的其它标签!
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
4、回到 1、处第六行 return build(parser.parse());
这块代码就是返回一个DefaultSqlSessionFactory对象,
SqlSessionFactory是接口,DefaultSqlSessionFactory是实现类
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
以上就是对源码Configuration的解读
源码解析 SqlSession
// 创建SQLSession对象
SqlSession sqlSession = factory.openSession();
这块代码调用openSession()方法创建了SqlSession对象,
这个方法具体干了哪些事?跟进去看下:
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false); // configuration.getDefaultExecutorType() = SIMPLE
}
再次跟进去:
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();
}
}
这块代码很清楚的告诉我们干了哪些事:
读取配置文件信息,创建事务,创建Executor执行器,这个执行器就是操作数据库,执行sql语句的。
以上就是对SqlSession源码的解读
源码解析 Executor
还是看上面的第十行代码, final Executor executor = configuration.newExecutor(tx, execType);
这行代码传入俩参数事务,Executor的类型,默认是simple
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) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) { // 默认true 因为一级缓存默认开启
executor = new CachingExecutor(executor);
}
// 责任链模式拦截器
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
Mybatis的Executor执行器两大类,缓存的CachingExecutor和非缓存的BaseExecutor,
非缓存的BaseExcutor是个接口,有三个实现类:
BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理
ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map<String, Statement>内,供下一次使用。
SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。(可以是Statement或PrepareStatement对象)
缓存的CachingExecutor
先从缓存中获取查询结果,存在就返回,不存在,再委托给Executor delegate去数据库取,delegate可以是上面任一的SimpleExecutor、ReuseExecutor、BatchExecutor。
Mybatis二级缓存的配置
<!-- 1.sqlmapconfig.xml -->
<settings>
<!--这个配置使全局的映射器(二级缓存)启用或禁用缓存-->
<setting name="cacheEnabled" value="true" />
</settings>
<!-- 2.xxxMapper.xml -->
<mapper namespace="fun.chenqi.ssm.dao.IUserDao">
<<cache/>
<!-- 这个标签必须开启,否则还是一级缓存,一级缓存默认开启,就算 <setting name="cacheEnabled" value="true" />这行代码不写也开启一级缓存。-->
一级缓存和二级缓存的区别
一级缓存是会话(Session)级别,同一次会话中执行多次相同的sql查询,除了第一次查数据库,剩下的查询直接查缓存。
二级缓存基于同一个namespace,不同的sqlsession会话也能缓存起来。
分析代码
如果 <setting name="cacheEnabled" value="false" /> 即关闭了缓存 ,最后返回的Executor传啥就是啥
如果 <setting name="cacheEnabled" value="true" /> 即开启缓存,一级缓存默认开启,这句代码可省略,二级缓存需要开启 <<cache/> 。不管是那种执行器,最后返回的一定是可缓存的CachingExecutor执行器。
源码解析 MappedStatement、StatementHandler、ResultSetHandler
在上面Mybatis的使用中,这块代码:
IUserDao proxyUserDao = session.getMapper(IUserDao.class);
List<User> all = proxyUserDao.findAll();
其实是通过动态代理来实现,这块的源码等会在看,先看操作数据库的另外一种实现方式:
List<User> list = sqlSession.selectList("fun.chenqi.ssm.dao.IUserDao2.userByUsername", "张");
这种方式是使用sqlsession自带的接口执行查询,第一个参数是mapper.xml对应的sql语句,第二个参数是执行sql传入的参数。来跟下这块的代码:
// 这块代码调用了本类的selectList()方法,跟进去
@Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
下面的代码MappedStatement登场
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// 传入fun.chenqi.ssm.dao.IUserDao2.userByUsername,根据配置configuration获取信息
// MappedStatement是一个被封装的操作数据库的对象,包含sql语句,输入参数等。
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()
这是个接口有俩实现类,BaseExecutor和CacheExecutor,由前面的代码我们知道先走CacheExecutor缓存执行器:
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
// query():
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, parameterObject, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
如果缓存有数据直接返回,如果没有数据,调用BaseExecutor的query():
@Override
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++;
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; // 13行
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); // 17行
}
} 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;
}
resultHandler登场,它是处理返回结果的
上面的代码第13行和17行重要
13行是查localCache缓存看看有没有数据,有数据返回给resultHandler,没有的话执行17行queryFromDatabase(),查询数据库。
这里有个疑问,刚才不是查过cache缓存了么,现在怎么又多出来一个localCache缓存。
我通过debug得知原来是这样的!
因为我们在sqlmapconfig.xml配置了<setting name="cacheEnabled" value="true" />
这行代码默认就是true可以不写,它表示了每次mybatis的查询会先查二级缓存,二级缓存是跨Sqlsession的,二级缓存没有会查一级缓存localCache!
我们配置了true,所以执行器默认的就是CacheExecutor,它里面会查二级缓存。当我设置为false的时候,执行器变成了SimpleExecutor,根本不走二级缓存的代码,它会查一级缓存,没有的话才会查库。
接着跟第17行代码:
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 {
// 执行doQuery()查库
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;
}
跟doQuery()代码:
@Override
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());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
这里StatementHandler登场了,它是负责操作数据库的
继续跟handler.<E>query(stmt, resultHandler);
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = boundSql.getSql();
statement.execute(sql);
return resultSetHandler.<E>handleResultSets(statement);
}
这里就是操作数据库执行sql语句的代码了,
然后通过resultSetHandler.<E>handleResultSets(statement);处理返回结果:
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<Object>();
int resultSetCount = 0;
ResultSetWrapper rsw = getFirstResultSet(stmt);
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
return collapseSingleResultList(multipleResults);
}
以上就是mybatis一个完整sql查询执行过程!
再次回到
IUserDao proxyUserDao = session.getMapper(IUserDao.class);
List<User> all = proxyUserDao.findAll();
这里使用了动态代理来实现的,可以在程序运行期间,在不改变源代码的情况下对原代码增强
通过session.getMapper(IUserDao.class);获取要代理对象的信息
一直跟进去会到这里:
@Override
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
.......
// 很明显了 这里就是通过反射创建真实对象的代理对象
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
List<User> all = proxyUserDao.findAll();
代码执行到这里的时候,会对这块代码增强:
@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操作数据库了!
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;
}
以上就是MyBatis代理的方式执行sql的全过程!
四天时间终于撸完MyBatis核心代码,接下来继续撸Spring源码...