How to get updated objects after flush() in the same transaction (Hibernate/ Spring boot) - 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

Related

Save and lock entity with Hibernate

I'm looking for a way to save and immediately lock an entity on a DB in order to avoid that other thread access the entity before the thread creator ends.
I'm using Hibernate 4.3.11 and Spring 4.2.5.
Thanks in advance.
Although there is lock mode - LockMode.WRITE - but as the documentation states
A WRITE lock is obtained when an object is updated or inserted. This
lock mode is for internal use only and is not a valid mode for load()
or lock() (both of which throw exceptions if WRITE is specified)..
If it's just that you are only inserting rows then you cannot specifically lock the database rows using hibernate as the rows are not yet committed.
The moment your code (hibernate or without) inserts rows in database and not yet commits - there are transactional locks held which gets released on transaction commit. The nature of locks and the manner in which this internally happens is database specific. However if you are interested in locking some rows (already existing) , then you
can query the data using
session.get(TestEntity.class, 1, LockMode.PESSIMISTIC_WRITE);
This will hold a pessimistic lock (typically by issuing SELECT .... FOR UPDATE) for the duration of transaction and no other thread/transaction can modify the data on which lock has been taken.
A possible way should be increase transaction level to serializable.
This level ensure data is locked until is not used in other transaction.
Hibernate offer's two types of locks Optimistic and Pessimistic. Its straight forward.
1)Optimistic uses versioning where in it will have a version column in the database and check it before it updates or else throw the exception
2)Pessimistic is some thing like a database handles the locking on that row and it will get released after the operation is completed, there are few options are there which is similarly like how you imagine like read lock, write lock
https://docs.jboss.org/hibernate/orm/4.0/devguide/en-US/html/ch05.html
If you are using PostgreSQL I think the below example works:
#Query(value = """with ins_artist as (
insert into artist
values (301, 'Whoever1')
returning *
) select artist_id
from ins_artist
for update""", nativeQuery = true)
#Transactional(propagation = Propagation.REQUIRED)
Long insertArtist(); // returns artist ID
PS: I ran this query on https://postgres.devmountain.com/ . But it would need testing on a Java app.

findBy appear to update the database

I have the following code:
println "######## RUNNING ProfessionaCustomer - ${pcCounter} under ${accountCustomer.customerNumber} Professional SQLid ${it.id}"
def professionalCustomerId = it.customerId
def professionalCustomer = ProfessionalCustomer.findByCustomerNumber(professionalCustomerId)
I have SQL logging on and I get:
######## RUNNING ProfessionaCustomer - 31 under 106450 Professional SQLid 100759
Hibernate: update base_domain set version=?, account_name=?, address_line1=?, address_line2=?, city=?, customer_number=?, date_created=?, disabled=?, last_updated=?, postal_code=?, primary_phone=?, state_or_province=? where id=? and version=?
Hibernate: update base_domain set version=?, address1=?, address2=?, city=?, customer_number=?, date_created=?, disabled=?, first_name=?, last_name=?, last_updated=?, middle_name=?, phone_number=?, postal_code=?, state=? where id=? and version=?
Hibernate: insert into account_customer_professionals (account_customer_id, professional_customer_id) values (?, ?)
Hibernate: select this_.id as id1_3_0_, this_.version as version2_3_0_, this_.address1 as address70_3_0_, this_.address2 as address71_3_0_, this_.city as city7_3_0_, this_.customer_number as customer8_3_0_, this_.date_created as date_cre9_3_0_, this_.disabled as disable10_3_0_, this_.first_name as first_n19_3_0_, this_.last_name as last_na20_3_0_, this_.last_updated as last_up11_3_0_, this_.middle_name as middle_72_3_0_, this_.phone_number as phone_n73_3_0_, this_.postal_code as postal_12_3_0_, this_.state as state74_3_0_ from base_domain this_ where this_.class='com.eveo.nplate.model.ProfessionalCustomer' and this_.customer_number=? limit ?
Which is updating the DB. This would explain why this is so slow, but I can't see any reason for this to happen.
Why would 'findBy' cause an update?
Hibernate doesn't immediately execute creates, updates, or deletes until it thinks it has to - it waits as long as possible (although it's rather pessimistic) and only flushes these changes when you tell it to, or when it thinks it needs to. In general the only time it will flush without an explicit call is when running queries. This is because any of the new instances, updated instances, and deleted instances that are in-memory (cached in the Hibernate Session, the 1st-level cache) could affect the query results, so they must be flushed to the database so you get the proper results for your query.
One exception to this is calling save() on a new instance. Grails flushes this because typically the id is assigned by the database, either via an auto-increment column or a sequence. To ensure that the in-memory state is the same as the database, it flushes the save() call so it can retrieve the id and set it in the instance. But if you retrieve a persistence instance (e.g. with a get() call, or with a criteria query, finder, etc.) and modify it, calling save() on that does not get automatically flushed. The same goes for delete() calls - not flushed.
Think of delete() and save() calls on persistent instances as messages to Hibernate that the action should be performed "eventually".
So when you execute a finder, or a criteria, "where", or HQL query, Hibernate will flush any un-flushed changes for you. If you don't want that to happen (e.g. in a custom domain class validator closure) you can run the query in a separate session, e.g. with the withNewSession method.
If you don't flush the session at all, either explicitly on the Session instance or by adding flush:true to a save or delete call, the session will be flushed, since Grails registers an OpenSessionInView interceptor that starts a session at the beginning of each request, and flushes and closes it at the end. This helps with lazy loading; since there's a session open and bound to a ThreadLocal in a known location, Hibernate and GORM (via Spring's HibernateTemplate) can use that open session to retrieve lazy-loaded collections and instances on-demand after the query runs.
Note also that you do not need to flush in a transaction. The transaction manager is a Spring HibernateTransactionManager that flushes before committing.
Probably there was some transaction in the session that was not persisted in the database.
When you ran the findBy hibernate took advantage of the connection to run the two queries. I believe this is what happened.

Why does #Transactional save automatically to database

I have a method annotated with #Transactional. I retrieve an object from my DB, change a field, and then return from the method. Without saving my object, the database gets updated anyway which is strange.
Could you please tell me how to avoid this beahvior?
This behaviour is one of the main purposes of transactionality.
Before the transactional method is about to return, the transaction commits, meaning all changes to the managed entities are flushed to the database.
If an error occurs, the transaction will be rolled back, meaning that no changes will be committed to the database.
You are probably getting the LazyInitializationException when trying to access a lazily loaded property, probably a collection from an entity. Lazily loded properties do not get instantiated when you fetch an entitiy from DB.
If you access a lazily loaded property in a transaction, the persistence provider will create a query, instantiate the result and attach it to the 'parent' entity.
EDIT: If you want to have the lazy properties loaded AND be able to change your entity without the changes being persisted to the DB, you can fetch the entity with fetch joins for the lazy properties.
em.createQuery("SELECT e FROM MyEntity e JOIN FETCH e.lazyProp");
Then proceed with one of the methods described by #orid.
If you are not using fetch joins, you will need to access the lazily loaded properties while still inside the transaction:
myEntity.getLazyProp().size();
Note the call to size(). Calling the getter is not enough as you will get a proxy. You need to perform an operation that needs the actual data from the property.
This a normal JPA behavior.
Once you retrieve an object via find() or so, that object is regarded as attached, or belongs to a persistence context. Once you exit the method the #Transactional triggers a Spring transaction management aspect which flushes every "dirty" object to database and commits the transaction. Since your object is already changed within the context of the persistence context and the transaction, the changes are saved to the database even without the need to explicitly call a save method.
If you want to change your object without affecting the database, you have two options:
Update the field after returning from the method annotated with #Transactional
If withing the method, call detach on the entity manager

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