[hibernate-dev] Hibernate Envers > link between to auditable entities

Chris Cranford crancran at gmail.com
Thu Sep 1 11:51:46 EDT 2016

Tarek -

See inline.

On 09/01/2016 10:11 AM, Dev Stack wrote:
> Hi Chris,
> Thank you for your answer.
> 1) yes, we can use Set<>
> 2) When we load an offer we have to load also the associated product.
> So, we have to keep the link from offer to product.
> the link from product to offer is not needed for now.
What I meant here was more along the lines of keeping the bidirectional 
but instead of the @ManyToOne being the owner of the mapping, we'd 
invert it so
that the @OneToMany owned the relationship.  The mapping would then be:

public class Product {
   @JoinColumn(name = "product_offer")
   @AuditMappedBy(mappedBy = "product")
   private Set<Offer> offers;

public class Offer {
   @JoinColumn(name = "product_offer", insertable = false, updatable = 
   private Product product;

We want to do this to take advantage of Envers' 
See below in your point 2 as to why.

> Thanks,
> Tarek
> 2016-09-01 15:55 GMT+02:00 Chris Cranford <crancran at gmail.com 
> <mailto:crancran at gmail.com>>:
>     Tarek -
>     There are a few ways we can tackle what you want all within the
>     scope of Envers,
>     but some of it highly depends upon details specific to the
>     relationship between
>     Product (P) and Offer (O).
>     1. What is the collection type used in Product for the related offers?
>        If this happens to be a List<>, would it be possible to use a
>     Set<> instead?
>     2. Is there a requirement that Offer should own the relationship
>     with Product?
>        In other words, is it possible to invert the relationship such
>     that the
>        collection side is the owner instead?
>     Chris
>     On 09/01/2016 06:14 AM, Dev Stack wrote:
>         Hello,
>         we have in our model entity P (product) and entity O (offer).
>         The two
>         entities have a link.
>         one instance of P can have one or many O instances.
>         an instance O has a reference to only one instance of P.
>         The link is managed in the O side.
>         P and O are revisioned by Hibernate Envers.
>         Two use cases to cover:
>         1) If P instance is updated (new revision) we want that O keep
>         the link
>         with the old revision of P.
You get this by default.

Regardless of which side owns the relationship, any change made 
specifically to a
Product that does not change the associated Offer instances, only a 
revision is
triggered for Product, allowing Offer instances to point to the older 
versions of a
Product when they're queried.

If we assume a Product and Offer were created in revision 1 and 
subsequently the
Product was modified in revision 2, the following are true:

   auditReader.getRevisions( Offer.class, offerId ) = [1]
   auditReader.getRevisions( Product.class, productId) = [1, 2]

So querying Offer at revision 1 would give you the product instance at 
revision 1

   Offer offer = auditReader.find( Offer.class, offerId, 1 );
   offer.getProduct() = Product at revision 1

>         2) When I update O instance, I will move the reference to the
>         last revision
>         of O instance.
>         What we did is, inside O class we added to attributes P.id and
>         P.revision.
>         So when we load the object P we use these to fields to load
>         manually (O DAO
>         has reference P DAO).
I think it makes more sense to enforce this through Envers than 
polluting the domain
model, but that's just my opinion ;).

The cleanest way is to invert the relationship as I described above and 
change the
collection type to a Set<>.  The setting REVISION_ON_COLLECTION_CHANGE will
take affect since Product now owns the relationship and any change you 
make to
an Offer, the owning entity (Product) will be revised automatically.

In other words, changes to an Offer will force Product to update and so 
the revision
number query logic from 1) holds true and helps us here too.

NOTE: Just be sure you implement the appropriate equals/hashCode logic 
for Offer
so that changes are properly detected.

If you decide you don't want to use REVISION_ON_COLLECTION_CHANGE and invert
the relationship between Product and Offer, the only other partial 
Envers' solution
I see might be to introduce some attribute on Product that is audited 
that your
business tier couldchange when an offer is modified, causing the Product to
be audited like the following:

   changedOffer.setCode( "NEWCODE" );
   changedOffer.getProduct().pulse(); // updates a field to trigger revision
   entityManager.merge( changedOffer );

Hope this helps.

More information about the hibernate-dev mailing list