我有一个班级客户。我希望能够审计这个类的属性的变化(不是整个类 - 只是它的属性)。
public class Client {
private Long id;
private String firstName;
private String lastName;
private String email;
private String mobileNumber;
private Branch companyBranch;
实际上,使用@Audited注释审核整个实体非常容易。
但我想要的是使用我的类结构来审核这些更改。
这是我想要的结果类:
public class Action {
private String fieldName;
private String oldValue;
private String newValue;
private String action;
private Long modifiedBy;
private Date changeDate;
private Long clientID;
结果应如下所示:
fieldName +“已从”+ oldValue +“更改为”+ newValue +“,”clientID +“由”modifiedBy“更改;
- 乔治的比尔盖茨将mobileNumber从555改为999。
我这样做的原因是我需要将这些更改存储到Action表下的DB中 - 因为我将来自不同实体的审核属性,我想将它们存储在一起,然后有能力在需要时获取它们。
我怎样才能做到这一点?
谢谢
Aop是正确的方法。您可以将AspectJ与字段一起使用 set()
切入点满足您的需求。同 before
方面,您可以提取必要的信息来填充Action对象。
您还可以使用自定义类Annotation @AopAudit
检测要审核的类。您必须在类路径中定义此类注释,并将其放在要审核的目标类下。
这种方法看起来像这样:
AopAudit.java
@Retention(RUNTIME)
@Target(TYPE)
public @interface AopAudit {
}
Client.java
@AopAudit
public class Client {
private Long id;
private String firstName;
private String lastName;
private String email;
private String mobileNumber;
}
AuditAnnotationAspect.aj
import org.aspectj.lang.reflect.FieldSignature;
import java.lang.reflect.Field;
public aspect FieldAuditAspect {
pointcut auditField(Object t, Object value): set(@(*.AopAudit) * *.*) && args(value) && target(t);
pointcut auditType(Object t, Object value): set(* @(*.AopAudit) *.*) && args(value) && target(t);
before(Object target, Object newValue): auditField(target, newValue) || auditType(target, newValue) {
FieldSignature sig = (FieldSignature) thisJoinPoint.getSignature();
Field field = sig.getField();
field.setAccessible(true);
Object oldValue;
try
{
oldValue = field.get(target);
}
catch (IllegalAccessException e)
{
throw new RuntimeException("Failed to create audit Action", e);
}
Action a = new Action();
a.setFieldName(sig.getName());
a.setOldValue(oldValue == null ? null : oldValue.toString());
a.setNewValue(newValue == null ? null : newValue.toString());
}
}
这是定义的AspectJ方面 auditField
用于捕获字段集操作的切入点 before
创造的逻辑 Audit
目的。
启用 AspectJ Compile Time Weaving
如果是,你必须做以下事情 Maven
:
的pom.xml
...
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
</dependencies>
...
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.6</version>
<configuration>
<showWeaveInfo>true</showWeaveInfo>
<source>${java.source}</source>
<target>${java.target}</target>
<complianceLevel>${java.target}</complianceLevel>
<encoding>UTF-8</encoding>
<verbose>false</verbose>
<XnoInline>false</XnoInline>
</configuration>
<executions>
<execution>
<id>aspectj-compile</id>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>aspectj-compile-test</id>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
这个 Maven
配置使AspectJ编译器能够对类进行字节码后处理。
applicationContext.xml中
<bean class="AuditAnnotationAspect" factory-method="aspectOf"/>
此外,您可能需要将方面实例添加到Spring Application Context以进行依赖项注入。
UPD:
这里 是这种AspectJ项目配置的一个例子
如果您使用的是Hibernate,则可以使用 Hibernate Envers,并定义自己的 RevisionEntity
(如果你想合作的话 java.time
你需要Hibernate 5.x.在早期版本中,即使是自定义JSR-310实用程序也不能用于审计目的)
如果您没有使用Hibernate或想要使用纯JPA解决方案,那么您需要使用JPA编写自定义解决方案 EntityListeners
机制。
我不确切知道“modifiedBy”属性是什么(应用程序的用户或其他客户端?),但忽略了这个属性,你可以捕获setter中所有属性的修改
(注意:更改setter实现或将其他参数添加到setter是不好的做法,这项工作应该使用LOGGER或AOP完成):
public class Client {
private Long id;
private String firstName;
private String lastName;
private String email;
private String mobileNumber;
private Branch companyBranch;
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn("client_ID");
List<Action> actions = new ArrayList<String>();
public void setFirstName(String firstName,Long modifiedBy){
// constructor Action(fieldName, oldValue, newValue ,modifiedBy)
this.actions.add(new Action("firstName",this.firstName,firstName,modifiedBy));
this.firstName=firstName;
}
//the same work for lastName,email,mobileNumber,companyBranch
}
注意:最好和正确的解决方案是使用LOGGER或AOP
AOP绝对是您案例的解决方案,我使用Spring AOP实现了类似的案例来保持实体修订。需要使用此解决方案的要点 周围 切入点。
另一种解决方案是使用 org.hibernate.Interceptor
, org.hibernate.EmptyInterceptor
应该是适当的扩展,我写一些简单的代码来模拟它(拿你的客户代码):
@Entity
public class Client {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
private String email;
private String mobileNumber;
// getter and setter
}
该 Interceptor
履行
public class StateChangeInterceptor extends EmptyInterceptor {
@Override
public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) {
if (entity instanceof Client) {
for (int i = 0; i < propertyNames.length; i++) {
if (currentState[i] == null && previousState[i] == null) {
return false;
} else {
if (!currentState[i].equals(previousState[i])) {
System.out.println(propertyNames[i] + " was changed from " + previousState[i] + " to " + currentState[i] + " for " + id);
}
}
}
}
return true;
}
@Override
public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
return super.onSave(entity, id, state, propertyNames, types);
}
}
注册inceptor,我正在使用spring boot,所以只需将其添加到 application.properties
spring.jpa.properties.hibernate.ejb.interceptor=io.cloudhuang.jpa.StateChangeInterceptor
这是测试
@Test
public void testStateChange() {
Client client = new Client();
client.setFirstName("Liping");
client.setLastName("Huang");
entityManager.persist(client);
entityManager.flush();
client.setEmail("test@example.com");
entityManager.persist(client);
entityManager.flush();
}
将获得如下输出:
email was changed from null to test@example.com for 1
所以假设它可以替换为 Action
对象。
这是一个开源项目 JaVers - Java的对象审计和差异框架
JaVers是用于审核数据更改的轻量级Java库。
你可以看看这个项目。
我希望您应该使用Audit属性覆盖实体的equals方法。
在DAO中,您只需使用您在实体内创建的equals方法将旧的instanceof实体与新实例进行比较。
您将能够识别这是否可审核。