mybatis更新时记录变更的字段日志java反射获取字段值转成string

news2024/11/15 17:42:16

 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 "";
    }

3.最后在业务数据中展示这些变更日志时,可能还要在查询时转换下,必须存储的项目Id,实际日志要展示项目中文,那就在日志列表的接口中自己识别转换下,最后效果如下:

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/823703.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

iPhone 8透明屏的透明度高吗?

iPhone 8是苹果公司于2017年推出的一款智能手机&#xff0c;它采用了全新的设计和技术&#xff0c;其中一个亮点就是透明屏。 透明屏是指屏幕具有透明度&#xff0c;可以透过屏幕看到背后的物体。 iPhone 8的透明屏采用了最新的OLED技术&#xff0c;这种技术可以实现更高的对比…

Jmeter —— jmeter参数化实现

jmeter参数化 在实际的测试工作中&#xff0c;我们经常需要对多组不同的输入数据&#xff0c;进行同样的测试操作步骤&#xff0c;以验证我们的软件的功能。这种测试方式在业界称为数据驱动测试&#xff0c; 而在实际测试工作中&#xff0c;测试工具中实现不同数据输入的过程称…

替代LT8711龙讯替代RTD2172 CS5265中文规格书4K60HZ转接线 设计Type-C转HDMI2.0高清投屏方案

龙迅LT8711是一款Type-C/DP1.2 to HDMI2.0方案芯片&#xff0c;北京集睿致远&#xff08;ASL&#xff09;推出的CS5265可以完全代替LT8711UX&#xff0c;封装尺寸比LT8711UX小的同时&#xff0c;CS5265的芯片集成度高&#xff0c;内置MCU&#xff0c;内置lLDO等&#xff0c;CS5…

【外卖系统】菜品信息分页查询

需求分析 当菜品数据很多时&#xff0c;用分页的形式来展示列表数据 代码开发 页面发送ajax请求&#xff0c;将分页查询参数提交到服务端&#xff0c;获取分页数据页面发送请求&#xff0c;请求服务端进行图片下载&#xff0c;用于页面图片展示 构造分页 注意&#xff1a;…

Unity简单抽奖效果实现

实现效果如下&#xff1a; 实现效果为&#xff1a;外部传入数值&#xff0c;正常显示 lhj抽取效果 用户可自定义行为&#xff1a;1&#xff0c;抽取的显示&#xff1b;2&#xff0c;抽取的最低时间 实现代码如下&#xff0c;可自行改写&#xff1a; using System.Collection…

最新 23 届计算机校招薪资汇总

24 届的秋招提前批已经开始了&#xff0c;比如米哈游、oppoe、tplink 等公司都已经录取开启提前批。 像腾讯、字节、阿里等一线大厂的话&#xff0c;根据往年的情况&#xff0c;估计是 7月下-8 月初。 所以今年参加秋招的同学&#xff0c;要抓紧复习了。 提前批通常就持续不到…

Minio 部署

minio 官网&#xff1a;https://www.minio.org.cn/ 部署文档&#xff1a;https://www.minio.org.cn/docs/minio/container/operations/install-deploy-manage/deploy-minio-single-node-single-drive.html# 选择自己的部署环境&#xff1a; 我用的docker: docker pull qua…

命令模式——请求发送者与接收者解耦

1、简介 1.1、概述 在软件开发中&#xff0c;经常需要向某些对象发送请求&#xff08;调用其中的某个或某些方法&#xff09;&#xff0c;但是并不知道请求的接收者是谁&#xff0c;也不知道被请求的操作是哪个。此时&#xff0c;特别希望能够以一种松耦合的方式来设计软件&a…

LaTex的下载与安装超详细windows版

1.LaTex的下载 &#xff08;texlive下载TexStudio下载&#xff09; &#xff08;1&#xff09;texlive下载&#xff1a; 这里清华镜像下载 &#xff08;2&#xff09;TexStudio下载&#xff1a; 点这里下载镜像 可以根据不同的系统选择不同的版本 2 .LaTex的安装 &#…

1分钟解决github push/pull报错443

1.打开https://www.ipaddress.com/ 2.复制如图IP地址 3.文件夹打开C:\Windows\System32\drivers\etc&#xff0c;复制hosts文件&#xff0c;粘贴到桌面 4.在桌面用记事本打开复制过来的hosts 5.在末尾加上一行&#xff0c;IP写刚才复制的 6.复制桌面的hosts,粘贴回C:\Window…

web开发中的安全和防御入门——csp (content-security-policy内容安全策略)

偶然碰到iframe跨域加载被拒绝的问题&#xff0c;原因是父页面默认不允许加载跨域的子页面&#xff0c;也就是的content-security-policy中没有设置允许跨域加载。 简单地说&#xff0c;content-security-policy能限制页面允许和不允许加载的所有资源&#xff0c;常见的包括&a…

3DEXPERIENCE用户角色 | Structural Performance Engineer 结构性能工程师

- 3DEXPERIENCE 用户角色 - 快速、真实和准确的仿真 推动产品工程创新并促进协作 通过 Structural Performance Engineer&#xff0c;您可以在基于云的 3DEXPERIENCE 平台上执行结构静态、频率、扭曲、模态动态响应&#xff0c;以及零件和装配体结构热仿真。 亮点 与 SOLIDW…

统一观测|借助 Prometheus 监控 ClickHouse 数据库

引言 ClickHouse 作为用于联机分析(OLAP)的列式数据库管理系统(DBMS), 最核心的特点是极致压缩率和极速查询性能。同时&#xff0c;ClickHouse 支持 SQL 查询&#xff0c;在基于大宽表的聚合分析查询场景下展现出优异的性能。因此&#xff0c;获得了广泛的应用。本文旨在分享阿…

【Golang】基于录制,自动生成go test接口自动化用例

目录 背景 框架 ginkgo初始化 抓包&运行脚本 目录说明 ∮./business ∮./conf ∮./utils ∮./testcase testcase 用例目录结构规则 示例 实现思路 解析Har数据 定义结构体 解析到json 转换请求数据 转换请求 转换请求参数 写业务请求数据 写gotest测试…

K8s总结

K8s 是什么 Kubernetes是一个开源的&#xff0c;用于管理云平台中多个主机上的容器化的应用&#xff0c;Kubernetes的目标是让部署容器化的应用简单并且高效&#xff08;powerful&#xff09;,Kubernetes提供了应用部署&#xff0c;规划&#xff0c;更新&#xff0c;维护的机制…

【从零开始学习JAVA | 第三十五篇】IO流综合练习

目录 前言&#xff1a; 1.拷贝文件&#xff08;含子文件&#xff09; 思路&#xff1a; 2.文件加密 思路&#xff1a; 3.修改文件中的数据&#xff1a; 思路&#xff1a; 总结&#xff1a; 前言&#xff1a; 在前面我们为大家介绍了FILE类和IO类。这篇文章我们来练习一…

SQL SERVER 中将数据表中的字段按分隔符分成多行多列

SQL SERVER 中将数据表中的字段按分隔符分成多行多列_sql按逗号拆分列为多行_帅气的苹果果的博客-CSDN博客 SELECTa.name,monitors SUBSTRING ( a.monitors, b.number, charindex( ,, a.monitors ,, b.number ) - b.number ) FROM( SELECT name, monitors FROM [dbo].[ssm_vi…

Packet Tracer - 连接有线和无线 LAN

Packet Tracer - 连接有线和无线 LAN 地址分配表 设备 接口 IP 地址 连接到 云 Eth6 N/A Fa0/0 Coax7 N/A Port0 电缆调制解调器 Port0 N/A Coax7 Port1 N/A 互联网 Router0 控制台端口 N/A RS232 Fa0/0 192.168.2.1/24 Eth6 Fa0/1 10.0.0.1/24 Fa…

安科瑞智能照明系统在医院智能建筑中应用的优势

摘要&#xff1a;现阶段&#xff0c;我国的社会经济的发展水平不断提高&#xff0c;为智能照明系统的发展带来了新的契机。文章主要介绍了几类智能照明系统&#xff0c;分析了其优点&#xff0c;并介绍了智能照明系统在医院建筑中的具体应用&#xff0c;具有一定的参考价值。 …

屏幕取色器Mac版_苹果屏幕取色工具_屏幕取色器工具

Sip for Mac 是Mac系统平台上的一款老牌的颜色拾取工具&#xff0c;是设计师和前端开发工作者必不可少的屏幕取色软件&#xff0c;你只需要用鼠标点一下即可轻松地对屏幕上的任何颜色进行采样和编码&#xff0c;并将颜色数据自动存到剪切板&#xff0c;方便随时粘贴出来。 Sip…