引入
在實際開發中,總會避免不了操作數據庫,而在數據庫中每個表都會有create_time
和update_time
字段記錄操作時間,我們在操作這兩個時間的時候也可能會出現不一致的情況,或者說這兩個字段實際上應該是系統生成的,而不是用戶去手動處理,於是想着在新增和修改操作的時候能讓系統自動處理這兩個字段。
實戰
1.導入pom文件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.1</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.mybatis.spring.boot</groupId>-->
<!-- <artifactId>mybatis-spring-boot-starter</artifactId>-->
<!-- <version>2.0.1</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
注意
1.1.實現代碼中是基於Mybatis-plus實現;
1.2.如果不使用Mybatis-Plus可以使用注釋掉的依賴
2.實現SQL攔截器
@Intercepts(value = {@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
public class SqlInterceptor extends AbstractSqlParserHandler implements Interceptor {
/**
* 創建時間
*/
private static final String CREATE_TIME = "createTime";
/**
* 更新時間
*/
private static final String UPDATE_TIME = "updateTime";
@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();
if (parameter.getClass().getSuperclass() != null) {
Field[] superField = parameter.getClass().getSuperclass().getDeclaredFields();
declaredFields = ArrayUtils.addAll(declaredFields, superField);
}
// mybatis plus判斷
boolean plus= parameter.getClass().getDeclaredFields().length == 1 && parameter.getClass().getDeclaredFields()[0].getName().equals("serialVersionUID");
//兼容mybatis plus
if (plus) {
Map<String, Object> updateParam = (Map<String, Object>) parameter;
Class<?> updateParamType = updateParam.get("param1").getClass();
declaredFields = updateParamType.getDeclaredFields();
if (updateParamType.getSuperclass() != null) {
Field[] superField = updateParamType.getSuperclass().getDeclaredFields();
declaredFields = ArrayUtils.addAll(declaredFields, superField);
}
}
String fieldName = null;
for (Field field : declaredFields) {
fieldName = field.getName();
if (Objects.equals(CREATE_TIME, fieldName)) {
if (SqlCommandType.INSERT.equals(sqlCommandType)) {
field.setAccessible(true);
field.set(parameter, new Timestamp(System.currentTimeMillis()));
}
}
if (Objects.equals(UPDATE_TIME, fieldName)) {
if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
field.setAccessible(true);
//兼容mybatis plus的update
if (plus) {
Map<String, Object> updateParam = (Map<String, Object>) parameter;
field.set(updateParam.get("param1"), new Timestamp(System.currentTimeMillis()));
} else {
field.set(parameter, new Timestamp(System.currentTimeMillis()));
}
}
}
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
if (target instanceof Executor) {
return Plugin.wrap(target, this);
}
return target;
}
@Override
public void setProperties(Properties properties) {
}
}
注意
2.1.這里寫死了CREATE_TIME
和UPDATE_TIME
也是遵循約定大於配置的原則,而不需要再寫例如字段上增加上增加注解之類的方式實現,讓用戶使用更加簡潔。
2.2.這里繼承了Mybatis-Plus中AbstractSqlParserHandler
就可以不用自己重復造輪子去解析SQL,如果不是使用Mybatis-Plus則只需要直接實現Mybatis中的Interceptor
接口,自己實現SQL攔截解析即可。
3.注入自定義SQL攔截器
@Configuration
@MapperScan(value = "com.xx.mapper")
public class MybatisPlusConfig {
@Bean
public SqlInterceptor sqlInterceptor() {
return new SqlInterceptor();
}
}
注意
3.1.如果想要讓自定義的SQL攔截器生效,那么這一步必須有,即注入SqlInterceptor
。
4.工具類
public class ArrayUtils {
/**
* 兩個數組相加
* @param target
* @param source
* @return 相加后新的數組集合
*/
public static Field[] addAll(Field[] target, Field[] source) {
if (target != null) {
List<Field> fieldTarget = Stream.of(target).collect(Collectors.toList());
if (source != null) {
List<Field> fieldsSource = Stream.of(source).collect(Collectors.toList());
for (Field field : fieldsSource) {
fieldTarget.add(field);
}
}
target = fieldTarget.toArray(new Field[fieldTarget.size()]);
return target;
}
return target;
}
}
總結
1.導入pom文件,引入相關依賴;
2.繼承Mybatis-Plus中AbstractSqlParserHandler
抽象類,實現Mybatis的Interceptor
接口,在對象轉換成SQL之前賦指定的字段值;
3.想要自定義的SQL攔截器生效,那么就需要注入自定義SQL攔截器。