开发者

JPA updatable attribute and JPQL

开发者 https://www.devze.com 2023-04-13 04:03 出处:网络
I have made a couple of observations in my test and I am finding them hard to understand. My tests perform some basic update (or merge) operations on an Entity which has one of its attribute set as up

I have made a couple of observations in my test and I am finding them hard to understand. My tests perform some basic update (or merge) operations on an Entity which has one of its attribute set as updatable=false.


@Entity
@Table(name = "EMPLOYEE")
public class Employee {

    @Id
    @GeneratedValue(generator = "some-generator")
    @Column(name = "EMP_ID", nullable = false)
    private Long id;

    @Column(name = "EMP_NAME", updatable = false)
    private String name;

    @Version
    @Column(name = "OBJECT_VERSION")
    private int version;

    public Employee() {
    }

    public Employee(String name) {
        this.name = name;
    }

    public void setName(String name) {
        this.name = name;
    }
}


Note - I am using Hibernate as my persistence provider.

My observations and doubts are

  1. The name property of the above Employee entity has updatable="false" set. If I load and set the name property of an employee entity and then call entityManager.merge(employee) the merge does not execute any update query which is the expected behavior 开发者_如何学Cbut it does not throw any exception too. Is it possible to configure JPA such that an runtime exception is thrown?

  2. Instead of calling entityManager.merge(employee), if the name property is updated using an JPQL query, the query executes successfully and the name property gets updated. What is the technical reasoning behind JPQL query behavior being different from JPA object update and why?

  3. If the update is executed using a JPQL query the version number is not incremented despite the @Version annotation. I understand that this behavior can be achieved by adding the "versioned" keyword in the update query but I would like to understand the technical reasoning behind the version property not be updated when JPQL query is executed.

Since JPQL is an object oriented query language (like HQL is), I think there should not be technical implementation issues for points 2 and 3. It would be great to hear some expert comments on this. Thanks.


  1. Well the specs simply say that when a column is updateable="false" it should be ignored by the generated SQL. Doesn't say anything about throwing an exception. You can however implement that using listeners. Just declare a method in your bean like "checkModif", annotate it with @PreUpdate and throw an exception from it if the field is modified (note however that this will effectively cancel the operation, and nothing will be updated)

  2. That's more likely a Hibernate bug. The JPA2 specs simply say that when a field is annotated with @Column(name = "EMP_NAME", updatable = false) the corresponding column should be ignored by the SQL generated by the provider. Doesn't say anything about a particular case when that SQL comes from JPQL. There's many little "mishaps" like this in Hibernate's JPA2 implementation (like not being able to search using the key in a map association

  3. AFAIK, the version number should be incremented only when using optimistic locking so when using JPQL you must specify if you actually want to increment it or not.

Example:

Scenario 1: You want to get an Employee entitity and then modify it. If you actually do some modifications to the Employee entity, you want those modifications persisted only if noone else also made some modifications to the same entity in the time between when you first read it and the time you commit your modification. For this to happen you read your entity like this:

Query query = entityManager.createQuery("SELECT e FROM EMPLOYEE e where e.id=3");
query.setLockMode(LockModeType.OPTIMISTIC);
Employee e = query.getSingleResult();

This way, if some other user uses the same above code to read the same entity while you're modifying it, and this new guys is quicker than you and commits a modification faster than you, then if you yourself modify something and try to commit you'll get an OptimisticLockException and you will not get your modifs commited, thus not overriding the (unknown by you at this point) modifs that the other guy did.

Scenario 2. You want to be sure to read an entity and when you save it, even though you didn't modify anything in it you want to only persist it (with the same values) if noone else modified anything else inthe mean time as well. For this you do

Query query = entityManager.createQuery("SELECT e FROM EMPLOYEE e where e.id=3");
query.setLockMode(LockModeType.OPTIMISTIC_FORCE_INCREMENT);
//etc etc...

In both scenarios the Employee entity must have a versioned field for this to work:

@Version
protected int version;

public int getVersion() {
    return version;
}

public void setVersion(int version) {
    this.version = version;
}
0

精彩评论

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

关注公众号