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
relation,
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:
@Entity
@Audited
public class Product {
...
@OneToMany
@JoinColumn(name = "product_offer")
@AuditMappedBy(mappedBy = "product")
private Set<Offer> offers;
...
}
@Entity
@Audited
public class Offer {
...
@ManyToOne
@JoinColumn(name = "product_offer", insertable = false, updatable =
false)
private Product product;
...
}
We want to do this to take advantage of Envers'
REVISION_ON_COLLECTION_CHANGE.
See below in your point 2 as to why.
Thanks,
Tarek
2016-09-01 15:55 GMT+02:00 Chris Cranford <crancran(a)gmail.com
<mailto:crancran@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:
entityManager.getTransaction().begin();
changedOffer.setCode( "NEWCODE" );
changedOffer.getProduct().pulse(); // updates a field to trigger revision
entityManager.merge( changedOffer );
entityManager.getTransaction().commit();
Hope this helps.
Chris