mybatis更新时记录变更的字段日志&&java反射获取字段值转成string
·
1.先写个注解,加在entity的字段上,标记要记录这个字段的更新记录,再加个变更记录表(你们可以用自己的日志表)
@Retention(RetentionPolicy.RUNTIME)
@Target({java.lang.annotation.ElementType.FIELD})
@Documented
public @interface ModifyAware {
/**
* 字段名称,这里直接写,不去找其他注解或者解析doc了,必填
*/
public abstract String name();
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@TableName("modifyrecord")
@ApiModel("变更记录表")
public class ModifyRecord extends CommonBaseEntity {
private static final long serialVersionUID = 7314951651619250712L;
/**
* 变更表名
*/
@TableField("tableName")
@ApiModelProperty("变更表名/业务名称")
private String tableName;
/**
* 变更记录Id
*/
@TableField("recordKey")
@ApiModelProperty("变更记录Id")
private Integer recordKey;
/**
* 变更字段
*/
@TableField("modifyColumn")
@ApiModelProperty("变更字段")
private String modifyColumn;
/**
* 变更前内容
*/
@ApiModelProperty("变更前内容")
@TableField("beforeContent")
private String beforeContent;
/**
* 变更后内容
*/
@TableField("afterContent")
@ApiModelProperty("变更后内容")
private String afterContent;
/**
* 变更时间
*/
@TableField("modifyTime")
@ApiModelProperty("变更时间")
private Date modifyTime;
/**
* 变更原因
*/
@TableField("modifyReason")
@ApiModelProperty("变更原因")
private String modifyReason;
/**
* 变更人
*/
@TableField("modifier")
@ApiModelProperty("变更人")
private String modifier;
/**
* 变更人姓名
*/
@TableField(exist = false)
@ApiModelProperty("变更人姓名")
private String modifierName;
public ModifyRecord(String tableName, Integer recordKey, String modifyColumn, String beforeContent, String afterContent, Date modifyTime, String modifyReason, String modifier) {
this.tableName = tableName;
this.recordKey = recordKey;
this.modifyColumn = modifyColumn;
this.modifyTime = modifyTime;
this.beforeContent = beforeContent;
this.afterContent = afterContent;
this.modifyReason = modifyReason;
this.modifier = modifier;
}
}
2.再在BaseService和impl实现中增加modify方法(如果没有就自己写,基本上用mybatis都会自己包一层service(继承IService)服务,多定义一些好用的增删改查的方法),等同update,实现变更记录的逻辑,最好前端增加一个变更原因字段,带到entity里面,做变更原因记录,我这里先写死;
PS:这里反射取值时,直接穷举了字段基本类型,常规开发就这些,暂不支持list,map等集合的变化,有需要自己多写个if分支拓展下。
public int modify(T entity) {
Integer id = (Integer) EntityUtils.getFieldValueByFieldName("id", entity);
//获取旧数据
T old = this.getById(id);
//执行更新,保证业务执行
int num = getBaseMapper().updateById(entity);
//获取加了注解的字段
Field[] fields = entity.getClass().getDeclaredFields();
String className = entity.getClass().getSimpleName();
List<ModifyRecord> modifyRecordList = Lists.newArrayList();
for (Field field : fields) {
field.setAccessible(true);
if (!field.isAnnotationPresent(ModifyAware.class)) {
continue;
}
ModifyAware attr = field.getAnnotation(ModifyAware.class);
//获取实体旧值
Object oldValue = EntityUtils.getFieldValueByFieldName(field.getName(), old);
//获取新值
Object newValue = EntityUtils.getFieldValueByFieldName(field.getName(), entity);
//新旧比较
String beforeContent = "";
String afterContent = "";
if (null == oldValue && null == newValue) {
continue;
} else if (null == oldValue) {
afterContent = getFieldValue(field, newValue);
} else if (null == newValue) {
beforeContent = getFieldValue(field, oldValue);
} else {
beforeContent = getFieldValue(field, oldValue);
afterContent = getFieldValue(field, newValue);
}
if (!beforeContent.equals(afterContent)) {//前后不一致
//保存变更
modifyRecordList.add(new ModifyRecord(className, id, attr.name(), beforeContent, afterContent, new Date(), "信息更新", LoginUtil.getLoginName()));
}
}
//无则直接返回
if (CollectionUtils.isEmpty(modifyRecordList)) {
return num;
}
//有直接存储
ModifyRecordMapper modifyRecordMapper = SpringContextUtil.getBean(ModifyRecordMapper.class);
modifyRecordMapper.insertBatch(modifyRecordList);
return num;
}
/**
* 获取字段值,并转成string
*
* @param field 字段
* @param value value
* @return 字符串类型的值
*/
private static String getFieldValue(Field field, Object value) {
String typeName = field.getType().getSimpleName();
if ("Integer".equalsIgnoreCase(typeName)) {//数字类型
return String.valueOf(value);
} else if ("BigInteger".equalsIgnoreCase(typeName)) {//数字类型
return String.valueOf(value);
} else if ("Short".equalsIgnoreCase(typeName)) {//数字类型
return String.valueOf(value);
} else if ("Float".equalsIgnoreCase(typeName)) {//数字类型
return String.valueOf(value);
} else if ("Double".equalsIgnoreCase(typeName)) {//数字类型
return String.valueOf(value);
} else if ("Long".equalsIgnoreCase(typeName)) {//数字类型
return String.valueOf(value);
} else if ("BigDecimal".equalsIgnoreCase(typeName)) {//数字类型
return ((BigDecimal) value).toPlainString();
} else if ("String".equalsIgnoreCase(typeName)) {//字符串
return value.toString();
} else if ("Date".equalsIgnoreCase(typeName)) {//日期
SimpleDateFormat sdf = new SimpleDateFormat(DatePattern.YYYY_MM_DD_HH_MM_SS);
return sdf.format(value);
}
return "";
}
工具类直接用
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* 实体操作工具
*
* @author storm
* @since 2020/11/10 21:15
*/
public class EntityUtils {
/**
* 实体集合转为id,entity的map
*
* @param entityList 集合
* @param <T> 泛型
* @return map
*/
public static <T> Map<Integer, T> toMap(List<T> entityList) {
Map<Integer, T> rMap = Maps.newHashMap();
for (T entity : entityList) {
rMap.put((Integer) EntityUtils.getFieldValueByFieldName("id", entity), entity);
}
return rMap;
}
/**
* 根据属性名获取属性值
*
* @param fieldName 属性名称
* @param object 对象
* @return 属性值
*/
public static Object getFieldValueByFieldName(String fieldName, Object object) {
try {
Field field = getFieldMap(object.getClass(), fieldName).get(fieldName);
if (null == field) {
return null;
}
//设置对象的访问权限,保证对private的属性的访问
field.setAccessible(true);
return field.get(object);
} catch (Exception e) {
return null;
}
}
/**
* 递归获取所有访问字段/搜索指定field并返回
*
* @param tempClass class
* @return map结果
*/
public static Map<String, Field> getFieldMap(Class<?> tempClass, String fieldName) {
Map<String, Field> rMap = Maps.newHashMap();
w:while (tempClass != null && !tempClass.isAssignableFrom(Object.class)) {//当父类为null的时候说明到达了最上层的父类(Object类).
for (Field field : tempClass.getDeclaredFields()) {
if (StringUtils.isNotEmpty(fieldName)) {
if (fieldName.equals(field.getName())) {
rMap.put(field.getName(), field);
break w;
}
} else {
rMap.put(field.getName(), field);
}
}
tempClass = tempClass.getSuperclass(); //得到父类继续循环
}
return rMap;
}
/**
* 获取实体的属性名集合(包含父级)
*
* @param tempClass 实体class
* @return 所有字段的arr
*/
public static String[] getAllFiledNameArr(Class<?> tempClass) {
List<String> fieldFiledNameList = Lists.newArrayList();
while (tempClass != null && !tempClass.isAssignableFrom(Object.class)) {//当父类为null的时候说明到达了最上层的父类(Object类).
for (Field field : tempClass.getDeclaredFields()) {
if (!"serialVersionUID".equals(field.getName())) {//排除序列号
fieldFiledNameList.add(field.getName());
}
}
tempClass = tempClass.getSuperclass(); //得到父类继续循环
}
return fieldFiledNameList.toArray(new String[0]);
}
/**
* 获取实体的属性名集合(不包含父级字段)
*
* @param entityClass 实体class
* @return 数据
*/
public static String[] getFiledNameArr(Class<?> entityClass) {
Field[] fields = entityClass.getDeclaredFields();
List<String> fieldFiledNameList = Lists.newArrayList();
for (Field field : fields) {
if (!"serialVersionUID".equals(field.getName())){
fieldFiledNameList.add(field.getName());
}
}
return fieldFiledNameList.toArray(new String[0]);
}
/**
* 获取实体的属性名集合(不包含父级字段和集合和map)
*
* @param entityClass 实体class
* @return 数据
*/
public static String[] getFiledNameArrWithoutCollection(Class<?> entityClass) {
Field[] fields = entityClass.getDeclaredFields();
List<String> fieldFiledNameList = Lists.newArrayList();
for (Field field : fields) {
if (!"serialVersionUID".equals(field.getName())&&!Collection.class.isAssignableFrom(field.getType())
&&!Map.class.isAssignableFrom(field.getType())) {//排除序列号,List和Map
fieldFiledNameList.add(field.getName());
}
}
return fieldFiledNameList.toArray(new String[0]);
}
/**
*
* @description: 实体类转Map
* @param object
* @return
*/
public static Map<String, Object> entityToMap(Object object) {
Map<String, Object> map = Maps.newHashMap();
for (Field field : object.getClass().getDeclaredFields()) {
try {
boolean flag = field.isAccessible();
field.setAccessible(true);
Object o = field.get(object);
map.put(field.getName(), o);
field.setAccessible(flag);
} catch (Exception e) {
e.printStackTrace();
}
}
return map;
}
/**
*
* @description: Map转实体类
* @param <T>
* @param map 需要初始化的数据,key字段必须与实体类的成员名字一样,否则赋值为空
* @param entity 需要转化成的实体类
* @return
*/
public static <T> T mapToEntity(Map<String, Object> map, Class<T> entity) {
T t = null;
try {
t = entity.newInstance();
for (Field field : entity.getDeclaredFields()) {
if (map.containsKey(field.getName())) {
boolean flag = field.isAccessible();
field.setAccessible(true);
Object object = map.get(field.getName());
if (object != null && field.getType().isAssignableFrom(object.getClass())) {
field.set(t, object);
}
field.setAccessible(flag);
}
}
return t;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return t;
}
}
3.最后在业务数据中展示这些变更日志时,可能还要在查询时转换下,必须存储的项目Id,实际日志要展示项目中文,那就在日志列表的接口中自己识别转换下,最后效果如下:

更多推荐
所有评论(0)