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:
parent
e183c40ee7
commit
04fae574ae
|
@ -40,6 +40,7 @@ import org.hibernate.envers.entities.mapper.MultiPropertyMapper;
|
|||
import org.hibernate.envers.entities.mapper.SubclassPropertyMapper;
|
||||
import org.hibernate.envers.tools.StringTools;
|
||||
import org.hibernate.envers.tools.Triple;
|
||||
import org.hibernate.envers.RelationTargetAuditMode;
|
||||
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
|
@ -348,7 +349,7 @@ public final class AuditMetadataGenerator {
|
|||
String parentEntityName = null;
|
||||
EntityConfiguration entityCfg = new EntityConfiguration(entityName, idMapper, propertyMapper,
|
||||
parentEntityName);
|
||||
notAuditedEntitiesConfigurations.put(pc.getEntityName(), entityCfg);
|
||||
notAuditedEntitiesConfigurations.put(entityName, entityCfg);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -476,6 +477,36 @@ public final class AuditMetadataGenerator {
|
|||
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.
|
||||
*
|
||||
|
|
|
@ -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"})
|
||||
private void addOneToManyAttached() {
|
||||
String mappedBy = getMappedBy(propertyValue);
|
||||
|
||||
EntityConfiguration referencedEntityConfiguration = mainGenerator.getEntitiesConfigurations()
|
||||
.get(referencedEntityName);
|
||||
|
||||
if (referencedEntityConfiguration == null) {
|
||||
throwRelationNotAudited(referencedEntityName);
|
||||
// Impossible to get here.
|
||||
throw new AssertionError();
|
||||
}
|
||||
IdMappingData referencedIdMapping = referencedEntityConfiguration.getIdMappingData();
|
||||
IdMappingData referencedIdMapping = mainGenerator.getReferencedIdMappingData(referencingEntityName,
|
||||
referencedEntityName, propertyAuditingData, false);
|
||||
IdMappingData referencingIdMapping = referencingEntityConfiguration.getIdMappingData();
|
||||
|
||||
// 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);
|
||||
|
||||
// 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).
|
||||
MiddleIdData referencedIdData = new MiddleIdData(mainGenerator.getVerEntCfg(), referencedIdMapping,
|
||||
MiddleIdData referencedIdData = createMiddleIdData(referencedIdMapping,
|
||||
null, referencedEntityName);
|
||||
|
||||
// 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.
|
||||
MiddleIdData referencingIdData = new MiddleIdData(mainGenerator.getVerEntCfg(), referencingIdMapping,
|
||||
MiddleIdData referencingIdData = createMiddleIdData(referencingIdMapping,
|
||||
referencingPrefixRelated, referencingEntityName);
|
||||
|
||||
// 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 referencedEntityName = getReferencedEntityName(value);
|
||||
EntityConfiguration referencedEntityConfiguration = mainGenerator.getEntitiesConfigurations()
|
||||
.get(referencedEntityName);
|
||||
if (referencedEntityConfiguration == null) {
|
||||
throwRelationNotAudited(referencedEntityName);
|
||||
// Impossible to get here.
|
||||
throw new AssertionError();
|
||||
}
|
||||
IdMappingData referencedIdMapping = referencedEntityConfiguration.getIdMappingData();
|
||||
|
||||
IdMappingData referencedIdMapping = mainGenerator.getReferencedIdMappingData(referencingEntityName,
|
||||
referencedEntityName, propertyAuditingData, true);
|
||||
|
||||
// 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).
|
||||
|
@ -410,7 +403,7 @@ public final class CollectionMetadataGenerator {
|
|||
}
|
||||
|
||||
// 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);
|
||||
// And adding it to the generator builder.
|
||||
queryGeneratorBuilder.addRelation(referencedIdData);
|
||||
|
@ -541,9 +534,4 @@ public final class CollectionMetadataGenerator {
|
|||
throw new MappingException("Unable to read the mapped by attribute for " + propertyName + " in "
|
||||
+ referencingEntityName + "!");
|
||||
}
|
||||
|
||||
private void throwRelationNotAudited(String referencedEntityName) {
|
||||
throw new MappingException("An audited relation from " + referencingEntityName +
|
||||
" to a non-audited entity: " + referencedEntityName);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,10 +30,8 @@ import org.hibernate.envers.configuration.GlobalConfiguration;
|
|||
import org.hibernate.envers.configuration.AuditEntitiesConfiguration;
|
||||
import org.hibernate.envers.entities.mapper.relation.MiddleComponentData;
|
||||
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.RelationQueryGenerator;
|
||||
import org.hibernate.envers.entities.mapper.relation.query.ThreeEntityQueryGenerator;
|
||||
import org.hibernate.envers.entities.mapper.relation.query.TwoEntityQueryGenerator;
|
||||
import org.hibernate.envers.entities.mapper.relation.query.*;
|
||||
import org.hibernate.MappingException;
|
||||
|
||||
/**
|
||||
* 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,
|
||||
componentDatas);
|
||||
} else if (idDatas.size() == 1) {
|
||||
return new TwoEntityQueryGenerator(globalCfg, verEntCfg, auditMiddleEntityName, referencingIdData,
|
||||
idDatas.get(0), componentDatas);
|
||||
if (idDatas.get(0).isAudited()) {
|
||||
return new TwoEntityQueryGenerator(globalCfg, verEntCfg, auditMiddleEntityName, referencingIdData,
|
||||
idDatas.get(0), componentDatas);
|
||||
} else {
|
||||
return new TwoEntityOneAuditedQueryGenerator(verEntCfg, auditMiddleEntityName, referencingIdData,
|
||||
idDatas.get(0), componentDatas);
|
||||
}
|
||||
} 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,
|
||||
idDatas.get(0), idDatas.get(1), componentDatas);
|
||||
} else {
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
package org.hibernate.envers.configuration.metadata;
|
||||
|
||||
import org.dom4j.Element;
|
||||
import org.hibernate.envers.RelationTargetAuditMode;
|
||||
import org.hibernate.envers.entities.EntityConfiguration;
|
||||
import org.hibernate.envers.entities.IdMappingData;
|
||||
import org.hibernate.envers.entities.PropertyData;
|
||||
|
@ -55,24 +54,8 @@ public final class ToOneRelationMetadataGenerator {
|
|||
CompositeMapperBuilder mapper, String entityName, boolean insertable) {
|
||||
String referencedEntityName = ((ToOne) value).getReferencedEntityName();
|
||||
|
||||
EntityConfiguration configuration = mainGenerator.getEntitiesConfigurations().get(referencedEntityName);
|
||||
if (configuration == null) {
|
||||
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();
|
||||
IdMappingData idMapping = mainGenerator.getReferencedIdMappingData(entityName, referencedEntityName,
|
||||
propertyAuditingData, true);
|
||||
|
||||
String lastPropertyPrefix = propertyAuditingData.getName() + "_";
|
||||
|
||||
|
|
|
@ -33,44 +33,51 @@ import org.hibernate.envers.entities.mapper.id.IdMapper;
|
|||
* @author Adam Warski (adam at warski dot org)
|
||||
*/
|
||||
public final class MiddleIdData {
|
||||
/**
|
||||
* Original id mapper of the related entity.
|
||||
*/
|
||||
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;
|
||||
/**
|
||||
* Name of the related entity.
|
||||
*/
|
||||
private final String entityName;
|
||||
/**
|
||||
* Versions name of the related entity.
|
||||
*/
|
||||
private final String versionsEntityName;
|
||||
private final String auditEntityName;
|
||||
|
||||
public MiddleIdData(AuditEntitiesConfiguration verEntCfg, IdMappingData mappingData, String prefix,
|
||||
String entityName) {
|
||||
String entityName, boolean audited) {
|
||||
this.originalMapper = mappingData.getIdMapper();
|
||||
this.prefixedMapper = mappingData.getIdMapper().prefixMappedProperties(prefix);
|
||||
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() {
|
||||
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() {
|
||||
return prefixedMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Name of the related entity (regular, not audited).
|
||||
*/
|
||||
public String getEntityName() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,8 +66,16 @@ public class BasicCollectionInitializor<T extends Collection> extends AbstractCo
|
|||
@SuppressWarnings({"unchecked"})
|
||||
protected void addToCollection(T collection, Object collectionRow) {
|
||||
Object elementData = ((List) collectionRow).get(elementComponentData.getComponentIndex());
|
||||
Object element = elementComponentData.getComponentMapper().mapToObjectFromFullMap(entityInstantiator,
|
||||
(Map<String, Object>) elementData, null, revision);
|
||||
|
||||
// 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);
|
||||
} else {
|
||||
element = elementData;
|
||||
}
|
||||
collection.add(element);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ public class QueryGeneratorTools {
|
|||
MiddleIdData idData, String revisionPropertyPath, String originalIdPropertyName,
|
||||
String alias1, String alias2) {
|
||||
// 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);
|
||||
// WHERE
|
||||
Parameters maxERevQbParameters = maxERevQb.getRootParameters();
|
||||
|
|
|
@ -28,6 +28,8 @@ import org.hibernate.envers.reader.AuditReaderImplementor;
|
|||
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
|
||||
* for mapping relations). The query can select, apart from selecting the content of the relation table, also data of
|
||||
* other "related" entities.
|
||||
|
|
|
@ -85,8 +85,8 @@ public final class ThreeEntityQueryGenerator implements RelationQueryGenerator {
|
|||
|
||||
// SELECT new list(ee) FROM middleEntity ee
|
||||
QueryBuilder qb = new QueryBuilder(versionsMiddleEntityName, "ee");
|
||||
qb.addFrom(referencedIdData.getVersionsEntityName(), "e");
|
||||
qb.addFrom(indexIdData.getVersionsEntityName(), "f");
|
||||
qb.addFrom(referencedIdData.getAuditEntityName(), "e");
|
||||
qb.addFrom(indexIdData.getAuditEntityName(), "f");
|
||||
qb.addProjection("new list", "ee, e, f", false, false);
|
||||
// WHERE
|
||||
Parameters rootParameters = qb.getRootParameters();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -78,7 +78,7 @@ public final class TwoEntityQueryGenerator implements RelationQueryGenerator {
|
|||
|
||||
// SELECT new list(ee) FROM middleEntity 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);
|
||||
// WHERE
|
||||
Parameters rootParameters = qb.getRootParameters();
|
||||
|
|
|
@ -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 + ")";
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue