一、sqlSession运行过程

SqlSession采用了门面模式,提供了API(增删改查),辅助API(提交关闭)。

核心执行组件Executor,执行操作。提供了(改和查的操作),维护缓存,辅助API(提交、关闭执行器、批处理刷新)

映射器的动态代理

Mapper映射是通过动态代理来实现的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private Map<Method, MapperMethod> methodCache = new ConcurrentHashMap();

public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}

public Class<T> getMapperInterface() {
return this.mapperInterface;
}

public Map<Method, MapperMethod> getMethodCache() {
return this.methodCache;
}

//这里就是动态代理
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}

public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
}

这里可以看到动态代理对接口的绑定,它的作用是生成动态代理对象,代理的方法放入MapperProxy类中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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;
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//判断它是否是一个类
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
MapperMethod mapperMethod = this.cachedMapperMethod(method);
return mapperMethod.execute(this.sqlSession, args);
}
}

private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
this.methodCache.put(method, mapperMethod);
}

return mapperMethod;
}
}

Mybatis使用的是InvocationHandler,是一个JDK动态代理。上面运用了invoke方法。一旦mapper是一个代理对象,那么它就会运行到invoke方法里面,invoke首先判断它是否是一个类,显然这里Mapper是一个接口不是类,所以判定失败。那么就会生成MapperMethod对象,它是通过cachedMapperMethod方法对其初始化的,然后执行execute方法,把sqlSession和当前运行的参数传递进去。

MapperMethod采用命令模式运行,根据上下文跳转,它可能跳转到许多方法中,我们不需要全部明白。我们可以看到里面的executeForMany方法,再看看它的实现,实际上它最后就是通过sqlSession对象去运行对象的SQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
Object param = this.method.convertArgsToSqlCommandParam(args);
List result;
if (this.method.hasRowBounds()) {
RowBounds rowBounds = this.method.extractRowBounds(args);
result = sqlSession.selectList(this.command.getName(), param, rowBounds);
} else {
result = sqlSession.selectList(this.command.getName(), param);
}

if (!this.method.getReturnType().isAssignableFrom(result.getClass())) {
return this.method.getReturnType().isArray() ? this.convertToArray(result) : this.convertToDeclaredCollection(sqlSession.getConfiguration(), result);
} else {
return result;
}
}

总结

Mybatis映射器的XML文件的命名空间对应的便是这个接口的全路径,那么它根据全路径和方法名便能够绑定起来,通过动态代理技术,让这个接口跑起来。而后采用命令模式,最后还是使用SqlSession接口的方法使得它能够执行查询,有了这层封装我们便可以使用接口编程,这样编程就更简单了。

二、SqlSession的四大对象

Mapper执行的过程是通过 ExecutorStatementHandlerParameterHandlerResultHandler来完成数据库操作和结果返回的。

  • Executor 代表执行器,由它来调度StatementHandler、ParameterHandler.ResultHandler等来执行对应的SQL。
  • StatementHandler的作用是使用数据库的Statement (PreparedStatement)执行操作,它是四大对象的核心,起到承上启下的作用。
  • ParameterHandler 用于SQL对参数的处理
  • ResultHandler是进行最后数据集(ResultSet)的封装返回处理的。

执行器(Executor)

执行器(Executor)起到了至关重要的作用。它是一个真正执行Java和数据库交互的东西。在 MyBatis中存在三种执行器。我们可以在 MyBatis 的配置文件中进行选择

image-20220404232203027
  • SIMPLE,简易执行器,不配置它就是默认执行器。
  • REUSE,是一种执行器重用预处理语句
  • BATCH,执行器重用语句和批量更新,它是针对批量专用的执行器。

Configuration类中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public Executor newExecutor(Transaction transaction, ExecutorTypeexecutorType){
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){
executor = new CachingExecutor(executor);
}
//mybatus插件,这里为我们构建一层层的动态代理对象
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}

简单执行器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class SimpleExecutor extends BaseExecutor {
...
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
//根据Configuration来构建StatementHandler
Configuration configuration = ms.getConfiguration();
// 使用prepareStatement方法,对SQL编译并对参数进行初始化
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
...
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
// 调用StatementHandler的 prepare()进行了预编译和基础设置
stmt = handler.prepare(connection);
//通过StatementHandler 的 parameterize()来设置参数并执行,resultHandler 再组装查询结果返回给调用者来完成一次查询
handler.parameterize(stmt);
return stmt;
}
}

创建Executor需要2个参数configuration和transaction,configuration是从配置文件中获取,由SqlSessionFactory获取,而事务是从connection中获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private Configuration configuration;
private Connection connection;
private JdbcTransaction jdbcTransaction;
private static final String URL = "jdbc:mysql://localhost:3306/ssm";
private static final String USERNAME = "root";
private static final String PASSWORD = "123456";

@Before
public void init() throws IOException, SQLException {
SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory build = factoryBuilder.build(inputStream);
configuration = build.getConfiguration();
connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
jdbcTransaction = new JdbcTransaction(connection);
}
}

创建Executor之后,就可以通过executor.doQuery()执行具体的查询语句。这里需要5个参数

  • MappedStatement ms : SQL声明
  • Object parameter: 参数
  • RowBounds rowBounds: 行范围,要不要分页
  • ResultHandler resultHandler: 结果处理器
  • BoundSql boundSql: 动态SQL语句
1
2
3
4
5
6
7
8
9
10
//简单执行器
@Test
public void simpleTest() throws SQLException {
SimpleExecutor executor = new SimpleExecutor(configuration, jdbcTransaction);
MappedStatement ms = configuration.getMappedStatement("com.lq.mybatis.mapper.AccountMapper.selectById");
List<Object> objects = executor.doQuery(ms, 1, RowBounds.DEFAULT,
SimpleExecutor.NO_RESULT_HANDLER,
ms.getBoundSql(10));
System.out.println(objects);
}

简单执行器,无论SQL是否一样,每次都会进行预编译,浪费性能。

1
2
3
4
5
6
7
[com.lq.mybatis.mapper.AccountMapper.selectById]-==>  Preparing: select id ,name ,money from account where id = ? 
[com.lq.mybatis.mapper.AccountMapper.selectById]-==> Parameters: 1(Integer)
[com.lq.mybatis.mapper.AccountMapper.selectById]-<== Total: 1
[com.lq.mybatis.mapper.AccountMapper.selectById]-ooo Using Connection [com.mysql.jdbc.JDBC4Connection@5bb21b69]
[com.lq.mybatis.mapper.AccountMapper.selectById]-==> Preparing: select id ,name ,money from account where id = ?
[com.lq.mybatis.mapper.AccountMapper.selectById]-==> Parameters: 1(Integer)
[com.lq.mybatis.mapper.AccountMapper.selectById]-<== Total: 1

批处理执行器

1
2
3
4
5
6
7
8
9
10
11
//批处理
//批处理执行器
@Test
public void batchTest() throws SQLException {
BatchExecutor executor = new BatchExecutor(configuration, jdbcTransaction);
MappedStatement ms = configuration.getMappedStatement("com.lq.mybatis.mapper.AccountMapper.selectById");
List<Object> objects = executor.doQuery(ms, 1, RowBounds.DEFAULT,
SimpleExecutor.NO_RESULT_HANDLER,
ms.getBoundSql(10));
System.out.println(objects);
}

但是打印日志还是显示2次

1
2
3
4
5
6
7
[com.lq.mybatis.mapper.AccountMapper.selectById]-==>  Preparing: select id ,name ,money from account where id = ? 
[com.lq.mybatis.mapper.AccountMapper.selectById]-==> Parameters: 1(Integer)
[com.lq.mybatis.mapper.AccountMapper.selectById]-<== Total: 1
[com.lq.mybatis.mapper.AccountMapper.selectById]-ooo Using Connection [com.mysql.jdbc.JDBC4Connection@c46bcd4]
[com.lq.mybatis.mapper.AccountMapper.selectById]-==> Preparing: select id ,name ,money from account where id = ?
[com.lq.mybatis.mapper.AccountMapper.selectById]-==> Parameters: 1(Integer)
[com.lq.mybatis.mapper.AccountMapper.selectById]-<== Total: 1

批处理值针对修改操作才执行一次。必须要手动刷新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//批处理执行器,必须进行手动刷新
@Test
public void batchUpdateTest() throws SQLException {
BatchExecutor executor = new BatchExecutor(configuration, jdbcTransaction);
MappedStatement ms = configuration.getMappedStatement("com.lq.mybatis.mapper.AccountMapper.updateAccount");
Map<Object, Object> map = new HashMap<>();
Account account = new Account();
account.setId(1);
account.setName("aaaa");
account.setMoney(2000.0);
map.put("account", account);
//需要2个参数,SQL声明,具体参数
executor.doUpdate(ms, map);
executor.doUpdate(ms, map);
executor.flushStatements(false); //只是在设置参数,必须手动刷新
}

基础执行器

基础执行器中包含了一级缓存,query和update,这两个方法都会调用具体的方法执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public abstract class BaseExecutor implements Executor {

protected Transaction transaction;

protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
protected PerpetualCache localCache;
protected PerpetualCache localOutputParameterCache;
protected Configuration configuration;

protected int queryStack = 0;
private boolean closed;

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
//创建缓存key
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

//查询的方法
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 {
//走缓存的逻辑, localCache获取本地一级缓存
queryStack++;
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
//没有就会从数据库查询
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//这里就会调用子类的doQuery方法
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
deferredLoads.clear(); // issue 601
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
clearLocalCache(); // issue 482
}
}
return list;
}
}

SimpleExecutor和ReuseExecutor都只会进行一次调用,相同的查询会走缓存的逻辑。

1
2
3
4
5
6
7
8
9
@Test
public void baseQueryTest() throws SQLException {
SimpleExecutor executor = new SimpleExecutor(configuration, jdbcTransaction);
MappedStatement ms = configuration.getMappedStatement("com.lq.mybatis.mapper.AccountMapper.selectById");
executor.query(ms, 1, RowBounds.DEFAULT,
SimpleExecutor.NO_RESULT_HANDLER);
executor.query(ms, 1, RowBounds.DEFAULT,
SimpleExecutor.NO_RESULT_HANDLER);
}

CachingExecutor

image-20220404235522241

缓存执行器,二级缓存,装饰者模式装饰了具体的Executor。二级缓存和一级缓存不同,一级缓存执行过就会缓存。二级缓存必须提交之后才会有缓存。是一个跨线程的缓存。

先走二级缓存,再走一级缓存。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void cacheQueryTest() throws SQLException {
SimpleExecutor executor = new SimpleExecutor(configuration, jdbcTransaction);

//装饰者模式,二级缓存相关逻辑
CachingExecutor cachingExecutor = new CachingExecutor(executor);

MappedStatement ms = configuration.getMappedStatement("com.lq.mybatis.mapper.AccountMapper.selectById");
cachingExecutor.query(ms, 1, RowBounds.DEFAULT,
SimpleExecutor.NO_RESULT_HANDLER);
cachingExecutor.commit(true);
cachingExecutor.query(ms, 1, RowBounds.DEFAULT,
SimpleExecutor.NO_RESULT_HANDLER);
cachingExecutor.commit(true);
}

数据库会话器

数据库会话器(StatementHandler)就是专门处理数据库会话

JDBC处理器,基于JDBC构建Slatement并设置参数,然后执行Sql。每调用会话当中一次SQL,都会有与之相对应的且唯一的Statemet实例。

Configuration类中:

1
2
3
4
5
6
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//创建的真实对象是一个RoutingStatementHandler对象,和Executor一样,用代理对象做一层层的封装
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}

RoutingStatementHandler不是我们真实的服务对象,它是通过适配模式找到对应的StatementHandler 来执行的。在 MyBatis 中,StatementHandler和Executor一样分为三种:普通SimpleStatementHandler、预处理PreparedStatementHandler、存储过程CallableStatementHandler

在初始化 RoutingStatementHandler对象的时候它会根据上下文环境决定创建哪个StatementHandler对象,我们看看RoutingStatementHandler 的源码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class RoutingStatementHandler implements StatementHandler {

private final StatementHandler delegate;

public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
}

数据库会话器定义了一个对象的适配器delegate,它是一个StatementHandler接口对象,构造方法根据配置来适配对应的StatementHandler对象。它的作用是给实现类对象的使用提供一个统一、简易的使用适配器。此为对象的适配模式,可以让我们使用现有的类和方法对外提供服务,也可以根据实际的需求对外屏蔽一些方法,甚至是加入新的服务
我们现在以最常用的PreparedStatementHandler为例,看看MyBatis是怎么执行查询的。执行器有三个主要的方法,prepare、parameterize和 query

先看他们的父类BaseStatementHandler,这个类主要用于处理共性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public abstract class BaseStatementHandler implements StatementHandler {
...
public Statement prepare(Connection connection) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
statement = instantiateStatement(connection);
//设置超时时间
setStatementTimeout(statement);
//设置返回行数
setFetchSize(statement);
return statement;
} catch (SQLException e) {
closeStatement(statement);
throw e;
} catch (Exception e) {
closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}

//这个抽象方法来具体实现statement
protected abstract Statement instantiateStatement(Connection connection) throws SQLException;

}

instantiateStatement()方法是对SQL进行了预编译。首先,做一些基础配置,比如超时,获取的最大行数等的设置。然后,Executor 会调用parameterize()方法去设置参数。这个时候它是调用ParameterHandler去完成的,这里先看StatementHandler的查询方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class PreparedStatementHandler extends BaseStatementHandler {
...
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}

//调用参数处理器设置参数
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
//创建statement
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
} else {
return connection.prepareStatement(sql, keyColumnNames);
}
} else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
//这里就会创建一个预处理的Statement
return connection.prepareStatement(sql);
} else {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}
}

由于在执行前参数和SQL都被prepare()方法预编译,参数在parameterize()方法上已经进行了设置。所以到这里已经很简单了。我们只要执行SQL,然后返回结果就可以了。执行之后我们看到了ResultSetHandler对结果的封装和返回

一条查询SQL的执行流程:预编译、设置参数、执行、结果集映射

  1. Executor先调用StatementHandlerprepare()方法预编译SQL语句,同时设置一些基本运行的参数。
  2. 然后用parameterize()方法启用ParameterHandler 设置参数,完成预编译。
  3. 跟着就是执行查询,而update()也是这样的,最后如果需要查询,我们就用ResultSetHandler封装结果返回给调用者。
image-20220417224757303

参数处理器

完成对预编译参数的设置,javabean转换为jdbc

参数转换

image-20220417230107195

ParamNameResolver帮助我们把参数转换为Object就是查询的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public Object getNamedParams(Object[] args) {
final int paramCount = names.size();
if (args == null || paramCount == 0) {
return null;
//没有Param注解 并且参数为1直接返回
} else if (!hasParamAnnotation && paramCount == 1) {
return args[names.firstKey()];
//多参数
} else {
final Map<String, Object> param = new ParamMap<>();
int i = 0;
for (Map.Entry<Integer, String> entry : names.entrySet()) {
param.put(entry.getValue(), args[entry.getKey()]);
// add generic param names (param1, param2, ...)
//参数 key为param注解的值,value为具体的值
final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
// ensure not to overwrite parameter named with @Param
if (!names.containsValue(genericParamName)) {
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}

参数映射处理

image-20220418231724430
1
2
3
4
5
6
7
public interface ParameterHandler {
//作用是返回参数对象
Object getParameterObject();
//设置预编译SQL语句参数
void setParameters(PreparedStatement ps)
throws SQLException;
}

看实现类DefaultParameterHandler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public void setParameters(PreparedStatement ps) throws SQLException {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
//根据sql语句中参数?解析为parameterMappings参数映射就是#{}中的值
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
MetaObject metaObject = parameterObject == null ? null : configuration.newMetaObject(parameterObject);
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
//获得参数名 例如id
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
//如果参数类型匹配,直接放入
} else if
//只有单个值才会有单个值映射处理器
(typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
//如果是一个map对象
} else {
//把map对象换成一个metaObejct,这样就可以通过属性名称获取对应的值
//属性名和映射名一一对应
value = metaObject == null ? null : metaObject.getValue(propertyName);
}
//通过TypeHandler设置对应的值
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) jdbcType = configuration.getJdbcTypeForNull();
//这里会根据类型,自动设置sql语句的类型
typeHandler.setParameter(ps, i + 1, value, jdbcType);
}
}
}
}

是从parameterObject对象中取参数,然后使用typeHandler进行参数处理,如果你有设置typeHandler,那么它就会根据签名注册的typeHandler对参数进行处理。而typeHandler也是在 MyBatis初始化的时候,注册在Configuration里面的,我们需要的时候可以直接拿来用。这样就完成了参数的设置。

结果处理器

image-20220418233415695

  • ResultSetHandler:结果集处理器
  • ResultContext:结果提出
  • ResultHandler:结果处理器

三、SqlSession运行总结

image-20220404160855195

SqlSession是通过Executor创建StatementHandler来运行的,而StatementHandler要经过下
面三步。

  • prepared 预编译SQL。
  • parameterize设置参数。
  • query/update执行SQL。

其中parameterize是调用parameterHandler的方法去设置的,而参数是根据类型处理器.typeHandler去处理的。query/update 方法是通过resultHandler进行处理结果的封装,如果是update的语句,它就返回整数,否则它就通过typeHandler 处理结果类型,然后用 ObjectFactory提供的规则组装对象,返回给调用者。这便是SqlSession 执行的过程。