I implemented a service for entity object
and it uses pure jpa
, I used spring
, so in spring xml config
configured hibernate
as a jpa
impl. I am using spring
data for crud
operations.
But in our system our entity
object is being pulled/updated many times and there is high contention(concurrency
) to data. From our code in many places many classes
just inject
the service bean and call getEntity
method to get an entity, after it they change the entity(which is detached as my understanding, but in the same thread so em object should be the same as per my knowledge) so it takes for a while the entity comes back to service to be updated, they call save()
method of the service to save an entity. Save()
method is nothing but just calls merge()
crud operation. it is transactional with @Transactional
annotation. Here is a problem arises, when someone pulls an entity object and while changing it someone else might pull and change it and save it back, so my entity read is dirty and if save it, I will override already updated entity. The problem is here we are changing the entity outside of service and calling save back. Here spring data repository classes are DAO layer.
Optimistic lock
is one solution but We did not like it for some reasons, so it does not work for us. I am thinking about pessimistic lock
. For example, when I get an entity for update by locking, then change it somewhere else in different place and call back(the entity
was already locked against update!) does it work? I am not sure if it still EntityManager
object there which I used for pulling an entity. If it is there it takes quite long time to pass those 'smart' logics before it gets updated and unlocks it.
Here is simple example code for the scenario:
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's merge() method
}
get(Long id){
//call to spring data's 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 cannot move that tons of logic
inside the service layer, it is quite specific and this kind of different logics are in 100s of places.
So is there any way to come around and at least apply pessimistic lock to this situation?
For pessimistic locking, you can try the below code
Locking while fetching the entity
entityManager.find(Entity.class, pk, LockModeType.PESSIMISTIC_WRITE);
Applying lock afterwards
entityManager.lock(entity, LockModeType.PESSIMISTIC_WRITE);
Setting lock mode in query
query.setLockMode(LockModeType.PESSIMISTIC_WRITE);
Else, you can try adding a synchronized method in 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 don't have to move all your logic inside service, but delegate only the database operations. Also, it will be the common place for code alterations without affecting application logic.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With