我正在使用Spring Boot + Rest + Jpa。因此,如果用户编辑映射到employee表的任何员工实体(empId,salary,age,location,role,experience)列,我应该跟踪旧的和新的更改并进入审计表,列已更改。
public EmployeeEntity update(int empId, EmployeeEntity employeeEntity ) {
em = emf.createEntityManager();
EmployeeEntity beforeObj = em.find(EmployeeEntity .class,empId );
try {
em.getTransaction().begin();
em.merge(employeeEntity);
em.getTransaction().commit();
}
finally {
em.close();
}
return employeeEntity;
}
所以现在我的实体有beforeObj(具有旧值的雇员实体)和employeeEntity(更新后的值),因此对于例如工资和位置正在更新,我必须跟踪这些并在审计表中更新。需要做的只是简单而不是使用Envers或任何其他JPA工具,因为只有最小的事务才会发生。
这是我的实体类:
@Entity
@Table(name = "TEMPLOYEE")
public class EmployeeEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "EMP_NO")
private int EMP_NO;
@Column(name = "SALARY_VAL")
private int SALARY_VA;
@Column(name = "ROLE_NAME")
private String ROLE_NAME;
@Column(name = "EXPERIENCE")
private int EXPERIENCE;
@Column(name = "LOCATION")
private String LOCATION;
// Getters and Setters
同样需要见解。
答案 0 :(得分:1)
使用JPA时,没有“旧”或“新”对象。您通常通过EntityManager
从数据库中检索对象,对其进行修改,然后提交事务(在该事务中,对象的当前状态将持久保存到数据库中。)
据我所知,你有两个选择:
1)创建数据库触发器,在您对employee-table的更新时填充审计表。如果这样做,请将audit-table远离JPA映射,让它只存在于数据库中。
2)为您的员工创建一个更改日志实体。
public enum EmployeeChangeType {
SALARY_UPDATED,
LOCATION_UPDATED
}
@Entity
public class Change {
@Id
@GeneratedValue
private Long id;
private EmployeeChangeType changeType;
private String oldValue;
private String newValue;
private String changedByUser;
//...
}
@Entity
public class Employee {
//...
@OneToMany
private List<Change> changes = new ArrayList<>();
//...
public void updateSalary(final Integer newSalary, final String updatedBy) {
changes.add(new Change(EmployeeChangeType.SALARY_UPDATED, SALARY_VAL, newSalary, updatedBy));
SALARY_VAL = newSalary;
}
不是一个完整的例子,但希望你能得到图片..
此外,不需要在实体中使用全封闭变量名称,JPA的重点是将数据库设计与Java设计分开。
更新: 奖金解决方案: 在某些情况下,使用您的实体的修订版。然后,对实体的更新将导致数据库中的新行。在我看来,这不是一个非常漂亮的解决方法,应该谨慎使用..
@Entity
public class Employee {
@Lazy
@OneToOne
private Employee lastVersion;
//some identifier that is common for all versions of employee
private Integer employeeNumber;
public Employee(final Employee currentVersion) {
//copy all fields from currentVersion to this, except id
this.lastVersion = currentVersion;
}
//...
}
您通常会这样做:
final Employee currentVersion = employeeRepo.getCurrentVersion(employeeId);
final Employee newVersion = new Employee(currentVersion);
//do changes to employee
employeeRepo.persist(newVersion);
有点让我觉得误用JPA,但可能适用于某些情况......
答案 1 :(得分:0)
尽管@Tobb已经提交了一个可靠的答案,但在实际上线之前需要批准员工记录更改的情况下,您可以采取另一种方法。您可以将“审计”表实现为“员工历史记录”,其中“员工历史记录”包含“员工”所执行的每一列以及用于跟踪谁更改了哪些数据以及何时更改的列。您可以使用“过期时间戳”和“活动时间戳”列来跟踪哪个“历史记录”记录应与真实“员工”表中的活动记录匹配。
然后,当普通用户对员工记录进行更改时,更改的记录仅保存到“员工历史记录”表中,并且可以触发“审核请求”以提醒需要审核和批准的人员改变,以便可以将更改写入真正的“员工”表。当审阅者执行审计时,“历史”表将包含旧的未更改的员工历史记录(具有NULL过期时间戳的记录和最近的活动时间戳记)和新建议的更改员工历史记录(两者都已过期)和活动时间戳NULL)。审阅者执行审计后,旧的未更改的员工历史记录将具有当前过期的时间戳,新更新的员工历史记录将具有NULL过期时间戳和最近的活动时间戳。新更新的员工历史记录也将与更新的关联员工记录匹配。
答案 2 :(得分:0)
您应该查看有关JPA和Hibernate中事件的Hibernate文档:
http://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#events
在那里,您将看到如何以通用方式实现它。 我希望这会有所帮助。