JPA使用集合更新实体的最佳实践

时间:2011-01-06 15:23:52

标签: java jpa collections entity

我在Glassfish容器中使用JPA。我有以下Modell(未完成)

@Entity
public class Node {
    @Id
    private String serial;
    @Version
    @Column(updatable=false)
    protected Integer version;
    private String name;
    @ManyToMany(cascade = {CascadeType.PERSIST,CascadeType.MERGE})
    private Set<LUN> luns = new HashSet<LUN>();

@Entity
public class LUN {
    @Id
    private String wwid;
    @Version
    @Column(updatable=false)
    protected Integer version;
    private String vendor;
    private String model;
    private Long capacity;
    @ManyToMany(mappedBy = "luns")
    private Set<Node> nodes = new HashSet<Node>();

此信息将每日更新。现在我的问题是,做这件事的最佳做法是什么。

我的第一种方法是,我每天在客户端(带有LUN)上生成Node Objects,并通过服务将其合并到数据库(我想让JPA完成工作)。

现在我做了一些没有LUN的测试。我在无状态EJB中有以下服务:

public void updateNode(Node node) {
    if (!nodeInDB(node)) {
        LOGGER.log(Level.INFO, "persisting node {0} the first time", node.toString());
        em.persist(node);
    } else {
        LOGGER.log(Level.INFO, "merging node {0}", node.toString());
        node = em.merge(node);
    }
}

测试:

@Test
public void addTest() throws Exception {
    Node node = new Node();
    node.setName("hostname");
    node.setSerial("serial");
    nodeManager.updateNode(node);
    nodeManager.updateNode(node);
    node.setName("newhostname");
    nodeManager.updateNode(node);
}

这没有@Version字段。使用@Version字段,我得到一个OptimisticLockException。

这是错误的做法吗?我是否必须始终执行em.find(...),然后通过getter和setter修改托管实体?

感谢任何帮助。

BR Rene

1 个答案:

答案 0 :(得分:1)

@version注释用于启用乐观锁定。

当您使用乐观锁定时,每次成功写入表格都会增加版本计数器,每次持久保存实体时都会对其进行读取和比较。如果在您第一次找到实体时读取的版本与写入时表中的版本不匹配,则会引发异常。

只有一次阅读版本列后,您的程序会多次更新该表。因此,在第二次调用persist()或merge()时,版本号不匹配,并且查询失败。这是使用乐观锁定时的预期行为:您尝试覆盖自首次读取以来更改的行。

回答上一个问题:每次写入数据库后都需要读取更改后的@version信息。您可以通过调用em.refresh()来完成此操作。

但是,您应该考虑重新考虑您的策略:乐观锁最好用于事务,以确保在用户执行更改时数据的一致性。这些通常读取数据,将其显示给用户,等待更改,然后在用户完成任务后保留数据。在这种情况下,您不会真正想要也不需要多次写入相同的数据行,因为事务可能由于每次写入调用的乐观锁定而失败 - 这会使事情复杂化而不是使它们更简单。