JPA merge () best practice

I implemented a service for an entity object and uses pure jpa , I used spring , so hibernate configured as jpa impl in spring xml config . I use spring data for crud operations. But in our system, our entity object is pulled / updated many times, and for data there is high competition ( concurrency ). From our code in many places there are many classes just inject bean service and calling the getEntity method to get the entity, after it they change the entity (which separates as my understanding, but in the same thread, em should be the same as in my knowledge), therefore it takes some time, when the object returns to the service being updated, it calls the save() method of the service to save the object. save() method is just a call to merge() crud operation. It is transactional with @Transactional annotation. Here the problem arises when someone pulls an entity object, and by changing it, someone else can pull and change it and save it back, so my entity is read, and if I save it, I will override the already updated object. The problem is that we change the entity outside the service and call it back. Here, the spring datastore classes are the DAO layer.

Optimistic lock is one solution, but for some reason we did not like it, so it does not work for us. I am thinking of a pessimistic lock . For example, when I get an entity to update by blocking, then I change it somewhere else and call back (the entity already blocked from updating!) Does it work? I'm not sure if there is an EntityManager object that I used to extend the object. If so, it takes quite a while to pass these smart logics before they are updated and unlocked.

Here is a simple sample code for the script:

 class SomeEntity { Long id; String field1; String field2; String field3; String field4; String field5; String field6; String field7; //getters and setters, column annotations } class SomeEntityServiceImple implemenet SomeEntityService{ @Transactional save(SomeEntity se){ //call to spring data merge() method } get(Long id){ //call to spring data findOne() } } //this class might be spring bean, might not be. If it is not I will get SomeEntityService bean from shared appContext class SomeCrazyClass{ @Autowired SomeEntityService service; someMethod(){ SomeEntity e = service.get(1L); //do tons of logic here, change entity object, call some another methods and again, takes 3 seond service.save(e); //Probably detached and someone updated this object, probably overriding it. } } } 

Here I can’t move this tons of logic inside the service layer, it’s quite specific, and this kind of logic is in 100 places.

So, is there a way to come and at least apply a pessimistic block to this situation?

+4
source share
2 answers

For pessimistic locking you can try the code below

  • Lock when retrieving an object

    entityManager.find(Entity.class, pk, LockModeType.PESSIMISTIC_WRITE);

  • Apply lock after

    entityManager.lock(entity, LockModeType.PESSIMISTIC_WRITE);

  • Request lock mode setting

    query.setLockMode(LockModeType.PESSIMISTIC_WRITE);

Otherwise, you can try adding a synchronized method to SomeEntityServiceImpl .

 public synchronized void saveEntity(e){ { Object o = get(id); //-- Fetch most recent entity applyChanges(o, e); //-- Applying custom changes save(o); //-- Persisting entity } 

Therefore, you do not need to move all your logic inside the service, but only delegate database operations. In addition, this will be a common place for code changes without affecting the application logic.

+2
source

Move the @Transactional annotation to your top level to "someMethod" from "SomeCrazyClass". Changes made to this method should be part of a single transactional stream. As far as I understand, this stream should be atomic, that is, it should all be perfect or not have it (full rollback).

Enabling the @Transactional annotation above one saved save method does not contain a point to this annotation. You can still leave it there (with the common default value), but you should also add it one level up.

You must follow this suggestion in your application. This means that you must wrap your business logic methods with the Transactional annotation.

-1
source

All Articles