Merge pull request #246 from lukasz-antoniak/HHH-6825
HHH-6825 - AuditException with @OneToOne-mappedBy and @PrimaryKeyJoinColumn
This commit is contained in:
commit
81ee788466
|
@ -49,6 +49,7 @@ import org.hibernate.envers.tools.StringTools;
|
|||
import org.hibernate.envers.tools.Triple;
|
||||
import org.hibernate.mapping.Collection;
|
||||
import org.hibernate.mapping.Join;
|
||||
import org.hibernate.mapping.OneToOne;
|
||||
import org.hibernate.mapping.PersistentClass;
|
||||
import org.hibernate.mapping.Property;
|
||||
import org.hibernate.mapping.Table;
|
||||
|
@ -193,8 +194,15 @@ public final class AuditMetadataGenerator {
|
|||
} else if (type instanceof OneToOneType) {
|
||||
// only second pass
|
||||
if (!firstPass) {
|
||||
toOneRelationMetadataGenerator.addOneToOneNotOwning(propertyAuditingData, value,
|
||||
currentMapper, entityName);
|
||||
OneToOne oneToOne = (OneToOne) value;
|
||||
if (oneToOne.getReferencedPropertyName() != null) {
|
||||
toOneRelationMetadataGenerator.addOneToOneNotOwning(propertyAuditingData, value,
|
||||
currentMapper, entityName);
|
||||
} else {
|
||||
// @OneToOne relation marked with @PrimaryKeyJoinColumn
|
||||
toOneRelationMetadataGenerator.addOneToOnePrimaryKeyJoinColumn(propertyAuditingData, value,
|
||||
currentMapper, entityName, insertable);
|
||||
}
|
||||
}
|
||||
} else if (type instanceof CollectionType) {
|
||||
// only second pass
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.hibernate.envers.entities.PropertyData;
|
|||
import org.hibernate.envers.entities.mapper.CompositeMapperBuilder;
|
||||
import org.hibernate.envers.entities.mapper.id.IdMapper;
|
||||
import org.hibernate.envers.entities.mapper.relation.OneToOneNotOwningMapper;
|
||||
import org.hibernate.envers.entities.mapper.relation.OneToOnePrimaryKeyJoinColumnMapper;
|
||||
import org.hibernate.envers.entities.mapper.relation.ToOneIdMapper;
|
||||
import org.hibernate.envers.tools.MappingTools;
|
||||
import org.hibernate.mapping.OneToOne;
|
||||
|
@ -41,6 +42,7 @@ import org.hibernate.mapping.Value;
|
|||
/**
|
||||
* Generates metadata for to-one relations (reference-valued properties).
|
||||
* @author Adam Warski (adam at warski dot org)
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
public final class ToOneRelationMetadataGenerator {
|
||||
private final AuditMetadataGenerator mainGenerator;
|
||||
|
@ -131,7 +133,29 @@ public final class ToOneRelationMetadataGenerator {
|
|||
|
||||
// Adding mapper for the id
|
||||
PropertyData propertyData = propertyAuditingData.getPropertyData();
|
||||
mapper.addComposite(propertyData, new OneToOneNotOwningMapper(owningReferencePropertyName,
|
||||
referencedEntityName, propertyData));
|
||||
mapper.addComposite(propertyData, new OneToOneNotOwningMapper(entityName, referencedEntityName,
|
||||
owningReferencePropertyName, propertyData));
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked"})
|
||||
void addOneToOnePrimaryKeyJoinColumn(PropertyAuditingData propertyAuditingData, Value value,
|
||||
CompositeMapperBuilder mapper, String entityName, boolean insertable) {
|
||||
String referencedEntityName = ((ToOne) value).getReferencedEntityName();
|
||||
|
||||
IdMappingData idMapping = mainGenerator.getReferencedIdMappingData(entityName, referencedEntityName,
|
||||
propertyAuditingData, true);
|
||||
|
||||
String lastPropertyPrefix = MappingTools.createToOneRelationPrefix(propertyAuditingData.getName());
|
||||
|
||||
// Generating the id mapper for the relation
|
||||
IdMapper relMapper = idMapping.getIdMapper().prefixMappedProperties(lastPropertyPrefix);
|
||||
|
||||
// Storing information about this relation
|
||||
mainGenerator.getEntitiesConfigurations().get(entityName).addToOneRelation(propertyAuditingData.getName(),
|
||||
referencedEntityName, relMapper, insertable);
|
||||
|
||||
// Adding mapper for the id
|
||||
PropertyData propertyData = propertyAuditingData.getPropertyData();
|
||||
mapper.addComposite(propertyData, new OneToOnePrimaryKeyJoinColumnMapper(entityName, referencedEntityName, propertyData));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
package org.hibernate.envers.entities.mapper.relation;
|
||||
|
||||
import org.hibernate.NonUniqueResultException;
|
||||
import org.hibernate.envers.configuration.AuditConfiguration;
|
||||
import org.hibernate.envers.entities.PropertyData;
|
||||
import org.hibernate.envers.exception.AuditException;
|
||||
import org.hibernate.envers.reader.AuditReaderImplementor;
|
||||
|
||||
import javax.persistence.NoResultException;
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Template class for property mappers that manage one-to-one relation.
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
public abstract class AbstractOneToOneMapper extends AbstractToOneMapper {
|
||||
private final String entityName;
|
||||
private final String referencedEntityName;
|
||||
|
||||
protected AbstractOneToOneMapper(String entityName, String referencedEntityName, PropertyData propertyData) {
|
||||
super(propertyData);
|
||||
this.entityName = entityName;
|
||||
this.referencedEntityName = referencedEntityName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nullSafeMapToEntityFromMap(AuditConfiguration verCfg, Object obj, Map data, Object primaryKey,
|
||||
AuditReaderImplementor versionsReader, Number revision) {
|
||||
EntityInfo referencedEntity = getEntityInfo(verCfg, referencedEntityName);
|
||||
|
||||
Object value = null;
|
||||
try {
|
||||
value = queryForReferencedEntity(versionsReader, referencedEntity, (Serializable) primaryKey, revision);
|
||||
} catch (NoResultException e) {
|
||||
value = null;
|
||||
} catch (NonUniqueResultException e) {
|
||||
throw new AuditException("Many versions results for one-to-one relationship " + entityName +
|
||||
"." + getPropertyData().getBeanName() + ".", e);
|
||||
}
|
||||
|
||||
setPropertyValue(obj, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param versionsReader Audit reader.
|
||||
* @param referencedEntity Referenced entity descriptor.
|
||||
* @param primaryKey Referenced entity identifier.
|
||||
* @param revision Revision number.
|
||||
* @return Referenced object or proxy of one-to-one relation.
|
||||
*/
|
||||
protected abstract Object queryForReferencedEntity(AuditReaderImplementor versionsReader, EntityInfo referencedEntity,
|
||||
Serializable primaryKey, Number revision);
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
package org.hibernate.envers.entities.mapper.relation;
|
||||
|
||||
import org.hibernate.collection.spi.PersistentCollection;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.envers.configuration.AuditConfiguration;
|
||||
import org.hibernate.envers.entities.EntityConfiguration;
|
||||
import org.hibernate.envers.entities.PropertyData;
|
||||
import org.hibernate.envers.entities.mapper.PersistentCollectionChangeData;
|
||||
import org.hibernate.envers.entities.mapper.PropertyMapper;
|
||||
import org.hibernate.envers.reader.AuditReaderImplementor;
|
||||
import org.hibernate.envers.tools.reflection.ReflectionTools;
|
||||
import org.hibernate.property.Setter;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Base class for property mappers that manage to-one relation.
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
public abstract class AbstractToOneMapper implements PropertyMapper {
|
||||
private final PropertyData propertyData;
|
||||
|
||||
protected AbstractToOneMapper(PropertyData propertyData) {
|
||||
this.propertyData = propertyData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mapToMapFromEntity(SessionImplementor session, Map<String, Object> data, Object newObj, Object oldObj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mapToEntityFromMap(AuditConfiguration verCfg, Object obj, Map data, Object primaryKey,
|
||||
AuditReaderImplementor versionsReader, Number revision) {
|
||||
if (obj != null) {
|
||||
nullSafeMapToEntityFromMap(verCfg, obj, data, primaryKey, versionsReader, revision);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PersistentCollectionChangeData> mapCollectionChanges(String referencingPropertyName,
|
||||
PersistentCollection newColl,
|
||||
Serializable oldColl, Serializable id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param verCfg Audit configuration.
|
||||
* @param entityName Entity name.
|
||||
* @return Entity class, name and information whether it is audited or not.
|
||||
*/
|
||||
protected EntityInfo getEntityInfo(AuditConfiguration verCfg, String entityName) {
|
||||
EntityConfiguration entCfg = verCfg.getEntCfg().get(entityName);
|
||||
boolean isRelationAudited = true;
|
||||
if (entCfg == null) {
|
||||
// a relation marked as RelationTargetAuditMode.NOT_AUDITED
|
||||
entCfg = verCfg.getEntCfg().getNotVersionEntityConfiguration(entityName);
|
||||
isRelationAudited = false;
|
||||
}
|
||||
Class entityClass = ReflectionTools.loadClass(entCfg.getEntityClassName());
|
||||
return new EntityInfo(entityClass, entityName, isRelationAudited);
|
||||
}
|
||||
|
||||
protected void setPropertyValue(Object targetObject, Object value) {
|
||||
Setter setter = ReflectionTools.getSetter(targetObject.getClass(), propertyData);
|
||||
setter.set(targetObject, value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Bean property that represents the relation.
|
||||
*/
|
||||
protected PropertyData getPropertyData() {
|
||||
return propertyData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameter {@code obj} is never {@code null}.
|
||||
* @see PropertyMapper#mapToEntityFromMap(AuditConfiguration, Object, Map, Object, AuditReaderImplementor, Number)
|
||||
*/
|
||||
public abstract void nullSafeMapToEntityFromMap(AuditConfiguration verCfg, Object obj, Map data, Object primaryKey,
|
||||
AuditReaderImplementor versionsReader, Number revision);
|
||||
|
||||
/**
|
||||
* Simple descriptor of an entity.
|
||||
*/
|
||||
protected class EntityInfo {
|
||||
private final Class entityClass;
|
||||
private final String entityName;
|
||||
private final boolean audited;
|
||||
|
||||
public EntityInfo(Class entityClass, String entityName, boolean audited) {
|
||||
this.entityClass = entityClass;
|
||||
this.entityName = entityName;
|
||||
this.audited = audited;
|
||||
}
|
||||
|
||||
public Class getEntityClass() { return entityClass; }
|
||||
public String getEntityName() { return entityName; }
|
||||
public boolean isAudited() { return audited; }
|
||||
}
|
||||
}
|
|
@ -22,78 +22,35 @@
|
|||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.envers.entities.mapper.relation;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.persistence.NoResultException;
|
||||
|
||||
import org.hibernate.NonUniqueResultException;
|
||||
import org.hibernate.collection.spi.PersistentCollection;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.envers.configuration.AuditConfiguration;
|
||||
import org.hibernate.envers.entities.EntityConfiguration;
|
||||
import org.hibernate.envers.entities.PropertyData;
|
||||
import org.hibernate.envers.entities.mapper.PersistentCollectionChangeData;
|
||||
import org.hibernate.envers.entities.mapper.PropertyMapper;
|
||||
import org.hibernate.envers.exception.AuditException;
|
||||
import org.hibernate.envers.query.AuditEntity;
|
||||
import org.hibernate.envers.reader.AuditReaderImplementor;
|
||||
import org.hibernate.envers.tools.reflection.ReflectionTools;
|
||||
import org.hibernate.property.Setter;
|
||||
|
||||
import javax.persistence.OneToOne;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Property mapper for not owning side of {@link OneToOne} relation.
|
||||
* @author Adam Warski (adam at warski dot org)
|
||||
* @author Hern<EFBFBD>n Chanfreau
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
public class OneToOneNotOwningMapper implements PropertyMapper {
|
||||
private String owningReferencePropertyName;
|
||||
private String owningEntityName;
|
||||
private PropertyData propertyData;
|
||||
public class OneToOneNotOwningMapper extends AbstractOneToOneMapper {
|
||||
private final String owningReferencePropertyName;
|
||||
|
||||
public OneToOneNotOwningMapper(String owningReferencePropertyName, String owningEntityName,
|
||||
public OneToOneNotOwningMapper(String notOwningEntityName, String owningEntityName, String owningReferencePropertyName,
|
||||
PropertyData propertyData) {
|
||||
super(notOwningEntityName, owningEntityName, propertyData);
|
||||
this.owningReferencePropertyName = owningReferencePropertyName;
|
||||
this.owningEntityName = owningEntityName;
|
||||
this.propertyData = propertyData;
|
||||
}
|
||||
|
||||
public boolean mapToMapFromEntity(SessionImplementor session, Map<String, Object> data, Object newObj, Object oldObj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void mapToEntityFromMap(AuditConfiguration verCfg, Object obj, Map data, Object primaryKey, AuditReaderImplementor versionsReader, Number revision) {
|
||||
if (obj == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
EntityConfiguration entCfg = verCfg.getEntCfg().get(owningEntityName);
|
||||
if(entCfg == null) {
|
||||
// a relation marked as RelationTargetAuditMode.NOT_AUDITED
|
||||
entCfg = verCfg.getEntCfg().getNotVersionEntityConfiguration(owningEntityName);
|
||||
}
|
||||
|
||||
Class<?> entityClass = ReflectionTools.loadClass(entCfg.getEntityClassName());
|
||||
|
||||
Object value;
|
||||
|
||||
try {
|
||||
value = versionsReader.createQuery().forEntitiesAtRevision(entityClass, owningEntityName, revision)
|
||||
.add(AuditEntity.relatedId(owningReferencePropertyName).eq(primaryKey)).getSingleResult();
|
||||
} catch (NoResultException e) {
|
||||
value = null;
|
||||
} catch (NonUniqueResultException e) {
|
||||
throw new AuditException("Many versions results for one-to-one relationship: (" + owningEntityName +
|
||||
", " + owningReferencePropertyName + ")");
|
||||
}
|
||||
|
||||
Setter setter = ReflectionTools.getSetter(obj.getClass(), propertyData);
|
||||
setter.set(obj, value, null);
|
||||
}
|
||||
|
||||
public List<PersistentCollectionChangeData> mapCollectionChanges(String referencingPropertyName,
|
||||
PersistentCollection newColl,
|
||||
Serializable oldColl,
|
||||
Serializable id) {
|
||||
return null;
|
||||
@Override
|
||||
protected Object queryForReferencedEntity(AuditReaderImplementor versionsReader, EntityInfo referencedEntity,
|
||||
Serializable primaryKey, Number revision) {
|
||||
return versionsReader.createQuery().forEntitiesAtRevision(referencedEntity.getEntityClass(),
|
||||
referencedEntity.getEntityName(), revision)
|
||||
.add(AuditEntity.relatedId(owningReferencePropertyName).eq(primaryKey))
|
||||
.getSingleResult();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
package org.hibernate.envers.entities.mapper.relation;
|
||||
|
||||
import org.hibernate.envers.Audited;
|
||||
import org.hibernate.envers.entities.PropertyData;
|
||||
import org.hibernate.envers.query.AuditEntity;
|
||||
import org.hibernate.envers.reader.AuditReaderImplementor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.PrimaryKeyJoinColumn;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Property mapper for {@link OneToOne} with {@link PrimaryKeyJoinColumn} relation.
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
public class OneToOnePrimaryKeyJoinColumnMapper extends AbstractOneToOneMapper {
|
||||
public OneToOnePrimaryKeyJoinColumnMapper(String entityName, String referencedEntityName, PropertyData propertyData) {
|
||||
super(entityName, referencedEntityName, propertyData);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object queryForReferencedEntity(AuditReaderImplementor versionsReader, EntityInfo referencedEntity,
|
||||
Serializable primaryKey, Number revision) {
|
||||
if (referencedEntity.isAudited()) {
|
||||
// Audited relation.
|
||||
return versionsReader.createQuery().forEntitiesAtRevision(referencedEntity.getEntityClass(),
|
||||
referencedEntity.getEntityName(), revision)
|
||||
.add(AuditEntity.id().eq(primaryKey))
|
||||
.getSingleResult();
|
||||
} else {
|
||||
// Not audited relation.
|
||||
return createNotAuditedEntityReference(versionsReader, referencedEntity.getEntityClass(),
|
||||
referencedEntity.getEntityName(), primaryKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Hibernate proxy or retrieve the complete object of referenced, not audited entity. According to
|
||||
* {@link Audited#targetAuditMode()}} documentation, reference shall point to current (non-historical) version
|
||||
* of an entity.
|
||||
*/
|
||||
private Object createNotAuditedEntityReference(AuditReaderImplementor versionsReader, Class<?> entityClass,
|
||||
String entityName, Serializable primaryKey) {
|
||||
EntityPersister entityPersister = versionsReader.getSessionImplementor().getFactory().getEntityPersister(entityName);
|
||||
if (entityPersister.hasProxy()) {
|
||||
// If possible create a proxy. Returning complete object may affect performance.
|
||||
return versionsReader.getSession().load(entityClass, primaryKey);
|
||||
} else {
|
||||
// If proxy is not allowed (e.g. @Proxy(lazy=false)) construct the original object.
|
||||
return versionsReader.getSession().get(entityClass, primaryKey);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,36 +25,28 @@ package org.hibernate.envers.entities.mapper.relation;
|
|||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.collection.spi.PersistentCollection;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.envers.configuration.AuditConfiguration;
|
||||
import org.hibernate.envers.entities.EntityConfiguration;
|
||||
import org.hibernate.envers.entities.PropertyData;
|
||||
import org.hibernate.envers.entities.mapper.PersistentCollectionChangeData;
|
||||
import org.hibernate.envers.entities.mapper.PropertyMapper;
|
||||
import org.hibernate.envers.entities.mapper.id.IdMapper;
|
||||
import org.hibernate.envers.entities.mapper.relation.lazy.ToOneDelegateSessionImplementor;
|
||||
import org.hibernate.envers.reader.AuditReaderImplementor;
|
||||
import org.hibernate.envers.tools.Tools;
|
||||
import org.hibernate.envers.tools.reflection.ReflectionTools;
|
||||
import org.hibernate.property.Setter;
|
||||
|
||||
/**
|
||||
* @author Adam Warski (adam at warski dot org)
|
||||
* @author Hern<EFBFBD>n Chanfreau
|
||||
*/
|
||||
public class ToOneIdMapper implements PropertyMapper {
|
||||
public class ToOneIdMapper extends AbstractToOneMapper {
|
||||
private final IdMapper delegate;
|
||||
private final PropertyData propertyData;
|
||||
private final String referencedEntityName;
|
||||
private final boolean nonInsertableFake;
|
||||
|
||||
public ToOneIdMapper(IdMapper delegate, PropertyData propertyData, String referencedEntityName, boolean nonInsertableFake) {
|
||||
super(propertyData);
|
||||
this.delegate = delegate;
|
||||
this.propertyData = propertyData;
|
||||
this.referencedEntityName = referencedEntityName;
|
||||
this.nonInsertableFake = nonInsertableFake;
|
||||
}
|
||||
|
@ -75,42 +67,22 @@ public class ToOneIdMapper implements PropertyMapper {
|
|||
return nonInsertableFake ? false : !Tools.entitiesEqual(session, referencedEntityName, newObj, oldObj);
|
||||
}
|
||||
|
||||
public void mapToEntityFromMap(AuditConfiguration verCfg, Object obj, Map data, Object primaryKey,
|
||||
AuditReaderImplementor versionsReader, Number revision) {
|
||||
if (obj == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object entityId;
|
||||
entityId = delegate.mapToIdFromMap(data);
|
||||
Object value;
|
||||
if (entityId == null) {
|
||||
value = null;
|
||||
} else {
|
||||
public void nullSafeMapToEntityFromMap(AuditConfiguration verCfg, Object obj, Map data, Object primaryKey,
|
||||
AuditReaderImplementor versionsReader, Number revision) {
|
||||
Object entityId = delegate.mapToIdFromMap(data);
|
||||
Object value = null;
|
||||
if (entityId != null) {
|
||||
if (versionsReader.getFirstLevelCache().contains(referencedEntityName, revision, entityId)) {
|
||||
value = versionsReader.getFirstLevelCache().get(referencedEntityName, revision, entityId);
|
||||
} else {
|
||||
EntityConfiguration entCfg = verCfg.getEntCfg().get(referencedEntityName);
|
||||
if(entCfg == null) {
|
||||
// a relation marked as RelationTargetAuditMode.NOT_AUDITED
|
||||
entCfg = verCfg.getEntCfg().getNotVersionEntityConfiguration(referencedEntityName);
|
||||
}
|
||||
|
||||
Class<?> entityClass = ReflectionTools.loadClass(entCfg.getEntityClassName());
|
||||
EntityInfo referencedEntity = getEntityInfo(verCfg, referencedEntityName);
|
||||
|
||||
value = versionsReader.getSessionImplementor().getFactory().getEntityPersister(referencedEntityName).
|
||||
createProxy((Serializable)entityId, new ToOneDelegateSessionImplementor(versionsReader, entityClass, entityId, revision, verCfg));
|
||||
createProxy((Serializable)entityId, new ToOneDelegateSessionImplementor(versionsReader, referencedEntity.getEntityClass(),
|
||||
entityId, revision, verCfg));
|
||||
}
|
||||
}
|
||||
|
||||
Setter setter = ReflectionTools.getSetter(obj.getClass(), propertyData);
|
||||
setter.set(obj, value, null);
|
||||
}
|
||||
|
||||
public List<PersistentCollectionChangeData> mapCollectionChanges(String referencingPropertyName,
|
||||
PersistentCollection newColl,
|
||||
Serializable oldColl,
|
||||
Serializable id) {
|
||||
return null;
|
||||
setPropertyValue(obj, value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
package org.hibernate.envers.test.integration.onetoone.bidirectional.primarykeyjoincolumn;
|
||||
|
||||
import org.hibernate.annotations.Proxy;
|
||||
import org.hibernate.envers.Audited;
|
||||
import org.hibernate.envers.RelationTargetAuditMode;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
@Entity
|
||||
@Audited
|
||||
public class Account implements Serializable {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long accountId;
|
||||
|
||||
private String type;
|
||||
|
||||
@OneToOne(optional = false)
|
||||
@PrimaryKeyJoinColumn
|
||||
private Person owner;
|
||||
|
||||
public Account() {
|
||||
}
|
||||
|
||||
public Account(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Account(Long accountId, String type) {
|
||||
this.accountId = accountId;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof Account)) return false;
|
||||
|
||||
Account account = (Account) o;
|
||||
|
||||
if (accountId != null ? !accountId.equals(account.accountId) : account.accountId != null) return false;
|
||||
if (type != null ? !type.equals(account.type) : account.type != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = accountId != null ? accountId.hashCode() : 0;
|
||||
result = 31 * result + (type != null ? type.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Account(accountId = " + accountId + ", type = " + type + ")";
|
||||
}
|
||||
|
||||
public Long getAccountId() {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
public void setAccountId(Long accountId) {
|
||||
this.accountId = accountId;
|
||||
}
|
||||
|
||||
public Person getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(Person owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
package org.hibernate.envers.test.integration.onetoone.bidirectional.primarykeyjoincolumn;
|
||||
|
||||
import org.hibernate.envers.Audited;
|
||||
import org.hibernate.envers.RelationTargetAuditMode;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
@Entity
|
||||
@Audited
|
||||
public class AccountNotAuditedOwners implements Serializable {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long accountId;
|
||||
|
||||
private String type;
|
||||
|
||||
@OneToOne(mappedBy = "account", optional = false)
|
||||
@Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
|
||||
private NotAuditedNoProxyPerson owner;
|
||||
|
||||
@OneToOne(mappedBy = "account", optional = false, fetch = FetchType.LAZY)
|
||||
@Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
|
||||
private NotAuditedProxyPerson coOwner;
|
||||
|
||||
public AccountNotAuditedOwners() {
|
||||
}
|
||||
|
||||
public AccountNotAuditedOwners(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public AccountNotAuditedOwners(Long accountId, String type) {
|
||||
this.accountId = accountId;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof AccountNotAuditedOwners)) return false;
|
||||
|
||||
AccountNotAuditedOwners account = (AccountNotAuditedOwners) o;
|
||||
|
||||
if (accountId != null ? !accountId.equals(account.accountId) : account.accountId != null) return false;
|
||||
if (type != null ? !type.equals(account.type) : account.type != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = accountId != null ? accountId.hashCode() : 0;
|
||||
result = 31 * result + (type != null ? type.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AccountNotAuditedOwners(accountId = " + accountId + ", type = " + type + ")";
|
||||
}
|
||||
|
||||
public Long getAccountId() {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
public void setAccountId(Long accountId) {
|
||||
this.accountId = accountId;
|
||||
}
|
||||
|
||||
public NotAuditedNoProxyPerson getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(NotAuditedNoProxyPerson owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public NotAuditedProxyPerson getCoOwner() {
|
||||
return coOwner;
|
||||
}
|
||||
|
||||
public void setCoOwner(NotAuditedProxyPerson coOwner) {
|
||||
this.coOwner = coOwner;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package org.hibernate.envers.test.integration.onetoone.bidirectional.primarykeyjoincolumn;
|
||||
|
||||
import org.hibernate.annotations.Proxy;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
@Entity
|
||||
@Proxy(lazy = false)
|
||||
public class NotAuditedNoProxyPerson implements Serializable {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long personId;
|
||||
|
||||
private String name;
|
||||
|
||||
@OneToOne(optional = false)
|
||||
@PrimaryKeyJoinColumn
|
||||
private AccountNotAuditedOwners account;
|
||||
|
||||
public NotAuditedNoProxyPerson() {
|
||||
}
|
||||
|
||||
public NotAuditedNoProxyPerson(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public NotAuditedNoProxyPerson(Long personId, String name) {
|
||||
this.personId = personId;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof NotAuditedNoProxyPerson)) return false;
|
||||
|
||||
NotAuditedNoProxyPerson person = (NotAuditedNoProxyPerson) o;
|
||||
|
||||
if (personId != null ? !personId.equals(person.personId) : person.personId != null) return false;
|
||||
if (name != null ? !name.equals(person.name) : person.name != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = personId != null ? personId.hashCode() : 0;
|
||||
result = 31 * result + (name != null ? name.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NotAuditedNoProxyPerson(personId = " + personId + ", name = " + name + ")";
|
||||
}
|
||||
|
||||
public Long getPersonId() {
|
||||
return personId;
|
||||
}
|
||||
|
||||
public void setPersonId(Long personId) {
|
||||
this.personId = personId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public AccountNotAuditedOwners getAccount() {
|
||||
return account;
|
||||
}
|
||||
|
||||
public void setAccount(AccountNotAuditedOwners account) {
|
||||
this.account = account;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package org.hibernate.envers.test.integration.onetoone.bidirectional.primarykeyjoincolumn;
|
||||
|
||||
import org.hibernate.annotations.Proxy;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
@Entity
|
||||
@Proxy(lazy = true)
|
||||
public class NotAuditedProxyPerson implements Serializable {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long personId;
|
||||
|
||||
private String name;
|
||||
|
||||
@OneToOne(optional = false, fetch = FetchType.LAZY)
|
||||
@PrimaryKeyJoinColumn
|
||||
private AccountNotAuditedOwners account;
|
||||
|
||||
public NotAuditedProxyPerson() {
|
||||
}
|
||||
|
||||
public NotAuditedProxyPerson(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public NotAuditedProxyPerson(Long personId, String name) {
|
||||
this.personId = personId;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof NotAuditedProxyPerson)) return false;
|
||||
|
||||
NotAuditedProxyPerson person = (NotAuditedProxyPerson) o;
|
||||
|
||||
if (personId != null ? !personId.equals(person.personId) : person.personId != null) return false;
|
||||
if (name != null ? !name.equals(person.name) : person.name != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = personId != null ? personId.hashCode() : 0;
|
||||
result = 31 * result + (name != null ? name.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NotAuditedProxyPerson(personId = " + personId + ", name = " + name + ")";
|
||||
}
|
||||
|
||||
public Long getPersonId() {
|
||||
return personId;
|
||||
}
|
||||
|
||||
public void setPersonId(Long personId) {
|
||||
this.personId = personId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public AccountNotAuditedOwners getAccount() {
|
||||
return account;
|
||||
}
|
||||
|
||||
public void setAccount(AccountNotAuditedOwners account) {
|
||||
this.account = account;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
package org.hibernate.envers.test.integration.onetoone.bidirectional.primarykeyjoincolumn;
|
||||
|
||||
import org.hibernate.ejb.Ejb3Configuration;
|
||||
import org.hibernate.envers.RevisionType;
|
||||
import org.hibernate.envers.query.AuditEntity;
|
||||
import org.hibernate.envers.test.AbstractEntityTest;
|
||||
import org.hibernate.envers.test.Priority;
|
||||
import org.hibernate.proxy.HibernateProxy;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-6825")
|
||||
public class OneToOneWithPrimaryKeyJoinTest extends AbstractEntityTest {
|
||||
private Long personId = null;
|
||||
private Long accountId = null;
|
||||
private Long proxyPersonId = null;
|
||||
private Long noProxyPersonId = null;
|
||||
private Long accountNotAuditedOwnersId = null;
|
||||
|
||||
public void configure(Ejb3Configuration cfg) {
|
||||
cfg.addAnnotatedClass(Person.class);
|
||||
cfg.addAnnotatedClass(Account.class);
|
||||
cfg.addAnnotatedClass(AccountNotAuditedOwners.class);
|
||||
cfg.addAnnotatedClass(NotAuditedNoProxyPerson.class);
|
||||
cfg.addAnnotatedClass(NotAuditedProxyPerson.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Priority(10)
|
||||
public void initData() {
|
||||
EntityManager em = getEntityManager();
|
||||
|
||||
// Revision 1
|
||||
em.getTransaction().begin();
|
||||
Person person = new Person("Robert");
|
||||
Account account = new Account("Saving");
|
||||
person.setAccount(account);
|
||||
account.setOwner(person);
|
||||
em.persist(person);
|
||||
em.persist(account);
|
||||
em.getTransaction().commit();
|
||||
|
||||
// Revision 2
|
||||
em.getTransaction().begin();
|
||||
NotAuditedNoProxyPerson noProxyPerson = new NotAuditedNoProxyPerson("Kinga");
|
||||
NotAuditedProxyPerson proxyPerson = new NotAuditedProxyPerson("Lukasz");
|
||||
AccountNotAuditedOwners accountNotAuditedOwners = new AccountNotAuditedOwners("Standard");
|
||||
noProxyPerson.setAccount(accountNotAuditedOwners);
|
||||
proxyPerson.setAccount(accountNotAuditedOwners);
|
||||
accountNotAuditedOwners.setOwner(noProxyPerson);
|
||||
accountNotAuditedOwners.setCoOwner(proxyPerson);
|
||||
em.persist(accountNotAuditedOwners);
|
||||
em.persist(noProxyPerson);
|
||||
em.persist(proxyPerson);
|
||||
em.getTransaction().commit();
|
||||
|
||||
personId = person.getPersonId();
|
||||
accountId = account.getAccountId();
|
||||
accountNotAuditedOwnersId = accountNotAuditedOwners.getAccountId();
|
||||
proxyPersonId = proxyPerson.getPersonId();
|
||||
noProxyPersonId = noProxyPerson.getPersonId();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRevisionsCounts() {
|
||||
Assert.assertEquals(Arrays.asList(1), getAuditReader().getRevisions(Person.class, personId));
|
||||
Assert.assertEquals(Arrays.asList(1), getAuditReader().getRevisions(Account.class, accountId));
|
||||
Assert.assertEquals(Arrays.asList(2), getAuditReader().getRevisions(AccountNotAuditedOwners.class, accountNotAuditedOwnersId));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHistoryOfPerson() {
|
||||
Person personVer1 = new Person(personId, "Robert");
|
||||
Account accountVer1 = new Account(accountId, "Saving");
|
||||
personVer1.setAccount(accountVer1);
|
||||
accountVer1.setOwner(personVer1);
|
||||
|
||||
Object[] result = ((Object[]) getAuditReader().createQuery().forRevisionsOfEntity(Person.class, false, true)
|
||||
.add(AuditEntity.id().eq(personId))
|
||||
.getResultList().get(0));
|
||||
|
||||
Assert.assertEquals(personVer1, result[0]);
|
||||
Assert.assertEquals(personVer1.getAccount(), ((Person)result[0]).getAccount());
|
||||
Assert.assertEquals(RevisionType.ADD, result[2]);
|
||||
|
||||
Assert.assertEquals(personVer1, getAuditReader().find(Person.class, personId, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHistoryOfAccount() {
|
||||
Person personVer1 = new Person(personId, "Robert");
|
||||
Account accountVer1 = new Account(accountId, "Saving");
|
||||
personVer1.setAccount(accountVer1);
|
||||
accountVer1.setOwner(personVer1);
|
||||
|
||||
Object[] result = ((Object[]) getAuditReader().createQuery().forRevisionsOfEntity(Account.class, false, true)
|
||||
.add(AuditEntity.id().eq(accountId))
|
||||
.getResultList().get(0));
|
||||
|
||||
Assert.assertEquals(accountVer1, result[0]);
|
||||
Assert.assertEquals(accountVer1.getOwner(), ((Account)result[0]).getOwner());
|
||||
Assert.assertEquals(RevisionType.ADD, result[2]);
|
||||
|
||||
Assert.assertEquals(accountVer1, getAuditReader().find(Account.class, accountId, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHistoryOfAccountNotAuditedOwners() {
|
||||
NotAuditedNoProxyPerson noProxyPersonVer1 = new NotAuditedNoProxyPerson(noProxyPersonId, "Kinga");
|
||||
NotAuditedProxyPerson proxyPersonVer1 = new NotAuditedProxyPerson(proxyPersonId, "Lukasz");
|
||||
AccountNotAuditedOwners accountNotAuditedOwnersVer1 = new AccountNotAuditedOwners(accountNotAuditedOwnersId, "Standard");
|
||||
noProxyPersonVer1.setAccount(accountNotAuditedOwnersVer1);
|
||||
proxyPersonVer1.setAccount(accountNotAuditedOwnersVer1);
|
||||
accountNotAuditedOwnersVer1.setOwner(noProxyPersonVer1);
|
||||
accountNotAuditedOwnersVer1.setCoOwner(proxyPersonVer1);
|
||||
|
||||
Object[] result = ((Object[]) getAuditReader().createQuery().forRevisionsOfEntity(AccountNotAuditedOwners.class, false, true)
|
||||
.add(AuditEntity.id().eq(accountNotAuditedOwnersId))
|
||||
.getResultList().get(0));
|
||||
|
||||
Assert.assertEquals(accountNotAuditedOwnersVer1, result[0]);
|
||||
Assert.assertEquals(RevisionType.ADD, result[2]);
|
||||
// Checking non-proxy reference
|
||||
Assert.assertEquals(accountNotAuditedOwnersVer1.getOwner(), ((AccountNotAuditedOwners)result[0]).getOwner());
|
||||
// Checking proxy reference
|
||||
Assert.assertTrue(((AccountNotAuditedOwners)result[0]).getCoOwner() instanceof HibernateProxy);
|
||||
Assert.assertEquals(proxyPersonVer1.getPersonId(), ((AccountNotAuditedOwners)result[0]).getCoOwner().getPersonId());
|
||||
|
||||
Assert.assertEquals(accountNotAuditedOwnersVer1, getAuditReader().find(AccountNotAuditedOwners.class, accountNotAuditedOwnersId, 2));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
package org.hibernate.envers.test.integration.onetoone.bidirectional.primarykeyjoincolumn;
|
||||
|
||||
import org.hibernate.annotations.Proxy;
|
||||
import org.hibernate.envers.Audited;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToOne;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
@Entity
|
||||
@Audited
|
||||
public class Person implements Serializable {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long personId;
|
||||
|
||||
private String name;
|
||||
|
||||
@OneToOne(mappedBy = "owner")
|
||||
private Account account;
|
||||
|
||||
public Person() {
|
||||
}
|
||||
|
||||
public Person(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Person(Long personId, String name) {
|
||||
this.personId = personId;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof Person)) return false;
|
||||
|
||||
Person person = (Person) o;
|
||||
|
||||
if (personId != null ? !personId.equals(person.personId) : person.personId != null) return false;
|
||||
if (name != null ? !name.equals(person.name) : person.name != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = personId != null ? personId.hashCode() : 0;
|
||||
result = 31 * result + (name != null ? name.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Person(personId = " + personId + ", name = " + name + ")";
|
||||
}
|
||||
|
||||
public Long getPersonId() {
|
||||
return personId;
|
||||
}
|
||||
|
||||
public void setPersonId(Long personId) {
|
||||
this.personId = personId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Account getAccount() {
|
||||
return account;
|
||||
}
|
||||
|
||||
public void setAccount(Account account) {
|
||||
this.account = account;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue