spring-data jpa's deleteAll() method call records are not audited in hibernate envers - spring-boot

By default for save(), delete(), saveAll() hibernate envers audit the record change.
But for deleteAll() it is not auditing.

In application.properties override org.hibernate.envers.store_data_at_delete
org.hibernate.envers.store_data_at_delete = true
Default value is false
"org.hibernate.envers.store_data_at_delete" Should the entity data be stored in the revision when the entity is deleted (instead of only storing the id and all other properties as null). This is not normally needed, as the data is present in the last-but-one revision. Sometimes, however, it is easier and more efficient to access it in the last revision (then the data that the entity contained before deletion is stored twice).

Related

#cachePut for data update in list of object in spring boot

Hi I am using Spring Boot Cache in my application. I am able to fetch data from db and cached that data.
#Cacheable("employee")
public Optional<List<Employee>> employeeData(){
log.info("Fetched employee details from DB and cached in memory!!");
return employeeRepository.findActiveEmployee();
}
I want to delete or update or add new record in cached object.
How can I use #cachePut to update existing record or insert record or delete existing record based on some condition.
You can keep your original method as is to call it whenever you want to fetch data from cache. Note that the #Cacheable annotation does not execute the method's body if the cache with name "employee" is not empty, instead it returns the results from cache.
#Cacheable("employee")
public Optional<List<Employee>> employeeData(){
}
Then proceed in creating a new method annotated with #CachePut. Having in mind that #CachePut annotation will both execute the method as well as cache the results each and every time:
#CachePut(value="employee", condition="#name=='Tom'")
public Optional<List<Employee>> employeeDataCacheByName(String name){
log.info("Fetched employee details from DB and cached in memory depending on condition!!");
return employeeRepository.findActiveEmployee();
}
The above method will run the query every time and put into employee cache the results if the name argument is "Tom" (condition logic is up to you, this is just an example). This way cache is always updated with the results from the database (as long as the condition is truly evaluated).
For deleting (I don't think #CachePut can be used) maybe you can combine the #CacheEvict annotation, you can see an example in this Answer: https://stackoverflow.com/a/62488344/3635454

Spring data JPA with H2 database not returning non-merged data

I have an entity with created_date (updateable = false) and updated_date fields. I have #PreUpdate method where I change the updated_date value only (not change created_date), and #PrePersist method which sets new created_date and updated_date values. On Persist the created_date and updated_date are rightly persisted and the returned entity has the correct values. When I pass in the entity to merge, it rightly updates the updated_date (#PreUpdate), and I don't pass the created_date in input. In the database the right updated_date value is updated and created_date value is not changed rightly. But the returned entity has the created_date value set to null. Any Ideas why? Shouldn't the merged entity return the full entity loaded from the database?
Thanks
Sam
I think that is inline with the JPA merge javadoc.
Merge - Merges the state of the given entity into the current persistence context and returns the managed instance that the state was merged to.
(With hibernate as persistence provider) Merge starts with loading the data from the database for that entity, then copies detached entities state to the newly loaded entity. Subsequently, at a later point, during the transaction commit phase(or flush) the dirty checking mechanism fires the update query but won't include the fields marked as updatable=false.
So it doesn't attempt to reload the object with the data in the database after the UPDATE.
To trigger reload, you can rely on refresh(...) that will reload the data.
If it is spring-data-jpa it doesn't expose any refresh method, so you need to add it to your repository and and an example can be found here and discussion on this topic in the spring forum here.

How to actualize entity in Spring JPA? Actualize or create new one?

I'm wondering what is best practice to update JPA entity in Spring project - update original entity or create new? I see these two approaches:
Use original - Actualize necessary fields in original entity and save this updated entity back to the repository.
Use copy - manually create new instance of entity, set all field from original entity (+ updated fields) into new entity and save the entity back to the repository.
What approach do you use / is recommended? And why?
When it comes to updating, the standard way would be to retrieve the entity reference(read below) and make changes within a transactional method:
private JpaRepository repo;
#Transactional(readOnly = false)
public void performChanges(Integer id){
Entity e = repo.getOne(id);
// alter the entity object
}
Few things regarding the example:
You would want to use the getOne method of JpaRepository as much as possible as it is in general faster than the findOne of the CrudRepository. The only trick is that you have to be sure that entity actually exists in the database with the given id. Otherwise you would get an exception. This does not occur regarding the findOne method so you would need to make that decision regarding each transactional method which alters a single entity within your application.
You do not need to trigger any persist or save methods on the EntityManager as the changes will be automatically flushed when the transaction is commited.. and that is on method return.
Regarding your second option, I dont think thats much of a use as you would need to get the data using above method anyway. If you intend to use that entity outside of the transaction, then again you could use the one retrieved from the exmaple above and then perform merge once it is again needed within the transactional context and thus Persistence Provider.
Getting an entity and then just updating that entity is the easiest way to do that. Also this is faster than a creation of a copy since EntityManager manages an entity and know that managed entity already exists in DB (so no need to execute additional query).
Anyway, there is third and the fastest approach: using executeUpdate on Query object.
entityManager
.createQuery("update EntityName set fieldName = :fieldName where id = :id")
.setParameter("fieldName", "test")
.setParameter("id", id)
.executeUpdate();
It is faster due to bypassing the persistent context

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