Hibernate / Spring: EntityManager.lock would work as expected, em.find not - spring

i am trying to use manual locking inside a spring #Transactional method, using
EntityManager.find(class, id, LockModeType.PESSIMISTIC_WRITE);
However, when viewing the sql, hibernate does NOT generate any select ... for update statements (acquire an exclusive lock on the DB row) as i would expect, and the method does not synchronize correctly.
Strangely, calling em.lock() after fetching the entity using em.find() causes a select ... for update as I would expect for find(), as does adding an #Lock(PESSIMISTIC_WRITE) annotation to a fetching query.
However I would prefer using em.find() over the other two methods, but i cannot get that to work.
So, why is EntityManager.find silently ignoring lock options, while other locking methods work?
I am using Hibernate 5.4.4.FINAL, tried upgrading to 5.4.11.FINAL.
I also tried setting Isolation levels to SERIALIZABLE and READ_COMMITTED in the #Transactional.
Neither of these altered my results.

Related

How to get updated objects after flush() in the same transaction (Hibernate/ Spring boot)

I have a list of ~10 000 objects.
I am trying to call an mysql update query (procedure) and then to get the updated objects inside same transaction.
Can this be achieved ?
When I call a delete statement + flush(), hibernate retrieves me correct objects (deleted objects are missing)
But when I try update statement + flush(), hibernate retrieves me the initial unchanged objects.
#Transactional
void test() {
//...
em.createQuery("delete from StorefrontProduct sp where sp in (:storefrontProducts)")
.setParameter("storefrontProducts", storefrontProductsToDelete)
.executeUpdate();
// example
em.createQuery("update StorefrontProduct sp set sp.orderIndex=0 where sp.id=90")
.executeUpdate();
em.flush();
//Simple JPA query
List<StorefrontProduct> result = repository.findAllByPreviousOrderIndexIsNotNull();
//additional code....
}
After running the code from above and putting a breakpoint after findAll call, provided objects from 1-st query were deleted and flushed, but the update query was not flushed.
That is known counterintuitive behaviour of Hibernate.
First of all, em.flush() call might be superfluous if flush mode set to AUTO (in that case Hibernate automatically synchronises persistence context (session-level cache) with underlying database prior executing update/delete queries).
Delete and successive Select case:
you issues delete then select, since select does not see deleted records anymore you do not see deleted records in resultset, however if you call findById you may find deleted records.
Update and successive Select case:
you issues update then select, when processing resultset Hibernate sees both records stored in database and records stored in persistence context and it assumes that persistence context is a source of truth, that is the reason why you see "stale" data.
There are following options to mitigate that counterintuitive behaviour:
do not perform direct updates, use "slow" find/save API instead
either detach or refresh stale entities after direct update, em.clear() may also help, however it completely cleans up persistence context, which might be undesirable

Nested transactions in Spring and Hibernate

I have a Spring Boot application with persistence using Hibernate/JPA.
I am using transactions to manage my database persistence, and I am using the #Transactional annotation to define the methods that should execute transactionally.
I have three main levels of transaction granularity when persisting:
Batches of entities to be persisted
Single entities to be persisted
Single database operations that persist an entity
Therefore, you can imagine that I have three levels of nested transactions when thinking about the whole persistence flux.
The interaction between between levels 2 and 3 works transparently as I desire because without specifying any Propagation behaviour for the transaction, the default is the REQUIRED behaviour, and so the entire entity (level 2) is rolled back because level 3 will support the transaction defined in level 2.
However, the problem is that I need an interaction between 1 and 2 that is slightly different. I need an entity to be rolled back individually if an error were to occur, but I wouldn't like the entire batch to be rolled back. That being said, I need to specify a propagation behavior in the level 2 annotation #Transactional(propagation = X) that follows these requirements.
I've tried REQUIRES_NEW but that doesn't work because it commits some of the entities from level 2 even if the whole batch had to be rolled back, which can also happen.
The behaviour that seems to fit the description better is NESTED, but that is not accepted when using Spring and Hibernate JPA, see here for more information.
This last link offers alternatives for the NESTED type, but I would like to know if NESTED would've really solved my problem, or if there was another behaviour that suited the job better.
I guess NESTED would roughly do what you want but I would question if this really is necessary. I don't know what you are trying to do or what the error condition is, but maybe you can get rid of the error condition by using some kind of WHERE clause or an UPSERT statement: Hibernate Transactions and Concurrency Using attachDirty (saveOrUpdate)

Spring Transaction propagation: can't get the #OneToMany related entities when using the same transaction for creation and consultation operation

I have the following problem: I am working on a spring-boot application which offers REST services and use a relational (SQL) database using spring-data-jpa.
I have two REST services:
- a entity-creation service, which create the child-entity, the parent-entity and associate them in a same transaction. When this service ends, the data are committed into the database.
- an entity consultation service, which get back the parent-entity with its children
These two services are annotated with the #Transactional annotation. It production case, it works well: I can create an parent-entity with its children in one transaction (which is commited/ended), and get it in another transaction latter.
The problem is when I want to create integration-tests. My idea was to annotate each test with the #Transactional annotation, and do a rollback after each test. This way I keep my database clean between each test, and I don't have a generate the schema again or clean all the records in the database.
The integration test consists in creating a parent and its children and then reading it, everything in one transaction (as the test is annotated with #Transaction). When reading the entity previously created in the same transaction, I can get the parent entity, but the children are not fetched (null value). I am not sure to understand very well the transaction mechanism: I was thinking that using the #Transactional on the test method, the services (annotated with "#Transactional") invoked by this test should detect and use the same transaction opened by the test method (the propagation is configured to "REQUIRED"). Hence as the transaction uses the same EntityManager, this one should be able to return the relation between the parent entity and its children created previously in the same transaction, even if the data has not been committed to the database. The strange thing is that it retrieve the parent entity (which has not been yet committed into the database), but not its children. Is my understanding of the transaction concept correct? If not, could someone explains me what am I missing?
Also, if someone did something similar, could he explain me how he did it please?
My code is quite complex. I first want to know if I understand well how are transaction managed and if someone already did something similar. If really it is required, I can send more information about my implementation (how the transaction-manager and the entity-manager are initialized, the JPA entities, the services etc...)
Binding the Entity-manager in my test and calling its flush method from my test,between the creation and the reading, the reading operation works well: I get the parent entity with its children. But the data are written into the database during the creation to read it latter during the read operation. And I don't want the transaction to be committed as I need my test to work on an empty database. My misunderstanding is not so much about the Transaction mechanism, but more about the entity-manager: it does not keep as a cache the entities created and theirs relations...
This post help me.
Issue with #Transactional annotations in Spring JPA
As a final word, I am thinking about calling an SQL script before each test to empty my database.

JPA: Native Queries does not trigger execution of cached inserts/updates in same transaction

I have a JUnit Test where I set up the test data in the beginning of the test case, and then test for the conditions of the test case afterward in the same test method. The query that tests the conditions of the test case is a native query. I know that I have to explicitly call EntityManager.flush() in order for my inserts/updates to be written immediately to the DB since they are in the same transaction. Additionally, I noticed that I can replace entityManager.flush() with a JPA query, which seems to achieve the same thing. I have heard that JPA will cache DB operations in the same transaction until there is a need to execute them immediately, such as when select queries are issued. So this all makes sense. My question is, why doesn't this behavior also apply to native queries? Here my native query does not trigger the immediate execution of insert/updates in testSetup(), thus causing my assert to fail.
#Test
#Transactional
public void testCase() {
testSetup();
entityManager.flush(); // can be replaced with entityManager.createQuery("from Person");
List resultList = entityManager.createNativeQuery("select * from Person").getResultList();
Assert.assertTrue(resultList.size() == 1);
}
tl;dr - native queries bypass the persistence context and the cache.
This includes the queries you create by calling createNativeQuery, obviously. But bulk updates (UPDATE and DELETE), though expressed in JPQL are translated to native queries by the provider and bypass the persistence context and the cache as well.
Thus flushing or executing other queries will not have the expected effect.
Besides, if your native queries have changed the data on entities that are managed in the current persistence context or are cached, the entities will not be refreshed automatically and will become stale.

Getting Exception when trying to update record second time in hibernate

We keep user object in session. When we update it first time ( using entityManager.merge( ent )) it works fine; but second time I get following exception:
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.kids.domain.Child#22]
at org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:492)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:256)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:86)
at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:781)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:766)
I am using spring with hibernate & jpa. Spring is injecting EntityManager in DAO. I do understand that if I would do a fresh find() everytime then make all the changes on that object and then merge().. it will work fine.. but in my case I cannot do that.
I also checked that the hashcode of EntityManager used is same both time; i.e same EntityManager instance is getting used both time then why it gives this exception when trying to update a record second time
Any help would be highly appreciated.
I tried lots of things but nothing worked. Even on internet there was no solution. Finally, when I removed version from my entities ( i.e. remove OptimisticLockException support provided by hibernate ).. everything started working fine. Probably , I will try to handle OLE myself.
When you call merge method in the current transaction, hibernate will copy the state of the given object onto the persistent object with the same identifier and returns new manageable entity. You need to work on the manageable entity which is returned by the merge in subsequent operations because this entity has new version than in DB.

Resources