Mybatis是一个非常流行的Java ORM框架,它为开发者提供了一种简单的方式来操作关系型数据库。Mybatis插件是Mybatis的一个重要扩展功能,它允许开发者通过自定义插件来增强Mybatis的功能。在这篇文章中,我们将介绍如何使用Mybatis插件来实现自动填充字段的功能。
为什么不用mybatis-plus
相信大家在增删改查的时候都会有一个自动填充字段的需求,一翻网上的”攻略“,大部分文章都会告诉你用mybatis-plus。但是如果我们想要对mybatis有一个较深的了解,那么应该多用mybatis而不是mybatis-plus,一来因为面试的时候面试官可不会问你mybatis-plus那么几个简单的CRUD操作,二来强迫自己使用mybatis的话能够让自己学习到更多底层的原理和知识,对自己的发展是非常有益的。
那么,文归正题。
这个自定义插件主要用来做什么?
我们今天主要是要写一个mybatis插件,这个插件的任务就是能够让我们在进行insert和update操作的时候自动地对某些自定义的字段进行自定义的填充。
网上有很多的教程也是教你如何使用mybatis拦截器来实现字段的自动填充,但是,很多都是将自动填充的逻辑写死在拦截方法中,没办法做到自定义。
比如说,我有一个实体,我要在插入或者更新的时候自动填充它的createTime和updateTime。那么好,我吭哧吭哧写了一个拦截器,里面告诉mybatis说我要填充createTime和updateTime。但是第二天,主管告诉我,createId和updateId也要能够自动填充,行,再往逻辑里加。
这样一来,问题就出现了,当有新的自动填充的需求的时候,我都需要去修改代码逻辑,而且还要兼顾原有的实体类。这样到了后期,代码会非常难维护和修改。
让自填字段配置化
在经历了以上的问题后,我们肯定会想,能不能够用配置文件来定义这些自填充字段,并且不会影响到没有这些字段的实体的插入和更新?
答案是肯定的。
话不多说,先上代码
@Intercepts({
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class MybatisInsertAutoFillPlugin implements Interceptor {
private String[] fields;
private String[] values;
private String[] expressions;
@Override
public Object plugin(Object target) {
return Interceptor.super.plugin(target);
}
@Override
public void setProperties(Properties properties) {
this.fields = properties.stringPropertyNames().toArray(new String[0]);
expressions = new String[fields.length];
values = new String[fields.length];
for (int i = 0; i < expressions.length; i++) {
expressions[i] = properties.getProperty(fields[i]);
values[i] = "?";
}
}
@Override
public Object intercept(Invocation invocation) throws Throwable {
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
SqlCommandType commandType = mappedStatement.getSqlCommandType();
if (commandType == SqlCommandType.INSERT) {
Object p = invocation.getArgs()[1];
SpelExpressionParser parser = new SpelExpressionParser();
for (int i = 0; i < this.fields.length; i++) {
ReflectUtil.setFieldValue(p, this.fields[i], parser.parseExpression(this.expressions[i]).getValue());
}
}
return invocation.proceed();
}
}
使用的时候,只需要在安装mybatis插件的时候定义我们要自填充的字段就可以了。
如下配置文件所示,我们在plugins标签中引入我们的自填充插件MybatisInsertAutoFillPlugin,然后在它下面的property中填写要填充的字段名和要填充的值。
<?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>
<!--开启驼峰命名-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<plugins>
<plugin interceptor="com.yahaha.cloud.core.plugin.MybatisPagePlugin">
<property name="dialect" value="mysql"/>
</plugin>
<plugin interceptor="com.yahaha.cloud.core.plugin.MybatisInsertAutoFillPlugin">
<property name="createTime" value="T(java.time.LocalDateTime).now()"/>
<property name="updateTime" value="T(java.time.LocalDateTime).now()"/>
</plugin>
<plugin interceptor="com.yahaha.cloud.core.plugin.MybatisUpdateAutoFillPlugin">
<property name="updateTime" value="T(java.time.LocalDateTime).now()"/>
</plugin>
</plugins>
</configuration>
代码不长,也很容易懂。
1、读取自定义填充字段
首先就是 setProperties方法,这个方法就是用来读取配置文件中plugin下的property的,我们在这个方法里将填充字段名和它们的值表达式进行了存储,分别存到了fields数组和expressions数组中,至于values,是没有用的(懒得删了)。
2、解析表达式
重点就是intercept方法了,首先我们拿到mappedStatement对象,你可以把它认为是对mapper.xml文件中定义的sql语句的描述对象。
然后通过mappedStatement获取commandType,这个commandType就是表示你这条sql语句是什么类型的,如select、update、insert、delete。接下来我们用
if (commandType == SqlCommandType.INSERT)来判断这条sql是不是插入sql。
如果是,就创建一个SpelExpressionParser对象,这个对象是用来做什么的呢?看回xml配置文件,里面我们对createTime的值的定义并不是一个固定值,而是一个表达式,因为我们需要即时地计算当前的时间,而这个SpelExpressionParser对象就是用来解析这条表达式的。
此时我们的createTime就可以获得LocalDateTime.now()的返回值了。
3、填充字段值
我们拦截的是Executor的update方法,这个方法有两个传参,第一个就是mappedStatement,就是我们要执行的当前sql的描述对象,第二个就是这条sql语句的动态参数。
我们用Object p = invocation.getArgs()[1]来获取这个动态参数对象,然后用反射的方法填充其中字段的值,ReflectUtil是我自己写的一个反射工具类,如果有需要的话,可以通过这个链接去下载。
Java反射工具类
当然,使用一些第三方的反射工具类也是可以的。
最后调用proceed方法,完成我们的拦截任务。