HHH-5580 - Query fix
This commit is contained in:
parent
98342a7e2d
commit
88eda2b081
|
@ -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.
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue