我在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
答案 0 :(得分:1)
@version注释用于启用乐观锁定。
当您使用乐观锁定时,每次成功写入表格都会增加版本计数器,每次持久保存实体时都会对其进行读取和比较。如果在您第一次找到实体时读取的版本与写入时表中的版本不匹配,则会引发异常。
只有一次阅读版本列后,您的程序会多次更新该表。因此,在第二次调用persist()或merge()时,版本号不匹配,并且查询失败。这是使用乐观锁定时的预期行为:您尝试覆盖自首次读取以来更改的行。
回答上一个问题:每次写入数据库后都需要读取更改后的@version信息。您可以通过调用em.refresh()来完成此操作。
但是,您应该考虑重新考虑您的策略:乐观锁最好用于事务,以确保在用户执行更改时数据的一致性。这些通常读取数据,将其显示给用户,等待更改,然后在用户完成任务后保留数据。在这种情况下,您不会真正想要也不需要多次写入相同的数据行,因为事务可能由于每次写入调用的乐观锁定而失败 - 这会使事情复杂化而不是使它们更简单。