参考
背景
- 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;
/*** 创建人uuid*/@Column(columnDefinition = " varchar(100) default '1' comment '创建人uuid'")private String createUserUuid;/*** 更新人名称*/@Column(columnDefinition = " varchar(100) comment '更新人名称'")private String updateUserName;/*** 更新人uuid*/@Column(columnDefinition = " varchar(100) comment '更新人uuid'")private String updateUserUuid;/*** 表示该字段为创建时间字段,在这个实体被insert的时候,会自动为其赋值*/@Column(columnDefinition = "timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期' ")private LocalDateTime createTime;/*** 表示该字段为修改时间字段,在这个实体被update的时候,会自动为其赋值*/@Column(columnDefinition = "timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新日期'")private LocalDateTime updateTime;
}
<a name="o2gOQ"></a>## 第二步 数据实体实现公共字段- 关注点 `@Embeddable`- `@EntityListeners(AuditListener.class)`- `AuditListener.class<br />````java@Table(name = "mysql80")@Entity@Setter@Getter@Builder@AllArgsConstructor@NoArgsConstructor@DynamicInsert //动态插入@DynamicUpdate //动态赋值@EntityListeners(AuditListener.class)public class Mysql80 implements Auditable {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Integer id;private String name,sex;@Embeddedprivate AuditBean audit;}
第三步 新增接口
package io.tn.jpa.audit.config;import io.tn.jpa.audit.bean.AuditBean;public interface Auditable {AuditBean getAudit();void setAudit(AuditBean audit);}
第四步 使用上面新增的接口建立监听器
- 关注点
@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 {
@PrePersistpublic void setCreatedOn(Auditable auditable) {AuditBean audit = auditable.getAudit();if(audit == null) {audit = new AuditBean();auditable.setAudit(audit);}audit.setCreateUserUuid("123");audit.setCreateUserName("asd");audit.setCreateTime(LocalDateTime.now());audit.setUpdateUserUuid("123");audit.setUpdateUserName("asd");audit.setUpdateTime(LocalDateTime.now());}@PreUpdatepublic void setUpdadtedOn(Auditable auditable) {AuditBean audit = auditable.getAudit();audit.setUpdateUserUuid("123");audit.setUpdateUserName("asd");audit.setUpdateTime(LocalDateTime.now());}
}
<a name="cTMbv"></a>## 第五步测试- dao```javaimport com.detabes.jpa.server.dao.JpaBasicsDao;import io.tn.jpa.audit.bean.Mysql80;/*** 测试审计** @author tn* @className Mysql80Dao* @date 2021-02-03 12:12*/public interface Mysql80Dao extends JpaBasicsDao<Mysql80, Integer> {@Query("update Mysql80 m set m.sex=?1 where m.id=?2 ")@Modifying@Transactional(rollbackOn=Exception.class)int updata(String sex, Integer id);}
- 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 {
@Resourceprivate Mysql80Dao mysql80Dao;@Testvoid contextLoads() {}@Testvoid testJpaAudit(){Mysql80 mysql80 = new Mysql80();mysql80.setName("adaa");mysql80.setSex("assda");mysql80Dao.save(mysql80);}@Testvoid testJpaAuditUpdate2() throws Exception {Mysql80 mysql801 = mysql80Dao.findById(3).get();mysql801.setSex("sad");mysql801.setSex("323");mysql80Dao.save(mysql801);}@Testvoid testJpaAuditUpdate() throws Exception {Mysql80 mysql80 = new Mysql80();mysql80.setName("我的");mysql80.setSex("我的");mysql80.setId(3);mysql80Dao.updateEntity(mysql80,"id");}@Testvoid testJpaAuditSql(){mysql80Dao.updata("mysql80",1);}
}
<a name="d0Cak"></a># 实践 - 2<a name="wI0QB"></a>## 第一步 建立公共字段的实体- 关注点 `@EntityListeners({CustomAuditingListener.class})`- `CustomAuditingListener.class<br />````javapackage com.detabes.user.bean;import com.detabes.jpa.server.entity.mysql.JpaFields;import com.detabes.user.config.jpa.CustomAuditingListener;import lombok.Getter;import lombok.Setter;import org.hibernate.annotations.DynamicInsert;import org.hibernate.annotations.DynamicUpdate;import org.hibernate.annotations.SelectBeforeUpdate;import javax.persistence.*;/*** 公共的实体类** @author tn* @className CommonBean* @date 2021-01-21 14:20*/@MappedSuperclass@DynamicInsert@DynamicUpdate@SelectBeforeUpdate@Access(AccessType.FIELD)@Getter@Setter@EntityListeners({CustomAuditingListener.class})public class CommonBean<B> extends JpaFields<B> {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)@Column(columnDefinition = "int(11) COMMENT '主键,自动生成'")private Integer id;/*** 创建人名称*/@Column(columnDefinition = " varchar(100) default 'admin' comment '创建人名称'")private String createUserName;/*** 创建人uuid*/@Column(columnDefinition = " varchar(100) default '1' comment '创建人uuid'")private String createUserUuid;/*** 更新人名称*/@Column(columnDefinition = " varchar(100) comment '更新人名称'")private String updateUserName;/*** 更新人uuid*/@Column(columnDefinition = " varchar(100) comment '更新人uuid'")private String updateUserUuid;}
第二步 实体继承公共实体
package com.detabes.user.bean;import lombok.Getter;import lombok.Setter;import org.hibernate.annotations.DynamicInsert;import org.hibernate.annotations.DynamicUpdate;import javax.persistence.Column;import javax.persistence.Entity;import javax.persistence.Index;import javax.persistence.Table;/*** 测试** @author tn* @version 1* @date 2021-01-22 10:05:52*/@Entity@Table(name = "test", schema = "test")@org.hibernate.annotations.Table(appliesTo = "test", comment = "测试")@Getter@Setter@DynamicUpdate@DynamicInsertpublic class TestBean extends CommonBean<TestBean> {/*** 全系统唯一UUID*/@Column(columnDefinition = " varchar(100) not null comment '全系统唯一UUID'")private String uuid;/*** name*/@Column(columnDefinition = " varchar(100) not null comment 'name'")private String name;}
第三步 注册监听器
- 关注点
@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) {
markForCreate(obj);
}
@PreUpdate private void preUpdate(Object obj) {
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()
, userTokenInfo.getUuid()};
//反射填充字段 try {
addValue(ae, ae.getClass(), CREATE_FILED, propertyValue);
} catch (Exception e) {
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 {
addValue(ae, ae.getClass(), UPDATE_FILED, propertyValue);
} catch (Exception e) {
e.printStackTrace();
} }
protected void addValue(Object object, Class<?> aClass, String[] propertyName, String[] propertyValue) throws NoSuchFieldException, IllegalAccessException { for (int i = 0; i < propertyName.length; i++) {
String filedName = propertyName[i];Field declaredField = ReflectUtil.getField(aClass,filedName);declaredField.setAccessible(true);//填充对应的值declaredField.set(object, propertyValue[i]);
} } }
第四步测试
- 同上
- 注意点: 更新时发生了更新字段未更新义sql时同样发生了字段为赋值 在
- 在实际使用中出现了事务问题
- 在做查询后对其返回值进行修改后更新到表中出现了事务问题,原本方法是没有事物的,由于使用了上面的方法导致强行加上了事务,解决方法是在具体的数据库操作方法上加上事物
