diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/query/AuditQueryCreator.java b/hibernate-envers/src/main/java/org/hibernate/envers/query/AuditQueryCreator.java index a103196241..3101769736 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/query/AuditQueryCreator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/query/AuditQueryCreator.java @@ -30,6 +30,7 @@ import org.hibernate.envers.query.impl.RevisionsOfEntityQuery; import org.hibernate.envers.reader.AuditReaderImplementor; import static org.hibernate.envers.tools.ArgumentsTools.*; +import static org.hibernate.envers.tools.Tools.getTargetClassIfProxied; /** * @author Adam Warski (adam at warski dot org) @@ -57,6 +58,7 @@ public class AuditQueryCreator { public AuditQuery forEntitiesAtRevision(Class c, Number revision) { checkNotNull(revision, "Entity revision"); checkPositive(revision, "Entity revision"); + c = getTargetClassIfProxied(c); return new EntitiesAtRevisionQuery(auditCfg, auditReaderImplementor, c, revision); } @@ -73,6 +75,7 @@ public class AuditQueryCreator { public AuditQuery forEntitiesAtRevision(Class c, String entityName, Number revision) { checkNotNull(revision, "Entity revision"); checkPositive(revision, "Entity revision"); + c = getTargetClassIfProxied(c); return new EntitiesAtRevisionQuery(auditCfg, auditReaderImplementor, c, entityName, revision); } @@ -92,6 +95,7 @@ public class AuditQueryCreator { public AuditQuery forEntitiesModifiedAtRevision(Class c, String entityName, Number revision) { checkNotNull(revision, "Entity revision"); checkPositive(revision, "Entity revision"); + c = getTargetClassIfProxied(c); return new EntitiesModifiedAtRevisionQuery(auditCfg, auditReaderImplementor, c, entityName, revision); } @@ -110,6 +114,7 @@ public class AuditQueryCreator { public AuditQuery forEntitiesModifiedAtRevision(Class c, Number revision) { checkNotNull(revision, "Entity revision"); checkPositive(revision, "Entity revision"); + c = getTargetClassIfProxied(c); return new EntitiesModifiedAtRevisionQuery(auditCfg, auditReaderImplementor, c, revision); } @@ -134,6 +139,7 @@ public class AuditQueryCreator { * unless an order or projection is added. */ public AuditQuery forRevisionsOfEntity(Class c, boolean selectEntitiesOnly, boolean selectDeletedEntities) { + c = getTargetClassIfProxied(c); return new RevisionsOfEntityQuery(auditCfg, auditReaderImplementor, c, selectEntitiesOnly,selectDeletedEntities); } @@ -159,6 +165,7 @@ public class AuditQueryCreator { * unless an order or projection is added. */ public AuditQuery forRevisionsOfEntity(Class c, String entityName, boolean selectEntitiesOnly, boolean selectDeletedEntities) { + c = getTargetClassIfProxied(c); return new RevisionsOfEntityQuery(auditCfg, auditReaderImplementor, c, entityName, selectEntitiesOnly,selectDeletedEntities); } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/reader/AuditReaderImpl.java b/hibernate-envers/src/main/java/org/hibernate/envers/reader/AuditReaderImpl.java index 257e71a46e..0a43375fc6 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/reader/AuditReaderImpl.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/reader/AuditReaderImpl.java @@ -24,6 +24,7 @@ package org.hibernate.envers.reader; import static org.hibernate.envers.tools.ArgumentsTools.checkNotNull; import static org.hibernate.envers.tools.ArgumentsTools.checkPositive; +import static org.hibernate.envers.tools.Tools.getTargetClassIfProxied; import java.util.ArrayList; import java.util.Collections; import java.util.Date; @@ -90,13 +91,14 @@ public class AuditReaderImpl implements AuditReaderImplementor { public T find(Class cls, Object primaryKey, Number revision) throws IllegalArgumentException, NotAuditedException, IllegalStateException { - + cls = getTargetClassIfProxied(cls); return this.find(cls, cls.getName(), primaryKey, revision); } @SuppressWarnings({"unchecked"}) public T find(Class cls, String entityName, Object primaryKey, Number revision) throws IllegalArgumentException, NotAuditedException, IllegalStateException { + cls = getTargetClassIfProxied(cls); checkNotNull(cls, "Entity class"); checkNotNull(entityName, "Entity name"); checkNotNull(primaryKey, "Primary key"); @@ -128,7 +130,7 @@ public class AuditReaderImpl implements AuditReaderImplementor { public List getRevisions(Class cls, Object primaryKey) throws IllegalArgumentException, NotAuditedException, IllegalStateException { - + cls = getTargetClassIfProxied(cls); return this.getRevisions(cls, cls.getName(), primaryKey); } @@ -136,6 +138,7 @@ public class AuditReaderImpl implements AuditReaderImplementor { public List getRevisions(Class cls, String entityName, Object primaryKey) throws IllegalArgumentException, NotAuditedException, IllegalStateException { // todo: if a class is not versioned from the beginning, there's a missing ADD rev - what then? + cls = getTargetClassIfProxied(cls); checkNotNull(cls, "Entity class"); checkNotNull(entityName, "Entity name"); checkNotNull(primaryKey, "Primary key"); @@ -193,6 +196,7 @@ public class AuditReaderImpl implements AuditReaderImplementor { @SuppressWarnings({"unchecked"}) public T findRevision(Class revisionEntityClass, Number revision) throws IllegalArgumentException, RevisionDoesNotExistException, IllegalStateException { + revisionEntityClass = getTargetClassIfProxied(revisionEntityClass); checkNotNull(revision, "Entity revision"); checkPositive(revision, "Entity revision"); checkSession(); @@ -217,6 +221,7 @@ public class AuditReaderImpl implements AuditReaderImplementor { @SuppressWarnings({"unchecked"}) public Map findRevisions(Class revisionEntityClass, Set revisions) throws IllegalArgumentException, IllegalStateException { + revisionEntityClass = getTargetClassIfProxied(revisionEntityClass); Map result = new HashMap(revisions.size()); for (Number revision : revisions) { @@ -303,6 +308,7 @@ public class AuditReaderImpl implements AuditReaderImplementor { @SuppressWarnings({"unchecked"}) public T getCurrentRevision(Class revisionEntityClass, boolean persist) { + revisionEntityClass = getTargetClassIfProxied(revisionEntityClass); if (!(session instanceof EventSource)) { throw new IllegalArgumentException("The provided session is not an EventSource!"); } @@ -319,6 +325,7 @@ public class AuditReaderImpl implements AuditReaderImplementor { } public boolean isEntityClassAudited(Class entityClass) { + entityClass = getTargetClassIfProxied(entityClass); return this.isEntityNameAudited(entityClass.getName()); } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/tools/Tools.java b/hibernate-envers/src/main/java/org/hibernate/envers/tools/Tools.java index 9dcd0fb138..a8d7660fe7 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/tools/Tools.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/tools/Tools.java @@ -31,6 +31,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; +import javassist.util.proxy.ProxyFactory; import org.hibernate.Session; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; @@ -39,6 +40,7 @@ import org.hibernate.proxy.HibernateProxy; /** * @author Adam Warski (adam at warski dot org) * @author Hern�n Chanfreau + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) */ public class Tools { public static Map newHashMap() { @@ -95,6 +97,23 @@ public class Tools { } } + /** + * @param clazz Class wrapped with a proxy or not. + * @param Class type. + * @return Returns target class in case it has been wrapped with a proxy. If {@code null} reference is passed, + * method returns {@code null}. + */ + @SuppressWarnings({"unchecked"}) + public static Class getTargetClassIfProxied(Class clazz) { + if (clazz == null) { + return null; + } else if (ProxyFactory.isProxyClass(clazz)) { + // Get the source class of Javassist proxy instance. + return (Class) clazz.getSuperclass(); + } + return clazz; + } + public static boolean objectsEqual(Object obj1, Object obj2) { if (obj1 == null) { return obj2 == null; diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/proxy/QueryingWithProxyObjectTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/proxy/QueryingWithProxyObjectTest.java new file mode 100644 index 0000000000..1cef78cdbe --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/proxy/QueryingWithProxyObjectTest.java @@ -0,0 +1,62 @@ +package org.hibernate.envers.test.integration.proxy; + +import org.hibernate.MappingException; +import org.hibernate.envers.test.AbstractSessionTest; +import org.hibernate.envers.test.Priority; +import org.hibernate.envers.test.entities.StrTestEntity; +import org.hibernate.testing.TestForIssue; +import org.junit.Assert; +import org.junit.Test; + +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.List; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +public class QueryingWithProxyObjectTest extends AbstractSessionTest { + private Integer id = null; + + @Override + protected void initMappings() throws MappingException, URISyntaxException { + config.addAnnotatedClass(StrTestEntity.class); + } + + @Test + @Priority(10) + public void initData() { + // Revision 1 + getSession().getTransaction().begin(); + StrTestEntity ste = new StrTestEntity("data"); + getSession().persist(ste); + getSession().getTransaction().commit(); + id = ste.getId(); + } + + @Test + @TestForIssue(jiraKey="HHH-4760") + @SuppressWarnings("unchecked") + public void testQueryingWithProxyObject() { + StrTestEntity originalSte = new StrTestEntity("data", id); + // Load the proxy instance + StrTestEntity proxySte = (StrTestEntity) getSession().load(StrTestEntity.class, id); + + Assert.assertTrue(getAuditReader().isEntityClassAudited(proxySte.getClass())); + + StrTestEntity ste = getAuditReader().find(proxySte.getClass(), proxySte.getId(), 1); + Assert.assertEquals(originalSte, ste); + + List revisions = getAuditReader().getRevisions(proxySte.getClass(), proxySte.getId()); + Assert.assertEquals(Arrays.asList(1), revisions); + + List entities = getAuditReader().createQuery().forEntitiesAtRevision(proxySte.getClass(), 1).getResultList(); + Assert.assertEquals(Arrays.asList(originalSte), entities); + + ste = (StrTestEntity) getAuditReader().createQuery().forRevisionsOfEntity(proxySte.getClass(), true, false).getSingleResult(); + Assert.assertEquals(originalSte, ste); + + ste = (StrTestEntity) getAuditReader().createQuery().forEntitiesModifiedAtRevision(proxySte.getClass(), 1).getSingleResult(); + Assert.assertEquals(originalSte, ste); + } +}