HHH-4090:

- adding support for many-to-many relations from an audited, to a non-audited entity

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@18201 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Adam Warski 2009-12-10 20:28:27 +00:00
parent e183c40ee7
commit 04fae574ae
13 changed files with 493 additions and 75 deletions

View File

@ -40,6 +40,7 @@ import org.hibernate.envers.entities.mapper.MultiPropertyMapper;
import org.hibernate.envers.entities.mapper.SubclassPropertyMapper; import org.hibernate.envers.entities.mapper.SubclassPropertyMapper;
import org.hibernate.envers.tools.StringTools; import org.hibernate.envers.tools.StringTools;
import org.hibernate.envers.tools.Triple; import org.hibernate.envers.tools.Triple;
import org.hibernate.envers.RelationTargetAuditMode;
import org.hibernate.MappingException; import org.hibernate.MappingException;
import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Configuration;
@ -348,7 +349,7 @@ public final class AuditMetadataGenerator {
String parentEntityName = null; String parentEntityName = null;
EntityConfiguration entityCfg = new EntityConfiguration(entityName, idMapper, propertyMapper, EntityConfiguration entityCfg = new EntityConfiguration(entityName, idMapper, propertyMapper,
parentEntityName); parentEntityName);
notAuditedEntitiesConfigurations.put(pc.getEntityName(), entityCfg); notAuditedEntitiesConfigurations.put(entityName, entityCfg);
return; return;
} }
@ -476,6 +477,36 @@ public final class AuditMetadataGenerator {
throw new MappingException(message); throw new MappingException(message);
} }
/**
* Reads the id mapping data of a referenced entity.
* @param entityName Name of the entity which is the source of the relation.
* @param referencedEntityName Name of the entity which is the target of the relation.
* @param propertyAuditingData Auditing data of the property that is the source of the relation.
* @param allowNotAuditedTarget Are not-audited target entities allowed.
* @throws MappingException If a relation from an audited to a non-audited entity is detected, which is not
* mapped using {@link RelationTargetAuditMode#NOT_AUDITED}.
* @return The id mapping data of the related entity.
*/
IdMappingData getReferencedIdMappingData(String entityName, String referencedEntityName,
PropertyAuditingData propertyAuditingData,
boolean allowNotAuditedTarget) {
EntityConfiguration configuration = getEntitiesConfigurations().get(referencedEntityName);
if (configuration == null) {
RelationTargetAuditMode relationTargetAuditMode = propertyAuditingData.getRelationTargetAuditMode();
configuration = getNotAuditedEntitiesConfigurations().get(referencedEntityName);
if (configuration == null || !allowNotAuditedTarget || !RelationTargetAuditMode.NOT_AUDITED.equals(relationTargetAuditMode)) {
throw new MappingException("An audited relation from " + entityName + "."
+ propertyAuditingData.getName() + " to a not audited entity " + referencedEntityName + "!"
+ (allowNotAuditedTarget ?
" Such mapping is possible, but has to be explicitly defined using @Audited(targetAuditMode = NOT_AUDITED)." :
""));
}
}
return configuration.getIdMappingData();
}
/** /**
* Get the notAuditedEntitiesConfigurations property. * Get the notAuditedEntitiesConfigurations property.
* *

View File

@ -155,28 +155,26 @@ public final class CollectionMetadataGenerator {
} }
} }
private MiddleIdData createMiddleIdData(IdMappingData idMappingData, String prefix, String entityName) {
return new MiddleIdData(mainGenerator.getVerEntCfg(), idMappingData, prefix, entityName,
mainGenerator.getEntitiesConfigurations().containsKey(entityName));
}
@SuppressWarnings({"unchecked"}) @SuppressWarnings({"unchecked"})
private void addOneToManyAttached() { private void addOneToManyAttached() {
String mappedBy = getMappedBy(propertyValue); String mappedBy = getMappedBy(propertyValue);
EntityConfiguration referencedEntityConfiguration = mainGenerator.getEntitiesConfigurations() IdMappingData referencedIdMapping = mainGenerator.getReferencedIdMappingData(referencingEntityName,
.get(referencedEntityName); referencedEntityName, propertyAuditingData, false);
if (referencedEntityConfiguration == null) {
throwRelationNotAudited(referencedEntityName);
// Impossible to get here.
throw new AssertionError();
}
IdMappingData referencedIdMapping = referencedEntityConfiguration.getIdMappingData();
IdMappingData referencingIdMapping = referencingEntityConfiguration.getIdMappingData(); IdMappingData referencingIdMapping = referencingEntityConfiguration.getIdMappingData();
// Generating the id mappers data for the referencing side of the relation. // Generating the id mappers data for the referencing side of the relation.
MiddleIdData referencingIdData = new MiddleIdData(mainGenerator.getVerEntCfg(), referencingIdMapping, MiddleIdData referencingIdData = createMiddleIdData(referencingIdMapping,
mappedBy + "_", referencingEntityName); mappedBy + "_", referencingEntityName);
// And for the referenced side. The prefixed mapper won't be used (as this collection isn't persisted // And for the referenced side. The prefixed mapper won't be used (as this collection isn't persisted
// in a join table, so the prefix value is arbitrary). // in a join table, so the prefix value is arbitrary).
MiddleIdData referencedIdData = new MiddleIdData(mainGenerator.getVerEntCfg(), referencedIdMapping, MiddleIdData referencedIdData = createMiddleIdData(referencedIdMapping,
null, referencedEntityName); null, referencedEntityName);
// Generating the element mapping. // Generating the element mapping.
@ -293,7 +291,7 @@ public final class CollectionMetadataGenerator {
} }
// Storing the id data of the referencing entity: original mapper, prefixed mapper and entity name. // Storing the id data of the referencing entity: original mapper, prefixed mapper and entity name.
MiddleIdData referencingIdData = new MiddleIdData(mainGenerator.getVerEntCfg(), referencingIdMapping, MiddleIdData referencingIdData = createMiddleIdData(referencingIdMapping,
referencingPrefixRelated, referencingEntityName); referencingPrefixRelated, referencingEntityName);
// Creating a query generator builder, to which additional id data will be added, in case this collection // Creating a query generator builder, to which additional id data will be added, in case this collection
@ -390,14 +388,9 @@ public final class CollectionMetadataGenerator {
String prefixRelated = prefix + "_"; String prefixRelated = prefix + "_";
String referencedEntityName = getReferencedEntityName(value); String referencedEntityName = getReferencedEntityName(value);
EntityConfiguration referencedEntityConfiguration = mainGenerator.getEntitiesConfigurations()
.get(referencedEntityName); IdMappingData referencedIdMapping = mainGenerator.getReferencedIdMappingData(referencingEntityName,
if (referencedEntityConfiguration == null) { referencedEntityName, propertyAuditingData, true);
throwRelationNotAudited(referencedEntityName);
// Impossible to get here.
throw new AssertionError();
}
IdMappingData referencedIdMapping = referencedEntityConfiguration.getIdMappingData();
// Adding related-entity (in this case: the referenced entities id) id mapping to the xml only if the // Adding related-entity (in this case: the referenced entities id) id mapping to the xml only if the
// relation isn't inverse (so when <code>xmlMapping</code> is not null). // relation isn't inverse (so when <code>xmlMapping</code> is not null).
@ -410,7 +403,7 @@ public final class CollectionMetadataGenerator {
} }
// Storing the id data of the referenced entity: original mapper, prefixed mapper and entity name. // Storing the id data of the referenced entity: original mapper, prefixed mapper and entity name.
MiddleIdData referencedIdData = new MiddleIdData(mainGenerator.getVerEntCfg(), referencedIdMapping, MiddleIdData referencedIdData = createMiddleIdData(referencedIdMapping,
prefixRelated, referencedEntityName); prefixRelated, referencedEntityName);
// And adding it to the generator builder. // And adding it to the generator builder.
queryGeneratorBuilder.addRelation(referencedIdData); queryGeneratorBuilder.addRelation(referencedIdData);
@ -541,9 +534,4 @@ public final class CollectionMetadataGenerator {
throw new MappingException("Unable to read the mapped by attribute for " + propertyName + " in " throw new MappingException("Unable to read the mapped by attribute for " + propertyName + " in "
+ referencingEntityName + "!"); + referencingEntityName + "!");
} }
private void throwRelationNotAudited(String referencedEntityName) {
throw new MappingException("An audited relation from " + referencingEntityName +
" to a non-audited entity: " + referencedEntityName);
}
} }

View File

@ -30,10 +30,8 @@ import org.hibernate.envers.configuration.GlobalConfiguration;
import org.hibernate.envers.configuration.AuditEntitiesConfiguration; import org.hibernate.envers.configuration.AuditEntitiesConfiguration;
import org.hibernate.envers.entities.mapper.relation.MiddleComponentData; import org.hibernate.envers.entities.mapper.relation.MiddleComponentData;
import org.hibernate.envers.entities.mapper.relation.MiddleIdData; import org.hibernate.envers.entities.mapper.relation.MiddleIdData;
import org.hibernate.envers.entities.mapper.relation.query.OneEntityQueryGenerator; import org.hibernate.envers.entities.mapper.relation.query.*;
import org.hibernate.envers.entities.mapper.relation.query.RelationQueryGenerator; import org.hibernate.MappingException;
import org.hibernate.envers.entities.mapper.relation.query.ThreeEntityQueryGenerator;
import org.hibernate.envers.entities.mapper.relation.query.TwoEntityQueryGenerator;
/** /**
* Builds query generators, for reading collection middle tables, along with any related entities. * Builds query generators, for reading collection middle tables, along with any related entities.
@ -66,9 +64,19 @@ public final class QueryGeneratorBuilder {
return new OneEntityQueryGenerator(verEntCfg, auditMiddleEntityName, referencingIdData, return new OneEntityQueryGenerator(verEntCfg, auditMiddleEntityName, referencingIdData,
componentDatas); componentDatas);
} else if (idDatas.size() == 1) { } else if (idDatas.size() == 1) {
if (idDatas.get(0).isAudited()) {
return new TwoEntityQueryGenerator(globalCfg, verEntCfg, auditMiddleEntityName, referencingIdData, return new TwoEntityQueryGenerator(globalCfg, verEntCfg, auditMiddleEntityName, referencingIdData,
idDatas.get(0), componentDatas); idDatas.get(0), componentDatas);
} else {
return new TwoEntityOneAuditedQueryGenerator(verEntCfg, auditMiddleEntityName, referencingIdData,
idDatas.get(0), componentDatas);
}
} else if (idDatas.size() == 2) { } else if (idDatas.size() == 2) {
// All entities must be audited.
if (!idDatas.get(0).isAudited() || !idDatas.get(1).isAudited()) {
throw new MappingException("Ternary relations using @Audited(targetAuditMode = NOT_AUDITED) are not supported.");
}
return new ThreeEntityQueryGenerator(globalCfg, verEntCfg, auditMiddleEntityName, referencingIdData, return new ThreeEntityQueryGenerator(globalCfg, verEntCfg, auditMiddleEntityName, referencingIdData,
idDatas.get(0), idDatas.get(1), componentDatas); idDatas.get(0), idDatas.get(1), componentDatas);
} else { } else {

View File

@ -24,7 +24,6 @@
package org.hibernate.envers.configuration.metadata; package org.hibernate.envers.configuration.metadata;
import org.dom4j.Element; import org.dom4j.Element;
import org.hibernate.envers.RelationTargetAuditMode;
import org.hibernate.envers.entities.EntityConfiguration; import org.hibernate.envers.entities.EntityConfiguration;
import org.hibernate.envers.entities.IdMappingData; import org.hibernate.envers.entities.IdMappingData;
import org.hibernate.envers.entities.PropertyData; import org.hibernate.envers.entities.PropertyData;
@ -55,24 +54,8 @@ public final class ToOneRelationMetadataGenerator {
CompositeMapperBuilder mapper, String entityName, boolean insertable) { CompositeMapperBuilder mapper, String entityName, boolean insertable) {
String referencedEntityName = ((ToOne) value).getReferencedEntityName(); String referencedEntityName = ((ToOne) value).getReferencedEntityName();
EntityConfiguration configuration = mainGenerator.getEntitiesConfigurations().get(referencedEntityName); IdMappingData idMapping = mainGenerator.getReferencedIdMappingData(entityName, referencedEntityName,
if (configuration == null) { propertyAuditingData, true);
configuration = mainGenerator.getNotAuditedEntitiesConfigurations().get(referencedEntityName);
if (configuration != null) {
RelationTargetAuditMode relationTargetAuditMode = propertyAuditingData.getRelationTargetAuditMode();
if (!RelationTargetAuditMode.NOT_AUDITED.equals(relationTargetAuditMode)) {
throw new MappingException("An audited relation from " + entityName + "."
+ propertyAuditingData.getName() + " to a not audited entity " + referencedEntityName + "!"
+ ". Such mapping is possible, but has to be strictly defined using RelationTargetAuditMode.NOT_AUDITED in @Audited.");
}
}
}
if (configuration == null) {
throw new MappingException("An audited relation from " + entityName + "."
+ propertyAuditingData.getName() + " to a not audited entity " + referencedEntityName + "!");
}
IdMappingData idMapping = configuration.getIdMappingData();
String lastPropertyPrefix = propertyAuditingData.getName() + "_"; String lastPropertyPrefix = propertyAuditingData.getName() + "_";

View File

@ -33,44 +33,51 @@ import org.hibernate.envers.entities.mapper.id.IdMapper;
* @author Adam Warski (adam at warski dot org) * @author Adam Warski (adam at warski dot org)
*/ */
public final class MiddleIdData { public final class MiddleIdData {
/**
* Original id mapper of the related entity.
*/
private final IdMapper originalMapper; private final IdMapper originalMapper;
/**
* Prefixed id mapper (with the names for the id fields that are used in the middle table) of the related entity.
*/
private final IdMapper prefixedMapper; private final IdMapper prefixedMapper;
/**
* Name of the related entity.
*/
private final String entityName; private final String entityName;
/** private final String auditEntityName;
* Versions name of the related entity.
*/
private final String versionsEntityName;
public MiddleIdData(AuditEntitiesConfiguration verEntCfg, IdMappingData mappingData, String prefix, public MiddleIdData(AuditEntitiesConfiguration verEntCfg, IdMappingData mappingData, String prefix,
String entityName) { String entityName, boolean audited) {
this.originalMapper = mappingData.getIdMapper(); this.originalMapper = mappingData.getIdMapper();
this.prefixedMapper = mappingData.getIdMapper().prefixMappedProperties(prefix); this.prefixedMapper = mappingData.getIdMapper().prefixMappedProperties(prefix);
this.entityName = entityName; this.entityName = entityName;
this.versionsEntityName = verEntCfg.getAuditEntityName(entityName); this.auditEntityName = audited ? verEntCfg.getAuditEntityName(entityName) : null;
} }
/**
* @return Original id mapper of the related entity.
*/
public IdMapper getOriginalMapper() { public IdMapper getOriginalMapper() {
return originalMapper; return originalMapper;
} }
/**
* @return prefixed id mapper (with the names for the id fields that are used in the middle table) of the related entity.
*/
public IdMapper getPrefixedMapper() { public IdMapper getPrefixedMapper() {
return prefixedMapper; return prefixedMapper;
} }
/**
* @return Name of the related entity (regular, not audited).
*/
public String getEntityName() { public String getEntityName() {
return entityName; return entityName;
} }
public String getVersionsEntityName() { /**
return versionsEntityName; * @return Audit name of the related entity.
*/
public String getAuditEntityName() {
return auditEntityName;
}
/**
* @return Is the entity, to which this middle id data correspond, audited.
*/
public boolean isAudited() {
return auditEntityName != null;
} }
} }

View File

@ -66,8 +66,16 @@ public class BasicCollectionInitializor<T extends Collection> extends AbstractCo
@SuppressWarnings({"unchecked"}) @SuppressWarnings({"unchecked"})
protected void addToCollection(T collection, Object collectionRow) { protected void addToCollection(T collection, Object collectionRow) {
Object elementData = ((List) collectionRow).get(elementComponentData.getComponentIndex()); Object elementData = ((List) collectionRow).get(elementComponentData.getComponentIndex());
Object element = elementComponentData.getComponentMapper().mapToObjectFromFullMap(entityInstantiator,
// If the target entity is not audited, the elements may be the entities already, so we have to check
// if they are maps or not.
Object element;
if (elementData instanceof Map) {
element = elementComponentData.getComponentMapper().mapToObjectFromFullMap(entityInstantiator,
(Map<String, Object>) elementData, null, revision); (Map<String, Object>) elementData, null, revision);
} else {
element = elementData;
}
collection.add(element); collection.add(element);
} }
} }

View File

@ -37,7 +37,7 @@ public class QueryGeneratorTools {
MiddleIdData idData, String revisionPropertyPath, String originalIdPropertyName, MiddleIdData idData, String revisionPropertyPath, String originalIdPropertyName,
String alias1, String alias2) { String alias1, String alias2) {
// SELECT max(e.revision) FROM versionsReferencedEntity e2 // SELECT max(e.revision) FROM versionsReferencedEntity e2
QueryBuilder maxERevQb = qb.newSubQueryBuilder(idData.getVersionsEntityName(), alias2); QueryBuilder maxERevQb = qb.newSubQueryBuilder(idData.getAuditEntityName(), alias2);
maxERevQb.addProjection("max", revisionPropertyPath, false); maxERevQb.addProjection("max", revisionPropertyPath, false);
// WHERE // WHERE
Parameters maxERevQbParameters = maxERevQb.getRootParameters(); Parameters maxERevQbParameters = maxERevQb.getRootParameters();

View File

@ -28,6 +28,8 @@ import org.hibernate.envers.reader.AuditReaderImplementor;
import org.hibernate.Query; import org.hibernate.Query;
/** /**
* TODO: cleanup implementations and extract common code
*
* Implementations of this interface provide a method to generate queries on a relation table (a table used * Implementations of this interface provide a method to generate queries on a relation table (a table used
* for mapping relations). The query can select, apart from selecting the content of the relation table, also data of * for mapping relations). The query can select, apart from selecting the content of the relation table, also data of
* other "related" entities. * other "related" entities.

View File

@ -85,8 +85,8 @@ public final class ThreeEntityQueryGenerator implements RelationQueryGenerator {
// SELECT new list(ee) FROM middleEntity ee // SELECT new list(ee) FROM middleEntity ee
QueryBuilder qb = new QueryBuilder(versionsMiddleEntityName, "ee"); QueryBuilder qb = new QueryBuilder(versionsMiddleEntityName, "ee");
qb.addFrom(referencedIdData.getVersionsEntityName(), "e"); qb.addFrom(referencedIdData.getAuditEntityName(), "e");
qb.addFrom(indexIdData.getVersionsEntityName(), "f"); qb.addFrom(indexIdData.getAuditEntityName(), "f");
qb.addProjection("new list", "ee, e, f", false, false); qb.addProjection("new list", "ee, e, f", false, false);
// WHERE // WHERE
Parameters rootParameters = qb.getRootParameters(); Parameters rootParameters = qb.getRootParameters();

View File

@ -0,0 +1,108 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC.
*
* 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.entities.mapper.relation.query;
import java.util.Collections;
import org.hibernate.envers.RevisionType;
import org.hibernate.envers.configuration.AuditEntitiesConfiguration;
import org.hibernate.envers.entities.mapper.id.QueryParameterData;
import org.hibernate.envers.entities.mapper.relation.MiddleComponentData;
import org.hibernate.envers.entities.mapper.relation.MiddleIdData;
import org.hibernate.envers.reader.AuditReaderImplementor;
import org.hibernate.envers.tools.query.Parameters;
import org.hibernate.envers.tools.query.QueryBuilder;
import org.hibernate.Query;
/**
* Selects data from a relation middle-table and a related non-audited entity.
* @author Adam Warski (adam at warski dot org)
*/
public final class TwoEntityOneAuditedQueryGenerator implements RelationQueryGenerator {
private final String queryString;
private final MiddleIdData referencingIdData;
public TwoEntityOneAuditedQueryGenerator(
AuditEntitiesConfiguration verEntCfg,
String versionsMiddleEntityName,
MiddleIdData referencingIdData,
MiddleIdData referencedIdData,
MiddleComponentData... componentDatas) {
this.referencingIdData = referencingIdData;
/*
* The query that we need to create:
* SELECT new list(ee, e) FROM referencedEntity e, middleEntity ee
* WHERE
* (entities referenced by the middle table; id_ref_ed = id of the referenced entity)
* ee.id_ref_ed = e.id_ref_ed AND
* (only entities referenced by the association; id_ref_ing = id of the referencing entity)
* ee.id_ref_ing = :id_ref_ing AND
* (the association at revision :revision)
* ee.revision = (SELECT max(ee2.revision) FROM middleEntity ee2
* WHERE ee2.revision <= :revision AND ee2.originalId.* = ee.originalId.*) AND
* (only non-deleted entities and associations)
* ee.revision_type != DEL
*/
String revisionPropertyPath = verEntCfg.getRevisionNumberPath();
String originalIdPropertyName = verEntCfg.getOriginalIdPropName();
String eeOriginalIdPropertyPath = "ee." + originalIdPropertyName;
// SELECT new list(ee) FROM middleEntity ee
QueryBuilder qb = new QueryBuilder(versionsMiddleEntityName, "ee");
qb.addFrom(referencedIdData.getEntityName(), "e");
qb.addProjection("new list", "ee, e", false, false);
// WHERE
Parameters rootParameters = qb.getRootParameters();
// ee.id_ref_ed = e.id_ref_ed
referencedIdData.getPrefixedMapper().addIdsEqualToQuery(rootParameters, eeOriginalIdPropertyPath,
referencedIdData.getOriginalMapper(), "e");
// ee.originalId.id_ref_ing = :id_ref_ing
referencingIdData.getPrefixedMapper().addNamedIdEqualsToQuery(rootParameters, originalIdPropertyName, true);
// ee.revision = (SELECT max(...) ...)
QueryGeneratorTools.addAssociationAtRevision(qb, rootParameters, referencingIdData, versionsMiddleEntityName,
eeOriginalIdPropertyPath, revisionPropertyPath, originalIdPropertyName, componentDatas);
// ee.revision_type != DEL
rootParameters.addWhereWithNamedParam(verEntCfg.getRevisionTypePropName(), "!=", "delrevisiontype");
StringBuilder sb = new StringBuilder();
qb.build(sb, Collections.<String, Object>emptyMap());
queryString = sb.toString();
}
public Query getQuery(AuditReaderImplementor versionsReader, Object primaryKey, Number revision) {
Query query = versionsReader.getSession().createQuery(queryString);
query.setParameter("revision", revision);
query.setParameter("delrevisiontype", RevisionType.DEL);
for (QueryParameterData paramData: referencingIdData.getPrefixedMapper().mapToQueryParametersFromId(primaryKey)) {
paramData.setParameterValue(query);
}
return query;
}
}

View File

@ -78,7 +78,7 @@ public final class TwoEntityQueryGenerator implements RelationQueryGenerator {
// SELECT new list(ee) FROM middleEntity ee // SELECT new list(ee) FROM middleEntity ee
QueryBuilder qb = new QueryBuilder(versionsMiddleEntityName, "ee"); QueryBuilder qb = new QueryBuilder(versionsMiddleEntityName, "ee");
qb.addFrom(referencedIdData.getVersionsEntityName(), "e"); qb.addFrom(referencedIdData.getAuditEntityName(), "e");
qb.addProjection("new list", "ee, e", false, false); qb.addProjection("new list", "ee, e", false, false);
// WHERE // WHERE
Parameters rootParameters = qb.getRootParameters(); Parameters rootParameters = qb.getRootParameters();

View File

@ -0,0 +1,116 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC.
*
* 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 javax.persistence.*;
import org.hibernate.envers.Audited;
import org.hibernate.envers.RelationTargetAuditMode;
import org.hibernate.envers.test.entities.UnversionedStrTestEntity;
import java.util.List;
/**
* Audited entity with a many-to-many-reference to not audited entity.
* @author Toamsz Bech
* @author Adam Warski
*/
@Entity
public class M2MTargetNotAuditedEntity {
@Id
private Integer id;
@Audited
private String data;
@Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
@ManyToMany(fetch = FetchType.LAZY)
private List<UnversionedStrTestEntity> references;
public M2MTargetNotAuditedEntity() { }
public M2MTargetNotAuditedEntity(Integer id, String data, List<UnversionedStrTestEntity> references) {
this.id = id;
this.data = data;
this.references = references;
}
public M2MTargetNotAuditedEntity(String data, List<UnversionedStrTestEntity> references) {
this.data = data;
this.references = references;
}
public M2MTargetNotAuditedEntity(Integer id, String data) {
this.id = id;
this.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;
}
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof M2MTargetNotAuditedEntity)) return false;
M2MTargetNotAuditedEntity that = (M2MTargetNotAuditedEntity) o;
if (data != null ? !data.equals(that.getData()) : that.getData() != null) return false;
//noinspection RedundantIfStatement
if (id != null ? !id.equals(that.getId()) : that.getId() != null) return false;
return true;
}
public int hashCode() {
int result;
result = (id != null ? id.hashCode() : 0);
result = 31 * result + (data != null ? data.hashCode() : 0);
return result;
}
public String toString() {
return "M2MTargetNotAuditedEntity(id = " + id + ", data = " + data + ")";
}
}

View File

@ -0,0 +1,167 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC.
*
* 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.integration.manytomany.unidirectional;
import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
import javax.persistence.EntityManager;
import org.hibernate.ejb.Ejb3Configuration;
import org.hibernate.envers.test.AbstractEntityTest;
import static org.hibernate.envers.test.tools.TestTools.*;
import org.hibernate.envers.test.entities.UnversionedStrTestEntity;
import org.hibernate.envers.test.entities.manytomany.unidirectional.M2MTargetNotAuditedEntity;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
* A test for auditing a many-to-many relation where the target entity is not audited.
* @author Adam Warski
*/
public class M2MRelationNotAuditedTarget extends AbstractEntityTest {
private Integer tnae1_id;
private Integer tnae2_id;
private Integer uste1_id;
private Integer uste2_id;
public void configure(Ejb3Configuration cfg) {
cfg.addAnnotatedClass(M2MTargetNotAuditedEntity.class);
cfg.addAnnotatedClass(UnversionedStrTestEntity.class);
}
@BeforeClass(dependsOnMethods = "init")
public void initData() {
EntityManager em = getEntityManager();
UnversionedStrTestEntity uste1 = new UnversionedStrTestEntity("str1");
UnversionedStrTestEntity uste2 = new UnversionedStrTestEntity("str2");
// No revision
em.getTransaction().begin();
em.persist(uste1);
em.persist(uste2);
em.getTransaction().commit();
// Revision 1
em.getTransaction().begin();
uste1 = em.find(UnversionedStrTestEntity.class, uste1.getId());
uste2 = em.find(UnversionedStrTestEntity.class, uste2.getId());
M2MTargetNotAuditedEntity tnae1 = new M2MTargetNotAuditedEntity(1, "tnae1", new ArrayList<UnversionedStrTestEntity>());
M2MTargetNotAuditedEntity tnae2 = new M2MTargetNotAuditedEntity(2, "tnae2", new ArrayList<UnversionedStrTestEntity>());
tnae2.getReferences().add(uste1);
tnae2.getReferences().add(uste2);
em.persist(tnae1);
em.persist(tnae2);
em.getTransaction().commit();
// Revision 2
em.getTransaction().begin();
tnae1 = em.find(M2MTargetNotAuditedEntity.class, tnae1.getId());
tnae2 = em.find(M2MTargetNotAuditedEntity.class, tnae2.getId());
tnae1.getReferences().add(uste1);
tnae2.getReferences().remove(uste1);
em.getTransaction().commit();
// Revision 3
em.getTransaction().begin();
tnae1 = em.find(M2MTargetNotAuditedEntity.class, tnae1.getId());
tnae2 = em.find(M2MTargetNotAuditedEntity.class, tnae2.getId());
//field not changed!!!
tnae1.getReferences().add(uste1);
tnae2.getReferences().remove(uste2);
em.getTransaction().commit();
// Revision 4
em.getTransaction().begin();
tnae1 = em.find(M2MTargetNotAuditedEntity.class, tnae1.getId());
tnae2 = em.find(M2MTargetNotAuditedEntity.class, tnae2.getId());
tnae1.getReferences().add(uste2);
tnae2.getReferences().add(uste1);
em.getTransaction().commit();
//
tnae1_id = tnae1.getId();
tnae2_id = tnae2.getId();
uste1_id = uste1.getId();
uste2_id = uste2.getId();
}
@Test
public void testRevisionsCounts() {
List<Number> revisions = getAuditReader().getRevisions(M2MTargetNotAuditedEntity.class, tnae1_id);
assert Arrays.asList(1, 2, 4).equals(revisions);
revisions = getAuditReader().getRevisions(M2MTargetNotAuditedEntity.class, tnae2_id);
assert Arrays.asList(1, 2, 3, 4).equals(revisions);
}
@Test
public void testHistoryOfTnae1_id() {
UnversionedStrTestEntity uste1 = getEntityManager().find(UnversionedStrTestEntity.class, uste1_id);
UnversionedStrTestEntity uste2 = getEntityManager().find(UnversionedStrTestEntity.class, uste2_id);
M2MTargetNotAuditedEntity rev1 = getAuditReader().find(M2MTargetNotAuditedEntity.class, tnae1_id, 1);
M2MTargetNotAuditedEntity rev2 = getAuditReader().find(M2MTargetNotAuditedEntity.class, tnae1_id, 2);
M2MTargetNotAuditedEntity rev3 = getAuditReader().find(M2MTargetNotAuditedEntity.class, tnae1_id, 3);
M2MTargetNotAuditedEntity rev4 = getAuditReader().find(M2MTargetNotAuditedEntity.class, tnae1_id, 4);
checkList(rev1.getReferences());
checkList(rev2.getReferences(), uste1);
checkList(rev3.getReferences(), uste1);
checkList(rev4.getReferences(), uste1, uste2);
}
@Test
public void testHistoryOfTnae2_id() {
UnversionedStrTestEntity uste1 = getEntityManager().find(UnversionedStrTestEntity.class, uste1_id);
UnversionedStrTestEntity uste2 = getEntityManager().find(UnversionedStrTestEntity.class, uste2_id);
M2MTargetNotAuditedEntity rev1 = getAuditReader().find(M2MTargetNotAuditedEntity.class, tnae2_id, 1);
M2MTargetNotAuditedEntity rev2 = getAuditReader().find(M2MTargetNotAuditedEntity.class, tnae2_id, 2);
M2MTargetNotAuditedEntity rev3 = getAuditReader().find(M2MTargetNotAuditedEntity.class, tnae2_id, 3);
M2MTargetNotAuditedEntity rev4 = getAuditReader().find(M2MTargetNotAuditedEntity.class, tnae2_id, 4);
checkList(rev1.getReferences(), uste1, uste2);
checkList(rev2.getReferences(), uste2);
checkList(rev3.getReferences());
checkList(rev4.getReferences(), uste1);
}
}