Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
737 views
in Technique[技术] by (71.8m points)

jpa - OptimisticLockException not thrown when version has changed in spring-boot project

Model structure:

@MappedSuperclass
public class BaseModel<K extends Comparable> implements Serializable, Comparable<Object> {

    private static final long serialVersionUID = 1L;

    @Id
    private K id;

    @Version
    private Integer version;

    // getter/setter
}

@Entity
public class MyEntity extends BaseModel<String> {
    // some fields and it's getter/setter
}

Record in my database for my_entity:

id: 1 version: 1 ...

Below is my update method:

void update(String id, Integer currentVersion, ....) {
    MyEntity myEntity = myRepository.findOne(id);
    myEntity.setVersion(currentVersion);
    // other assignments

    myRepository.save(myEntity);
}

Below is the query being fired when this method is invoked.

update my_entity set version=?, x=?, y=?, ...
where id=? and version=?

I am expecting OptimisticLockException when currentVersion passed in above method is other than 1.

Can any body help me why I am not getting OptimisticLockException? I am using spring-boot for my webmvc project.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Section 11.1.54 of the JPA specification notes that:

In general, fields or properties that are specified with the Version annotation should not be updated by the application.

From experience, I can advise that some JPA providers (OpenJPA being one) actually throw an exception should you try to manually update the version field.

While not strictly an answer to your question, you can re-factor as below to ensure both portability between JPA providers and strict compliance with the JPA specification:

public void update(String id, Integer currentVersion) throws MyWrappedException {
    MyEntity myEntity = myRepository.findOne(id);

    if(currentVersion != myEntity.getVersion()){
        throw new MyWrappedException();
    }

    myRepository.save(myEntity);

   //still an issue here however: see below
}

Assuming your update(...) method is running in a transaction however you still have an issue with the above as section 3.4.5 of the JPA specification notes:

3.4.5 OptimisticLockException Provider implementations may defer writing to the database until the end of the transaction, when consistent with the lock mode and flush mode settings in effect. In this case, an optimistic lock check may not occur until commit time, and the OptimisticLockException may be thrown in the "before completion" phase of the commit. If the OptimisticLockException must be caught or handled by the application, the flush method should be used by the application to force the database writes to occur. This will allow the application to catch and handle optimistic lock exceptions.

Essentially then, 2 users can submit concurrent modifications for the same Entity. Both threads can pass the initial check however one will fail when the updates are flushed to the database which may be on transaction commit i.e. after your method has completed.

In order that you can catch and handle the OptimisticLock exception, your code should then look something like the below:

public void update(String id, Integer currentVersion) throws MyWrappedException {
    MyEntity myEntity = myRepository.findOne(id);

    if(currentVersion != myEntity.getVersion()){
        throw new MyWrappedException();
    }

    myRepository.save(myEntity);

    try{
       myRepository.flush()
    }
    catch(OptimisticLockingFailureException  ex){
       throw new MyWrappedException();
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...