如何将更新的实体与旧实体进行比较,并仅将更新的值存储在数据库中

时间:2017-09-22 15:25:15

标签: java hibernate jpa spring-rest

我正在使用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

同样需要见解。

3 个答案:

答案 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

在那里,您将看到如何以通用方式实现它。 我希望这会有所帮助。