简体   繁体   中英

update and return data in spring data JPA

For concurrency purpose, I have got a requirement to update the state of a column of the database to USED while selecting from AVAILABLE pool.

I was thinking to try @Modifying , and @Query (query to update the state based on the where clause)

It is all fine, but this is an update query and so it doesn't return the updated data.

So, is it possible in spring data, to update and return a row, so that whoever read the row first can use it exclusively.

My update query is something like UPDATE MyObject o SET o.state = 'USED' WHERE o.id = (select min(id) from MyObject a where a.state='AVAILABLE') , so basically the lowest available id will be marked used. There is a option of locking, but these requires exceptional handling and if exception occur for another thread, then try again, which is not approved in my scenario

I suggest to use multiple transactions plus Optimistic Locking. Make sure your entity has an attribute annotated with @Version .

In the first transaction load the entity, mark it as USED , close the transaction.

This will flush and commit the changes and make sure nobody else touched the entity in the mean time.

In the second transaction you can no do whatever you want to do with the entity.

For these small transactions I find it clumsy to move them to separate methods so I can use @Transactional . I therefore use the TransactionTemplate instead.

You need to explicitly declare a transaction to avoid other transactions being able to read the values involved until it's commited. The level with best performance allowing it is READ_COMMITED , which doesn't allow dirty reads from other transactions (suits your case). So the code will look like this:

Repo:

@Repository
public interface MyObjectRepository extends JpaRepository<MyObject, Long> {

    @Modifying
    @Query("UPDATE MyObject o SET o.state = 'USED' WHERE o.id = :id")
    void lockObject(@Param("id") long id);

    @Query("select min(id) from MyObject a where a.state='AVAILABLE'")
    Integer minId();
}

Service:

@Transactional(isolation=Isolation.READ_COMMITTED)
public MyObject findFirstAvailable(){
    Integer minId;
    if ((minId = repo.minId()) != null){
        repo.lockObject(minId);
        return repo.findOne(minId);
    }
    return null;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM