项目中我们经常碰到以下几种情况:
1.实体类中存在着很多相同的数据库字段,例如:创建日期、修改日期等等。
2.字段属性具备默认值。例如:是否删除、是否有效等等
对于相同字段的,我们可以抽父类,然后父类中构建相应的set方法,但是那些非父类的属性处理其他太麻烦,还容易忘记。
尽管MybatisPlus也具备此功能,但某些时候并不能满足我们的需求,因此打造一个自己的填充器是一个非常不错的选择。
1. 填充器注解枚举类:
package org.bluedream.dream.fill.annotation;
/**
* TargetField 枚举
*/
public enum FillType {
DEFAULT,
INSERT,
UPDATE,
INSERT_UPDATE;
private FillType() {
}
}
2. 填充器注解:
package org.bluedream.dream.fill.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//描述注解的使用范围,取值于:ElementType(PACKAGE:包、TYPE:[类、接口、枚举、Annotation类型]、CONSTRUCTOR:用于描述构造器、FIELD:描述属性、METHOD:方法
@Target(value = {ElementType.FIELD})
//注解的保存级别:取值于:RetentionPolicy(SOURCE:源文件有效、CLASS:class文件有效、RUNTIME:运行时有效,为RUNTIME时可以被反射读取。
@Retention(value = RetentionPolicy.RUNTIME)
public @interface TargetField {
FillType fill() default FillType.DEFAULT;
String value() default "";
String fillMethod();
}
3. 注解解释类:
package org.bluedream.dream.fill;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.bluedream.dream.fill.annotation.FillType;
@Data
@AllArgsConstructor
public class AnnotationAnalysis {
private String fieldName; //属性名称
private Class fieldType; //属性类型
private FillType fillType; //填充时机:DEFAULT:insert
private String fillMethodName; //填充方法名
private String fillValue; //填充值
}
4. 填充器:
package org.bluedream.dream.fill;
import org.bluedream.dream.fill.annotation.TargetField;
import org.bluedream.dream.utils.CreateGUIDUtil;
import org.bluedream.dream.utils.EmptyUtil;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 实体类 公共字段 填充器
* TargetField 注解说明:
* fill:确定何时 加载填充器。取值类型:FillType 枚举值:DEFAULT\INSERT\UPDATE\INSERT_UPDATE , DEFAULT时,插入时有效。
* value:
* 1.属性为 Timestamp 类型时,value值无效,填充器自动取当前时间。boolean、Boolean时,传入 字符串true或者false。字符串类型直接传值即可
* 2.id字段,系统自动生成id,如需手工传入,请重写GoalFieldFill类中的setEntity方法。
* 3.默认 填充器会自动处理 String、Timestamp、boolean、Boolean数据,其他类型需要重写 GoalFieldFill类中的setEntity方法。
* fillMethod:定义 填充器 调用的set方法名
* @param <T>
*/
public class GoalFieldFill<T> {
private List<Field> fieldList = new ArrayList<>() ;
private List<Method> methods = new ArrayList<>();
private List<AnnotationAnalysis> annotationAnalyses = new ArrayList<>();
private boolean isNewRecord = false;
/**
* Test
* @param args
* @throws Exception
*/
/* public static void main(String[] args) throws Exception {
EntityFill ef = new EntityFill();
BaseEntity be = new BaseEntity();
ef.fillUpdate(be);
}*/
/**
* Insert 填充器
* @param entity
* @return
* @throws Exception
*/
public T fillInsert(T entity) throws Exception{
setAnnotationAnalyses(entity);
setInsertEntity(entity);
return entity;
}
/**
* Update 填充器
* @param entity
* @return
* @throws Exception
*/
public T fillUpdate(T entity) throws Exception{
setAnnotationAnalyses(entity);
setUpdateEntity(entity);
return entity;
}
/**
* Insert 注入:INSERT 、 INSERT_UPDATE 时插入相关属性
* @param entity
* @throws Exception
*/
private void setInsertEntity(T entity) throws Exception {
Method m;
for (AnnotationAnalysis f1:this.annotationAnalyses
) {
if ("DEFAULT".equals(f1.getFillType().toString()) || "INSERT".equals(f1.getFillType().toString()) || "INSERT_UPDATE".equals(f1.getFillType().toString()))
setEntity(entity, f1);
}
}
/**
* Update 注入:UPDATE、INSERT_UPDATE 时插入相关属性
* @param entity
* @throws Exception
*/
private void setUpdateEntity(T entity) throws Exception{
Method m;
for (AnnotationAnalysis f1:this.annotationAnalyses
) {
if ("UPDATE".equals(f1.getFillType().toString()) || "INSERT_UPDATE".equals(f1.getFillType().toString()))
setEntity(entity, f1);
}
}
/**
* 填充器 基础方法 允许重写此方法
* @param entity
* @param f1 填充器解析
* @throws NoSuchMethodException
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
public void setEntity(T entity, AnnotationAnalysis f1) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
Method m;
String fillType = f1.getFillType().toString();
String fieldType = f1.getFieldType().toString();
boolean isField = isField(f1.getFieldName() , entity);
boolean isMethod = isMethod(f1.getFillMethodName() , entity);
if (isField && isMethod){
m = entity.getClass().getMethod(f1.getFillMethodName() , f1.getFieldType());
if ("class java.sql.Timestamp".equals(fieldType)){
Timestamp nowTime = new Timestamp(System.currentTimeMillis());
m.invoke(entity , nowTime);
}else if ("boolean".equals(fieldType) || "class java.lang.Boolean".equals(fieldType)){
m.invoke(entity , Boolean.parseBoolean(f1.getFillValue()));
}
else if ("id".equals(f1.getFieldName())){
m.invoke(entity , CreateGUIDUtil.GenerateGUID());
}else {
m.invoke(entity , f1.getFillValue());
}
}
}
/**
* 根据获取到的含有 TargetField 注解的属性,初始化 AnnotationAnalysis 信息 ,并添加到 List
* @param entity
*/
private void setAnnotationAnalyses(T entity){
try {
//获取类中所有属性
this.fieldList = getObjectFields(entity);
for (Field f1:fieldList
) {
TargetField targetField = f1.getAnnotation(TargetField.class);
if (EmptyUtil.isNoEmpty(targetField)){
AnnotationAnalysis annotationAnalysis = new AnnotationAnalysis(f1.getName() , f1.getType() , targetField.fill() , targetField.fillMethod() , targetField.value());
this.annotationAnalyses.add(annotationAnalysis);
}
}
}catch (Exception e1){
e1.printStackTrace();
}
}
/**
* 返回 entity 对象中的 isNewRecord 属性值
* @param entity
* @return
* @throws Exception
*/
public boolean getIsNewRecord(T entity) throws Exception {
Method m;
boolean newRecordIsExist = isMethod("getIsNewRecord" , entity);
if (newRecordIsExist){
m = entity.getClass().getMethod("getIsNewRecord");
isNewRecord = (boolean)m.invoke(entity);
}
return isNewRecord;
}
/**
* 返回 entity 对象的所有属性,包含父类
* @param entity
* @return
*/
private List<Field> getObjectFields(T entity) throws NullPointerException{
Class clazz = entity.getClass();
while (clazz != null) {//当父类为null的时候说明到达了最上层的父类(Object类).
fieldList.addAll(Arrays.asList(clazz .getDeclaredFields()));
clazz = clazz.getSuperclass(); //得到父类,然后赋给自己
}
return fieldList;
}
/**
* 返回 字段的 type信息
* @param fieldName
* @param entity
* @return
* @throws Exception
*/
private String getFieldType(String fieldName , T entity) throws Exception{
Class clazz = entity.getClass();
return clazz.getDeclaredField(fieldName).getType().toString();
}
/**
* 判断 Class entity 是否存在名称为 fieldName 的属性
* @param fieldName
* @param entity
* @return
* @throws NullPointerException
*/
private Boolean isField(String fieldName , T entity) throws NullPointerException{
fieldList = getObjectFields(entity);
for (Field f1:fieldList
) {
if (fieldName.equals(f1.getName()))
return true;
}
return false;
}
/**
* 返回 entity 对象中的所有方法,包含父类
* @param entity
* @return
* @throws NullPointerException
*/
private List<Method> getObjectMethods(T entity) throws NullPointerException{
Class clazz = entity.getClass();
while (clazz != null) {//当父类为null的时候说明到达了最上层的父类(Object类).
methods.addAll(Arrays.asList(clazz .getDeclaredMethods()));
clazz = clazz.getSuperclass(); //得到父类,然后赋给自己
}
return methods;
}
/**
* 判断 Class entity 是否存在名称为 methodName 的方法
* @param methodName
* @param entity
* @return
* @throws NullPointerException
*/
private Boolean isMethod(String methodName , T entity) throws NullPointerException{
methods = getObjectMethods(entity);
for (Method m1:methods
) {
if (methodName.equals(m1.getName()))
return true;
}
return false;
}
}
5. 填充器如何使用:
5.1 实体类属性添加注解:
// todo:测试 注入值
@TargetField(fill = FillType.INSERT , value = "system" , fillMethod = "setCreateBy")
protected String createBy;
5.2 service层相关方法填充:
@Override
public boolean save(T entity) {
GoalFieldFill<T> fill = new GoalFieldFill<>();
try {
boolean isNewRecord = fill.getIsNewRecord(entity);
if (isNewRecord){
fill.fillInsert(entity);
}else {
return updateById(entity);
}
}catch (Exception e1){
e1.printStackTrace();
}
return super.save(entity);
}