From a41b7262366111acff2bfcf6d6197a7e4a6b10c9 Mon Sep 17 00:00:00 2001 From: Chris Cranford Date: Wed, 31 Aug 2016 13:08:47 -0400 Subject: [PATCH] HHH-11063 - Added test case. --- ...ComponentCollectionHashcodeChangeTest.java | 200 +++++++++++ .../hashcode/ListHashcodeChangeTest.java | 311 +++++++++++++++++ .../hashcode/SetHashcodeChangeTest.java | 312 ++++++++++++++++++ 3 files changed, 823 insertions(+) create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/hashcode/ComponentCollectionHashcodeChangeTest.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/hashcode/ListHashcodeChangeTest.java create mode 100644 hibernate-envers/src/test/java/org/hibernate/envers/test/integration/hashcode/SetHashcodeChangeTest.java diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/hashcode/ComponentCollectionHashcodeChangeTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/hashcode/ComponentCollectionHashcodeChangeTest.java new file mode 100644 index 000000000000..50bc55c60c99 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/hashcode/ComponentCollectionHashcodeChangeTest.java @@ -0,0 +1,200 @@ +/* + * 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.hashcode; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import javax.persistence.ElementCollection; +import javax.persistence.Embeddable; +import javax.persistence.Entity; +import javax.persistence.EntityManager; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +import org.hibernate.envers.Audited; +import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; +import org.hibernate.envers.test.Priority; +import org.hibernate.envers.test.tools.TestTools; +import org.hibernate.testing.TestForIssue; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * @author Chris Cranford + */ +@TestForIssue(jiraKey = "HHH-11063") +public class ComponentCollectionHashcodeChangeTest extends BaseEnversJPAFunctionalTestCase { + private Integer id; + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { ComponentEntity.class, Component.class }; + } + + @Test + @Priority(10) + public void initData() { + EntityManager entityManager = getEntityManager(); + try { + // Revision 1 - Create entity with 2 components + entityManager.getTransaction().begin(); + Component component1 = new Component(); + component1.setName( "User1" ); + component1.setData( "Test1" ); + Component component2 = new Component(); + component2.setName( "User2" ); + component2.setData( "Test2" ); + ComponentEntity entity = new ComponentEntity(); + entity.getComponents().add( component1 ); + entity.getComponents().add( component2 ); + entityManager.persist( entity ); + entityManager.getTransaction().commit(); + id = entity.getId(); + + // Revision 2 - Change component name inline. + // This effectively changes equality and hash of elment. + entityManager.getTransaction().begin(); + component1.setName( "User1-Inline" ); + entityManager.merge( entity ); + entityManager.getTransaction().commit(); + + // Revision 3 - Remove and add entity with same name. + entityManager.getTransaction().begin(); + entity.getComponents().remove( component2 ); + Component component3 = new Component(); + component3.setName( "User2" ); + component3.setData( "Test3" ); + entity.getComponents().add( component3 ); + entityManager.merge( entity ); + entityManager.getTransaction().commit(); + } + catch ( Exception e ) { + if ( entityManager.getTransaction().isActive() ) { + entityManager.getTransaction().rollback(); + } + } + finally { + entityManager.close(); + } + } + + @Test + public void testRevisionCounts() { + assertEquals( Arrays.asList( 1, 2, 3 ), getAuditReader().getRevisions( ComponentEntity.class, id ) ); + } + + @Test + public void testCollectionHistory() { + final ComponentEntity rev1 = getAuditReader().find( ComponentEntity.class, id, 1 ); + assertEquals( 2, rev1.getComponents().size() ); + assertEquals( + TestTools.makeSet( + new Component( "User1", "Test1" ), + new Component( "User2", "Test2" ) + ), + rev1.getComponents() + ); + + final ComponentEntity rev2 = getAuditReader().find( ComponentEntity.class, id, 2 ); + assertEquals( 2, rev2.getComponents().size() ); + assertEquals( + TestTools.makeSet( + new Component( "User1-Inline", "Test1" ), + new Component( "User2", "Test2" ) + ), + rev2.getComponents() + ); + + final ComponentEntity rev3 = getAuditReader().find( ComponentEntity.class, id, 3 ); + assertEquals( 2, rev3.getComponents().size() ); + assertEquals( + TestTools.makeSet( + new Component( "User1-Inline", "Test1" ), + new Component( "User2", "Test3" ) + ), + rev3.getComponents() + ); + } + + @Entity + @Audited + public static class ComponentEntity { + @Id + @GeneratedValue + private Integer id; + @ElementCollection + private Set components = new HashSet(); + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Set getComponents() { + return components; + } + + public void setComponents(Set components) { + this.components = components; + } + } + + @Audited + @Embeddable + public static class Component { + private String name; + private String data; + + Component() { + + } + + Component(String name, String data) { + this.name = name; + this.data = data; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + @Override + public int hashCode() { + return ( name != null ? name.hashCode() : 0 ); + } + + @Override + public boolean equals(Object object) { + if ( object == this ) { + return true; + } + if ( object == null || !( object instanceof Component ) ) { + return false; + } + Component that = (Component) object; + return !( name != null ? !name.equals( that.name ) : that.name != null ); + } + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/hashcode/ListHashcodeChangeTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/hashcode/ListHashcodeChangeTest.java new file mode 100644 index 000000000000..bb29a8da962f --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/hashcode/ListHashcodeChangeTest.java @@ -0,0 +1,311 @@ +/* + * 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.hashcode; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.EntityManager; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; + +import org.hibernate.envers.AuditReader; +import org.hibernate.envers.Audited; +import org.hibernate.envers.NotAudited; +import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; +import org.hibernate.envers.test.Priority; +import org.hibernate.testing.TestForIssue; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * @author Chris Cranford + */ +@TestForIssue(jiraKey = "HHH-11063") +public class ListHashcodeChangeTest extends BaseEnversJPAFunctionalTestCase { + private Integer authorId; + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Author.class, Book.class }; + } + + @Test + @Priority(10) + public void initData() { + // Revision 1 + EntityManager entityManager = getEntityManager(); + try { + entityManager.getTransaction().begin(); + final Author author = new Author(); + author.setFirstName( "TestFirstName" ); + author.setLastName( "lastName" ); + author.addBook( createBook1() ); + author.addBook( createBook2() ); + entityManager.persist( author ); + authorId = author.getId(); + entityManager.getTransaction().commit(); + } + catch ( Exception e ) { + if ( entityManager.getTransaction().isActive() ) { + entityManager.getTransaction().rollback(); + } + } + finally { + entityManager.close(); + } + // Revision 2 + // Removes all books and re-adds original 2 plus one new book + entityManager = getEntityManager(); + try { + entityManager.getTransaction().begin(); + final Author author = entityManager.find( Author.class, authorId ); + author.removeAllBooks(); + author.addBook( createBook1() ); + author.addBook( createBook2() ); + author.addBook( createBook3() ); + entityManager.merge( author ); + entityManager.getTransaction().commit(); + } + catch ( Exception e ) { + if( entityManager.getTransaction().isActive() ) { + entityManager.getTransaction().rollback(); + } + } + finally { + entityManager.close(); + } + } + + @Test + public void testRevisionCounts() { + + } + + @Test + // tests that Author has 3 books. + public void testAuthorState() { + EntityManager entityManager = getEntityManager(); + try { + final Author author = entityManager.find( Author.class, authorId ); + assertNotNull( author ); + assertEquals( 3, author.getBooks().size() ); + } + catch ( Exception e ) { + if ( entityManager.getTransaction().isActive() ) { + entityManager.getTransaction().rollback(); + } + } + finally { + entityManager.close(); + } + } + + @Test + public void testAuthorLastRevision() { + // tests that Author has 3 books, Book1, Book2, and Book3. + // where Book1 and Book2 were removed and re-added with the addition of Book3. + EntityManager entityManager = getEntityManager(); + try { + final AuditReader reader = getAuditReader(); + final List revisions = reader.getRevisions( Author.class, authorId ); + final Number lastRevision = revisions.get( revisions.size() - 1 ); + + final Author author = (Author) reader.createQuery() + .forEntitiesAtRevision( Author.class, lastRevision ) + .getSingleResult(); + + assertNotNull( author ); + assertEquals( 3, author.getBooks().size() ); + } + catch ( Exception e ) { + if ( entityManager.getTransaction().isActive() ) { + entityManager.getTransaction().rollback(); + } + } + finally { + entityManager.close(); + } + } + + private Book createBook1() { + Book book = new Book(); + book.setTitle( "Book1" ); + return book; + } + + private Book createBook2() { + Book book = new Book(); + book.setTitle( "Book2" ); + return book; + } + + private Book createBook3() { + Book book = new Book(); + book.setTitle( "Book3" ); + return book; + } + + @Entity + @Audited + public static class Author { + @Id + @GeneratedValue + private Integer id; + + private String firstName; + + private String lastName; + + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) + @JoinColumn(name = "author_id", referencedColumnName = "id", nullable = false) + private List books; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public List getBooks() { + return books; + } + + public void setBooks(List books) { + this.books = books; + } + + public void addBook(Book book) { + if ( this.books == null ) { + this.books = new ArrayList(); + } + book.setAuthor( this ); + this.books.add( book ); + } + + public void removeAllBooks() { + if ( this.books != null ) { + this.books.clear(); + } + } + + public Book getBook(String title) { + return books.stream().filter( b -> title.equals( b.getTitle() ) ).findFirst().orElse( null ); + } + + public void removeBook(String title) { + for( Iterator it = books.iterator(); it.hasNext(); ) { + Book book = it.next(); + if ( title.equals( title ) ) { + it.remove(); + } + } + } + + @Override + public String toString() { + return "Author{" + + "id=" + id + + ", firstName='" + firstName + '\'' + + ", lastName='" + lastName + '\'' + + '}'; + } + } + + @Entity + @Audited + public static class Book { + @Id + @GeneratedValue + private Integer id; + + private String title; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "author_id", nullable = false, insertable = false, updatable = false) + @NotAudited + private Author author; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public Author getAuthor() { + return author; + } + + public void setAuthor(Author author) { + this.author = author; + } + + @Override + public int hashCode() { + return Objects.hash( title ); + } + + @Override + public boolean equals(Object object) { + if ( this == object ) { + return true; + } + if ( object == null || getClass() != object.getClass() ) { + return false; + } + Book book = (Book) object; + return Objects.equals( title, book.title ); + } + + @Override + public String toString() { + return "Book{" + + "id=" + id + + ", title='" + title + '\'' + + ", author=" + author + + '}'; + } + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/hashcode/SetHashcodeChangeTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/hashcode/SetHashcodeChangeTest.java new file mode 100644 index 000000000000..5e6e4d953354 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/hashcode/SetHashcodeChangeTest.java @@ -0,0 +1,312 @@ +/* + * 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.hashcode; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.EntityManager; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; + +import org.hibernate.envers.AuditReader; +import org.hibernate.envers.Audited; +import org.hibernate.envers.NotAudited; +import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; +import org.hibernate.envers.test.Priority; +import org.hibernate.testing.TestForIssue; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * @author Chris Cranford + */ +@TestForIssue(jiraKey = "HHH-11063") +public class SetHashcodeChangeTest extends BaseEnversJPAFunctionalTestCase { + private Integer authorId; + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Author.class, Book.class }; + } + + @Test + @Priority(10) + public void initData() { + // Revision 1 + EntityManager entityManager = getEntityManager(); + try { + entityManager.getTransaction().begin(); + final Author author = new Author(); + author.setFirstName( "TestFirstName" ); + author.setLastName( "lastName" ); + author.addBook( createBook1() ); + author.addBook( createBook2() ); + entityManager.persist( author ); + authorId = author.getId(); + entityManager.getTransaction().commit(); + } + catch ( Exception e ) { + if ( entityManager.getTransaction().isActive() ) { + entityManager.getTransaction().rollback(); + } + } + finally { + entityManager.close(); + } + // Revision 2 + // Removes all books and re-adds original 2 plus one new book + entityManager = getEntityManager(); + try { + entityManager.getTransaction().begin(); + final Author author = entityManager.find( Author.class, authorId ); + author.removeAllBooks(); + author.addBook( createBook1() ); + author.addBook( createBook2() ); + author.addBook( createBook3() ); + entityManager.merge( author ); + entityManager.getTransaction().commit(); + } + catch ( Exception e ) { + if( entityManager.getTransaction().isActive() ) { + entityManager.getTransaction().rollback(); + } + } + finally { + entityManager.close(); + } + } + + @Test + public void testRevisionCounts() { + + } + + @Test + // tests that Author has 3 books. + public void testAuthorState() { + EntityManager entityManager = getEntityManager(); + try { + final Author author = entityManager.find( Author.class, authorId ); + assertNotNull( author ); + assertEquals( 3, author.getBooks().size() ); + } + catch ( Exception e ) { + if ( entityManager.getTransaction().isActive() ) { + entityManager.getTransaction().rollback(); + } + } + finally { + entityManager.close(); + } + } + + @Test + public void testAuthorLastRevision() { + // tests that Author has 3 books, Book1, Book2, and Book3. + // where Book1 and Book2 were removed and re-added with the addition of Book3. + EntityManager entityManager = getEntityManager(); + try { + final AuditReader reader = getAuditReader(); + final List revisions = reader.getRevisions( Author.class, authorId ); + final Number lastRevision = revisions.get( revisions.size() - 1 ); + + final Author author = (Author) reader.createQuery() + .forEntitiesAtRevision( Author.class, lastRevision ) + .getSingleResult(); + + assertNotNull( author ); + assertEquals( 3, author.getBooks().size() ); + } + catch ( Exception e ) { + if ( entityManager.getTransaction().isActive() ) { + entityManager.getTransaction().rollback(); + } + } + finally { + entityManager.close(); + } + } + + private Book createBook1() { + Book book = new Book(); + book.setTitle( "Book1" ); + return book; + } + + private Book createBook2() { + Book book = new Book(); + book.setTitle( "Book2" ); + return book; + } + + private Book createBook3() { + Book book = new Book(); + book.setTitle( "Book3" ); + return book; + } + + @Entity + @Audited + public static class Author { + @Id + @GeneratedValue + private Integer id; + + private String firstName; + + private String lastName; + + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) + @JoinColumn(name = "author_id", referencedColumnName = "id", nullable = false) + private Set books; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public Set getBooks() { + return books; + } + + public void setBooks(Set books) { + this.books = books; + } + + public void addBook(Book book) { + if ( this.books == null ) { + this.books = new HashSet(); + } + book.setAuthor( this ); + this.books.add( book ); + } + + public void removeAllBooks() { + if ( this.books != null ) { + this.books.clear(); + } + } + + public Book getBook(String title) { + return books.stream().filter( b -> title.equals( b.getTitle() ) ).findFirst().orElse( null ); + } + + public void removeBook(String title) { + for( Iterator it = books.iterator(); it.hasNext(); ) { + Book book = it.next(); + if ( title.equals( title ) ) { + it.remove(); + } + } + } + + @Override + public String toString() { + return "Author{" + + "id=" + id + + ", firstName='" + firstName + '\'' + + ", lastName='" + lastName + '\'' + + '}'; + } + } + + @Entity + @Audited + public static class Book { + @Id + @GeneratedValue + private Integer id; + + private String title; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "author_id", nullable = false, insertable = false, updatable = false) + @NotAudited + private Author author; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public Author getAuthor() { + return author; + } + + public void setAuthor(Author author) { + this.author = author; + } + + @Override + public int hashCode() { + return Objects.hash( title ); + } + + @Override + public boolean equals(Object object) { + if ( this == object ) { + return true; + } + if ( object == null || getClass() != object.getClass() ) { + return false; + } + Book book = (Book) object; + return Objects.equals( title, book.title ); + } + + @Override + public String toString() { + return "Book{" + + "id=" + id + + ", title='" + title + '\'' + + ", author=" + author + + '}'; + } + } +}