hibernate auditing with trigger - java

I'm using hibernate with jpa and i have implemented audit with postgres trigger.
In my source code i have used à listenner on preInsert, preUpdate and preDelete to create a revision. This revision is added only if the saved entity is annoted by custom #AuditedEntity
This revision line contain transactionId and all the modification in same transaction is logged into audit_revision_details with postgres triggers and with this mecanism i can retrieve all modifications done on an entity.
I have à User entity with #AuditedEntity annotation and in the user entity i have a RoleUser Set. When i'm change lastname of the user, all work fine, i have a revision line created in my audit_revision table and in audit_revision_details i can see the change.
But when i'm saving only element inside RoleUser (i'm saving a user and RoleUser is save with CascadeType.ALL), revision is not created in audit_revision because when i'm entering in preInsert/preDelete or preUpdate listener, the saved entity is RoleUser and RoleUser is not annoted by #AuditedEntity because it's User that have this annotation.
One solution is to add à field in User entity that i always update, with this, i always save User entity and i'have my revision but it's not very clean.
Another one is to check if the entity is annoted by #AuditedEntity or entity_name is 'RoleUser' and i always create revision...but if i change my lastname at the same time i change a role, i risk to have 2 revision in bdd so i need to check if one exist before...
I don't want to use hibernate-envers ( and envers have the same problem...)
How can i tell hibernate to force the revision creation when i only change entity inside another one?
Does anyone have implement same things and can help me?
Thanks

One solution is to add à field in User entity that i always update, with this, i always save User entity and i'have my revision but it's not very clean.
I think that is going to be your only viable option, unless you can make all the *ToMany associations owned i.e. remove the mappedBy part.
By making the many side the owning side, Hibernate will treat an entity as dirty when the collection changes somehow, though I don't think that changes to the entities within the collection will cause the same. If you want that changes to the elements also cause the entity to be treated as dirty, you will have to use an #ElementCollection.
Overall, introducing a field on User like modificationTimestamp which you update is probably the easiest way to achieve this.

Related

Automatically remove deleted entity from all collections referencing it

Is it possible, when using Hibernate and cascade delete, to remove the cascade-deleted entity from all other entities that have a reference to it?
Take this diagram for example:
User has a MembershipOffer to join a Group. In my model is possible to remove groups with pending offers, and when that happens, I want all the offers of the deleted group to be deleted as well.
I understand that in order to do that, I will need to change the MembershipOffer-Group association to be bidirectional and set the cascade flag on it. However, when MembershipOffer is deleted, what happens to the User's reference to it?
I assume Hibernate won't go through every reference to the deleted object and remove it from appropriate collections. Will I have to delete the references manually, or is there a automatic way to do it?
Note:
I am using a relational database and from purely SQL-based look I don't see a problem. MembershipOffer table contains FK of both User and Group. When Group is deleted, so are all Offers containing its FK. With their removal the link between Offer and User is deleted. The problem I have is that in the code User has a field Set<MembershipOffer> membershipOffers which may contain a reference to the just-deleted Offer (which will be invalid if I try to access it).

How to persist all versions of an entity by JPA?

I am working on auditing the changes of an entity, say PersonEntity.
Existing codes support creating or editing a person's profile and persisting the corresponding PersonEntity. If editing, it will update the PersonEntity in database. It also supports get query of the persons based on a few properties like age. Furthermore, it also provides API to set the "created by", "updated by" and "updated time" - generally it is quite auditable.
My work is to make it even more auditable by persisting all the old versions of a PersonEntity. I think I have two options:
Persist a new PersonEntity even if editing. In that way, all versions of PersonEntity are in the database. I should also, 1) add a property in the PersonEntity to indicate whether it is the up-to-date one; 2) when creating or updating PersonEntity, I need to set that flag; 3) change all the query methods to only get PersonEntity that is up-to-date.
In my view that is not a good option since 3) will cause a bunch of code change, and will slow down the query as well.
Creating a new class with the same property as PersonEntity, say PersonAuditEntity. When creating or updating PersonEntity, always persist one new PersonAuditEntity.
But in that way, two similar classes are existing in the same time. My initial solution is to simply wrap a PersonEntity into PersonAuditEntity, but it seems that JPA does not support embed an entity in another entity. Is that right?
Any other suggestions will be highly appreciated.
Did you take a look at Hibernate ORM Envers? This supports auditing the entities like for eg:
#Audited
private String name;
Will record changes to name atribute for the entity.
Another option.
Add a new column which gets set to inactive when the row is replaced with a new one. Only add new rows and never update a row.
All selects need to use an additional where clause "and inactive = false"

JPA - Redirect one entity Update/Delete statements to a mirror table

I am working with Java EE 7 on a Wildfly server. I have a strange scenario, where the client has two tables - "employees" and "employees_modified". The second table has the exactly same structure as the first one and servers as a modification storage. So if an employee changes his name from "john" to "john-1", we will write to employees_modified
insert into employees_modified(first_name) values("john")
Please note that the other fields in the table "employees_modified" are empty.
The question is: is there a way to somehow map the two tables and overwrite the values from employees by those in employees_modified where they are present.
I looked at #Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) and #AttributeOverrides but those solutions don't seem to fit to my scenario.
Look at hibernate envers, it solves your problem simply. Attach envers to your project. Place the first table under audit with annotation #Audited and #AuditTable(value = "employees_modified"). But as pointed by #Predrag Maric it is important to leave other fields of the second table empty, you can use #PostPersist (or listener in pure hibernate) method in entity. In this method you can describe additional logic employees_modified entity creation and persisting.
You can use #SQLUpdate and #SQLDelete to customize the CRUD statements to be redirected to a different table:
#Entity
#SQLUpdate( sql="UPDATE employees_modified SET name = ? WHERE id = ?")
#SQLDelete( sql="DELETE FROM employees_modified WHERE id = ?")
public class Employees {
...
}
If it's only for auditing, I agree with the answers before me (triggers, events etc).
If you actually want to access that "employees_modified" table (e.g. run complex Hibernate queries) than you can use a second persistent unit. The following post: https://developer.jboss.org/thread/237078 seems to indicate that's a recommendation from hibernate. Obviously the 2nd unit will need xml configuration rather than annotation - at least it can't rely on the same #Table annotation.
BTW there's also some documentation about a #SecondaryTable annotation, but it's my understanding that it doesn't match your case (because your business probably needs to treat those tables differently - sometimes you want to view just the history, sometimes just the live data)/

Does Hibernate Envers creates revision to audit tables if you update without changing any field values?

I am currently testing hibernate envers and I am confused with its behavior. Sometimes when we do updates without doing any changes, it does not record an audit. But currently, it does. Just like to make sure.
This can occur if Hibernate does not have access to the old state of the entity to compare to the new state. This typically occurs with when persisting detached entities. When there is no old state to compare, Hibernate assumes that the audited property has changed and logs a revision.
You can get around this issue by re-attaching detached entities using the merge() method before persisting the entity.
From my experience, it only creates an entry in the revision table if you have made changes to the entity.

merging / re-attaching IN JPA / Hibernate without updating the DB

Working with JPA / Hibernate in an OSIV Web environment is driving me mad ;)
Following scenario: I have an entity A that is loaded via JPA and has a collection of B entities. Those B entities have a required field.
When the user adds a new B to A by pressing a link in the webapp, that required field is not set (since there is no sensible default value).
Upon the next http request, the OSIV filter tries to merge the A entity, but this fails as Hibernate complains that the new B has a required field is not set.
javax.persistence.PersistenceException: org.hibernate.PropertyValueException: not-null property references a null or transient value
Reading the JPA spec, i see no sign that those checks are required in the merge phase (i have no transaction active)
I can't keep the collection of B's outside of A and only add them to A when the user presses 'save' (aka entitymanager.persist()) as the place where the save button is does not know about the B's, only about A.
Also A and B are only examples, i have similar stuff all over the place ..
Any ideas? Do other JPA implementaions behave the same here?
Thanks in advance.
I did a lot reading and testing. The problem come from my misunderstanding of JPA / Hibernate. merge() always does a hit on the DB and also schedules an update for the entity. I did not find any mention of this in the JPA spec, but the 'Java Persistence with Hibernate' book does mention it.
Looking through the EntityManager (and Session as fallback) API it looks as if there is no means of just assigning an entity to the current persistent context WITHOUT scheduling an update. After all, what I want is to navigate the object graph, changing properties as needed and trigger an update (with version check if needed) later on. Something i think every Webapp out there using ORM must do?
The basic workflow i 'm looking for:
load an entity from the DB (or create a new one)
let the entity (and all its associations become detached (as the EntitManager closes at the end of a HTTP request)
when the next HTTP request comes in, work again with those objects, navigating the tree without fear of LazyInitExceptions
call a method that persists all changes made during 1-3)
With the OSIV filter from spring in conjunction with an IModel implementation from wicket i thought i have archived this.
I basically see 2 possible ways out of it:
a) load the entity and all the associations needed when entering a certain page (use case), letting them become detached, adding/ changing them as needed in the course of several http requests. Than reattach them when the user initiates a save (validators will ensure a valid state) and submit them to the database.
b) use the current setup, but make sure that all newly added entities have all their required fields set (probably using some wizard components). i would still have all the updates to the database for every merge(), but hopefully the database admin won't realize ;)
How do other people work with JPA in a web environment? Any other options for me?

Categories