Question

I have an audited entity A. Entity A holds field 'name' and a collection of entities B (annotated as Many-to-many relationship). I created an instance of A, defined name, collection of entities B and save all it into DB. This is revision #1. Then I changed name of A and update it in DB. This is revision #2. I use the following method to get all entities of class A at revision #2

List<A> list = getAuditReader().createQuery().forEntitiesAtRevision(A.class, 2)
    .add(AuditEntity.revisionNumber().eq((int) revisionId)).getResultList();

I get entity A at revision #2, but Envers also fetches collection of entities B related to this A from revision #1. Here an example of query used by Envers:

SELECT a_b_aud.a_id, a_b_aud.b_id
FROM   a_b_aud CROSS JOIN b_aud
WHERE  a_b_aud.b_id=b_aud.id 
       AND b_aud.rev=(SELECT max(b_aud2.rev)) FROM b_aud AS b_aud2 WHERE b_aud2.rev<=2 AND b_aud.id=b_aud2.id)
       AND a_b_aud.rev=(SELECT max(a_b_aud2.rev)) FROM a_b_aud AS a_b_aud2 WHERE a_b_aud2.rev<=2 AND a_b_aud.a_id=a_b_aud2.a_id AND a_b_aud.b_id=a_b_aud2.b_id)

But actually I need NULL as a collection of entities B in case of there were no changes for it at revision #2 (because of performance issue).

There are two subselects in this query. And if we have more then one collection of entities related to A (C, D, E, F) and for about 100 thousands rows for each b_aud and a_b_aud the query above takes a lot of time. I defined entity B as not audited (i.e. did not add @Audited annotation into B) and defined A B relation by the following:

@ManyToMany
@Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE})
@JoinTable(name = "a_b", joinColumns = @JoinColumn(name = a_id))
@Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
public Set<B> getBs();

It fixes first SUBSELECT. But I can not find standard solution to not query B's if it do not exist for requested revision (in my case #2). So the query should look like:

SELECT a_b_aud.a_id, a_b_aud.b_id
FROM   a_b_aud CROSS JOIN b_aud
WHERE  a_b_aud.b_id=b_aud.id b_aud.rev=2 AND a_b_aud.rev=2

The only solution I found is using native sql query and to execute it using hibernate template. Then convert result values into entity A using ResultTransformer.

Could anybody help with this issue? Is there a standard configuration/annotation I need to add to avoid second SUBSELECT?

Was it helpful?

Solution

There's no option in Envers not to load related entities when requested. Not however, that the B entities are always loaded lazily (regardless of the annotations on the relation), so if you don't want to execute the query which loads them, simply do not access that field.

If you want better read performance, you may also want to look at the validity audit strategy, see http://docs.jboss.org/hibernate/core/4.1/devguide/en-US/html/ch15.html#d5e4085. It has faster reads, but slower writes.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top