Skip to content

Commit

Permalink
HHH-12498 - Fix NPE when audited entity composite-key references non-…
Browse files Browse the repository at this point in the history
…audited entity.
  • Loading branch information
Naros committed Apr 17, 2018
1 parent d9cdd58 commit 11456a9
Show file tree
Hide file tree
Showing 2 changed files with 275 additions and 1 deletion.
Expand Up @@ -112,7 +112,18 @@ private void generateSecondPass(String entityName, Component component) {
final String referencedEntityName = ( (ToOne) property.getValue() ).getReferencedEntityName();

final String prefix = mainGenerator.getVerEntCfg().getOriginalIdPropName() + "." + propertyData.getName();
final IdMapper relMapper = mainGenerator.getEntitiesConfigurations().get( referencedEntityName ).getIdMapper();

final IdMapper relMapper;
if ( mainGenerator.getEntitiesConfigurations().containsKey( referencedEntityName ) ) {
relMapper = mainGenerator.getEntitiesConfigurations().get( referencedEntityName ).getIdMapper();
}
else if ( mainGenerator.getNotAuditedEntitiesConfigurations().containsKey( referencedEntityName ) ) {
relMapper = mainGenerator.getNotAuditedEntitiesConfigurations().get( referencedEntityName ).getIdMapper();
}
else {
throw new MappingException( "Unable to locate entity configuration for [" + referencedEntityName + "]" );
}

final IdMapper prefixedMapper = relMapper.prefixMappedProperties( prefix + "." );

mainGenerator.getEntitiesConfigurations().get( entityName ).addToOneRelation(
Expand Down
@@ -0,0 +1,263 @@
/*
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.envers.test.integration.ids.embeddedid;

import java.io.Serializable;
import java.util.Arrays;
import java.util.List;

import javax.persistence.Embeddable;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;

import org.hibernate.envers.Audited;
import org.hibernate.envers.RevisionType;
import org.hibernate.envers.query.AuditEntity;
import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase;
import org.hibernate.envers.test.Priority;
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.assertNull;
import static org.junit.Assert.assertTrue;

/**
* Test an audited entity with an embeddable composite key that has an association
* to a non-audited entity type.
*
* @author Chris Cranford
*/
@TestForIssue(jiraKey = "HHH-12498")
public class RelationInsideEmbeddableNotAuditedTest extends BaseEnversJPAFunctionalTestCase {
private Integer authorId;
private BookId bookId1;
private BookId bookId2;

@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[]{ Book.class, Author.class };
}

@Test
@Priority(10)
public void initData() {
// Revision 1, persist author and book
doInJPA( this::entityManagerFactory, entityManager -> {
final Author author = new Author();
author.setName( "Stephen King" );
entityManager.persist( author );
authorId = author.getId();

final Book book = new Book();
book.setId( new BookId() );
book.getId().setId( 1 );
book.getId().setAuthor( author );
book.setName( "Gunslinger" );
book.setEdition( 1 );
entityManager.persist( book );
this.bookId1 = book.getId();
} );

// Revision 2, persist new book
doInJPA( this::entityManagerFactory, entityManager -> {
final Author author = entityManager.find( Author.class, authorId );

final Book book = new Book();
book.setId( new BookId() );
book.getId().setId( 2 );
book.getId().setAuthor( author );
book.setName( "Gunslinger" );
book.setEdition( 2 );
entityManager.persist( book );
this.bookId2 = book.getId();
} );

// Modify books
doInJPA( this::entityManagerFactory, entityManager -> {
final Book book1 = entityManager.find( Book.class, bookId1 );
book1.setName( "Gunslinger: Dark Tower" );
entityManager.merge( book1 );
} );

doInJPA( this::entityManagerFactory, entityManager -> {
final Book book2 = entityManager.find( Book.class, bookId2 );
book2.setName( "Gunslinger: Dark Tower" );
entityManager.merge( book2 );
} );

//! Delete books
doInJPA( this::entityManagerFactory, entityManager -> {
final Book book1 = entityManager.find( Book.class, bookId1 );
entityManager.remove( book1 );

final Book book2 = entityManager.find( Book.class, bookId2 );
entityManager.remove( book2 );
} );
}

@Test
public void tesRevisionCounts() {
assertEquals( Arrays.asList( 1, 3, 5 ), getAuditReader().getRevisions( Book.class, bookId1 ) );
assertEquals( Arrays.asList( 2, 4, 5 ), getAuditReader().getRevisions( Book.class, bookId2 ) );
}

@Test
public void testRevisionHistoryBook1() {
final Book rev1 = getAuditReader().find( Book.class, bookId1, 1 );
assertNotNull( rev1.getId().getAuthor() );

final Book rev3 = getAuditReader().find( Book.class, bookId1, 3 );
assertNotNull( rev3.getId().getAuthor() );

final Book rev5 = getAuditReader().find( Book.class, bookId1, 5 );
assertNull( rev5 );
}

@Test
public void testRevisionHistoryBook2() {
final Book rev2 = getAuditReader().find( Book.class, bookId2, 2 );
assertNotNull( rev2.getId().getAuthor() );

final Book rev4 = getAuditReader().find( Book.class, bookId2, 4 );
assertNotNull( rev4.getId().getAuthor() );

final Book rev5 = getAuditReader().find( Book.class, bookId2, 5 );
assertNull( rev5 );
}

@Test
@SuppressWarnings("unchecked")
public void testSelectDeletedEntitiesBook1() {
List<Book> books = (List<Book>) getAuditReader().createQuery()
.forRevisionsOfEntity( Book.class, true, true )
.add( AuditEntity.id().eq( bookId1 ) )
.add( AuditEntity.revisionType().eq( RevisionType.DEL ) )
.getResultList();

assertTrue( !books.isEmpty() );

final Book book = books.get( 0 );
assertNotNull( book.getId() );
assertNotNull( book.getId().getAuthor() );
assertEquals( authorId, book.getId().getAuthor().getId() );
}

@Test
@SuppressWarnings("unchecked")
public void testSelectDeletedEntitiesBook2() {
List<Book> books = (List<Book>) getAuditReader().createQuery()
.forRevisionsOfEntity( Book.class, true, true )
.add( AuditEntity.id().eq( bookId2 ) )
.add( AuditEntity.revisionType().eq( RevisionType.DEL ) )
.getResultList();

assertTrue( !books.isEmpty() );

final Book book = books.get( 0 );
assertNotNull( book.getId() );
assertNotNull( book.getId().getAuthor() );
assertEquals( authorId, book.getId().getAuthor().getId() );
}

@Audited
@Entity(name = "Book")
public static class Book {
@EmbeddedId
private BookId id;
private String name;
private Integer edition;

public BookId getId() {
return id;
}

public void setId(BookId id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Integer getEdition() {
return edition;
}

public void setEdition(Integer edition) {
this.edition = edition;
}
}

@Embeddable
public static class BookId implements Serializable {
private Integer id;

@ManyToOne(fetch = FetchType.LAZY)
private Author author;

BookId() {

}

BookId(Integer id, Author author) {
this.id = id;
this.author = author;
}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public Author getAuthor() {
return author;
}

public void setAuthor(Author author) {
this.author = author;
}
}

@Entity(name = "Author")
public static class Author {
@Id
@GeneratedValue
private Integer id;
private String name;

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}
}

0 comments on commit 11456a9

Please sign in to comment.