参考

背景

  • jpa自带的审计只关注了四个字段
    • @CreatedDate
    • @CreatedBy
    • @LastModifiedDate
    • @LastModifiedBy
  • 而业务需求中需要在填充两个个字段

实践 - 1

第一步 建立公共字段的实体

  • 关注点 @Embeddable ```java package io.tn.jpa.audit.bean;

import lombok.Getter; import lombok.Setter;

import javax.persistence.Column; import javax.persistence.Embeddable; import java.time.LocalDateTime;

/**

  • 测试审计 *
  • @author tn
  • @className AuditBean
  • @date 2021-02-03 12:09 */ @Embeddable @Getter @Setter public class AuditBean {

    /**

    • 创建人名称 */ @Column(columnDefinition = “ varchar(100) default ‘admin’ comment ‘创建人名称’”) private String createUserName;
  1. /**
  2. * 创建人uuid
  3. */
  4. @Column(columnDefinition = " varchar(100) default '1' comment '创建人uuid'")
  5. private String createUserUuid;
  6. /**
  7. * 更新人名称
  8. */
  9. @Column(columnDefinition = " varchar(100) comment '更新人名称'")
  10. private String updateUserName;
  11. /**
  12. * 更新人uuid
  13. */
  14. @Column(columnDefinition = " varchar(100) comment '更新人uuid'")
  15. private String updateUserUuid;
  16. /**
  17. * 表示该字段为创建时间字段,在这个实体被insert的时候,会自动为其赋值
  18. */
  19. @Column(columnDefinition = "timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期' ")
  20. private LocalDateTime createTime;
  21. /**
  22. * 表示该字段为修改时间字段,在这个实体被update的时候,会自动为其赋值
  23. */
  24. @Column(columnDefinition = "timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新日期'")
  25. private LocalDateTime updateTime;

}

  1. <a name="o2gOQ"></a>
  2. ## 第二步 数据实体实现公共字段
  3. - 关注点 `@Embeddable`
  4. - `@EntityListeners(AuditListener.class)`
  5. - `AuditListener.class<br />`
  6. ```java
  7. @Table(name = "mysql80")
  8. @Entity
  9. @Setter
  10. @Getter
  11. @Builder
  12. @AllArgsConstructor
  13. @NoArgsConstructor
  14. @DynamicInsert //动态插入
  15. @DynamicUpdate //动态赋值
  16. @EntityListeners(AuditListener.class)
  17. public class Mysql80 implements Auditable {
  18. @Id
  19. @GeneratedValue(strategy = GenerationType.IDENTITY)
  20. private Integer id;
  21. private String name,sex;
  22. @Embedded
  23. private AuditBean audit;
  24. }

第三步 新增接口

  1. package io.tn.jpa.audit.config;
  2. import io.tn.jpa.audit.bean.AuditBean;
  3. public interface Auditable {
  4. AuditBean getAudit();
  5. void setAudit(AuditBean audit);
  6. }

第四步 使用上面新增的接口建立监听器

  • 关注点 @PrePersist @PreUpdate ```java package io.tn.jpa.audit.config;

import io.tn.jpa.audit.bean.AuditBean;

import javax.persistence.PrePersist; import javax.persistence.PreUpdate; import java.time.LocalDateTime;

public class AuditListener {

  1. @PrePersist
  2. public void setCreatedOn(Auditable auditable) {
  3. AuditBean audit = auditable.getAudit();
  4. if(audit == null) {
  5. audit = new AuditBean();
  6. auditable.setAudit(audit);
  7. }
  8. audit.setCreateUserUuid("123");
  9. audit.setCreateUserName("asd");
  10. audit.setCreateTime(LocalDateTime.now());
  11. audit.setUpdateUserUuid("123");
  12. audit.setUpdateUserName("asd");
  13. audit.setUpdateTime(LocalDateTime.now());
  14. }
  15. @PreUpdate
  16. public void setUpdadtedOn(Auditable auditable) {
  17. AuditBean audit = auditable.getAudit();
  18. audit.setUpdateUserUuid("123");
  19. audit.setUpdateUserName("asd");
  20. audit.setUpdateTime(LocalDateTime.now());
  21. }

}

  1. <a name="cTMbv"></a>
  2. ## 第五步测试
  3. - dao
  4. ```java
  5. import com.detabes.jpa.server.dao.JpaBasicsDao;
  6. import io.tn.jpa.audit.bean.Mysql80;
  7. /**
  8. * 测试审计
  9. *
  10. * @author tn
  11. * @className Mysql80Dao
  12. * @date 2021-02-03 12:12
  13. */
  14. public interface Mysql80Dao extends JpaBasicsDao<Mysql80, Integer> {
  15. @Query("update Mysql80 m set m.sex=?1 where m.id=?2 ")
  16. @Modifying
  17. @Transactional(rollbackOn=Exception.class)
  18. int updata(String sex, Integer id);
  19. }
  • test
    • 注意点: 更新时发生了更新字段未更新,自定义sql时同样发生了字段为赋值 ```java package io.tn.jpa.audit;

import io.tn.jpa.audit.bean.Mysql80; import io.tn.jpa.audit.dao.Mysql80Dao; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;

@SpringBootTest class AuditApplicationTests {

  1. @Resource
  2. private Mysql80Dao mysql80Dao;
  3. @Test
  4. void contextLoads() {
  5. }
  6. @Test
  7. void testJpaAudit(){
  8. Mysql80 mysql80 = new Mysql80();
  9. mysql80.setName("adaa");
  10. mysql80.setSex("assda");
  11. mysql80Dao.save(mysql80);
  12. }
  13. @Test
  14. void testJpaAuditUpdate2() throws Exception {
  15. Mysql80 mysql801 = mysql80Dao.findById(3).get();
  16. mysql801.setSex("sad");
  17. mysql801.setSex("323");
  18. mysql80Dao.save(mysql801);
  19. }
  20. @Test
  21. void testJpaAuditUpdate() throws Exception {
  22. Mysql80 mysql80 = new Mysql80();
  23. mysql80.setName("我的");
  24. mysql80.setSex("我的");
  25. mysql80.setId(3);
  26. mysql80Dao.updateEntity(mysql80,"id");
  27. }
  28. @Test
  29. void testJpaAuditSql(){
  30. mysql80Dao.updata("mysql80",1);
  31. }

}

  1. <a name="d0Cak"></a>
  2. # 实践 - 2
  3. <a name="wI0QB"></a>
  4. ## 第一步 建立公共字段的实体
  5. - 关注点 `@EntityListeners({CustomAuditingListener.class})`
  6. - `CustomAuditingListener.class<br />`
  7. ```java
  8. package com.detabes.user.bean;
  9. import com.detabes.jpa.server.entity.mysql.JpaFields;
  10. import com.detabes.user.config.jpa.CustomAuditingListener;
  11. import lombok.Getter;
  12. import lombok.Setter;
  13. import org.hibernate.annotations.DynamicInsert;
  14. import org.hibernate.annotations.DynamicUpdate;
  15. import org.hibernate.annotations.SelectBeforeUpdate;
  16. import javax.persistence.*;
  17. /**
  18. * 公共的实体类
  19. *
  20. * @author tn
  21. * @className CommonBean
  22. * @date 2021-01-21 14:20
  23. */
  24. @MappedSuperclass
  25. @DynamicInsert
  26. @DynamicUpdate
  27. @SelectBeforeUpdate
  28. @Access(AccessType.FIELD)
  29. @Getter
  30. @Setter
  31. @EntityListeners({CustomAuditingListener.class})
  32. public class CommonBean<B> extends JpaFields<B> {
  33. @Id
  34. @GeneratedValue(strategy = GenerationType.IDENTITY)
  35. @Column(columnDefinition = "int(11) COMMENT '主键,自动生成'")
  36. private Integer id;
  37. /**
  38. * 创建人名称
  39. */
  40. @Column(columnDefinition = " varchar(100) default 'admin' comment '创建人名称'")
  41. private String createUserName;
  42. /**
  43. * 创建人uuid
  44. */
  45. @Column(columnDefinition = " varchar(100) default '1' comment '创建人uuid'")
  46. private String createUserUuid;
  47. /**
  48. * 更新人名称
  49. */
  50. @Column(columnDefinition = " varchar(100) comment '更新人名称'")
  51. private String updateUserName;
  52. /**
  53. * 更新人uuid
  54. */
  55. @Column(columnDefinition = " varchar(100) comment '更新人uuid'")
  56. private String updateUserUuid;
  57. }

第二步 实体继承公共实体

  1. package com.detabes.user.bean;
  2. import lombok.Getter;
  3. import lombok.Setter;
  4. import org.hibernate.annotations.DynamicInsert;
  5. import org.hibernate.annotations.DynamicUpdate;
  6. import javax.persistence.Column;
  7. import javax.persistence.Entity;
  8. import javax.persistence.Index;
  9. import javax.persistence.Table;
  10. /**
  11. * 测试
  12. *
  13. * @author tn
  14. * @version 1
  15. * @date 2021-01-22 10:05:52
  16. */
  17. @Entity
  18. @Table(name = "test", schema = "test")
  19. @org.hibernate.annotations.Table(appliesTo = "test", comment = "测试")
  20. @Getter
  21. @Setter
  22. @DynamicUpdate
  23. @DynamicInsert
  24. public class TestBean extends CommonBean<TestBean> {
  25. /**
  26. * 全系统唯一UUID
  27. */
  28. @Column(columnDefinition = " varchar(100) not null comment '全系统唯一UUID'")
  29. private String uuid;
  30. /**
  31. * name
  32. */
  33. @Column(columnDefinition = " varchar(100) not null comment 'name'")
  34. private String name;
  35. }

第三步 注册监听器

  • 关注点 @PrePersist @PreUpdate ```java package com.detabes.user.config.jpa;

import com.detabes.user.util.RequestUtil; import com.detabes.user.vo.user.UserTokenVO; import org.springframework.beans.factory.aspectj.ConfigurableObject; import org.springframework.stereotype.Component;

import javax.persistence.PrePersist; import javax.persistence.PreUpdate; import java.lang.reflect.Field;

/**

  • description: jpa自动填充默认信息 *
  • @author lmz
  • @company Peter
  • @date 2021/2/3 13:41
  • @expection
  • @return */ @Component public class CustomAuditingListener implements ConfigurableObject { private final String[] CREATE_FILED = {“createUserName”, “createUserUuid”, “updateUserName”, “updateUserUuid”}; private final String[] UPDATE_FILED = {“updateUserName”, “updateUserUuid”};

    @PrePersist private void prePersist(Object obj) {

    1. markForCreate(obj);

    }

    @PreUpdate private void preUpdate(Object obj) {

    1. markForUpdate(obj);

    }

    /**

    • description: 新增时的填充 *
    • @param ae ae
    • @return void
    • @author lmz
    • @company Peter
    • @date 2021/2/3 13:45
    • @expection */ public void markForCreate(Object ae) { UserTokenVO userTokenInfo = RequestUtil.getUserTokenInfo(); String[] propertyValue = {userTokenInfo.getLoginName(), userTokenInfo.getUuid(), userTokenInfo.getLoginName()

      1. , userTokenInfo.getUuid()};

      //反射填充字段 try {

      1. addValue(ae, ae.getClass(), CREATE_FILED, propertyValue);

      } catch (Exception e) {

      1. e.printStackTrace();

      }

      }

      /**

    • description: 更新时的填充 *
    • @param ae ae
    • @return void
    • @author lmz
    • @company Peter
    • @date 2021/2/3 13:46
    • @expection */ public void markForUpdate(Object ae) { UserTokenVO userTokenInfo = RequestUtil.getUserTokenInfo(); String[] propertyValue = {userTokenInfo.getLoginName(), userTokenInfo.getUuid()}; //反射填充字段 try {

      1. addValue(ae, ae.getClass(), UPDATE_FILED, propertyValue);

      } catch (Exception e) {

      1. e.printStackTrace();

      } }

      protected void addValue(Object object, Class<?> aClass, String[] propertyName, String[] propertyValue) throws NoSuchFieldException, IllegalAccessException { for (int i = 0; i < propertyName.length; i++) {

      1. String filedName = propertyName[i];
      2. Field declaredField = ReflectUtil.getField(aClass,filedName);
      3. declaredField.setAccessible(true);
      4. //填充对应的值
      5. declaredField.set(object, propertyValue[i]);

      } } }

```

第四步测试

  • 同上
  • 注意点: 更新时发生了更新字段未更新义sql时同样发生了字段为赋值 在
  • 在实际使用中出现了事务问题
    • 在做查询后对其返回值进行修改后更新到表中出现了事务问题,原本方法是没有事物的,由于使用了上面的方法导致强行加上了事务,解决方法是在具体的数据库操作方法上加上事物