目录
概述
之前的文章写过一篇自定义持久层框架,实现了Mybatis相似的功能。如果想快速掌握mybatis,强烈推荐简单阅读一下这篇文章。
源码地址
https://gitee.com/li_gang_x/my-project-model
有了上面的基础,阅读mybatis源码就不那么陌生了,你会有些许熟悉,似曾相识的感觉。下面我将带领读者进入到mybatis框架,领略大佬们是怎么实现这个持久层框架的,怎么优雅的实现。废话不多说,下面进入主题。
架构设计
要想 整体把握一个框架,不管要属性怎么使用,而且要知其所以然。整体把握一个框架,首先就要对框架整体有一个认知。
- a. 使⽤传统的MyBatis提供的API ;
- b. 使⽤Mapper代理的⽅式(生成代理对象进行实现)
Mybatis的调用链路简介
构件描述
- SqlSession
作为MyBatis ⼯作的主要顶层 API ,表示和数据库交互的会话,完成必要数据库增删改查功能
- Executor
MyBatis执⾏器,是 MyBatis 调度的核⼼,负责 SQL 语句的⽣成和查询缓存的维护
- StatementHandler
封装了JDBC Statement 操作,负责对 JDBC statement 的操作,如设置参数、将Statement 结果集转换成List 集合。
- ParameterHandler
负责对⽤户传递的参数转换成JDBC Statement 所需要的参数,
- ResultSetHandler
负责将JDBC 返回的 ResultSet 结果集对象转换成 List 类型的集合;
- TypeHandler
负责java 数据类型和 jdbc 数据类型之间的映射和转换
- MappedStatement
MappedStatement维护了⼀条< select | update | delete | insert >节点的封装
- SqlSource
负责根据用户传递的parameterObject,动态生成Sql语句,将信息封装到BoundSql对象中并返回
- BoundSql
表示动态⽣成的SQL 语句以及相应的参数信息
总体流程
- 加载配置并初始化
- 接收调⽤请求
- 处理操作请求
- 返回处理结果
- (A) 根据SQL的ID查找对应的MappedStatement对象。
- (B) 根据传⼊参数对象解析MappedStatement对象,得到最终要执⾏的SQL和执⾏传⼊参数。
- (C) 获取数据库连接,根据得到的最终SQL语句和执⾏传⼊参数到数据库执⾏,并得到执⾏结果。
- (D) 根据MappedStatement对象中的结果映射配置对得到的执⾏结果进⾏转换处理,并得到最终的处理结果。
- (E) 释放连接资源。
源码剖析
读取xml配置文件
public class SqlSessionFactoryBuilder {
//SqlSessionFactoryBuilder有9个build()方法
//发现mybatis文档老了,http://www.mybatis.org/core/java-api.html,关于这块对不上
//以下3个方法都是调用下面第4种方法
public SqlSessionFactory build(Reader reader) {
return build(reader, null, null);
}
public SqlSessionFactory build(Reader reader, String environment) {
return build(reader, environment, null);
}
public SqlSessionFactory build(Reader reader, Properties properties) {
return build(reader, null, properties);
}
//第4种方法是最常用的,它使用了一个参照了XML文档或更特定的SqlMapConfig.xml文件的Reader实例。
//可选的参数是environment和properties。Environment决定加载哪种环境(开发环境/生产环境),包括数据源和事务管理器。
//如果使用properties,那么就会加载那些properties(属性配置文件),那些属性可以用${propName}语法形式多次用在配置文件中。和Spring很像,一个思想?
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
//委托XMLConfigBuilder来解析xml文件,并构建
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
return build(parser.parse());
} catch (Exception e) {
//这里是捕获异常,包装成自己的异常并抛出的idiom?,最后还要reset ErrorContext
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
//以下3个方法都是调用下面第8种方法
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment) {
return build(inputStream, environment, null);
}
public SqlSessionFactory build(InputStream inputStream, Properties properties) {
return build(inputStream, null, properties);
}
//第8种方法和第4种方法差不多,Reader换成了InputStream
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.
}
}
}
//最后一个build方法使用了一个Configuration作为参数,并返回DefaultSqlSessionFactory
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
有通过Reader获取和通过Io流获取的两种方法,下面主要写通过输入流获取的方法
Inputstream inputstream = Resources.getResourceAsStream("mybatisconfig.xml");
//这⼀⾏代码正是初始化⼯作的开始。
SqlSessionFactory factory = new
SqlSessionFactoryBuilder().build(inputStream);
//第8种方法和第4种方法差不多,Reader换成了InputStream
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.
}
}
}
//最后一个build方法使用了一个Configuration作为参数,并返回DefaultSqlSessionFactory
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
Configuration 对象的结构和 xml 配置⽂件的对象⼏乎相同。回顾⼀下 xml 中的配置标签有哪些:properties ( 属性 ) , settings ( 设置 ) , typeAliases ( 类型别名 ) , typeHandlers ( 类型处理器 ) , objectFactory ( 对象⼯⼚ ) , mappers ( 映射器 ) 等 Configuration 也有对应的对象属性来封装它们也就是说,初始化配置⽂件信息的本质就是创建 Configuration 对象,将解析的 xml 数据封装到Configuration 内部属性中
<?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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
//解析配置
public Configuration parse() {
//如果已经解析过了,报错
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
//根节点是configuration
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
//解析配置
private void parseConfiguration(XNode root) {
try {
//分步骤解析
//issue #117 read properties first
//1.properties
propertiesElement(root.evalNode("properties"));
//2.类型别名
typeAliasesElement(root.evalNode("typeAliases"));
//3.插件
pluginElement(root.evalNode("plugins"));
//4.对象工厂
objectFactoryElement(root.evalNode("objectFactory"));
//5.对象包装工厂
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
//6.设置
settingsElement(root.evalNode("settings"));
// read it after objectFactory and objectWrapperFactory issue #631
//7.环境
environmentsElement(root.evalNode("environments"));
//8.databaseIdProvider
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//9.类型处理器
typeHandlerElement(root.evalNode("typeHandlers"));
//10.映射器
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
<select id="getUser" resultType="user" >
select * from user where id=#{id}
</select>
这样的⼀个select标签会在初始化配置⽂件时被解析封装成⼀个MappedStatement对象,然后存储在 Configuration对象的mappedStatements属性中,mappedStatements 是⼀个HashMap,存储时
key =全限定类名+⽅法名,
value =对应的MappedStatement对象。
- 在configuration中对应的属性为
Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement> ("Mapped Statements collection")
private void parseConfiguration(XNode root) {
try {
//省略其他标签的处理
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration.Cause:" + e, e);
}
}
// 5.调⽤的重载⽅法
public SqlSessionFactory build(Configuration config) {
//创建了 DefaultSqlSessionFactory 对象,传⼊ Configuration 对象。
return new DefaultSqlSessionFactory(config);
}
源码分析-执⾏SQL流程
先简单介绍SqlSession :
public class DefaultSqlSession implements SqlSession {
private Configuration configuration;
private Executor executor;
...
}
- BatchExecutor (重⽤语句并执⾏批量更新)
- ReuseExecutor (重⽤预处理语句 prepared statements)
- SimpleExecutor (普通的执⾏器,默认)
SqlSession sqlSession = factory.openSession();
List<User> list =
sqlSession.selectList("com.lagou.mapper.UserMapper.getUserByName");
进入到openSession方法
//6. 进⼊ openSession ⽅法。
public SqlSession openSession() {
//getDefaultExecutorType()传递的是SimpleExecutor
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null,
false);
}
可以看到调用的是openSessionFormDataSource
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);
//然后产生一个DefaultSqlSession
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();
}
}
//8.进⼊selectList⽅法,多个重载⽅法
@Override
public <E> List<E> selectList(String statement) {
return this.selectList(statement, null);
}
@Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
//核心selectList
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//根据statementId(全限定名+⽅法名)找到对应的MappedStatement 根据传⼊的全限定名+⽅法名从映射的Map中取出MappedStatement对象
MappedStatement ms = configuration.getMappedStatement(statement);
//调⽤Executor中的⽅法处理
//转而用执行器来查询结果,注意这里传入的ResultHandler是null
//RowBounds是⽤来逻辑分⻚
// wrapCollection(parameter)是⽤来装饰集合或者数组参数
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
//SqlSession.selectList会调用此方法
// 继续源码中的步骤,进⼊executor.query()
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//根据传⼊的参数动态获得SQL语句,最后返回⽤BoundSql对象表示
BoundSql boundSql = ms.getBoundSql(parameter);
//创建缓存Key 为本次查询创建缓存的Key
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
//查询
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@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.");
}
//先清局部缓存,再查询.但仅查询堆栈为0,才清。为了处理递归调用
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
//加一,这样递归调用到上面的时候就不会再清局部缓存了
queryStack++;
//先根据cachekey从localCache去查
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
//若查到localCache缓存,处理localOutputParameterCache
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
//如果是STATEMENT,清本地缓存
clearLocalCache();
}
}
return list;
}
下面进入到从数据库查询的逻辑
//从数据库查
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);
//如果是存储过程,OUT参数也加入缓存
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
这里有个doQuery 方法阅读过源码的同学都知道,do开头的一般是真正执行逻辑的方法
//query-->queryFromDatabase-->doQuery
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException;
这个方法是protected修饰的,是子类实现
//select
@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
//这里看到ResultHandler传入了
//传⼊参数创建StatementHandler对象来执⾏查询
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//准备语句 创建jdbc中的statement对象
stmt = prepareStatement(handler, ms.getStatementLog());
//StatementHandler.query
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
下面查看prepareStatement方法的实现
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//代码中的getConnection⽅法经过重重调⽤最后会调⽤openConnection⽅法,从连接池中获得连接
Connection connection = getConnection(statementLog);
//调用StatementHandler.prepare
stmt = handler.prepare(connection);
//调用StatementHandler.parameterize
handler.parameterize(stmt);
return stmt;
}
这里的连接是 JdbcTransaction 中从连接池中获取的连接
/**
* 从连接池获得连接的⽅法
* @throws SQLException
*/
protected void openConnection() throws SQLException {
if (log.isDebugEnabled()) {
log.debug("Opening JDBC Connection");
}
//从连接池获得连接
connection = dataSource.getConnection();
if (level != null) {
connection.setTransactionIsolation(level.getLevel());
}
setDesiredAutoCommit(autoCommmit);
}
上述的Executor.query()⽅法⼏经转折,最后会创建⼀个StatementHandler对象,然后将必要的参数传递给 StatementHandler,使⽤StatementHandler来完成对数据库的查询,最终返回List结果集。
(1 、根据传递的参数,完成 SQL 语句的动态解析,⽣成 BoundSql 对象,供 StatementHandler 使⽤;(2 、为查询创建缓存,以提⾼性能(3 、创建 JDBC 的 Statement 连接对象,传递给 *StatementHandler* 对象,返回 List 查询结果
源码剖析-StatementHandler
- 对于JDBC的PreparedStatement类型的对象,创建的过程中,我们使⽤的是SQL语句字符串会包 含若⼲个?占位符,我们其后再对占位符进⾏设值。StatementHandler通过 parameterize(statement)⽅法对 Statement 进⾏设值;
- StatementHandler 通过 List query(Statement statement, ResultHandler resultHandler)⽅法来 完成执⾏Statement,和将Statement对象返回的resultSet封装成List;
进⼊到 StatementHandler 的 parameterize(statement)⽅法的实现:
//参数化
void parameterize(Statement statement)
throws SQLException;
这里将参数进行替换设置
这个接口的实现是
PreparedStatementHandler 预处理语句处理器(PREPARED)
@Override
public void parameterize(Statement statement) throws SQLException {
//调用ParameterHandler.setParameters
parameterHandler.setParameters((PreparedStatement) statement);
}
下面是设置参数的具体实现
//设置参数
/** ParameterHandler 类的 setParameters(PreparedStatement ps) 实现
* 对某⼀个Statement进⾏设置参数
* */
@Override
public void setParameters(PreparedStatement ps) throws SQLException {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
//循环设参数
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
//如果不是OUT,才设进去
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
//若有额外的参数, 设为额外的参数
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
//若参数为null,直接设null
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
//若参数有相应的TypeHandler,直接设object
value = parameterObject;
} else {
//除此以外,MetaObject.getValue反射取得值设进去
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
// 每⼀个 Mapping都有⼀个 TypeHandler,根据 TypeHandler 来对preparedStatement 进⾏设置参数
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
//不同类型的set方法不同,所以委派给子类的setParameter方法
jdbcType = configuration.getJdbcTypeForNull();
}
//设置参数
typeHandler.setParameter(ps, i + 1, value, jdbcType);
}
}
}
}
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
// 1.调⽤preparedStatemnt。execute()⽅法,然后将resultSet交给ResultSetHandler处理
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
//2.使⽤ ResultHandler 来处理 ResultSet
return resultSetHandler.<E> handleResultSets(ps);
}
也是PreparedStatementHandler实现的方法
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
//多ResultSet的结果集合,每个ResultSet对应⼀个Object对象。
// ⽽实际上,每 个 Object 是List<Object> 对象。
//在不考虑存储过程的多ResultSet的情况,普通的查询,实际就⼀个ResultSet,
// 也就是说,multipleResults最多就⼀个元素。
final List<Object> multipleResults = new ArrayList<Object>();
int resultSetCount = 0;
//获得⾸个ResultSet对象,并封装成ResultSetWrapper对象
ResultSetWrapper rsw = getFirstResultSet(stmt);
//获得ResultMap数组
//在不考虑存储过程的多ResultSet的情况,普通的查询,实际就⼀个ResultSet,
// 也就是说,resultMaps就⼀个元素。
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
//一般resultMaps里只有一个元素
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
//获得ResultMap对象
ResultMap resultMap = resultMaps.get(resultSetCount);
//处理ResultSet,将结果添加到multipleResults中
handleResultSet(rsw, resultMap, multipleResults, null);
//获得下⼀个ResultSet对象,并封装成ResultSetWrapper对象
rsw = getNextResultSet(stmt);
//清理
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
//因为'mappedStatement.resultSets'只在存储过程中使⽤,本系列暂时不考虑,忽略即可
String[] resultSets = mappedStatement.getResulSets();
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++;
}
}
//如果是multipleResults单元素,则取⾸元素返回
return collapseSingleResultList(multipleResults);
}
Mapper代理⽅式:
public static void main(String[] args) {
//前三步都相同
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = factory.openSession();
//这⾥不再调⽤SqlSession的api,⽽是获得了接⼝对象,调⽤接⼝中的⽅法。
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> list = mapper.getUserByName("tom");
}
<mapper class="com.lagou.mapper.UserMapper"/>
<package name="com.lagou.mapper"/>
</mappers>
源码剖析-getmapper()
映射器注册机
DefaultSqlSession默认的sqlSession
@Override
public <T> T getMapper(Class<T> type) {
//最后会去调用MapperRegistry.getMapper
return configuration.<T>getMapper(type, this);
}
调用的是Configuration中的getMapper
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
最终调用的是MapperRegistry 中的 getMapper
//返回代理类
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//从 MapperRegistry 中的 HashMap 中拿 MapperProxyFactory
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);
}
}
这里的newInstance 方法如下
MapperProxyFactory
public T newInstance(SqlSession sqlSession) {
//创建了 JDK动态代理的Handler类
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
//调⽤了重载⽅法
return newInstance(mapperProxy);
}
下面是调用的JDK的动态代理
protected T newInstance(MapperProxy<T> mapperProxy) {
//用JDK自带的动态代理生成映射器
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
//构造,传⼊了 SqlSession,说明每个session中的代理对象的不同的!
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
...省略部分源码,剩余的在下面
}
//如果是Object定义的⽅法,直接调⽤
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//代理以后,所有Mapper的方法调用时,都会调用这个invoke方法
//并不是任何一个方法都需要执行调用代理对象进行执行,如果这个方法是Object中通用的方法(toString、hashCode等)无需执行
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
//这里优化了,去缓存中找MapperMethod
final MapperMethod mapperMethod = cachedMapperMethod(method);
//重点在这:MapperMethod最终调⽤了执⾏的⽅法
return mapperMethod.execute(sqlSession, args);
}
MapperMethod最终调⽤了 mapperMethod.execute(sqlSession, args); 执⾏的⽅法
/**
* 映射器代理,代理模式
*
*/
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//代理以后,所有Mapper的方法调用时,都会调用这个invoke方法
//并不是任何一个方法都需要执行调用代理对象进行执行,如果这个方法是Object中通用的方法(toString、hashCode等)无需执行
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
//这里优化了,去缓存中找MapperMethod
final MapperMethod mapperMethod = cachedMapperMethod(method);
//执行
return mapperMethod.execute(sqlSession, args);
}
//去缓存中找MapperMethod
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
//找不到才去new
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
}
MapperMethod进⼊execute⽅法:
方法类别的判断方法,在构造方法中初始化
private final SqlCommand command;
private final MethodSignature method;
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, method);
}
//执行
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
//可以看到执行时就是4种情况,insert|update|delete|select,分别调用SqlSession的4大类方法
//判断mapper中的⽅法类型,最终调⽤的还是SqlSession中的⽅法 switch(command.getType()) {
if (SqlCommandType.INSERT == command.getType()) {
//转换参数
Object param = method.convertArgsToSqlCommandParam(args);
//执⾏INSERT操作
// 转换 rowCount
result = rowCountResult(sqlSession.insert(command.getName(), param));
} else if (SqlCommandType.UPDATE == command.getType()) {
//转换参数
Object param = method.convertArgsToSqlCommandParam(args);
// 转换 rowCount
result = rowCountResult(sqlSession.update(command.getName(), param));
} else if (SqlCommandType.DELETE == command.getType()) {
//转换参数
Object param = method.convertArgsToSqlCommandParam(args);
// 转换 rowCount
result = rowCountResult(sqlSession.delete(command.getName(), param));
//⽆返回,并且有ResultHandler⽅法参数,则将查询的结果,提交给 ResultHandler 进⾏处理
} else if (SqlCommandType.SELECT == command.getType()) {
if (method.returnsVoid() && method.hasResultHandler()) {
//如果有结果处理器
executeWithResultHandler(sqlSession, args);
result = null;
//执⾏查询,返回列表
} else if (method.returnsMany()) {
//如果结果有多条记录
result = executeForMany(sqlSession, args);
//执⾏查询,返回Map
} else if (method.returnsMap()) {
//如果结果是map
result = executeForMap(sqlSession, args);
} else {
//否则就是一条记录 查询单条
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
} else {
throw new BindingException("Unknown execution method for: " + command.getName());
}
//返回结果为null,并且返回类型为基本类型,则抛出BindingException异常
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;
}
//这个方法对返回值的类型进行了一些检查,使得更安全
private Object rowCountResult(int rowCount) {
final Object result;
if (method.returnsVoid()) {
result = null;
} else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {
//如果返回值是大int或小int
result = Integer.valueOf(rowCount);
} else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {
//如果返回值是大long或小long
result = Long.valueOf(rowCount);
} else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {
//如果返回值是大boolean或小boolean
result = Boolean.valueOf(rowCount > 0);
} else {
throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: " + method.getReturnType());
}
return result;
}
下面是参数转换成sql 语句中的参数
public Object convertArgsToSqlCommandParam(Object[] args) {
final int paramCount = params.size();
if (args == null || paramCount == 0) {
//如果没参数
return null;
} else if (!hasNamedParameters && paramCount == 1) {
//如果只有一个参数
return args[params.keySet().iterator().next().intValue()];
} else {
//否则,返回一个ParamMap,修改参数名,参数名就是其位置
final Map<String, Object> param = new ParamMap<Object>();
int i = 0;
for (Map.Entry<Integer, String> entry : params.entrySet()) {
//1.先加一个#{0},#{1},#{2}...参数
param.put(entry.getValue(), args[entry.getKey().intValue()]);
// issue #71, add param names as param1, param2...but ensure backward compatibility
final String genericParamName = "param" + String.valueOf(i + 1);
if (!param.containsKey(genericParamName)) {
//2.再加一个#{param1},#{param2}...参数
//你可以传递多个参数给一个映射器方法。如果你这样做了,
//默认情况下它们将会以它们在参数列表中的位置来命名,比如:#{param1},#{param2}等。
//如果你想改变参数的名称(只在多参数情况下) ,那么你可以在参数上使用@Param(“paramName”)注解。
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}
好了Mybatis执行流程就讲到这了。后面会对部分进细节行更加详细的讲述
如果觉得本文对你有帮助,欢迎点赞,欢迎关注我,如果有补充欢迎评论交流,我将努力创作更多更好的文章。