HHH-5580 - Query fix

This commit is contained in:
Lukasz Antoniak 2011-04-29 13:44:39 +02:00
parent 98342a7e2d
commit 88eda2b081
5 changed files with 161 additions and 30 deletions

View File

@ -25,8 +25,8 @@ package org.hibernate.envers.query;
import static org.hibernate.envers.tools.ArgumentsTools.checkNotNull; import static org.hibernate.envers.tools.ArgumentsTools.checkNotNull;
import static org.hibernate.envers.tools.ArgumentsTools.checkPositive; import static org.hibernate.envers.tools.ArgumentsTools.checkPositive;
import org.hibernate.Query;
import org.hibernate.envers.configuration.AuditConfiguration; import org.hibernate.envers.configuration.AuditConfiguration;
import org.hibernate.envers.query.impl.EntitiesModifiedAtRevisionQuery;
import org.hibernate.envers.query.impl.EntitiesAtRevisionQuery; import org.hibernate.envers.query.impl.EntitiesAtRevisionQuery;
import org.hibernate.envers.query.impl.RevisionsOfEntityQuery; import org.hibernate.envers.query.impl.RevisionsOfEntityQuery;
import org.hibernate.envers.reader.AuditReaderImplementor; import org.hibernate.envers.reader.AuditReaderImplementor;
@ -55,18 +55,9 @@ public class AuditQueryCreator {
* projection is added. * projection is added.
*/ */
public AuditQuery forEntitiesAtRevision(Class<?> c, Number revision) { public AuditQuery forEntitiesAtRevision(Class<?> c, Number revision) {
return forEntitiesAtRevision(c, revision, false);
}
/**
* Returns deleted entities as well. Removed entities data depends on the value of
* <code>org.hibernate.envers.store_data_at_delete</code> parameter.
* @see #forEntitiesAtRevision(Class, Number)
*/
public AuditQuery forEntitiesAtRevision(Class<?> c, Number revision, boolean selectDeletedEntities) {
checkNotNull(revision, "Entity revision"); checkNotNull(revision, "Entity revision");
checkPositive(revision, "Entity revision"); checkPositive(revision, "Entity revision");
return new EntitiesAtRevisionQuery(auditCfg, auditReaderImplementor, c, revision, selectDeletedEntities); return new EntitiesAtRevisionQuery(auditCfg, auditReaderImplementor, c, revision);
} }
/** /**
@ -83,7 +74,22 @@ public class AuditQueryCreator {
checkNotNull(revision, "Entity revision"); checkNotNull(revision, "Entity revision");
checkPositive(revision, "Entity revision"); checkPositive(revision, "Entity revision");
return new EntitiesAtRevisionQuery(auditCfg, auditReaderImplementor, c, entityName, revision); return new EntitiesAtRevisionQuery(auditCfg, auditReaderImplementor, c, entityName, revision);
} }
/**
* In comparison to {@link #forEntitiesAtRevision(Class, String, Number)} this method will return an empty
* collection if an entity of a certain type has not been changed in a given revision.
* @param c Class of the entities for which to query.
* @param revision Revision number at which to execute the query.
* @return A query for entities changed at a given revision, to which conditions can be added and which
* can then be executed.
* @see #forEntitiesAtRevision(Class, String, Number)
*/
public AuditQuery forEntitiesAtCertainRevision(Class<?> c, Number revision) {
checkNotNull(revision, "Entity revision");
checkPositive(revision, "Entity revision");
return new EntitiesModifiedAtRevisionQuery(auditCfg, auditReaderImplementor, c, revision);
}
/** /**
* Creates a query, which selects the revisions, at which the given entity was modified. * Creates a query, which selects the revisions, at which the given entity was modified.

View File

@ -39,28 +39,19 @@ import org.hibernate.envers.reader.AuditReaderImplementor;
*/ */
public class EntitiesAtRevisionQuery extends AbstractAuditQuery { public class EntitiesAtRevisionQuery extends AbstractAuditQuery {
private final Number revision; private final Number revision;
private final boolean selectDeletedEntities;
public EntitiesAtRevisionQuery(AuditConfiguration verCfg,
AuditReaderImplementor versionsReader, Class<?> cls, String entityName,
Number revision) {
this(verCfg, versionsReader, cls, entityName, revision, false);
}
public EntitiesAtRevisionQuery(AuditConfiguration verCfg, public EntitiesAtRevisionQuery(AuditConfiguration verCfg,
AuditReaderImplementor versionsReader, Class<?> cls, AuditReaderImplementor versionsReader, Class<?> cls,
Number revision, boolean selectDeletedEntities) { Number revision) {
super(verCfg, versionsReader, cls); super(verCfg, versionsReader, cls);
this.revision = revision; this.revision = revision;
this.selectDeletedEntities = selectDeletedEntities;
} }
public EntitiesAtRevisionQuery(AuditConfiguration verCfg, public EntitiesAtRevisionQuery(AuditConfiguration verCfg,
AuditReaderImplementor versionsReader, Class<?> cls, String entityName, AuditReaderImplementor versionsReader, Class<?> cls, String entityName,
Number revision, boolean selectDeletedEntities) { Number revision) {
super(verCfg, versionsReader, cls, entityName); super(verCfg, versionsReader, cls, entityName);
this.revision = revision; this.revision = revision;
this.selectDeletedEntities = selectDeletedEntities;
} }
@SuppressWarnings({"unchecked"}) @SuppressWarnings({"unchecked"})
@ -95,10 +86,8 @@ public class EntitiesAtRevisionQuery extends AbstractAuditQuery {
verEntCfg.getRevisionEndFieldName(), true, referencedIdData, verEntCfg.getRevisionEndFieldName(), true, referencedIdData,
revisionPropertyPath, originalIdPropertyName, "e", "e2"); revisionPropertyPath, originalIdPropertyName, "e", "e2");
if (!selectDeletedEntities) { // e.revision_type != DEL
// e.revision_type != DEL qb.getRootParameters().addWhereWithParam(verEntCfg.getRevisionTypePropName(), "<>", RevisionType.DEL);
qb.getRootParameters().addWhereWithParam(verEntCfg.getRevisionTypePropName(), "<>", RevisionType.DEL);
}
// all specified conditions // all specified conditions
for (AuditCriterion criterion : criterions) { for (AuditCriterion criterion : criterions) {

View File

@ -0,0 +1,57 @@
package org.hibernate.envers.query.impl;
import org.hibernate.Query;
import org.hibernate.envers.configuration.AuditConfiguration;
import org.hibernate.envers.configuration.AuditEntitiesConfiguration;
import org.hibernate.envers.query.criteria.AuditCriterion;
import org.hibernate.envers.reader.AuditReaderImplementor;
import java.util.ArrayList;
import java.util.List;
/**
* In comparison to {@link EntitiesAtRevisionQuery} this query will return an empty collection if an entity
* of a certain type has not been changed in a given revision.
* @see EntitiesAtRevisionQuery
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
public class EntitiesModifiedAtRevisionQuery extends AbstractAuditQuery {
private final Number revision;
public EntitiesModifiedAtRevisionQuery(AuditConfiguration verCfg, AuditReaderImplementor versionsReader, Class<?> cls,
Number revision) {
super(verCfg, versionsReader, cls);
this.revision = revision;
}
@SuppressWarnings({"unchecked"})
public List list() {
/*
* The query that we need to create:
* SELECT new list(e) FROM versionsReferencedEntity e
* WHERE
* (all specified conditions, transformed, on the "e" entity) AND
* e.revision = :revision
*/
AuditEntitiesConfiguration verEntCfg = verCfg.getAuditEntCfg();
String revisionPropertyPath = verEntCfg.getRevisionNumberPath();
qb.getRootParameters().addWhereWithParam(revisionPropertyPath, "=", revision);
// all specified conditions
for (AuditCriterion criterion : criterions) {
criterion.addToQuery(verCfg, entityName, qb, qb.getRootParameters());
}
Query query = buildQuery();
List queryResult = query.list();
if (hasProjection) {
return queryResult;
} else {
List result = new ArrayList();
entityInstantiator.addInstancesFromVersionsEntities(entityName, result, queryResult, revision);
return result;
}
}
}

View File

@ -239,7 +239,7 @@ public class AuditReaderImpl implements AuditReaderImplementor {
List<Class> clazz = findEntityTypesChangedInRevision(revision); List<Class> clazz = findEntityTypesChangedInRevision(revision);
List result = new ArrayList(clazz.size()); List result = new ArrayList(clazz.size());
for (Class c : clazz) { for (Class c : clazz) {
result.addAll(createQuery().forEntitiesAtRevision(c, revision, true).getResultList()); result.addAll(createQuery().forEntitiesAtCertainRevision(c, revision).getResultList());
} }
return result; return result;
} }
@ -249,7 +249,7 @@ public class AuditReaderImpl implements AuditReaderImplementor {
List<Class> clazz = findEntityTypesChangedInRevision(revision); List<Class> clazz = findEntityTypesChangedInRevision(revision);
List result = new ArrayList(clazz.size()); List result = new ArrayList(clazz.size());
for (Class c : clazz) { for (Class c : clazz) {
result.addAll(createQuery().forEntitiesAtRevision(c, revision, true).add(new RevisionTypeAuditExpression(revisionType, "=")).getResultList()); result.addAll(createQuery().forEntitiesAtCertainRevision(c, revision).add(new RevisionTypeAuditExpression(revisionType, "=")).getResultList());
} }
return result; return result;
} }
@ -261,7 +261,7 @@ public class AuditReaderImpl implements AuditReaderImplementor {
for (RevisionType revisionType : RevisionType.values()) { for (RevisionType revisionType : RevisionType.values()) {
result.put(revisionType, new ArrayList()); result.put(revisionType, new ArrayList());
for (Class c : clazz) { for (Class c : clazz) {
List list = createQuery().forEntitiesAtRevision(c, revision, true).add(new RevisionTypeAuditExpression(revisionType, "=")).getResultList(); List list = createQuery().forEntitiesAtCertainRevision(c, revision).add(new RevisionTypeAuditExpression(revisionType, "=")).getResultList();
result.get(revisionType).addAll(list); result.get(revisionType).addAll(list);
} }
} }

View File

@ -0,0 +1,79 @@
package org.hibernate.envers.test.integration.reventity.trackmodifiedentities;
import org.hibernate.ejb.Ejb3Configuration;
import org.hibernate.envers.test.AbstractEntityTest;
import org.hibernate.envers.test.Priority;
import org.hibernate.envers.test.entities.StrTestEntity;
import org.junit.Test;
import javax.persistence.EntityManager;
import java.util.Arrays;
/**
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
public class TrackingEntitiesMultipleChangesTest extends AbstractEntityTest {
private Integer steId1 = null;
private Integer steId2 = null;
@Override
public void configure(Ejb3Configuration cfg) {
cfg.setProperty("org.hibernate.envers.track_entities_changed_in_revision", "true");
cfg.addAnnotatedClass(StrTestEntity.class);
}
@Test
@Priority(10)
public void initData() {
EntityManager em = getEntityManager();
// Revision 1 - Adding two entities
em.getTransaction().begin();
StrTestEntity ste1 = new StrTestEntity("x");
StrTestEntity ste2 = new StrTestEntity("y");
em.persist(ste1);
em.persist(ste2);
steId1 = ste1.getId();
steId2 = ste2.getId();
em.getTransaction().commit();
// Revision 2 - Adding first and removing second entity
em.getTransaction().begin();
ste1 = em.find(StrTestEntity.class, steId1);
ste2 = em.find(StrTestEntity.class, steId2);
ste1.setStr("z");
em.remove(ste2);
em.getTransaction().commit();
// Revision 3 - Modifying and removing the same entity.
em.getTransaction().begin();
ste1 = em.find(StrTestEntity.class, steId1);
ste1.setStr("a");
em.merge(ste1);
em.remove(ste1);
em.getTransaction().commit();
}
@Test
public void testTrackAddedTwoEntities() {
StrTestEntity ste1 = new StrTestEntity("x", steId1);
StrTestEntity ste2 = new StrTestEntity("y", steId2);
assert Arrays.asList(ste1, ste2).equals(getAuditReader().findEntitiesChangedInRevision(1));
}
@Test
public void testTrackUpdateAndRemoveDifferentEntities() {
StrTestEntity ste1 = new StrTestEntity("z", steId1);
StrTestEntity ste2 = new StrTestEntity(null, steId2);
assert Arrays.asList(ste1, ste2).equals(getAuditReader().findEntitiesChangedInRevision(2));
}
@Test
public void testTrackUpdateAndRemoveTheSameEntity() {
StrTestEntity ste1 = new StrTestEntity(null, steId1);
assert Arrays.asList(ste1).equals(getAuditReader().findEntitiesChangedInRevision(3));
}
}