更多MyBatis实战内容,请参考:MyBatis - 实战指南
1. 场景
在设计数据库字段时,往往会有表示创建时间或者更新时间的字段,假如名字分别叫create_time
和update_time
。如果在每个表的insert或update时,都要手动设置这些字段,则会很麻烦。
通过MyBatis拦截器,就可省去这些重复的步骤。拦截器能帮我们自动设置时间。
2. 实现方法
固定字段
假如说,数据库只管的每个table中,创建和更新时间都是一样的,这样是最方便的,也比较统一。
针对这种情况,可以直接写个通用的拦截器:
@Intercepts({
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class CustomInterceptor implements Interceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(EnvInterceptor.class);
@Override
public Object intercept(Invocation invocation) throws Throwable {
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
Object object = invocation.getArgs()[1];
//sql类型
SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
if (SqlCommandType.INSERT.equals(sqlCommandType)) {
//插入操作时,自动插入env
Field fieldCreate = object.getClass().getDeclaredField("create_time");
fieldCreate.setAccessible(true);
fieldCreate.set(object, new Date());
}else{
if (SqlCommandType.UPDATE.equals(sqlCommandType)) {
//update时,自动更新updated_at
Field fieldUpdate = object.getClass().getDeclaredField("updated_time");
fieldUpdate.setAccessible(true);
fieldUpdate.set(object, new Date());
}
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
注解形式
首先,实现自定义注解:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface CreateTime {
String value() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface UpdateTime {
String value() default "";
}
然后将自定义注解添加到对应的Date类型字段上:
@Data
public class Category {
private Integer id;
private String name;
private String descr;
@CreateTime
private Date createTime;
@UpdateTime
private Date updateTime;
}
实现自定义插件:
/**
* 自定义 Mybatis 插件,自动设置 createTime 和 updatTime 的值。
* 拦截 update 操作(添加和修改)
*/
@Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) })
public class CustomInterceptor implements Interceptor {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public Object intercept(Invocation invocation) throws Throwable {
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
// 获取 SQL 命令
SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
// 获取参数
Object parameter = invocation.getArgs()[1];
// 获取私有成员变量
Field[] declaredFields = parameter.getClass().getDeclaredFields();
for (Field field : declaredFields) {
if (field.getAnnotation(CreateTime.class) != null) {
if (SqlCommandType.INSERT.equals(sqlCommandType)) { // insert 语句插入 createTime
field.setAccessible(true);
field.set(parameter, new Date());
}
}
if (field.getAnnotation(UpdateTime.class) != null) { // insert 或 update 语句插入 updateTime
if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
field.setAccessible(true);
field.set(parameter, new Date());
}
}
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
原理是:
- Object parameter = invocation.getArgs()[1]; 获取执行对象
- 通过反射获取对象所有的Field
- 遍历所有Field,查看是否有指定注解
- 如果有
@CreateTime
或者@UpdateTime
注解,则设置最新时间
注册拦截器
最后,需要将拦截器注册到插件中
<?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>
<plugins>
<plugin interceptor="com.extlight.plugin.CustomInterceptor">
</plugin>
</plugins>
</configuration>