HHH-8174 - Envers support for @NotFound
This commit is contained in:
parent
b4ab20a97b
commit
f2d435ddc1
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -46,8 +46,7 @@ public class EntityConfiguration {
|
|||
private Map<String, RelationDescription> relations;
|
||||
private String parentEntityName;
|
||||
|
||||
public EntityConfiguration(
|
||||
String versionsEntityName, String entityClassName, IdMappingData idMappingData,
|
||||
public EntityConfiguration(String versionsEntityName, String entityClassName, IdMappingData idMappingData,
|
||||
ExtendedPropertyMapper propertyMapper, String parentEntityName) {
|
||||
this.versionsEntityName = versionsEntityName;
|
||||
this.entityClassName = entityClassName;
|
||||
|
@ -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,
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -45,10 +45,7 @@ public class ToOneIdMapper extends AbstractToOneMapper {
|
|||
private final String referencedEntityName;
|
||||
private final boolean nonInsertableFake;
|
||||
|
||||
public ToOneIdMapper(
|
||||
IdMapper delegate,
|
||||
PropertyData propertyData,
|
||||
String referencedEntityName,
|
||||
public ToOneIdMapper(IdMapper delegate, PropertyData propertyData, String referencedEntityName,
|
||||
boolean nonInsertableFake) {
|
||||
super( propertyData );
|
||||
this.delegate = delegate;
|
||||
|
@ -57,10 +54,7 @@ public class ToOneIdMapper extends AbstractToOneMapper {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean mapToMapFromEntity(
|
||||
SessionImplementor session,
|
||||
Map<String, Object> data,
|
||||
Object newObj,
|
||||
public boolean mapToMapFromEntity(SessionImplementor session, Map<String, Object> data, Object newObj,
|
||||
Object oldObj) {
|
||||
final HashMap<String, Object> newData = new HashMap<String, Object>();
|
||||
|
||||
|
@ -77,10 +71,7 @@ public class ToOneIdMapper extends AbstractToOneMapper {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void mapModifiedFlagsToMapFromEntity(
|
||||
SessionImplementor session,
|
||||
Map<String, Object> data,
|
||||
Object newObj,
|
||||
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,8 +94,7 @@ public class ToOneIdMapper extends AbstractToOneMapper {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void nullSafeMapToEntityFromMap(
|
||||
AuditConfiguration verCfg, Object obj, Map data, Object primaryKey,
|
||||
public void nullSafeMapToEntityFromMap(AuditConfiguration verCfg, Object obj, Map data, Object primaryKey,
|
||||
AuditReaderImplementor versionsReader, Number revision) {
|
||||
final Object entityId = delegate.mapToIdFromMap( data );
|
||||
Object value = null;
|
||||
|
@ -114,27 +104,35 @@ public class ToOneIdMapper extends AbstractToOneMapper {
|
|||
}
|
||||
else {
|
||||
final EntityInfo referencedEntity = getEntityInfo( verCfg, referencedEntityName );
|
||||
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
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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() );
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue