Hibernate: concurrent insert - spring

I have a multithread handler, which is taking entries from Kafka topic and saving data to PostgreSQL.
Entries in Kafka can represent:
create new entity
update existing entity
Since the handler is multithread, update entry can come prior to create entry. For that reason I check on update if entity persists in DB and make insert on negative case (and vice-versa: make update instead of insert if entity has already persisted)
I face a UK_CONSTAINT_VIOLATION here because changes made by different threads seem to be cached and at the moment of commit, contains few insert statements with the same UK.
I've tried with no success:
Mark handler with #Transactional(isolation = Isolation.READ_UNCOMMITTED)
Configure Hikari with:
hikari:
auto-commit: false
maximum-pool-size: 5
transaction-isolation: TRANSACTION_READ_UNCOMMITTED
Use caching on Entity level:
#Cacheable
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)

Related

Spring and Hibernate Transaction does not work for mass save operations

I am doing mass save with JpaRepository.I used #Transactional but it didn't work.I summarize the process I did:
I have two Enties
THeaderEntity
TDetailsEntity
First of all, I save the TheaderEntity because the THeaderEntity's information will be used in the TDetailsEntity (One To Many, CrudRepository.save(), 1 Header for 50 Details)
Then turn and save in the TDetailsEntity's loop.I want the entire process rollback if any registry gets an error.
#Transactional
public Result createTdetailsFromHeaderId(String token, String headerId, DetailRequests detailRequests)
I used #Transactional but only the record that received the error was rollback.
To answer why only 1 record is rolled back you might look into Spring's transactional logic - I suspect from your description that 1 transaction is opened for every save but an easy way to work on this is:
Adjust the #OneToMany relationship to cascade the persisting of entities.
Create the appropriate entities (1 THeaderEntity and 50 TDetailsEntity having all object references set correctly).
save the THeaderEntity that then should cascade the persisting to its TDetailsEntity.

how to stop default query on id when using #cacheable annotation

When using #Cacheable annotation(org.springframework.cache.annotation.Cacheable) on the repository with custom key like name,getting the issue of running extra query on id field on every consecutive request.
See the repository code below:
public interface StatusRepository extends JpaRepository<Status, Integer> {
#Cacheable(value = "StatusByStatusNameCache" , key="#statusName")
public Status findByStatusName(String statusName);
}
Above you can see that a cache is defined for status name only, now after running first request got the following Hibernate console with query on status name:
Hibernate: select status0_.status_id as status_i1_7_, status0_.status_name as status_n2_7_ from status status0_ where status0_.status_name=?
Hibernate: select event0_.event_id as event_id1_3_, event0_.event_name as event_na2_3_ from events event0_ where event0_.event_name=?
now then hit another second request getting hibernate query in console with id :
Hibernate: select event_.event_id, event_.event_name as event_na2_3_ from events event_ where event_.event_id=?
Hibernate: select requestcha_.request_channel_id, requestcha_.request_channel_name as request_2_6_ from request_channels requestcha_ where requestcha_.request_channel_id=?
Hibernate: select status_.status_id, status_.status_name as status_n2_7_ from status status_ where status_.status_id=?
I don't understand why this extra query is firing as status_name query is cached but how to stop this id query on every consecutive call after first request.
The #Cacheable annotation by Spring is completely independent from the any cache provided by Hibernate/your JPA implementation.
The query by id will not be prevented by caching the result of the by name query, because the caching for the id query would be done by JPAs first level cache, which doesn't know or care about Springs cache.
Here is what is probably going on:
findbyName
entity is in 1st level cache and in Springs cache.
Any access by id (e.g. navigating to the entity)
entity gets served from 1st level cache.
session ends.
entity is removed from 1st level cache
findByName
entity is served from Springs cache. Note that this is now a detached entity. Nothing is in the 1st level cache.
access by id
entity is loaded from database, since it is not found in the 1st level cache.
You should enable Hibernates 2nd level cache to cache entities across sessions for access by id.
I also would advise against combining the caches of JPA/Hibernate with Spring Caches and rather use JPAs own query cache to cache findByName. See Spring JPA Hibernate query cache doesn't work for how to make it work with Spring Data JPA.
Also take a look at this article by Vlad Mihalcea about interaction of query cache and 2nd level cache.
Note that Oliver Drotbohm seems to have a different opinion.

Hibernate PostUpdateEventListener is not giving old values state if saveorUpdate or update is used

We are implementing the activity log for our application. we decided to go with the approach of capture the hibernate postupdateevent listeners and capture the old values and new values and update our activity log. The approach works wherever we have merge operation. But when we used saveorupdate or update we were not getting the old values.
PostUpdateEvent postUpdateEvent = (PostUpdateEvent) event;
Field[] fields = postUpdateEvent.getEntity().getClass().getDeclaredFields();
String[] propertyNames = postUpdateEvent.getPersister().getEntityMetamodel().getPropertyNames();
Object[] newStates = postUpdateEvent.getState();
Object[] oldStates = postUpdateEvent.getOldState();
Edits:
As part of this there are few queries, on the gaps i have in my understanding.
With respect to hibernate, there are three states:
1.Transient
2.Persistant
3.Detached
If we create a new object, it is transient state. Now when we do update or saveorupdate or merge with the hibernate session, then the object becomes persistent.
In this scenario, if we use hibernate postupdateevent to capture the updates, we are getting the old values only when we use merge and we dont get the values when we use update, or saveorupdate.
My understanding was saveorupdate also does get call to determine if it is insert or update, in such cases i was expecting it to give old values in hibernate postupdate event when we do saveorupdate similar to what we get when we do merge.
But we dont get for saveorupdate and update calls, we just get the old values for merge.
It sounds like the postupdateevent listener if we use for audit logs, with saveorupdate or update, it wont give us the old values.
Am i missing something here?

Get original values from entity with Hibernate Envers

I have configured Hibernate Envers in my Springboot project and now it is saving each change in the entities I annotated with #Audited but, I have a doubt.
Envers stores the revision of the entity after the first change is done so, after one change I have the new values stored in the entity table and in the _AUD table. The next changes are stored in the _AUD table so I know what changed after the first update but the original values (the ones before the first change) are lost. Am I missing something? Is there a way to save the values before the change is done (as I already have the last values in the entity table)?.
There are three different revision types tracked by Envers:
ADD (REVTYPE=0) - INSERT
MOD (REVTYPE=1) - UPDATE
DEL (REVTYPE=2) - DELETE
This implies that if the entries are being inserted, updated, and deleted by Hibernate through a stateful session, Envers will pickup those changes and add the appropriate REVTYPE entry to the audit table.
If an entry is being manipulated outside of the scope of Hibernate's stateful session, Envers won't know about that change and the corresponding entry won't be added to the audit table. Based on the comments, this is why you don't see a REVTYPE=0 (aka INSERT) operation.
For situations like this, you'll need to make sure that you increment the revision number sequence and add the appropriate entries manually through your script or batch process that is inserting the row in order to guarantee that the Envers schema has the complete visibility to the entity's history.

HibernateDAOSupport Get method

I am working on a existing project which uses Hibernate and Spring. I see a following code which uses HibernateDAOSupport class,
Employee emp = getHibernateTemplate().get(Emplyee.class, 1001)
After the above line we set some property like emp.setAge(25); and at the end we don't call any Save or SaveOrUpdate method. But it's saving the data to DB. How is it possible ?
If it can Save then what is the difference between getHibernateTemplate().get() and getHibernateTemplate().save/SaveOrUpdate methods ?
This is expected behaviour of Hibernate and it is because the Employee entity is loaded into the PersistenceContext and therefore enters the 'persistent' entity lifecycle state.
When you commit the transaction, Hibernate will check any 'persistent' entities within the PersistenceContext to see if they are "dirty". Dirty means that any values of the entity have changed. Your call to emp.setAge(25) means that Hibernate understands that data within the entity has changed (it is dirty), and it should therefore make the changes persistent when the transaction commits.
It is worth reading and understanding how Hibernate manages entity states as it can be a little confusing to start with. The documentation is here.

Resources