HHH-13564 - Fix NullPointerException for audited entity with embedded-id

in mapping superclass that makes use of generics.
This commit is contained in:
Chris Cranford 2019-08-23 15:04:52 -04:00
parent 44c6f0fa3f
commit 5c95096e7c
2 changed files with 220 additions and 3 deletions

View File

@ -16,7 +16,6 @@ import java.util.Map;
import org.hibernate.envers.exception.AuditException;
import org.hibernate.envers.internal.entities.PropertyData;
import org.hibernate.envers.internal.tools.ReflectionTools;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.property.access.spi.Getter;
import org.hibernate.property.access.spi.Setter;
import org.hibernate.service.ServiceRegistry;
@ -73,11 +72,10 @@ public class EmbeddedIdMapper extends AbstractCompositeIdMapper implements Simpl
new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
final Getter getter = ReflectionTools.getGetter( obj.getClass(), idPropertyData, getServiceRegistry() );
final Setter setter = ReflectionTools.getSetter( obj.getClass(), idPropertyData, getServiceRegistry() );
try {
final Object subObj = ReflectHelper.getDefaultConstructor( getter.getReturnType() ).newInstance();
final Object subObj = instantiateCompositeId();
boolean ret = true;
for ( IdMapper idMapper : ids.values() ) {

View File

@ -0,0 +1,219 @@
/*
* 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.List;
import java.util.Objects;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.MappedSuperclass;
import org.hibernate.envers.Audited;
import org.hibernate.envers.RelationTargetAuditMode;
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;
/**
* Tests an entity mapping that uses an {@link EmbeddedId} mapping that makes use of generics.
*
* @author Chris Cranford
*/
@TestForIssue(jiraKey = "HHH-13564")
public class EmbeddedIdGenericsTest extends BaseEnversJPAFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] { NotificationType.class, Trigger.class };
}
@Test
@Priority(10)
public void initData() {
// Revision 1
// Store NotificationType and Trigger instance
doInJPA( this::entityManagerFactory, entityManager -> {
final NotificationType type = new NotificationType( "code" );
entityManager.persist( type );
Trigger trigger = new Trigger( "str", type );
entityManager.persist( trigger );
trigger.setActive( !trigger.isActive() );
entityManager.merge( trigger );
} );
}
@Test
public void testAuditQueryMappedSuperclassWithEmbeddedId() {
// There should be at least one revision for Trigger
List resultList = getAuditReader().createQuery().forRevisionsOfEntity( Trigger.class, true, true ).getResultList();
assertEquals( 1, resultList.size() );
// Trigger should be hydrated with a composite-id values below
Trigger entityInstance = (Trigger) resultList.get( 0 );
assertEquals( "str", entityInstance.getPk().getEventType() );
assertEquals( "code", entityInstance.getPk().getNotificationType().getCode() );
}
@MappedSuperclass
public abstract static class CompositeIdBaseEntity<PK extends Serializable> implements Serializable {
protected PK pk;
@EmbeddedId
public PK getPk() {
return pk;
}
public void setPk(PK pk) {
this.pk = pk;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
CompositeIdBaseEntity<?> that = (CompositeIdBaseEntity<?>) o;
return Objects.equals( pk, that.pk );
}
@Override
public int hashCode() {
return Objects.hash( pk );
}
}
@Audited
@Entity(name = "Trigger")
public static class Trigger extends CompositeIdBaseEntity<Trigger.TriggerPK> {
private boolean active;
Trigger() {
}
public Trigger(String eventType, NotificationType notificationType) {
this.pk = new TriggerPK( eventType, notificationType );
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
@Embeddable
public static class TriggerPK implements Serializable {
private String eventType;
private NotificationType notificationType;
TriggerPK() {
}
public TriggerPK(String eventType, NotificationType notificationType) {
this.eventType = eventType;
this.notificationType = notificationType;
}
@Column(nullable = false, insertable = false, updatable = false)
public String getEventType() {
return eventType;
}
public void setEventType(String eventType) {
this.eventType = eventType;
}
@Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
@ManyToOne
@JoinColumn(insertable = false, updatable = false, nullable = false)
public NotificationType getNotificationType() {
return notificationType;
}
public void setNotificationType(NotificationType notificationType) {
this.notificationType = notificationType;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
TriggerPK triggerPK = (TriggerPK) o;
return Objects.equals( eventType, triggerPK.eventType ) &&
Objects.equals( notificationType, triggerPK.notificationType );
}
@Override
public int hashCode() {
return Objects.hash( eventType, notificationType );
}
}
}
@Entity(name = "NotificationType")
public static class NotificationType {
@Id
private String code;
public NotificationType() {
}
public NotificationType(String code) {
this.code = code;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
NotificationType that = (NotificationType) o;
return Objects.equals( code, that.code );
}
@Override
public int hashCode() {
return Objects.hash( code );
}
}
}