mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-09 12:44:49 +00:00
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.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 void generateFirstPass(PersistentClass pc, ClassAuditingData auditingData
|
|||||||
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 @@ void throwUnsupportedTypeException(Type type, String entityName, String property
|
|||||||
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.
|
||||||
*
|
*
|
||||||
|
@ -155,28 +155,26 @@ void addCollection() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 @@ private void addWithMiddleTable() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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 @@ private MiddleComponentData addValueToMiddleTable(Value value, Element xmlMappin
|
|||||||
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 @@ private MiddleComponentData addValueToMiddleTable(Value value, Element xmlMappin
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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 @@ private String getMappedBy(Table collectionTable, PersistentClass referencedClas
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -30,10 +30,8 @@
|
|||||||
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 @@ RelationQueryGenerator build(MiddleComponentData... componentDatas) {
|
|||||||
return new OneEntityQueryGenerator(verEntCfg, auditMiddleEntityName, referencingIdData,
|
return new OneEntityQueryGenerator(verEntCfg, auditMiddleEntityName, referencingIdData,
|
||||||
componentDatas);
|
componentDatas);
|
||||||
} else if (idDatas.size() == 1) {
|
} else if (idDatas.size() == 1) {
|
||||||
return new TwoEntityQueryGenerator(globalCfg, verEntCfg, auditMiddleEntityName, referencingIdData,
|
if (idDatas.get(0).isAudited()) {
|
||||||
idDatas.get(0), componentDatas);
|
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) {
|
} 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 {
|
||||||
|
@ -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 @@ void addToOne(Element parent, PropertyAuditingData propertyAuditingData, Value v
|
|||||||
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() + "_";
|
||||||
|
|
||||||
|
@ -33,44 +33,51 @@
|
|||||||
* @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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,8 +66,16 @@ protected T initializeCollection(int size) {
|
|||||||
@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,
|
|
||||||
(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);
|
collection.add(element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ public static void addEntityAtRevision(GlobalConfiguration globalCfg, QueryBuild
|
|||||||
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();
|
||||||
|
@ -28,6 +28,8 @@
|
|||||||
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.
|
||||||
|
@ -85,8 +85,8 @@ public ThreeEntityQueryGenerator(GlobalConfiguration globalCfg,
|
|||||||
|
|
||||||
// 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();
|
||||||
|
@ -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 TwoEntityQueryGenerator(GlobalConfiguration globalCfg,
|
|||||||
|
|
||||||
// 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();
|
||||||
|
@ -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…
x
Reference in New Issue
Block a user