From 334e064272f016220eb2111d3eb9e8f74280a382 Mon Sep 17 00:00:00 2001 From: Chris Cranford Date: Thu, 4 Oct 2018 11:11:59 -0400 Subject: [PATCH] HHH-12992 - Added test cases. --- .../query/OrderByOneAuditEntityTest.java | 262 +++++++++++++ .../query/OrderByThreeEntityTest.java | 343 ++++++++++++++++++ .../query/OrderByTwoEntityOneAuditedTest.java | 269 ++++++++++++++ .../query/OrderByTwoEntityTest.java | 269 ++++++++++++++ 4 files changed, 1143 insertions(+) create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/OrderByOneAuditEntityTest.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/OrderByThreeEntityTest.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/OrderByTwoEntityOneAuditedTest.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/OrderByTwoEntityTest.java diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/OrderByOneAuditEntityTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/OrderByOneAuditEntityTest.java new file mode 100644 index 0000000000..d4a059bb78 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/OrderByOneAuditEntityTest.java @@ -0,0 +1,262 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.envers.test.integration.query; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; +import javax.persistence.OrderBy; + +import org.hibernate.annotations.BatchSize; +import org.hibernate.annotations.Fetch; +import org.hibernate.annotations.FetchMode; +import org.hibernate.envers.AuditMappedBy; +import org.hibernate.envers.Audited; +import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; +import org.junit.Test; + +import org.hibernate.testing.TestForIssue; + +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * Test the use of the {@link OrderBy} annotation on a one-to-many collection where + * both sides of the association are audited. + * + * This mapping invokes the use of the OneAuditEntityQueryGenerator which we want to + * verify orders the collection results properly. + * + * @author Chris Cranford + */ +@TestForIssue(jiraKey = "HHH-12992") +public class OrderByOneAuditEntityTest extends BaseEnversJPAFunctionalTestCase { + @Entity(name = "Parent") + @Audited + public static class Parent { + @Id + @GeneratedValue + private Integer id; + + @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) + @OrderBy("index, index2 desc") + @AuditMappedBy(mappedBy = "parent", positionMappedBy = "index") + @Fetch(FetchMode.SELECT) + @BatchSize(size = 100) + private List children = new ArrayList<>(); + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } + } + + @Entity(name = "Child") + @Audited + public static class Child { + @Id + private Integer id; + + private Integer index; + private Integer index2; + + @ManyToOne + private Parent parent; + + public Child() { + + } + + public Child(Integer id, Integer index) { + this.id = id; + this.index = index; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Integer getIndex() { + return index; + } + + public void setIndex(Integer index) { + this.index = index; + } + + public Integer getIndex2() { + return index2; + } + + public void setIndex2(Integer index2) { + this.index2 = index2; + } + + public Parent getParent() { + return parent; + } + + public void setParent(Parent parent) { + this.parent = parent; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + Child child = (Child) o; + return Objects.equals( id, child.id ) && + Objects.equals( index, child.index ); + } + + @Override + public int hashCode() { + return Objects.hash( id, index ); + } + + @Override + public String toString() { + return "Child{" + + "id=" + id + + ", index=" + index + + '}'; + } + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Parent.class, Child.class }; + } + + private Integer parentId; + + @Test + public void initData() { + // Rev 1 + this.parentId = doInJPA( this::entityManagerFactory, entityManager -> { + final Parent parent = new Parent(); + + final Child child1 = new Child(); + child1.setId( 1 ); + child1.setIndex( 1 ); + child1.setIndex2( 1 ); + child1.setParent( parent ); + parent.getChildren().add( child1 ); + + final Child child2 = new Child(); + child2.setId( 2 ); + child2.setIndex( 2 ); + child2.setIndex2( 2 ); + child2.setParent( parent ); + parent.getChildren().add( child2 ); + + entityManager.persist( parent ); + + return parent.getId(); + } ); + + // Rev 2 + doInJPA( this::entityManagerFactory, entityManager -> { + final Parent parent = entityManager.find( Parent.class, parentId ); + + final Child child = new Child(); + child.setId( 3 ); + child.setIndex( 3 ); + child.setIndex2( 3 ); + child.setParent( parent ); + parent.getChildren().add( child ); + + entityManager.merge( parent ); + } ); + + // Rev 3 + doInJPA( this::entityManagerFactory, entityManager -> { + final Parent parent = entityManager.find( Parent.class, parentId ); + parent.getChildren().removeIf( c -> c.getIndex() == 2 ); + entityManager.merge( parent ); + } ); + + // Rev 4 + doInJPA( this::entityManagerFactory, entityManager -> { + final Parent parent = entityManager.find( Parent.class, parentId ); + parent.getChildren().clear(); + entityManager.merge( parent ); + } ); + } + + @Test + public void testRevisionCounts() { + assertEquals( Arrays.asList( 1, 2, 3, 4 ), getAuditReader().getRevisions( Parent.class, this.parentId ) ); + assertEquals( Arrays.asList( 1, 4 ), getAuditReader().getRevisions( Child.class, 1 ) ); + assertEquals( Arrays.asList( 1, 3 ), getAuditReader().getRevisions( Child.class, 2 ) ); + assertEquals( Arrays.asList( 2, 4 ), getAuditReader().getRevisions( Child.class, 3 ) ); + } + + @Test + public void testRevision1History() { + final Parent parent = getAuditReader().find( Parent.class, this.parentId, 1 ); + assertNotNull( parent ); + assertTrue( !parent.getChildren().isEmpty() ); + assertEquals( 2, parent.getChildren().size() ); + assertEquals( Arrays.asList( new Child( 1, 1 ), new Child( 2, 2 ) ), parent.getChildren() ); + } + + @Test + public void testRevision2History() { + final Parent parent = getAuditReader().find( Parent.class, this.parentId, 2 ); + assertNotNull( parent ); + assertTrue( !parent.getChildren().isEmpty() ); + assertEquals( 3, parent.getChildren().size() ); + assertEquals( Arrays.asList( new Child( 1, 1 ), new Child( 2, 2 ), new Child( 3, 3 ) ), parent.getChildren() ); + } + + @Test + public void testRevision3History() { + final Parent parent = getAuditReader().find( Parent.class, this.parentId, 3 ); + assertNotNull( parent ); + assertTrue( !parent.getChildren().isEmpty() ); + assertEquals( 2, parent.getChildren().size() ); + assertEquals( Arrays.asList( new Child( 1, 1 ), new Child( 3, 3 ) ), parent.getChildren() ); + } + + @Test + public void testRevision4History() { + final Parent parent = getAuditReader().find( Parent.class, this.parentId, 4 ); + assertNotNull( parent ); + assertTrue( parent.getChildren().isEmpty() ); + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/OrderByThreeEntityTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/OrderByThreeEntityTest.java new file mode 100644 index 0000000000..251555ab46 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/OrderByThreeEntityTest.java @@ -0,0 +1,343 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.envers.test.integration.query; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Objects; + +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.OrderBy; + +import org.hibernate.envers.Audited; +import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; +import org.junit.Test; + +import org.hibernate.testing.TestForIssue; + +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * Test the use of the {@link OrderBy} annotation on a map-based element-collection + * that uses entities for the key and value. + * + * This mapping and association invokes the use of the ThreeEntityQueryGenerator which + * we want to verify orders the collection results properly. + * + * It's worth noting that a mapping like this orders the collection based on the value + * and not the key. + * + * @author Chris Cranford + */ +@TestForIssue(jiraKey = "HHH-12992") +public class OrderByThreeEntityTest extends BaseEnversJPAFunctionalTestCase { + @Entity(name = "Container") + @Audited + public static class Container { + @Id + @GeneratedValue + private Integer id; + @ElementCollection + @OrderBy("value desc") + private Map data = new HashMap<>(); + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Map getData() { + return data; + } + + public void setData(Map data) { + this.data = data; + } + } + + @Entity(name = "Key") + @Audited + public static class Key { + @Id + private Integer id; + private String value; + + public Key() { + + } + + public Key(Integer id, String value) { + this.id = id; + this.value = value; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + Key key = (Key) o; + return Objects.equals( id, key.id ) && + Objects.equals( value, key.value ); + } + + @Override + public int hashCode() { + return Objects.hash( id, value ); + } + + @Override + public String toString() { + return "Key{" + + "id=" + id + + ", value='" + value + '\'' + + '}'; + } + } + + @Entity(name = "Item") + @Audited + public static class Item { + @Id + private Integer id; + private String value; + + public Item() { + + } + + public Item(Integer id, String value) { + this.id = id; + this.value = value; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + Item item = (Item) o; + return Objects.equals( id, item.id ) && + Objects.equals( value, item.value ); + } + + @Override + public int hashCode() { + return Objects.hash( id, value ); + } + + @Override + public String toString() { + return "Item{" + + "id=" + id + + ", value='" + value + '\'' + + '}'; + } + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Container.class, Key.class, Item.class }; + } + + private Integer containerId; + + @Test + public void initData() { + // Rev 1 + this.containerId = doInJPA( this::entityManagerFactory, entityManager -> { + final Container container = new Container(); + + final Key key1 = new Key( 1, "A" ); + final Key key2 = new Key( 2, "B" ); + + final Item item1 = new Item( 1, "I1" ); + final Item item2 = new Item( 2, "I2" ); + + entityManager.persist( item1 ); + entityManager.persist( item2 ); + entityManager.persist( key1 ); + entityManager.persist( key2 ); + + container.getData().put( key1, item2 ); + container.getData().put( key2, item1 ); + entityManager.persist( container ); + + return container.getId(); + } ); + + // Rev 2 + doInJPA( this::entityManagerFactory, entityManager -> { + final Container container = entityManager.find( Container.class, containerId ); + + final Key key = new Key( 3, "C" ); + final Item item = new Item( 3, "I3" ); + + entityManager.persist( key ); + entityManager.persist( item ); + + container.getData().put( key, item ); + entityManager.merge( container ); + } ); + + // Rev 3 + doInJPA( this::entityManagerFactory, entityManager -> { + final Container container = entityManager.find( Container.class, containerId ); + container.getData().keySet().forEach( + key -> { + if ( "B".equals( key.getValue() ) ) { + final Item item = container.getData().get( key ); + container.getData().remove( key ); + entityManager.remove( key ); + entityManager.remove( item ); + } + } + ); + entityManager.merge( container ); + } ); + + // Rev 4 + doInJPA( this::entityManagerFactory, entityManager -> { + final Container container = entityManager.find( Container.class, containerId ); + container.getData().entrySet().forEach( + entry -> { + entityManager.remove( entry.getKey() ); + entityManager.remove( entry.getValue() ); + } + ); + container.getData().clear(); + entityManager.merge( container ); + } ); + } + + @Test + public void testRevisionCounts() { + assertEquals( Arrays.asList( 1, 2, 3, 4 ), getAuditReader().getRevisions( Container.class, this.containerId ) ); + + assertEquals( Arrays.asList( 1, 4 ), getAuditReader().getRevisions( Key.class, 1 ) ); + assertEquals( Arrays.asList( 1, 3 ), getAuditReader().getRevisions( Key.class, 2 ) ); + assertEquals( Arrays.asList( 2, 4 ), getAuditReader().getRevisions( Key.class, 3 ) ); + + assertEquals( Arrays.asList( 1, 3 ), getAuditReader().getRevisions( Item.class, 1 ) ); + assertEquals( Arrays.asList( 1, 4 ), getAuditReader().getRevisions( Item.class, 2 ) ); + assertEquals( Arrays.asList( 2, 4 ), getAuditReader().getRevisions( Item.class, 3 ) ); + } + + @Test + public void testRevision1History() { + final Container container = getAuditReader().find( Container.class, this.containerId, 1 ); + assertNotNull( container ); + assertTrue( !container.getData().isEmpty() ); + assertEquals( 2, container.getData().size() ); + + final Iterator> iterator = container.getData().entrySet().iterator(); + + final Map.Entry first = iterator.next(); + assertEquals( new Key( 1, "A" ), first.getKey() ); + assertEquals( new Item( 2, "I2" ), first.getValue() ); + + final Map.Entry second = iterator.next(); + assertEquals( new Key( 2, "B" ), second.getKey() ); + assertEquals( new Item( 1, "I1" ), second.getValue() ); + } + + @Test + public void testRevision2History() { + final Container container = getAuditReader().find( Container.class, this.containerId, 2 ); + assertNotNull( container ); + assertTrue( !container.getData().isEmpty() ); + assertEquals( 3, container.getData().size() ); + + final Iterator> iterator = container.getData().entrySet().iterator(); + + final Map.Entry first = iterator.next(); + assertEquals( new Key( 3, "C" ), first.getKey() ); + assertEquals( new Item( 3, "I3" ), first.getValue() ); + + final Map.Entry second = iterator.next(); + assertEquals( new Key( 1, "A" ), second.getKey() ); + assertEquals( new Item( 2, "I2" ), second.getValue() ); + + final Map.Entry third = iterator.next(); + assertEquals( new Key( 2, "B" ), third.getKey() ); + assertEquals( new Item( 1, "I1" ), third.getValue() ); + } + + @Test + public void testRevision3History() { + final Container container = getAuditReader().find( Container.class, this.containerId, 3 ); + assertNotNull( container ); + assertTrue( !container.getData().isEmpty() ); + assertEquals( 2, container.getData().size() ); + + final Iterator> iterator = container.getData().entrySet().iterator(); + + final Map.Entry first = iterator.next(); + assertEquals( new Key( 3, "C" ), first.getKey() ); + assertEquals( new Item( 3, "I3" ), first.getValue() ); + + final Map.Entry second = iterator.next(); + assertEquals( new Key( 1, "A" ), second.getKey() ); + assertEquals( new Item( 2, "I2" ), second.getValue() ); + } + + @Test + public void testRevision4History() { + final Container container = getAuditReader().find( Container.class, this.containerId, 4 ); + assertNotNull( container ); + assertTrue( container.getData().isEmpty() ); + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/OrderByTwoEntityOneAuditedTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/OrderByTwoEntityOneAuditedTest.java new file mode 100644 index 0000000000..c2671cc547 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/OrderByTwoEntityOneAuditedTest.java @@ -0,0 +1,269 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.envers.test.integration.query; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToMany; +import javax.persistence.OrderBy; + +import org.hibernate.annotations.BatchSize; +import org.hibernate.annotations.Fetch; +import org.hibernate.annotations.FetchMode; +import org.hibernate.envers.Audited; +import org.hibernate.envers.RelationTargetAuditMode; +import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; +import org.junit.Test; + +import org.hibernate.testing.TestForIssue; + +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * Test the use of the {@link OrderBy} annotation on a many-to-many collection + * where the two entities are audited but the association is not. + * + * The double audited entity but no association audited mapping invokes the use + * of the TwoEntityOneAuditedGenerator which we want to verify orders the + * collection results properly. + * + * @author Chris Cranford + */ +@TestForIssue(jiraKey = "HHH-12992") +public class OrderByTwoEntityOneAuditedTest extends BaseEnversJPAFunctionalTestCase { + @Entity(name = "Parent") + @Audited + public static class Parent { + @Id + @GeneratedValue + private Integer id; + + @Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED) + @ManyToMany(mappedBy = "parents", cascade = CascadeType.ALL, fetch = FetchType.EAGER) + @OrderBy("index, index2 desc") + @Fetch(FetchMode.SELECT) + @BatchSize(size = 100) + private List children = new ArrayList<>(); + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } + } + + @Entity(name = "Child") + @Audited + public static class Child { + @Id + private Integer id; + + private Integer index; + private Integer index2; + + @ManyToMany + private List parents = new ArrayList<>(); + + public Child() { + + } + + public Child(Integer id, Integer index) { + this.id = id; + this.index = index; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Integer getIndex() { + return index; + } + + public void setIndex(Integer index) { + this.index = index; + } + + public Integer getIndex2() { + return index2; + } + + public void setIndex2(Integer index2) { + this.index2 = index2; + } + + public List getParents() { + return parents; + } + + public void setParents(List parents) { + this.parents = parents; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + Child child = (Child) o; + return Objects.equals( id, child.id ) && + Objects.equals( index, child.index ); + } + + @Override + public int hashCode() { + return Objects.hash( id, index ); + } + + @Override + public String toString() { + return "Child{" + + "id=" + id + + ", index=" + index + + '}'; + } + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Parent.class, Child.class }; + } + + private Integer parentId; + + @Test + public void initData() { + // Rev 1 + this.parentId = doInJPA( this::entityManagerFactory, entityManager -> { + final Parent parent = new Parent(); + + final Child child1 = new Child(); + child1.setId( 1 ); + child1.setIndex( 1 ); + child1.setIndex2( 1 ); + child1.getParents().add( parent ); + parent.getChildren().add( child1 ); + + final Child child2 = new Child(); + child2.setId( 2 ); + child2.setIndex( 2 ); + child2.setIndex2( 2 ); + child2.getParents().add( parent ); + parent.getChildren().add( child2 ); + + entityManager.persist( parent ); + + return parent.getId(); + } ); + + // Rev 2 + doInJPA( this::entityManagerFactory, entityManager -> { + final Parent parent = entityManager.find( Parent.class, parentId ); + + final Child child = new Child(); + child.setId( 3 ); + child.setIndex( 3 ); + child.setIndex2( 3 ); + child.getParents().add( parent ); + parent.getChildren().add( child ); + + entityManager.merge( parent ); + } ); + + // Rev 3 + doInJPA( this::entityManagerFactory, entityManager -> { + final Parent parent = entityManager.find( Parent.class, parentId ); + parent.getChildren().removeIf( c -> { + if ( c.getIndex() == 2 ) { + c.getParents().remove( parent ); + return true; + } + return false; + } ); + entityManager.merge( parent ); + } ); + + // Rev 4 + doInJPA( this::entityManagerFactory, entityManager -> { + final Parent parent = entityManager.find( Parent.class, parentId ); + parent.getChildren().forEach( c -> c.getParents().clear() ); + parent.getChildren().clear(); + entityManager.merge( parent ); + } ); + } + + @Test + public void testRevisionCounts() { + assertEquals( Arrays.asList( 1, 2, 3, 4 ), getAuditReader().getRevisions( Parent.class, this.parentId ) ); + assertEquals( Arrays.asList( 1, 4 ), getAuditReader().getRevisions( Child.class, 1 ) ); + assertEquals( Arrays.asList( 1, 3 ), getAuditReader().getRevisions( Child.class, 2 ) ); + assertEquals( Arrays.asList( 2, 4 ), getAuditReader().getRevisions( Child.class, 3 ) ); + } + + @Test + public void testRevision1History() { + final Parent parent = getAuditReader().find( Parent.class, this.parentId, 1 ); + assertNotNull( parent ); + assertTrue( !parent.getChildren().isEmpty() ); + assertEquals( 2, parent.getChildren().size() ); + assertEquals( Arrays.asList( new Child( 1, 1 ), new Child( 2, 2 ) ), parent.getChildren() ); + } + + @Test + public void testRevision2History() { + final Parent parent = getAuditReader().find( Parent.class, this.parentId, 2 ); + assertNotNull( parent ); + assertTrue( !parent.getChildren().isEmpty() ); + assertEquals( 3, parent.getChildren().size() ); + assertEquals( Arrays.asList( new Child( 1, 1 ), new Child( 2, 2 ), new Child( 3, 3 ) ), parent.getChildren() ); + } + + @Test + public void testRevision3History() { + final Parent parent = getAuditReader().find( Parent.class, this.parentId, 3 ); + assertNotNull( parent ); + assertTrue( !parent.getChildren().isEmpty() ); + assertEquals( 2, parent.getChildren().size() ); + assertEquals( Arrays.asList( new Child( 1, 1 ), new Child( 3, 3 ) ), parent.getChildren() ); + } + + @Test + public void testRevision4History() { + final Parent parent = getAuditReader().find( Parent.class, this.parentId, 4 ); + assertNotNull( parent ); + assertTrue( parent.getChildren().isEmpty() ); + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/OrderByTwoEntityTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/OrderByTwoEntityTest.java new file mode 100644 index 0000000000..404fbf268b --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/query/OrderByTwoEntityTest.java @@ -0,0 +1,269 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.envers.test.integration.query; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToMany; +import javax.persistence.OrderBy; + +import org.hibernate.annotations.BatchSize; +import org.hibernate.annotations.Fetch; +import org.hibernate.annotations.FetchMode; +import org.hibernate.envers.AuditMappedBy; +import org.hibernate.envers.Audited; +import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; +import org.junit.Test; + +import org.hibernate.testing.TestForIssue; + +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * Test the use of the {@link OrderBy} annotation on a many-to-many collection + * where the two entities and the association are audited. + * + * The double auditd entity and association mapping invokes the use of the + * TwoEntityQueryGenerator which we want to verify orders the collection + * results properly. + * + * @author Chris Cranford + */ +@TestForIssue(jiraKey = "HHH-12992") +public class OrderByTwoEntityTest extends BaseEnversJPAFunctionalTestCase { + @Entity(name = "Parent") + @Audited + public static class Parent { + @Id + @GeneratedValue + private Integer id; + + @ManyToMany(mappedBy = "parents", cascade = CascadeType.ALL, fetch = FetchType.EAGER) + @OrderBy("index, index2 desc") + @AuditMappedBy(mappedBy = "parents", positionMappedBy = "index") + @Fetch(FetchMode.SELECT) + @BatchSize(size = 100) + private List children = new ArrayList<>(); + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } + } + + @Entity(name = "Child") + @Audited + public static class Child { + @Id + private Integer id; + + private Integer index; + private Integer index2; + + @ManyToMany + private List parents = new ArrayList<>(); + + public Child() { + + } + + public Child(Integer id, Integer index) { + this.id = id; + this.index = index; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Integer getIndex() { + return index; + } + + public void setIndex(Integer index) { + this.index = index; + } + + public Integer getIndex2() { + return index2; + } + + public void setIndex2(Integer index2) { + this.index2 = index2; + } + + public List getParents() { + return parents; + } + + public void setParents(List parents) { + this.parents = parents; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + Child child = (Child) o; + return Objects.equals( id, child.id ) && + Objects.equals( index, child.index ); + } + + @Override + public int hashCode() { + return Objects.hash( id, index ); + } + + @Override + public String toString() { + return "Child{" + + "id=" + id + + ", index=" + index + + '}'; + } + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Parent.class, Child.class }; + } + + private Integer parentId; + + @Test + public void initData() { + // Rev 1 + this.parentId = doInJPA( this::entityManagerFactory, entityManager -> { + final Parent parent = new Parent(); + + final Child child1 = new Child(); + child1.setId( 1 ); + child1.setIndex( 1 ); + child1.setIndex2( 1 ); + child1.getParents().add( parent ); + parent.getChildren().add( child1 ); + + final Child child2 = new Child(); + child2.setId( 2 ); + child2.setIndex( 2 ); + child2.setIndex2( 2 ); + child2.getParents().add( parent ); + parent.getChildren().add( child2 ); + + entityManager.persist( parent ); + + return parent.getId(); + } ); + + // Rev 2 + doInJPA( this::entityManagerFactory, entityManager -> { + final Parent parent = entityManager.find( Parent.class, parentId ); + + final Child child = new Child(); + child.setId( 3 ); + child.setIndex( 3 ); + child.setIndex2( 3 ); + child.getParents().add( parent ); + parent.getChildren().add( child ); + + entityManager.merge( parent ); + } ); + + // Rev 3 + doInJPA( this::entityManagerFactory, entityManager -> { + final Parent parent = entityManager.find( Parent.class, parentId ); + parent.getChildren().removeIf( c -> { + if ( c.getIndex() == 2 ) { + c.getParents().remove( parent ); + return true; + } + return false; + } ); + entityManager.merge( parent ); + } ); + + // Rev 4 + doInJPA( this::entityManagerFactory, entityManager -> { + final Parent parent = entityManager.find( Parent.class, parentId ); + parent.getChildren().forEach( c -> c.getParents().clear() ); + parent.getChildren().clear(); + entityManager.merge( parent ); + } ); + } + + @Test + public void testRevisionCounts() { + assertEquals( Arrays.asList( 1, 2, 3, 4 ), getAuditReader().getRevisions( Parent.class, this.parentId ) ); + assertEquals( Arrays.asList( 1, 4 ), getAuditReader().getRevisions( Child.class, 1 ) ); + assertEquals( Arrays.asList( 1, 3 ), getAuditReader().getRevisions( Child.class, 2 ) ); + assertEquals( Arrays.asList( 2, 4 ), getAuditReader().getRevisions( Child.class, 3 ) ); + } + + @Test + public void testRevision1History() { + final Parent parent = getAuditReader().find( Parent.class, this.parentId, 1 ); + assertNotNull( parent ); + assertTrue( !parent.getChildren().isEmpty() ); + assertEquals( 2, parent.getChildren().size() ); + assertEquals( Arrays.asList( new Child( 1, 1 ), new Child( 2, 2 ) ), parent.getChildren() ); + } + + @Test + public void testRevision2History() { + final Parent parent = getAuditReader().find( Parent.class, this.parentId, 2 ); + assertNotNull( parent ); + assertTrue( !parent.getChildren().isEmpty() ); + assertEquals( 3, parent.getChildren().size() ); + assertEquals( Arrays.asList( new Child( 1, 1 ), new Child( 2, 2 ), new Child( 3, 3 ) ), parent.getChildren() ); + } + + @Test + public void testRevision3History() { + final Parent parent = getAuditReader().find( Parent.class, this.parentId, 3 ); + assertNotNull( parent ); + assertTrue( !parent.getChildren().isEmpty() ); + assertEquals( 2, parent.getChildren().size() ); + assertEquals( Arrays.asList( new Child( 1, 1 ), new Child( 3, 3 ) ), parent.getChildren() ); + } + + @Test + public void testRevision4History() { + final Parent parent = getAuditReader().find( Parent.class, this.parentId, 4 ); + assertNotNull( parent ); + assertTrue( parent.getChildren().isEmpty() ); + } +}