参考
背景
- 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;
@Embedded
private 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 {
@PrePersist
public 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());
}
@PreUpdate
public void setUpdadtedOn(Auditable auditable) {
AuditBean audit = auditable.getAudit();
audit.setUpdateUserUuid("123");
audit.setUpdateUserName("asd");
audit.setUpdateTime(LocalDateTime.now());
}
}
<a name="cTMbv"></a>
## 第五步测试
- dao
```java
import 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 {
@Resource
private Mysql80Dao mysql80Dao;
@Test
void contextLoads() {
}
@Test
void testJpaAudit(){
Mysql80 mysql80 = new Mysql80();
mysql80.setName("adaa");
mysql80.setSex("assda");
mysql80Dao.save(mysql80);
}
@Test
void testJpaAuditUpdate2() throws Exception {
Mysql80 mysql801 = mysql80Dao.findById(3).get();
mysql801.setSex("sad");
mysql801.setSex("323");
mysql80Dao.save(mysql801);
}
@Test
void testJpaAuditUpdate() throws Exception {
Mysql80 mysql80 = new Mysql80();
mysql80.setName("我的");
mysql80.setSex("我的");
mysql80.setId(3);
mysql80Dao.updateEntity(mysql80,"id");
}
@Test
void testJpaAuditSql(){
mysql80Dao.updata("mysql80",1);
}
}
<a name="d0Cak"></a>
# 实践 - 2
<a name="wI0QB"></a>
## 第一步 建立公共字段的实体
- 关注点 `@EntityListeners({CustomAuditingListener.class})`
- `CustomAuditingListener.class<br />`
```java
package 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
@DynamicInsert
public 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时同样发生了字段为赋值 在
- 在实际使用中出现了事务问题
- 在做查询后对其返回值进行修改后更新到表中出现了事务问题,原本方法是没有事物的,由于使用了上面的方法导致强行加上了事务,解决方法是在具体的数据库操作方法上加上事物