开发者

JPA Best Practice to update an Entity with Collections

开发者 https://www.devze.com 2023-02-03 01:38 出处:网络
I am using JPA in a Glassfish Container. I have the following Modell (not complete) @Entity public class 开发者_运维百科Node {

I am using JPA in a Glassfish Container. I have the following Modell (not complete)

@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>();

This information will be updated daily. Now my question is, what is the best practice to do this.

My fist approach was, I generate the Node Objects on the client (with LUNs) every day new, and merge it to the Database (I wanted to let JPA do the work) via service.

Now I did some tests without LUNs yet. I have the following service in a stateless 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);
    }
}

The test:

@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);
}

This works without the @Version Field. With the @Version field I get an OptimisticLockException.

Is that the wrong approach? Do I have to always perform an em.find(...) and then modify the managed entity via getter and setter?

Any help is appreciated.

BR Rene


The @version annotation is used to enable optimistic locking.

When you use optimistic locking, each successful write to your table increases a version counter, which is read and compared every time you persist your entities. If the version read when you first find your entity doesn't match the version in the table at write time, an exception is thrown.

Your program updates the table several times after reading the version column only once. Therefore, at the second time you call persist() or merge(), the version numbers don't match, and your query fails. This is the expected behavior when using optimistic locking: you were trying to overwrite a row that was changed since the time you first read it.

To answer your last question: You need to read the changed @version information after every write to your database. You can do this by calling em.refresh().

You should, however, consider re-thinking your strategy: Optimistic locks are best used on transactions, to ensure data consistency while the user performs changes. These usually read the data, display it to the user, wait for changes, and then persist the data after the user has finished the task. You wouldn't really want nor need to write the same data rows several times in this context, because the transaction could fail due to optimistic locking on every one of these write calls - it would complicate things rather than make them more simple.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号