HHH-8174 - Envers support for @NotFound

This commit is contained in:
Lukasz Antoniak 2013-06-11 09:08:56 -07:00
parent b4ab20a97b
commit f2d435ddc1
11 changed files with 531 additions and 139 deletions

View File

@ -380,8 +380,12 @@
<para>
If you want to audit a relation, where the target entity is not audited (that is the case for example with
dictionary-like entities, which don't change and don't have to be audited), just annotate it with
<literal>@Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)</literal>. Then, when reading historic
versions of your entity, the relation will always point to the "current" related entity.
<literal>@Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)</literal>. Then, while reading historic
versions of your entity, the relation will always point to the "current" related entity. By default Envers
throws <classname>javax.persistence.EntityNotFoundException</classname> when "current" entity does not
exist in the database. Apply <literal>@NotFound(action = NotFoundAction.IGNORE)</literal> annotation
to silence the exception and assign null value instead. Hereby solution causes implicit eager loading
of to-one relations.
</para>
<para>

View File

@ -77,10 +77,8 @@ public final class ToOneRelationMetadataGenerator {
// Storing information about this relation
mainGenerator.getEntitiesConfigurations().get( entityName ).addToOneRelation(
propertyAuditingData.getName(),
referencedEntityName,
relMapper,
insertable
propertyAuditingData.getName(), referencedEntityName, relMapper,
insertable, MappingTools.ignoreNotFound( value )
);
// If the property isn't insertable, checking if this is not a "fake" bidirectional many-to-one relationship,
@ -154,10 +152,8 @@ public final class ToOneRelationMetadataGenerator {
// Storing information about this relation
mainGenerator.getEntitiesConfigurations().get( entityName ).addToOneNotOwningRelation(
propertyAuditingData.getName(),
owningReferencePropertyName,
referencedEntityName,
ownedIdMapper
propertyAuditingData.getName(), owningReferencePropertyName, referencedEntityName,
ownedIdMapper, MappingTools.ignoreNotFound( value )
);
// Adding mapper for the id
@ -191,10 +187,8 @@ public final class ToOneRelationMetadataGenerator {
// Storing information about this relation
mainGenerator.getEntitiesConfigurations().get( entityName ).addToOneRelation(
propertyAuditingData.getName(),
referencedEntityName,
relMapper,
insertable
propertyAuditingData.getName(), referencedEntityName, relMapper, insertable,
MappingTools.ignoreNotFound( value )
);
// Adding mapper for the id

View File

@ -46,9 +46,8 @@ public class EntityConfiguration {
private Map<String, RelationDescription> relations;
private String parentEntityName;
public EntityConfiguration(
String versionsEntityName, String entityClassName, IdMappingData idMappingData,
ExtendedPropertyMapper propertyMapper, String parentEntityName) {
public EntityConfiguration(String versionsEntityName, String entityClassName, IdMappingData idMappingData,
ExtendedPropertyMapper propertyMapper, String parentEntityName) {
this.versionsEntityName = versionsEntityName;
this.entityClassName = entityClassName;
this.idMappingData = idMappingData;
@ -58,60 +57,36 @@ public class EntityConfiguration {
this.relations = new HashMap<String, RelationDescription>();
}
public void addToOneRelation(String fromPropertyName, String toEntityName, IdMapper idMapper, boolean insertable) {
public void addToOneRelation(String fromPropertyName, String toEntityName, IdMapper idMapper, boolean insertable,
boolean ignoreNotFound) {
relations.put(
fromPropertyName,
new RelationDescription(
fromPropertyName,
RelationType.TO_ONE,
toEntityName,
null,
idMapper,
null,
null,
insertable
RelationDescription.toOne(
fromPropertyName, RelationType.TO_ONE, toEntityName, null, idMapper, null,
null, insertable, ignoreNotFound
)
);
}
public void addToOneNotOwningRelation(
String fromPropertyName,
String mappedByPropertyName,
String toEntityName,
IdMapper idMapper) {
public void addToOneNotOwningRelation(String fromPropertyName, String mappedByPropertyName,
String toEntityName, IdMapper idMapper, boolean ignoreNotFound) {
relations.put(
fromPropertyName,
new RelationDescription(
fromPropertyName,
RelationType.TO_ONE_NOT_OWNING,
toEntityName,
mappedByPropertyName,
idMapper,
null,
null,
true
RelationDescription.toOne(
fromPropertyName, RelationType.TO_ONE_NOT_OWNING, toEntityName, mappedByPropertyName,
idMapper, null, null, true, ignoreNotFound
)
);
}
public void addToManyNotOwningRelation(
String fromPropertyName,
String mappedByPropertyName,
String toEntityName,
IdMapper idMapper,
PropertyMapper fakeBidirectionalRelationMapper,
PropertyMapper fakeBidirectionalRelationIndexMapper) {
public void addToManyNotOwningRelation(String fromPropertyName, String mappedByPropertyName, String toEntityName,
IdMapper idMapper, PropertyMapper fakeBidirectionalRelationMapper,
PropertyMapper fakeBidirectionalRelationIndexMapper) {
relations.put(
fromPropertyName,
new RelationDescription(
fromPropertyName,
RelationType.TO_MANY_NOT_OWNING,
toEntityName,
mappedByPropertyName,
idMapper,
fakeBidirectionalRelationMapper,
fakeBidirectionalRelationIndexMapper,
true
RelationDescription.toMany(
fromPropertyName, RelationType.TO_MANY_NOT_OWNING, toEntityName, mappedByPropertyName,
idMapper, fakeBidirectionalRelationMapper, fakeBidirectionalRelationIndexMapper, true
)
);
}
@ -119,34 +94,18 @@ public class EntityConfiguration {
public void addToManyMiddleRelation(String fromPropertyName, String toEntityName) {
relations.put(
fromPropertyName,
new RelationDescription(
fromPropertyName,
RelationType.TO_MANY_MIDDLE,
toEntityName,
null,
null,
null,
null,
true
RelationDescription.toMany(
fromPropertyName, RelationType.TO_MANY_MIDDLE, toEntityName, null, null, null, null, true
)
);
}
public void addToManyMiddleNotOwningRelation(
String fromPropertyName,
String mappedByPropertyName,
String toEntityName) {
public void addToManyMiddleNotOwningRelation(String fromPropertyName, String mappedByPropertyName, String toEntityName) {
relations.put(
fromPropertyName,
new RelationDescription(
fromPropertyName,
RelationType.TO_MANY_MIDDLE_NOT_OWNING,
toEntityName,
mappedByPropertyName,
null,
null,
null,
true
RelationDescription.toMany(
fromPropertyName, RelationType.TO_MANY_MIDDLE_NOT_OWNING, toEntityName, mappedByPropertyName,
null, null, null, true
)
);
}
@ -175,6 +134,13 @@ public class EntityConfiguration {
return parentEntityName;
}
/**
* @return the className for the configured entity
*/
public String getEntityClassName() {
return entityClassName;
}
// For use by EntitiesConfigurations
String getVersionsEntityName() {
@ -184,11 +150,4 @@ public class EntityConfiguration {
Iterable<RelationDescription> getRelationsIterator() {
return relations.values();
}
/**
* @return the className for the configured entity
*/
public String getEntityClassName() {
return entityClassName;
}
}

View File

@ -34,21 +34,44 @@ public class RelationDescription {
private final RelationType relationType;
private final String toEntityName;
private final String mappedByPropertyName;
private final boolean ignoreNotFound;
private final IdMapper idMapper;
private final PropertyMapper fakeBidirectionalRelationMapper;
private final PropertyMapper fakeBidirectionalRelationIndexMapper;
private final boolean insertable;
private boolean bidirectional;
public RelationDescription(
String fromPropertyName, RelationType relationType, String toEntityName,
String mappedByPropertyName, IdMapper idMapper,
PropertyMapper fakeBidirectionalRelationMapper,
PropertyMapper fakeBidirectionalRelationIndexMapper, boolean insertable) {
public static RelationDescription toOne(String fromPropertyName, RelationType relationType, String toEntityName,
String mappedByPropertyName, IdMapper idMapper, PropertyMapper fakeBidirectionalRelationMapper,
PropertyMapper fakeBidirectionalRelationIndexMapper, boolean insertable,
boolean ignoreNotFound) {
return new RelationDescription(
fromPropertyName, relationType, toEntityName, mappedByPropertyName, idMapper, fakeBidirectionalRelationMapper,
fakeBidirectionalRelationIndexMapper, insertable, ignoreNotFound
);
}
public static RelationDescription toMany(String fromPropertyName, RelationType relationType, String toEntityName,
String mappedByPropertyName, IdMapper idMapper, PropertyMapper fakeBidirectionalRelationMapper,
PropertyMapper fakeBidirectionalRelationIndexMapper, boolean insertable) {
// Envers populates collections by executing dedicated queries. Special handling of
// @NotFound(action = NotFoundAction.IGNORE) can be omitted in such case as exceptions
// (e.g. EntityNotFoundException, ObjectNotFoundException) are never thrown.
// Therefore assigning false to ignoreNotFound.
return new RelationDescription(
fromPropertyName, relationType, toEntityName, mappedByPropertyName, idMapper, fakeBidirectionalRelationMapper,
fakeBidirectionalRelationIndexMapper, insertable, false
);
}
private RelationDescription(String fromPropertyName, RelationType relationType, String toEntityName,
String mappedByPropertyName, IdMapper idMapper, PropertyMapper fakeBidirectionalRelationMapper,
PropertyMapper fakeBidirectionalRelationIndexMapper, boolean insertable, boolean ignoreNotFound) {
this.fromPropertyName = fromPropertyName;
this.relationType = relationType;
this.toEntityName = toEntityName;
this.mappedByPropertyName = mappedByPropertyName;
this.ignoreNotFound = ignoreNotFound;
this.idMapper = idMapper;
this.fakeBidirectionalRelationMapper = fakeBidirectionalRelationMapper;
this.fakeBidirectionalRelationIndexMapper = fakeBidirectionalRelationIndexMapper;
@ -73,6 +96,10 @@ public class RelationDescription {
return mappedByPropertyName;
}
public boolean isIgnoreNotFound() {
return ignoreNotFound;
}
public IdMapper getIdMapper() {
return idMapper;
}

View File

@ -35,7 +35,8 @@ import org.hibernate.persister.entity.EntityPersister;
*/
public class ToOneEntityLoader {
/**
* Immediately loads historical entity or its current state when excluded from audit process.
* Immediately loads historical entity or its current state when excluded from audit process. Returns {@code null}
* reference if entity has not been found in the database.
*/
public static Object loadImmediate(
AuditReaderImplementor versionsReader,

View File

@ -45,11 +45,8 @@ public class ToOneIdMapper extends AbstractToOneMapper {
private final String referencedEntityName;
private final boolean nonInsertableFake;
public ToOneIdMapper(
IdMapper delegate,
PropertyData propertyData,
String referencedEntityName,
boolean nonInsertableFake) {
public ToOneIdMapper(IdMapper delegate, PropertyData propertyData, String referencedEntityName,
boolean nonInsertableFake) {
super( propertyData );
this.delegate = delegate;
this.referencedEntityName = referencedEntityName;
@ -57,11 +54,8 @@ public class ToOneIdMapper extends AbstractToOneMapper {
}
@Override
public boolean mapToMapFromEntity(
SessionImplementor session,
Map<String, Object> data,
Object newObj,
Object oldObj) {
public boolean mapToMapFromEntity(SessionImplementor session, Map<String, Object> data, Object newObj,
Object oldObj) {
final HashMap<String, Object> newData = new HashMap<String, Object>();
// If this property is originally non-insertable, but made insertable because it is in a many-to-one "fake"
@ -77,11 +71,8 @@ public class ToOneIdMapper extends AbstractToOneMapper {
}
@Override
public void mapModifiedFlagsToMapFromEntity(
SessionImplementor session,
Map<String, Object> data,
Object newObj,
Object oldObj) {
public void mapModifiedFlagsToMapFromEntity(SessionImplementor session, Map<String, Object> data, Object newObj,
Object oldObj) {
if ( getPropertyData().isUsingModifiedFlag() ) {
data.put( getPropertyData().getModifiedFlagPropertyName(), checkModified( session, newObj, oldObj ) );
}
@ -103,9 +94,8 @@ public class ToOneIdMapper extends AbstractToOneMapper {
}
@Override
public void nullSafeMapToEntityFromMap(
AuditConfiguration verCfg, Object obj, Map data, Object primaryKey,
AuditReaderImplementor versionsReader, Number revision) {
public void nullSafeMapToEntityFromMap(AuditConfiguration verCfg, Object obj, Map data, Object primaryKey,
AuditReaderImplementor versionsReader, Number revision) {
final Object entityId = delegate.mapToIdFromMap( data );
Object value = null;
if ( entityId != null ) {
@ -114,27 +104,35 @@ public class ToOneIdMapper extends AbstractToOneMapper {
}
else {
final EntityInfo referencedEntity = getEntityInfo( verCfg, referencedEntityName );
value = ToOneEntityLoader.createProxyOrLoadImmediate(
versionsReader, referencedEntity.getEntityClass(), referencedEntityName,
entityId, revision, RevisionType.DEL.equals(
data.get(
verCfg.getAuditEntCfg()
.getRevisionTypePropName()
)
), verCfg
);
boolean ignoreNotFound = false;
if ( !referencedEntity.isAudited() ) {
final String referencingEntityName = verCfg.getEntCfg().getEntityNameForVersionsEntityName( (String) data.get( "$type$" ) );
ignoreNotFound = verCfg.getEntCfg().get( referencingEntityName ).getRelationDescription( getPropertyData().getName() ).isIgnoreNotFound();
}
if ( ignoreNotFound ) {
// Eagerly loading referenced entity to silence potential (in case of proxy)
// EntityNotFoundException or ObjectNotFoundException. Assigning null reference.
value = ToOneEntityLoader.loadImmediate(
versionsReader, referencedEntity.getEntityClass(), referencedEntityName,
entityId, revision, RevisionType.DEL.equals( data.get( verCfg.getAuditEntCfg().getRevisionTypePropName() ) ),
verCfg
);
}
else {
value = ToOneEntityLoader.createProxyOrLoadImmediate(
versionsReader, referencedEntity.getEntityClass(), referencedEntityName,
entityId, revision, RevisionType.DEL.equals( data.get( verCfg.getAuditEntCfg().getRevisionTypePropName() ) ),
verCfg
);
}
}
}
setPropertyValue( obj, value );
}
public void addMiddleEqualToQuery(
Parameters parameters,
String idPrefix1,
String prefix1,
String idPrefix2,
String prefix2) {
public void addMiddleEqualToQuery(Parameters parameters, String idPrefix1, String prefix1,
String idPrefix2, String prefix2) {
delegate.addIdsEqualToQuery( parameters, prefix1, delegate, prefix2 );
}
}

View File

@ -24,6 +24,7 @@
package org.hibernate.envers.internal.tools;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.ManyToOne;
import org.hibernate.mapping.OneToMany;
import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.Value;
@ -63,4 +64,18 @@ public abstract class MappingTools {
return null;
}
/**
* @param value Persistent property.
* @return {@code false} if lack of associated entity shall raise an exception, {@code true} otherwise.
*/
public static boolean ignoreNotFound(Value value) {
if ( value instanceof ManyToOne ) {
return ( (ManyToOne) value ).isIgnoreNotFound();
}
else if ( value instanceof OneToMany ) {
return ( (OneToMany) value ).isIgnoreNotFound();
}
return false;
}
}

View File

@ -0,0 +1,111 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.envers.test.entities.manytomany.unidirectional;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import org.hibernate.envers.Audited;
import org.hibernate.envers.RelationTargetAuditMode;
import org.hibernate.envers.test.entities.UnversionedStrTestEntity;
/**
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
@Audited
@Entity
@Table(name = "M2M_N_AUD_NULL")
public class ManyToManyNotAuditedNullEntity implements Serializable {
@Id
private Integer id;
private String data;
@Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
@ManyToMany(fetch = FetchType.LAZY)
@NotFound(action = NotFoundAction.IGNORE)
private List<UnversionedStrTestEntity> references = new ArrayList<UnversionedStrTestEntity>();
protected ManyToManyNotAuditedNullEntity() {
}
public ManyToManyNotAuditedNullEntity(Integer id, String data) {
this.id = id;
this.data = data;
}
public boolean equals(Object o) {
if ( this == o ) return true;
if ( !( o instanceof ManyToManyNotAuditedNullEntity ) ) return false;
ManyToManyNotAuditedNullEntity that = (ManyToManyNotAuditedNullEntity) o;
if ( data != null ? !data.equals( that.getData() ) : that.getData() != null ) return false;
if ( id != null ? !id.equals( that.getId() ) : that.getId() != null ) return false;
return true;
}
public int hashCode() {
int result = ( id != null ? id.hashCode() : 0 );
result = 31 * result + ( data != null ? data.hashCode() : 0 );
return result;
}
public String toString() {
return "ManyToManyNotAuditedNullEntity(id = " + id + ", data = " + data + ")";
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public List<UnversionedStrTestEntity> getReferences() {
return references;
}
public void setReferences(List<UnversionedStrTestEntity> references) {
this.references = references;
}
}

View File

@ -0,0 +1,110 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.envers.test.entities.manytoone.unidirectional;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import org.hibernate.envers.Audited;
import org.hibernate.envers.RelationTargetAuditMode;
import org.hibernate.envers.test.entities.UnversionedStrTestEntity;
/**
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
@Audited
@Entity
@Table(name = "M2O_N_AUD_NULL")
public class ManyToOneNotAuditedNullEntity implements Serializable {
@Id
private Integer id;
private String data;
@Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
@ManyToOne(fetch = FetchType.LAZY, optional = true)
@NotFound(action = NotFoundAction.IGNORE)
private UnversionedStrTestEntity reference;
protected ManyToOneNotAuditedNullEntity() {
}
public ManyToOneNotAuditedNullEntity(Integer id, String data, UnversionedStrTestEntity reference) {
this.id = id;
this.data = data;
this.reference = reference;
}
public boolean equals(Object o) {
if ( this == o ) return true;
if ( !( o instanceof ManyToOneNotAuditedNullEntity ) ) return false;
ManyToOneNotAuditedNullEntity that = (ManyToOneNotAuditedNullEntity) o;
if ( data != null ? !data.equals( that.getData() ) : that.getData() != null ) return false;
if ( id != null ? !id.equals( that.getId() ) : that.getId() != null ) return false;
return true;
}
public int hashCode() {
int result = ( id != null ? id.hashCode() : 0 );
result = 31 * result + ( data != null ? data.hashCode() : 0 );
return result;
}
public String toString() {
return "ManyToOneNotAuditedNullEntity(id = " + id + ", data = " + data + ")";
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public UnversionedStrTestEntity getReference() {
return reference;
}
public void setReference(UnversionedStrTestEntity reference) {
this.reference = reference;
}
}

View File

@ -0,0 +1,111 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.envers.test.entities.onetomany;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import org.hibernate.envers.Audited;
import org.hibernate.envers.RelationTargetAuditMode;
import org.hibernate.envers.test.entities.UnversionedStrTestEntity;
/**
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
@Audited
@Entity
@Table(name = "O2M_N_AUD_NULL")
public class OneToManyNotAuditedNullEntity implements Serializable {
@Id
private Integer id;
private String data;
@Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
@OneToMany(fetch = FetchType.LAZY)
@NotFound(action = NotFoundAction.IGNORE)
private List<UnversionedStrTestEntity> references = new ArrayList<UnversionedStrTestEntity>();
protected OneToManyNotAuditedNullEntity() {
}
public OneToManyNotAuditedNullEntity(Integer id, String data) {
this.id = id;
this.data = data;
}
public boolean equals(Object o) {
if ( this == o ) return true;
if ( !( o instanceof OneToManyNotAuditedNullEntity ) ) return false;
OneToManyNotAuditedNullEntity that = (OneToManyNotAuditedNullEntity) o;
if ( data != null ? !data.equals( that.getData() ) : that.getData() != null ) return false;
if ( id != null ? !id.equals( that.getId() ) : that.getId() != null ) return false;
return true;
}
public int hashCode() {
int result = ( id != null ? id.hashCode() : 0 );
result = 31 * result + ( data != null ? data.hashCode() : 0 );
return result;
}
public String toString() {
return "OneToManyNotAuditedNullEntity(id = " + id + ", data = " + data + ")";
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public List<UnversionedStrTestEntity> getReferences() {
return references;
}
public void setReferences(List<UnversionedStrTestEntity> references) {
this.references = references;
}
}

View File

@ -25,26 +25,37 @@ package org.hibernate.envers.test.integration.proxy;
import javax.persistence.EntityManager;
import org.junit.Assert;
import org.junit.Test;
import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase;
import org.hibernate.envers.test.Priority;
import org.hibernate.envers.test.entities.UnversionedStrTestEntity;
import org.hibernate.envers.test.entities.manytomany.unidirectional.ManyToManyNotAuditedNullEntity;
import org.hibernate.envers.test.entities.manytoone.unidirectional.ManyToOneNotAuditedNullEntity;
import org.hibernate.envers.test.entities.manytoone.unidirectional.TargetNotAuditedEntity;
import org.hibernate.envers.test.entities.onetomany.OneToManyNotAuditedNullEntity;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer;
import org.junit.Test;
import org.hibernate.testing.TestForIssue;
/**
* @author Eugene Goroschenya
*/
public class ProxyIdentifier extends BaseEnversJPAFunctionalTestCase {
private TargetNotAuditedEntity tnae1;
private UnversionedStrTestEntity uste1;
private TargetNotAuditedEntity tnae1 = null;
private ManyToOneNotAuditedNullEntity mtonane1 = null;
private ManyToManyNotAuditedNullEntity mtmnane1 = null;
private OneToManyNotAuditedNullEntity otmnane1 = null;
private UnversionedStrTestEntity uste1 = null;
private UnversionedStrTestEntity uste2 = null;
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] {TargetNotAuditedEntity.class, UnversionedStrTestEntity.class};
return new Class[] {
TargetNotAuditedEntity.class, ManyToOneNotAuditedNullEntity.class, UnversionedStrTestEntity.class,
ManyToManyNotAuditedNullEntity.class, OneToManyNotAuditedNullEntity.class
};
}
@Test
@ -53,10 +64,12 @@ public class ProxyIdentifier extends BaseEnversJPAFunctionalTestCase {
EntityManager em = getEntityManager();
uste1 = new UnversionedStrTestEntity( "str1" );
uste2 = new UnversionedStrTestEntity( "str2" );
// No revision
em.getTransaction().begin();
em.persist( uste1 );
em.persist( uste2 );
em.getTransaction().commit();
// Revision 1
@ -65,24 +78,73 @@ public class ProxyIdentifier extends BaseEnversJPAFunctionalTestCase {
tnae1 = new TargetNotAuditedEntity( 1, "tnae1", uste1 );
em.persist( tnae1 );
em.getTransaction().commit();
// Revision 2
em.getTransaction().begin();
uste2 = em.find( UnversionedStrTestEntity.class, uste2.getId() );
mtonane1 = new ManyToOneNotAuditedNullEntity( 2, "mtonane1", uste2 );
mtmnane1 = new ManyToManyNotAuditedNullEntity( 3, "mtmnane1" );
mtmnane1.getReferences().add( uste2 );
otmnane1 = new OneToManyNotAuditedNullEntity( 4, "otmnane1" );
otmnane1.getReferences().add( uste2 );
em.persist( mtonane1 );
em.persist( mtmnane1 );
em.persist( otmnane1 );
em.getTransaction().commit();
em.clear();
// Revision 3
// Remove not audited target entity, so we can verify null reference
// when @NotFound(action = NotFoundAction.IGNORE) applied.
em.getTransaction().begin();
ManyToOneNotAuditedNullEntity tmp1 = em.find( ManyToOneNotAuditedNullEntity.class, mtonane1.getId() );
tmp1.setReference( null );
tmp1 = em.merge( tmp1 );
ManyToManyNotAuditedNullEntity tmp2 = em.find( ManyToManyNotAuditedNullEntity.class, mtmnane1.getId() );
tmp2.setReferences( null );
tmp2 = em.merge( tmp2 );
OneToManyNotAuditedNullEntity tmp3 = em.find( OneToManyNotAuditedNullEntity.class, otmnane1.getId() );
tmp3.setReferences( null );
tmp3 = em.merge( tmp3 );
em.remove( em.getReference( UnversionedStrTestEntity.class, uste2.getId() ) );
em.getTransaction().commit();
em.close();
}
@Test
public void testProxyIdentifier() {
TargetNotAuditedEntity rev1 = getAuditReader().find( TargetNotAuditedEntity.class, tnae1.getId(), 1 );
assert rev1.getReference() instanceof HibernateProxy;
Assert.assertTrue( rev1.getReference() instanceof HibernateProxy );
HibernateProxy proxyCreateByEnvers = (HibernateProxy) rev1.getReference();
LazyInitializer lazyInitializer = proxyCreateByEnvers.getHibernateLazyInitializer();
assert lazyInitializer.isUninitialized();
assert lazyInitializer.getIdentifier() != null;
assert lazyInitializer.getIdentifier().equals( tnae1.getId() );
assert lazyInitializer.isUninitialized();
Assert.assertTrue( lazyInitializer.isUninitialized() );
Assert.assertNotNull( lazyInitializer.getIdentifier() );
Assert.assertEquals( tnae1.getId(), lazyInitializer.getIdentifier() );
Assert.assertTrue( lazyInitializer.isUninitialized() );
assert rev1.getReference().getId().equals( uste1.getId() );
assert rev1.getReference().getStr().equals( uste1.getStr() );
assert !lazyInitializer.isUninitialized();
Assert.assertEquals( uste1.getId(), rev1.getReference().getId() );
Assert.assertEquals( uste1.getStr(), rev1.getReference().getStr() );
Assert.assertFalse( lazyInitializer.isUninitialized() );
}
@Test
@TestForIssue( jiraKey = "HHH-8174" )
public void testNullReferenceWithNotFoundActionIgnore() {
ManyToOneNotAuditedNullEntity mtoRev2 = getAuditReader().find( ManyToOneNotAuditedNullEntity.class, mtonane1.getId(), 2 );
Assert.assertEquals( mtonane1, mtoRev2 );
Assert.assertNull( mtoRev2.getReference() );
ManyToManyNotAuditedNullEntity mtmRev2 = getAuditReader().find( ManyToManyNotAuditedNullEntity.class, mtmnane1.getId(), 2 );
Assert.assertEquals( mtmnane1, mtmRev2 );
Assert.assertTrue( mtmRev2.getReferences().isEmpty() );
OneToManyNotAuditedNullEntity otmRev2 = getAuditReader().find( OneToManyNotAuditedNullEntity.class, otmnane1.getId(), 2 );
Assert.assertEquals( otmnane1, otmRev2 );
Assert.assertTrue( otmRev2.getReferences().isEmpty() );
}
}