【问题标题】:JPA Best Practice to update an Entity with Collections使用集合更新实体的 JPA 最佳实践
【发布时间】:2011-06-04 17:28:47
【问题描述】:

我在 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)上生成新的节点对象,并通过服务将其合并到数据库(我想让 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雷内

【问题讨论】:

    标签: java jpa collections entity


    【解决方案1】:

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

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

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

    回答您的最后一个问题:您需要在每次写入数据库后读取更改后的@version 信息。您可以通过调用 em.refresh() 来做到这一点。

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

    【讨论】:

    • 我不明白。我还没有添加任何 lun。
    • 对不起,我显然没有仔细阅读。我已经为你改变了答案。
    • 非常感谢您的回答。因此,您建议完全删除该场景的版本控制?然后客户端软件必须确保一次只更新一个节点。
    • 这真的取决于这些节点的用途,以及发生并行写入的场景是否会破坏您的逻辑,或者是不可接受的。如果是这样,你显然需要锁定——这就是它的发明目的——但是你应该尝试将你的写操作限制为每个事务一次,如果可能的话,并添加一些处理可能的 OptimisticLockExceptions 的方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-06
    • 1970-01-01
    • 2010-09-06
    • 2014-08-02
    相关资源
    最近更新 更多