MyBatisPlus

MyBatis执行流程

1.读取MyBatis配置文件(mybatis-config.xml)

mybatis-config.xml 为 MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等信息,例如数据库连接信息

2. 加载SQL映射文件

映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句,需要在 MyBatis 配置文件 mybatis-config.xml 中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。

1
2
3
4
5
<!--加载SQL映射文件-->
<mappers>
<mapper resource="UserDao.xml"></mapper>
<mapper resource="StudentDao.xml"></mapper>
</mappers>

3. 构造会话工厂SqlSessionFactory

通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。

InputStream inputStream = Resources.getResourceAsStream(“mybatis-config.xml”);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(inputStream);

4.创建会话对象SqlSession

由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法,是一个既可以发送sql执行并返回结果的,也可以获取mapper的接口

SqlSession session = factory.openSession();

5.Executor 执行器

MyBatis 底层定义了一个 Executor 接口来操作数据库,它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护。

6.MappedStatement 对象

在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等信息。

7.输入参数映射

输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型。输入参数映射过程类似于 JDBC 对 preparedStatement 对象设置参数的过程。

8.输出结果映射

输出结果类型可以是 Map、 List 等集合类型,也可以是基本数据类型和 POJO 类型。输出结果映射过程类似于 JDBC 对结果集的解析过程。

Mybatis 插件机制Interceptor

在 Mybatis 中,插件机制提供了强大的扩展能力,在 sql 最终执行之前,提供了四个拦截点,支持不同场景的功能扩展:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

例如我们需要对查询出来的数据进行解密(数据库保存的是加密信息,然后我们可以定义一个注解取修饰字段,假如字段被这个注解修饰的话,那么后续的操作我们则需要进行加密解密)

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
55
56
57
58
59
60
61
62
/**
* 解密
**/
@Intercepts({
@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
@Component
@Slf4j
public class DecryptInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object resultObject = invocation.proceed();
if (Objects.isNull(resultObject))
return null;
if (resultObject instanceof ArrayList) {
//基于selectList
ArrayList resultList = (ArrayList) resultObject;
if (!CollectionUtils.isEmpty(resultList) && needToDecrypt(resultList.get(0))) {
for (Object result : resultList) {
//逐一解密
decrypt(result);
}
}
} else if (needToDecrypt(resultObject))
//基于selectOne
decrypt(resultObject);
return resultObject;
}
@Override
public Object plugin(Object o) {
return Plugin.wrap(o, this);
}
// function needToDecrypt -> 这个方法其实就是判断字段是否有我们自定义的注解修饰
}

/**
* @description 加密和完整性计算拦截器 (目前完整性计算只支持mybatis plus 自带的API)
**/
@Slf4j
public class EncryptInterceptor extends JsqlParserSupport implements InnerInterceptor {
/**
* 变量占位符正则
*/
private static final Pattern PARAM_PAIRS_RE = Pattern.compile("#\\{ew\\.paramNameValuePairs\\.(" + Constants.WRAPPER_PARAM + "\\d+)\\}");

/**
* 如果查询条件是加密数据列,那么要将查询条件进行数据加密。
* 例如,手机号加密存储后,按手机号查询时,先把要查询的手机号进行加密,再和数据库存储的加密数据进行匹配
*/
@Override
public void beforeQuery(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 具体逻辑 不列出,可查看解密逻辑
}
/**
* 新增、更新数据时,如果包含隐私数据,则进行加密
* 还有个问题,进行修改时,如果传过来的实体类的属性有属于脱敏的数据,那么这个脱敏的数据没有被修改的情况下需要被识别出来,不更新到数据库
*/
@Override
public void beforeUpdate(Executor executor, MappedStatement mappedStatement, Object parameterObject) throws SQLException {
// 具体逻辑 不列出,可查看解密逻辑
}
}