HHH-11895 Support traversal of components in audit query API
This commit is contained in:
parent
25421733d6
commit
d9f3e82291
|
@ -14,6 +14,7 @@ import org.hibernate.envers.boot.registry.classloading.ClassLoaderAccessHelper;
|
|||
import org.hibernate.envers.boot.spi.EnversMetadataBuildingContext;
|
||||
import org.hibernate.envers.configuration.internal.metadata.reader.ComponentAuditingData;
|
||||
import org.hibernate.envers.configuration.internal.metadata.reader.PropertyAuditingData;
|
||||
import org.hibernate.envers.internal.entities.EntityConfiguration;
|
||||
import org.hibernate.envers.internal.entities.mapper.CompositeMapperBuilder;
|
||||
import org.hibernate.mapping.Component;
|
||||
import org.hibernate.mapping.Property;
|
||||
|
@ -80,6 +81,11 @@ public final class ComponentMetadataGenerator extends AbstractMetadataGenerator
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ( !firstPass ) {
|
||||
final EntityConfiguration owningEntityConfiguration = getAuditedEntityConfigurations().get( entityName );
|
||||
owningEntityConfiguration.addToOneComponent( propertyAuditingData.getName(), componentAuditingData );
|
||||
}
|
||||
}
|
||||
|
||||
private String getClassNameForComponent(Component component) {
|
||||
|
|
|
@ -237,7 +237,7 @@ public class MiddleTableCollectionMetadataGenerator extends AbstractCollectionMe
|
|||
MiddleIdData referencingIdData,
|
||||
String referencedPrefix,
|
||||
String auditMiddleEntityName) {
|
||||
// Only if this is a relation (when there is a referenced entity).
|
||||
// Only if this is a relation (when there is a referenced entity or a component).
|
||||
if ( context.getReferencedEntityName() != null ) {
|
||||
final IdMappingData referencedIdMapping = getReferencedIdMappingData(
|
||||
context.getReferencingEntityName(),
|
||||
|
@ -270,5 +270,12 @@ public class MiddleTableCollectionMetadataGenerator extends AbstractCollectionMe
|
|||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
context.getReferencingEntityConfiguration().addToManyComponent(
|
||||
context.getPropertyName(),
|
||||
auditMiddleEntityName,
|
||||
referencingIdData
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.envers.internal.entities;
|
||||
|
||||
import org.hibernate.envers.configuration.internal.metadata.reader.ComponentAuditingData;
|
||||
import org.hibernate.envers.internal.entities.mapper.relation.MiddleIdData;
|
||||
|
||||
/**
|
||||
* @author Felix Feisst (feisst dot felix at gmail dot com)
|
||||
*/
|
||||
public class ComponentDescription {
|
||||
|
||||
private final ComponentType type;
|
||||
private final String propertyName;
|
||||
private final String auditMiddleEntityName;
|
||||
private final MiddleIdData middleIdData;
|
||||
private final ComponentAuditingData auditingData;
|
||||
|
||||
private ComponentDescription(ComponentType type,
|
||||
String propertyName,
|
||||
String auditMiddleEntityName,
|
||||
MiddleIdData middleIdData,
|
||||
ComponentAuditingData componentAuditingData) {
|
||||
this.type = type;
|
||||
this.propertyName = propertyName;
|
||||
this.auditMiddleEntityName = auditMiddleEntityName;
|
||||
this.middleIdData = middleIdData;
|
||||
this.auditingData = componentAuditingData;
|
||||
}
|
||||
|
||||
public static ComponentDescription many(String propertyName, String auditMiddleEntityName, MiddleIdData middleIdData) {
|
||||
return new ComponentDescription( ComponentType.MANY, propertyName, auditMiddleEntityName, middleIdData, null );
|
||||
}
|
||||
|
||||
public static ComponentDescription one(String propertyName, ComponentAuditingData componentAuditingData) {
|
||||
return new ComponentDescription( ComponentType.ONE, propertyName, null, null, componentAuditingData );
|
||||
}
|
||||
|
||||
public ComponentType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getPropertyName() {
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
public String getAuditMiddleEntityName() {
|
||||
return auditMiddleEntityName;
|
||||
}
|
||||
|
||||
public MiddleIdData getMiddleIdData() {
|
||||
return middleIdData;
|
||||
}
|
||||
|
||||
public ComponentAuditingData getAuditingData() {
|
||||
return auditingData;
|
||||
}
|
||||
|
||||
public enum ComponentType {
|
||||
ONE, MANY
|
||||
}
|
||||
}
|
|
@ -124,6 +124,27 @@ public class EntitiesConfigurations {
|
|||
return descriptions;
|
||||
}
|
||||
|
||||
public ComponentDescription getComponentDescription(final String entityName, final String propertyName) {
|
||||
final EntityConfiguration entCfg;
|
||||
if ( isVersioned( entityName ) ) {
|
||||
entCfg = get( entityName );
|
||||
}
|
||||
else {
|
||||
entCfg = getNotVersionEntityConfiguration( entityName );
|
||||
}
|
||||
final ComponentDescription relDesc = entCfg.getComponentDescription( propertyName );
|
||||
if ( relDesc != null ) {
|
||||
return relDesc;
|
||||
}
|
||||
else if ( entCfg.getParentEntityName() != null ) {
|
||||
// The field may be declared in a superclass ...
|
||||
return getComponentDescription( entCfg.getParentEntityName(), propertyName );
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void addWithParentEntityNames(String entityName, Set<String> entityNames) {
|
||||
entityNames.add( entityName );
|
||||
final EntityConfiguration entCfg = entitiesConfigurations.get( entityName );
|
||||
|
|
|
@ -9,6 +9,7 @@ package org.hibernate.envers.internal.entities;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.envers.configuration.internal.metadata.reader.ComponentAuditingData;
|
||||
import org.hibernate.envers.internal.entities.mapper.ExtendedPropertyMapper;
|
||||
import org.hibernate.envers.internal.entities.mapper.PropertyMapper;
|
||||
import org.hibernate.envers.internal.entities.mapper.id.IdMapper;
|
||||
|
@ -31,6 +32,7 @@ public class EntityConfiguration {
|
|||
private final ExtendedPropertyMapper propertyMapper;
|
||||
// Maps from property name
|
||||
private final Map<String, RelationDescription> relations;
|
||||
private final Map<String, ComponentDescription> components;
|
||||
private final String parentEntityName;
|
||||
|
||||
public EntityConfiguration(
|
||||
|
@ -46,6 +48,7 @@ public class EntityConfiguration {
|
|||
this.parentEntityName = parentEntityName;
|
||||
|
||||
this.relations = new HashMap<>();
|
||||
this.components = new HashMap<>();
|
||||
}
|
||||
|
||||
public void addToOneRelation(
|
||||
|
@ -170,6 +173,14 @@ public class EntityConfiguration {
|
|||
);
|
||||
}
|
||||
|
||||
public void addToManyComponent(String propertyName, String auditMiddleEntityName, MiddleIdData middleIdData) {
|
||||
components.put( propertyName, ComponentDescription.many( propertyName, auditMiddleEntityName, middleIdData ) );
|
||||
}
|
||||
|
||||
public void addToOneComponent(String propertyName, ComponentAuditingData auditingData) {
|
||||
components.put( propertyName, ComponentDescription.one( propertyName, auditingData ) );
|
||||
}
|
||||
|
||||
public boolean isRelation(String propertyName) {
|
||||
return relations.get( propertyName ) != null;
|
||||
}
|
||||
|
@ -178,6 +189,10 @@ public class EntityConfiguration {
|
|||
return relations.get( propertyName );
|
||||
}
|
||||
|
||||
public ComponentDescription getComponentDescription(String propertyName) {
|
||||
return components.get( propertyName );
|
||||
}
|
||||
|
||||
public IdMappingData getIdMappingData() {
|
||||
return idMappingData;
|
||||
}
|
||||
|
|
|
@ -318,6 +318,7 @@ public class Parameters {
|
|||
*
|
||||
* @param configuration the configuration.
|
||||
* @param aliasToEntityNameMap alias to entity name map, never {@literal null}
|
||||
* @param aliasToComponentPropertyNameMap alias to component property name map, never {@literal null}
|
||||
* @param function the function.
|
||||
* @param op the operator.
|
||||
* @param value the scalar value.
|
||||
|
@ -325,6 +326,7 @@ public class Parameters {
|
|||
public void addWhereWithFunction(
|
||||
Configuration configuration,
|
||||
Map<String, String> aliasToEntityNameMap,
|
||||
Map<String, String> aliasToComponentPropertyNameMap,
|
||||
AuditFunction function,
|
||||
String op,
|
||||
Object value) {
|
||||
|
@ -333,6 +335,7 @@ public class Parameters {
|
|||
QueryBuilder.appendFunctionArgument(
|
||||
configuration,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
queryParamCounter,
|
||||
localQueryParamValues,
|
||||
alias,
|
||||
|
@ -354,6 +357,7 @@ public class Parameters {
|
|||
*
|
||||
* @param configuration the configuration.
|
||||
* @param aliasToEntityNameMap alias to entity name map, never {@literal null}
|
||||
* @param aliasToComponentPropertyNameMap alias to component property name map, never {@literal null}
|
||||
* @param function the function.
|
||||
* @param op the operator.
|
||||
* @param aliasRight the optional alias of the right property, may be {@literal null}
|
||||
|
@ -362,6 +366,7 @@ public class Parameters {
|
|||
public void addWhereWithFunction(
|
||||
Configuration configuration,
|
||||
Map<String, String> aliasToEntityNameMap,
|
||||
Map<String, String> aliasToComponentPropertyNameMap,
|
||||
AuditFunction function,
|
||||
String op,
|
||||
String aliasRight,
|
||||
|
@ -371,6 +376,7 @@ public class Parameters {
|
|||
QueryBuilder.appendFunctionArgument(
|
||||
configuration,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
queryParamCounter,
|
||||
localQueryParamValues,
|
||||
alias,
|
||||
|
@ -393,6 +399,7 @@ public class Parameters {
|
|||
*
|
||||
* @param configuration the configuration.
|
||||
* @param aliasToEntityNameMap alias to entity name map, never {@literal null}
|
||||
* @param aliasToComponentPropertyNameMap alias to component property name map, never {@literal null}
|
||||
* @param aliasLeft the optional alias of the left property, may be {@literal null}
|
||||
* @param left the property.
|
||||
* @param op the operator.
|
||||
|
@ -401,6 +408,7 @@ public class Parameters {
|
|||
public void addWhereWithFunction(
|
||||
Configuration configuration,
|
||||
Map<String, String> aliasToEntityNameMap,
|
||||
Map<String, String> aliasToComponentPropertyNameMap,
|
||||
String aliasLeft,
|
||||
String left,
|
||||
String op,
|
||||
|
@ -417,6 +425,7 @@ public class Parameters {
|
|||
QueryBuilder.appendFunctionArgument(
|
||||
configuration,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
queryParamCounter,
|
||||
localQueryParamValues,
|
||||
alias,
|
||||
|
@ -432,6 +441,7 @@ public class Parameters {
|
|||
*
|
||||
* @param configuration the configuration.
|
||||
* @param aliasToEntityNameMap alias to entity name map, never {@literal null}
|
||||
* @param aliasToComponentPropertyNameMap alias to component property name map, never {@literal null}
|
||||
* @param left the left-side function.
|
||||
* @param op the operator.
|
||||
* @param right the right-side function.
|
||||
|
@ -439,6 +449,7 @@ public class Parameters {
|
|||
public void addWhereWithFunction(
|
||||
Configuration configuration,
|
||||
Map<String, String> aliasToEntityNameMap,
|
||||
Map<String, String> aliasToComponentPropertyNameMap,
|
||||
AuditFunction left,
|
||||
String op,
|
||||
AuditFunction right) {
|
||||
|
@ -447,6 +458,7 @@ public class Parameters {
|
|||
QueryBuilder.appendFunctionArgument(
|
||||
configuration,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
queryParamCounter,
|
||||
localQueryParamValues,
|
||||
alias,
|
||||
|
@ -459,6 +471,7 @@ public class Parameters {
|
|||
QueryBuilder.appendFunctionArgument(
|
||||
configuration,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
queryParamCounter,
|
||||
localQueryParamValues,
|
||||
alias,
|
||||
|
|
|
@ -26,10 +26,10 @@ import org.hibernate.envers.internal.entities.RevisionTypeType;
|
|||
import org.hibernate.envers.internal.entities.mapper.id.QueryParameterData;
|
||||
import org.hibernate.envers.internal.tools.MutableInteger;
|
||||
import org.hibernate.envers.internal.tools.StringTools;
|
||||
import org.hibernate.envers.internal.tools.Triple;
|
||||
import org.hibernate.envers.query.criteria.AuditFunction;
|
||||
import org.hibernate.envers.query.criteria.AuditId;
|
||||
import org.hibernate.envers.query.criteria.AuditProperty;
|
||||
import org.hibernate.envers.query.criteria.internal.CriteriaTools;
|
||||
import org.hibernate.envers.query.order.NullPrecedence;
|
||||
import org.hibernate.envers.tools.Pair;
|
||||
import org.hibernate.query.Query;
|
||||
|
@ -220,11 +220,16 @@ public class QueryBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
public void addProjection(Configuration configuration, Map<String, String> aliasToEntityNameMap, AuditFunction function) {
|
||||
public void addProjection(
|
||||
Configuration configuration,
|
||||
Map<String, String> aliasToEntityNameMap,
|
||||
Map<String, String> aliasToComponentPropertyNameMap,
|
||||
AuditFunction function) {
|
||||
final StringBuilder expression = new StringBuilder();
|
||||
appendFunctionArgument(
|
||||
configuration,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
paramCounter,
|
||||
projectionQueryParamValues,
|
||||
alias,
|
||||
|
@ -237,6 +242,7 @@ public class QueryBuilder {
|
|||
protected static void appendFunctionArgument(
|
||||
Configuration configuration,
|
||||
Map<String, String> aliasToEntityNameMap,
|
||||
Map<String, String> aliasToComponentPropertyNameMap,
|
||||
MutableInteger paramCounter,
|
||||
Map<String, Object> queryParamValues,
|
||||
String alias,
|
||||
|
@ -253,6 +259,7 @@ public class QueryBuilder {
|
|||
appendFunctionArgument(
|
||||
configuration,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
paramCounter,
|
||||
queryParamValues,
|
||||
alias,
|
||||
|
@ -292,8 +299,14 @@ public class QueryBuilder {
|
|||
if ( propertyAlias != null ) {
|
||||
expression.append( propertyAlias ).append( '.' );
|
||||
}
|
||||
String propertyPrefix = CriteriaTools.determineComponentPropertyPrefix(
|
||||
configuration.getEnversService(),
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
propertyAlias
|
||||
);
|
||||
String propertyName = property.getPropertyNameGetter().get( configuration );
|
||||
expression.append( propertyName );
|
||||
expression.append( propertyPrefix.concat( propertyName ) );
|
||||
}
|
||||
else {
|
||||
String queryParam = "_p" + paramCounter.getAndIncrease();
|
||||
|
|
|
@ -53,6 +53,7 @@ public class AggregatedAuditExpression implements AuditCriterion, ExtendableCrit
|
|||
EnversService enversService,
|
||||
AuditReaderImplementor versionsReader,
|
||||
Map<String, String> aliasToEntityNameMap,
|
||||
Map<String, String> aliasToComponentPropertyNameMap,
|
||||
String baseAlias,
|
||||
QueryBuilder qb,
|
||||
Parameters parameters) {
|
||||
|
@ -64,8 +65,15 @@ public class AggregatedAuditExpression implements AuditCriterion, ExtendableCrit
|
|||
entityName,
|
||||
propertyNameGetter
|
||||
);
|
||||
String componentPrefix = CriteriaTools.determineComponentPropertyPrefix(
|
||||
enversService,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
effectiveAlias
|
||||
);
|
||||
String prefixedPropertyName = componentPrefix.concat( propertyName );
|
||||
|
||||
CriteriaTools.checkPropertyNotARelation( enversService, entityName, propertyName );
|
||||
CriteriaTools.checkPropertyNotARelation( enversService, entityName, prefixedPropertyName );
|
||||
|
||||
// Make sure our conditions are ANDed together even if the parent Parameters have a different connective
|
||||
Parameters subParams = parameters.addSubParameters( Parameters.AND );
|
||||
|
@ -78,17 +86,34 @@ public class AggregatedAuditExpression implements AuditCriterion, ExtendableCrit
|
|||
// Adding all specified conditions both to the main query, as well as to the
|
||||
// aggregated one.
|
||||
for ( AuditCriterion versionsCriteria : criterions ) {
|
||||
versionsCriteria.addToQuery( enversService, versionsReader, aliasToEntityNameMap, effectiveAlias, qb, subParams );
|
||||
versionsCriteria.addToQuery( enversService, versionsReader, aliasToEntityNameMap, subQueryAlias, subQb, subQb.getRootParameters() );
|
||||
versionsCriteria.addToQuery(
|
||||
enversService,
|
||||
versionsReader,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
effectiveAlias,
|
||||
qb,
|
||||
subParams
|
||||
);
|
||||
|
||||
versionsCriteria.addToQuery(
|
||||
enversService,
|
||||
versionsReader,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
subQueryAlias,
|
||||
subQb,
|
||||
subQb.getRootParameters()
|
||||
);
|
||||
}
|
||||
|
||||
// Setting the desired projection of the aggregated query
|
||||
switch ( mode ) {
|
||||
case MIN:
|
||||
subQb.addProjection( "min", subQb.getAlias(), propertyName, false );
|
||||
subQb.addProjection( "min", subQb.getAlias(), prefixedPropertyName, false );
|
||||
break;
|
||||
case MAX:
|
||||
subQb.addProjection( "max", subQb.getAlias(), propertyName, false );
|
||||
subQb.addProjection( "max", subQb.getAlias(), prefixedPropertyName, false );
|
||||
}
|
||||
|
||||
// Correlating subquery with the outer query by entity id. See JIRA HHH-7827.
|
||||
|
@ -102,7 +127,7 @@ public class AggregatedAuditExpression implements AuditCriterion, ExtendableCrit
|
|||
}
|
||||
|
||||
// Adding the constrain on the result of the aggregated criteria
|
||||
subParams.addWhere( effectiveAlias, propertyName, "=", subQb );
|
||||
subParams.addWhere( effectiveAlias, prefixedPropertyName, "=", subQb );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -36,6 +36,7 @@ public class AuditConjunction implements AuditCriterion, ExtendableCriterion {
|
|||
EnversService enversService,
|
||||
AuditReaderImplementor versionsReader,
|
||||
Map<String, String> aliasToEntityNameMap,
|
||||
Map<String, String> aliasToComponentPropertyNameMap,
|
||||
String alias,
|
||||
QueryBuilder qb,
|
||||
Parameters parameters) {
|
||||
|
@ -46,7 +47,15 @@ public class AuditConjunction implements AuditCriterion, ExtendableCriterion {
|
|||
}
|
||||
else {
|
||||
for ( AuditCriterion criterion : criterions ) {
|
||||
criterion.addToQuery( enversService, versionsReader, aliasToEntityNameMap, alias, qb, andParameters );
|
||||
criterion.addToQuery(
|
||||
enversService,
|
||||
versionsReader,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
alias,
|
||||
qb,
|
||||
andParameters
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ public interface AuditCriterion {
|
|||
EnversService enversService,
|
||||
AuditReaderImplementor versionsReader,
|
||||
Map<String, String> aliasToEntityNameMap,
|
||||
Map<String, String> aliasToComponentPropertyNameMap,
|
||||
String baseAlias,
|
||||
QueryBuilder qb,
|
||||
Parameters parameters);
|
||||
|
|
|
@ -36,6 +36,7 @@ public class AuditDisjunction implements AuditCriterion, ExtendableCriterion {
|
|||
EnversService enversService,
|
||||
AuditReaderImplementor versionsReader,
|
||||
Map<String, String> aliasToEntityNameMap,
|
||||
Map<String, String> aliasToComponentPropertyNameMap,
|
||||
String alias,
|
||||
QueryBuilder qb,
|
||||
Parameters parameters) {
|
||||
|
@ -46,7 +47,15 @@ public class AuditDisjunction implements AuditCriterion, ExtendableCriterion {
|
|||
}
|
||||
else {
|
||||
for ( AuditCriterion criterion : criterions ) {
|
||||
criterion.addToQuery( enversService, versionsReader, aliasToEntityNameMap, alias, qb, orParameters );
|
||||
criterion.addToQuery(
|
||||
enversService,
|
||||
versionsReader,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
alias,
|
||||
qb,
|
||||
orParameters
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -240,9 +240,15 @@ public class AuditFunction implements AuditProjection {
|
|||
EnversService enversService,
|
||||
AuditReaderImplementor auditReader,
|
||||
Map<String, String> aliasToEntityNameMap,
|
||||
Map<String, String> aliasToComponentPropertyNameMap,
|
||||
String baseAlias,
|
||||
QueryBuilder queryBuilder) {
|
||||
queryBuilder.addProjection( enversService.getConfig(), aliasToEntityNameMap, this );
|
||||
queryBuilder.addProjection(
|
||||
enversService.getConfig(),
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -405,20 +405,33 @@ public class AuditProperty<T> implements AuditProjection {
|
|||
// Projection on this property
|
||||
|
||||
@Override
|
||||
public void addProjectionToQuery(EnversService enversService, AuditReaderImplementor auditReader,
|
||||
Map<String, String> aliasToEntityNameMap, String baseAlias, QueryBuilder queryBuilder) {
|
||||
public void addProjectionToQuery(
|
||||
EnversService enversService,
|
||||
AuditReaderImplementor auditReader,
|
||||
Map<String, String> aliasToEntityNameMap,
|
||||
Map<String, String> aliasToComponentPropertyNameMap,
|
||||
String baseAlias,
|
||||
QueryBuilder queryBuilder) {
|
||||
String projectionEntityAlias = getAlias( baseAlias );
|
||||
String projectionEntityName = aliasToEntityNameMap.get( projectionEntityAlias );
|
||||
String propertyName = CriteriaTools.determinePropertyName(
|
||||
enversService,
|
||||
auditReader,
|
||||
projectionEntityName,
|
||||
propertyNameGetter );
|
||||
propertyNameGetter
|
||||
);
|
||||
String propertyNamePrefix = CriteriaTools.determineComponentPropertyPrefix(
|
||||
enversService,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
projectionEntityAlias
|
||||
);
|
||||
queryBuilder.addProjection(
|
||||
null,
|
||||
projectionEntityAlias,
|
||||
propertyName,
|
||||
false );
|
||||
propertyNamePrefix.concat( propertyName ),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
// Order
|
||||
|
@ -438,7 +451,12 @@ public class AuditProperty<T> implements AuditProjection {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Object convertQueryResult(EnversService enversService, EntityInstantiator entityInstantiator, String entityName, Number revision, Object value) {
|
||||
public Object convertQueryResult(
|
||||
EnversService enversService,
|
||||
EntityInstantiator entityInstantiator,
|
||||
String entityName,
|
||||
Number revision,
|
||||
Object value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,9 +21,9 @@ import org.hibernate.envers.query.criteria.AuditCriterion;
|
|||
* the corresponding entity name. The effect alias is either the alias that has been
|
||||
* specified at creation time of this expression or if that alias is null, the base
|
||||
* alias is used. This calculation is done in the
|
||||
* {@link AuditCriterion#addToQuery(EnversService, AuditReaderImplementor, Map, String, QueryBuilder, Parameters)}
|
||||
* {@link AuditCriterion#addToQuery(EnversService, AuditReaderImplementor, Map, Map, String, QueryBuilder, Parameters)}
|
||||
* implementation and then delegated for the concrete work to the template method
|
||||
* {@link #addToQuery(EnversService, AuditReaderImplementor, String, String, QueryBuilder, Parameters)}.
|
||||
* {@link #addToQuery(EnversService, AuditReaderImplementor, String, String, String, QueryBuilder, Parameters)}
|
||||
*
|
||||
* @author Felix Feisst (feisst dot felix at gmail dot com)
|
||||
*/
|
||||
|
@ -40,12 +40,19 @@ abstract class AbstractAtomicExpression implements AuditCriterion {
|
|||
EnversService enversService,
|
||||
AuditReaderImplementor versionsReader,
|
||||
Map<String, String> aliasToEntityNameMap,
|
||||
Map<String, String> aliasToComponentPropertyNameMap,
|
||||
String baseAlias,
|
||||
QueryBuilder qb,
|
||||
Parameters parameters) {
|
||||
final String effectiveAlias = alias == null ? baseAlias : alias;
|
||||
final String entityName = aliasToEntityNameMap.get( effectiveAlias );
|
||||
addToQuery(enversService, versionsReader, entityName, effectiveAlias, qb, parameters);
|
||||
final String componentPrefix = CriteriaTools.determineComponentPropertyPrefix(
|
||||
enversService,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
effectiveAlias
|
||||
);
|
||||
addToQuery(enversService, versionsReader, entityName, effectiveAlias, componentPrefix, qb, parameters);
|
||||
}
|
||||
|
||||
protected abstract void addToQuery(
|
||||
|
@ -53,6 +60,7 @@ abstract class AbstractAtomicExpression implements AuditCriterion {
|
|||
AuditReaderImplementor versionsReader,
|
||||
String entityName,
|
||||
String alias,
|
||||
String componentPrefix,
|
||||
QueryBuilder qb,
|
||||
Parameters parameters);
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ public class BetweenAuditExpression extends AbstractAtomicExpression {
|
|||
AuditReaderImplementor versionsReader,
|
||||
String entityName,
|
||||
String alias,
|
||||
String componentPrefix,
|
||||
QueryBuilder qb,
|
||||
Parameters parameters) {
|
||||
String propertyName = CriteriaTools.determinePropertyName(
|
||||
|
@ -41,10 +42,11 @@ public class BetweenAuditExpression extends AbstractAtomicExpression {
|
|||
entityName,
|
||||
propertyNameGetter
|
||||
);
|
||||
CriteriaTools.checkPropertyNotARelation( enversService, entityName, propertyName );
|
||||
String prefixedPropertyName = componentPrefix.concat( propertyName );
|
||||
CriteriaTools.checkPropertyNotARelation( enversService, entityName, prefixedPropertyName );
|
||||
|
||||
Parameters subParams = parameters.addSubParameters( Parameters.AND );
|
||||
subParams.addWhereWithParam( alias, propertyName, ">=", lo );
|
||||
subParams.addWhereWithParam( alias, propertyName, "<=", hi );
|
||||
subParams.addWhereWithParam( alias, prefixedPropertyName, ">=", lo );
|
||||
subParams.addWhereWithParam( alias, prefixedPropertyName, "<=", hi );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,10 +10,12 @@ import java.util.Arrays;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.envers.boot.internal.EnversService;
|
||||
import org.hibernate.envers.exception.AuditException;
|
||||
import org.hibernate.envers.internal.entities.ComponentDescription;
|
||||
import org.hibernate.envers.internal.entities.RelationDescription;
|
||||
import org.hibernate.envers.internal.entities.RelationType;
|
||||
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
||||
|
@ -66,6 +68,13 @@ public abstract class CriteriaTools {
|
|||
);
|
||||
}
|
||||
|
||||
public static ComponentDescription getComponent(
|
||||
EnversService enversService,
|
||||
String entityName,
|
||||
String propertyName) {
|
||||
return enversService.getEntitiesConfigurations().getComponentDescription( entityName, propertyName );
|
||||
}
|
||||
|
||||
public static String determinePropertyName(
|
||||
EnversService enversService,
|
||||
AuditReaderImplementor versionsReader,
|
||||
|
@ -119,6 +128,36 @@ public abstract class CriteriaTools {
|
|||
return propertyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param enversService The EnversService
|
||||
* @param aliasToEntityNameMap the map from aliases to entity names
|
||||
* @param aliasToComponentPropertyNameMap the map from aliases to component property name, if an alias is for a
|
||||
* component
|
||||
* @param alias the alias
|
||||
* @return The prefix that has to be used when referring to a property of a component. If no prefix is required or
|
||||
* the alias is not a component, the empty string is returned (but never null)
|
||||
*/
|
||||
public static String determineComponentPropertyPrefix(
|
||||
EnversService enversService,
|
||||
Map<String, String> aliasToEntityNameMap,
|
||||
Map<String, String> aliasToComponentPropertyNameMap,
|
||||
String alias) {
|
||||
String componentPrefix = "";
|
||||
final String entityName = aliasToEntityNameMap.get( alias );
|
||||
final String owningComponentPropertyName = aliasToComponentPropertyNameMap.get( alias );
|
||||
if ( owningComponentPropertyName != null ) {
|
||||
final ComponentDescription componentDescription = CriteriaTools.getComponent(
|
||||
enversService,
|
||||
entityName,
|
||||
owningComponentPropertyName
|
||||
);
|
||||
if ( componentDescription.getType() == ComponentDescription.ComponentType.ONE ) {
|
||||
componentPrefix = componentDescription.getPropertyName().concat( "_" );
|
||||
}
|
||||
}
|
||||
return componentPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sessionFactory Session factory.
|
||||
* @param entityName Entity name.
|
||||
|
|
|
@ -40,12 +40,14 @@ public class FunctionFunctionAuditExpression implements AuditCriterion {
|
|||
EnversService enversService,
|
||||
AuditReaderImplementor auditReader,
|
||||
Map<String, String> aliasToEntityNameMap,
|
||||
Map<String, String> aliasToComponentPropertyNameMap,
|
||||
String baseAlias,
|
||||
QueryBuilder queryBuilder,
|
||||
Parameters parameters) {
|
||||
parameters.addWhereWithFunction(
|
||||
enversService.getConfig(),
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
leftFunction,
|
||||
op,
|
||||
rightFunction
|
||||
|
|
|
@ -45,6 +45,7 @@ public class FunctionPropertyAuditExpression implements AuditCriterion {
|
|||
EnversService enversService,
|
||||
AuditReaderImplementor auditReader,
|
||||
Map<String, String> aliasToEntityNameMap,
|
||||
Map<String, String> aliasToComponentPropertyNameMap,
|
||||
String baseAlias,
|
||||
QueryBuilder queryBuilder,
|
||||
Parameters parameters) {
|
||||
|
@ -54,13 +55,22 @@ public class FunctionPropertyAuditExpression implements AuditCriterion {
|
|||
enversService,
|
||||
auditReader,
|
||||
entityName,
|
||||
propertyNameGetter );
|
||||
CriteriaTools.checkPropertyNotARelation( enversService, entityName, propertyName );
|
||||
propertyNameGetter
|
||||
);
|
||||
String propertyNamePrefix = CriteriaTools.determineComponentPropertyPrefix(
|
||||
enversService,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
effectiveAlias
|
||||
);
|
||||
String prefixedPropertyName = propertyNamePrefix.concat( propertyName );
|
||||
CriteriaTools.checkPropertyNotARelation( enversService, entityName, prefixedPropertyName );
|
||||
parameters.addWhereWithFunction(
|
||||
enversService.getConfig(),
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
effectiveAlias,
|
||||
propertyName,
|
||||
prefixedPropertyName,
|
||||
op,
|
||||
function
|
||||
);
|
||||
|
|
|
@ -33,6 +33,7 @@ public class IdentifierEqAuditExpression extends AbstractAtomicExpression {
|
|||
AuditReaderImplementor versionsReader,
|
||||
String entityName,
|
||||
String alias,
|
||||
String componentPrefix,
|
||||
QueryBuilder qb,
|
||||
Parameters parameters) {
|
||||
String prefix = enversService.getConfig().getOriginalIdPropertyName();
|
||||
|
|
|
@ -27,8 +27,12 @@ public class IlikeAuditExpression extends AbstractAtomicExpression {
|
|||
@Override
|
||||
protected void addToQuery(
|
||||
EnversService enversService,
|
||||
AuditReaderImplementor versionsReader, String entityName,
|
||||
String alias, QueryBuilder qb, Parameters parameters) {
|
||||
AuditReaderImplementor versionsReader,
|
||||
String entityName,
|
||||
String alias,
|
||||
String componentPrefix,
|
||||
QueryBuilder qb,
|
||||
Parameters parameters) {
|
||||
|
||||
String propertyName = CriteriaTools.determinePropertyName(
|
||||
enversService,
|
||||
|
@ -36,9 +40,10 @@ public class IlikeAuditExpression extends AbstractAtomicExpression {
|
|||
entityName,
|
||||
propertyNameGetter
|
||||
);
|
||||
CriteriaTools.checkPropertyNotARelation( enversService, entityName, propertyName );
|
||||
|
||||
parameters.addWhereWithFunction( alias, propertyName, " lower ", " like ", value.toLowerCase( Locale.ROOT ) );
|
||||
String prefixedPropertyName = componentPrefix.concat( propertyName );
|
||||
CriteriaTools.checkPropertyNotARelation( enversService, entityName, prefixedPropertyName );
|
||||
parameters.addWhereWithFunction( alias, prefixedPropertyName, " lower ", " like ", value.toLowerCase( Locale.ROOT ) );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ public class InAuditExpression extends AbstractAtomicExpression {
|
|||
AuditReaderImplementor versionsReader,
|
||||
String entityName,
|
||||
String alias,
|
||||
String componentPrefix,
|
||||
QueryBuilder qb,
|
||||
Parameters parameters) {
|
||||
String propertyName = CriteriaTools.determinePropertyName(
|
||||
|
@ -39,7 +40,8 @@ public class InAuditExpression extends AbstractAtomicExpression {
|
|||
entityName,
|
||||
propertyNameGetter
|
||||
);
|
||||
CriteriaTools.checkPropertyNotARelation( enversService, entityName, propertyName );
|
||||
parameters.addWhereWithParams( alias, propertyName, "in (", values, ")" );
|
||||
String prefixedPropertyName = componentPrefix.concat( propertyName );
|
||||
CriteriaTools.checkPropertyNotARelation( enversService, entityName, prefixedPropertyName );
|
||||
parameters.addWhereWithParams( alias, prefixedPropertyName, "in (", values, ")" );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,12 +32,30 @@ public class LogicalAuditExpression implements AuditCriterion {
|
|||
EnversService enversService,
|
||||
AuditReaderImplementor versionsReader,
|
||||
Map<String, String> aliasToEntityNameMap,
|
||||
Map<String, String> aliasToComponentPropertyNameMap,
|
||||
String alias,
|
||||
QueryBuilder qb,
|
||||
Parameters parameters) {
|
||||
Parameters opParameters = parameters.addSubParameters( op );
|
||||
|
||||
lhs.addToQuery( enversService, versionsReader, aliasToEntityNameMap, alias, qb, opParameters.addSubParameters( "and" ) );
|
||||
rhs.addToQuery( enversService, versionsReader, aliasToEntityNameMap, alias, qb, opParameters.addSubParameters( "and" ) );
|
||||
lhs.addToQuery(
|
||||
enversService,
|
||||
versionsReader,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
alias,
|
||||
qb,
|
||||
opParameters.addSubParameters( "and" )
|
||||
);
|
||||
|
||||
rhs.addToQuery(
|
||||
enversService,
|
||||
versionsReader,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
alias,
|
||||
qb,
|
||||
opParameters.addSubParameters( "and" )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,9 +28,18 @@ public class NotAuditExpression implements AuditCriterion {
|
|||
EnversService enversService,
|
||||
AuditReaderImplementor versionsReader,
|
||||
Map<String, String> aliasToEntityNameMap,
|
||||
Map<String, String> aliasToComponentPropertyNameMap,
|
||||
String alias,
|
||||
QueryBuilder qb,
|
||||
Parameters parameters) {
|
||||
criterion.addToQuery( enversService, versionsReader, aliasToEntityNameMap, alias, qb, parameters.addNegatedParameters() );
|
||||
criterion.addToQuery(
|
||||
enversService,
|
||||
versionsReader,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
alias,
|
||||
qb,
|
||||
parameters.addNegatedParameters()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ public class NotNullAuditExpression extends AbstractAtomicExpression {
|
|||
AuditReaderImplementor versionsReader,
|
||||
String entityName,
|
||||
String alias,
|
||||
String componentPrefix,
|
||||
QueryBuilder qb,
|
||||
Parameters parameters) {
|
||||
String propertyName = CriteriaTools.determinePropertyName(
|
||||
|
@ -42,10 +43,11 @@ public class NotNullAuditExpression extends AbstractAtomicExpression {
|
|||
entityName,
|
||||
propertyNameGetter
|
||||
);
|
||||
RelationDescription relatedEntity = CriteriaTools.getRelatedEntity( enversService, entityName, propertyName );
|
||||
String prefixedPropertyName = componentPrefix.concat( propertyName );
|
||||
RelationDescription relatedEntity = CriteriaTools.getRelatedEntity( enversService, entityName, prefixedPropertyName );
|
||||
|
||||
if ( relatedEntity == null ) {
|
||||
parameters.addNotNullRestriction( alias, propertyName );
|
||||
parameters.addNotNullRestriction( alias, prefixedPropertyName );
|
||||
}
|
||||
else if ( relatedEntity.getRelationType() == RelationType.TO_ONE ) {
|
||||
relatedEntity.getIdMapper().addIdEqualsToQuery( parameters, null, alias, null, false );
|
||||
|
|
|
@ -34,6 +34,7 @@ public class NullAuditExpression extends AbstractAtomicExpression {
|
|||
AuditReaderImplementor versionsReader,
|
||||
String entityName,
|
||||
String alias,
|
||||
String componentPrefix,
|
||||
QueryBuilder qb,
|
||||
Parameters parameters) {
|
||||
String propertyName = CriteriaTools.determinePropertyName(
|
||||
|
@ -43,9 +44,10 @@ public class NullAuditExpression extends AbstractAtomicExpression {
|
|||
propertyNameGetter
|
||||
);
|
||||
RelationDescription relatedEntity = CriteriaTools.getRelatedEntity( enversService, entityName, propertyName );
|
||||
String prefixedPropertyName = componentPrefix.concat( propertyName );
|
||||
|
||||
if ( relatedEntity == null ) {
|
||||
parameters.addNullRestriction( alias, propertyName );
|
||||
parameters.addNullRestriction( alias, prefixedPropertyName );
|
||||
}
|
||||
else if ( relatedEntity.getRelationType() == RelationType.TO_ONE ) {
|
||||
relatedEntity.getIdMapper().addIdEqualsToQuery( parameters, null, alias, null, true );
|
||||
|
|
|
@ -44,6 +44,7 @@ public class PropertyAuditExpression implements AuditCriterion {
|
|||
EnversService enversService,
|
||||
AuditReaderImplementor versionsReader,
|
||||
Map<String, String> aliasToEntityNameMap,
|
||||
Map<String, String> aliasToComponentPropertyNameMap,
|
||||
String baseAlias,
|
||||
QueryBuilder qb,
|
||||
Parameters parameters) {
|
||||
|
@ -57,15 +58,29 @@ public class PropertyAuditExpression implements AuditCriterion {
|
|||
entityName,
|
||||
propertyNameGetter
|
||||
);
|
||||
CriteriaTools.checkPropertyNotARelation( enversService, entityName, propertyName );
|
||||
String propertyNamePrefix = CriteriaTools.determineComponentPropertyPrefix(
|
||||
enversService,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
effectiveAlias
|
||||
);
|
||||
String otherPropertyNamePrefix = CriteriaTools.determineComponentPropertyPrefix(
|
||||
enversService,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
effectiveOtherAlias
|
||||
);
|
||||
String prefixedPropertyName = propertyNamePrefix.concat( propertyName );
|
||||
String prefixedOtherPropertyName = otherPropertyNamePrefix.concat( otherPropertyName );
|
||||
CriteriaTools.checkPropertyNotARelation( enversService, entityName, prefixedPropertyName );
|
||||
/*
|
||||
* Check that the other property name is not a relation. However, we can only
|
||||
* do this for audited entities. If the other property belongs to a non-audited
|
||||
* entity, we have to skip this check.
|
||||
*/
|
||||
if ( enversService.getEntitiesConfigurations().isVersioned( otherEntityName ) ) {
|
||||
CriteriaTools.checkPropertyNotARelation( enversService, otherEntityName, otherPropertyName );
|
||||
CriteriaTools.checkPropertyNotARelation( enversService, otherEntityName, prefixedOtherPropertyName );
|
||||
}
|
||||
parameters.addWhere( effectiveAlias, propertyName, op, effectiveOtherAlias, otherPropertyName );
|
||||
parameters.addWhere( effectiveAlias, prefixedPropertyName, op, effectiveOtherAlias, prefixedOtherPropertyName );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ public class PropertyFunctionAuditExpression implements AuditCriterion {
|
|||
EnversService enversService,
|
||||
AuditReaderImplementor auditReader,
|
||||
Map<String, String> aliasToEntityNameMap,
|
||||
Map<String, String> aliasToComponentPropertyNameMap,
|
||||
String baseAlias,
|
||||
QueryBuilder queryBuilder,
|
||||
Parameters parameters) {
|
||||
|
@ -53,11 +54,21 @@ public class PropertyFunctionAuditExpression implements AuditCriterion {
|
|||
* the other property belongs to a non-audited entity, we have to skip this check.
|
||||
*/
|
||||
if ( enversService.getEntitiesConfigurations().isVersioned( otherEntityName ) ) {
|
||||
CriteriaTools.checkPropertyNotARelation( enversService, otherEntityName, otherPropertyName );
|
||||
String otherPropertyNamePrefix = CriteriaTools.determineComponentPropertyPrefix(
|
||||
enversService,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap, effectiveOtherAlias
|
||||
);
|
||||
CriteriaTools.checkPropertyNotARelation(
|
||||
enversService,
|
||||
otherEntityName,
|
||||
otherPropertyNamePrefix.concat( otherPropertyName )
|
||||
);
|
||||
}
|
||||
parameters.addWhereWithFunction(
|
||||
enversService.getConfig(),
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
function,
|
||||
op,
|
||||
effectiveOtherAlias,
|
||||
|
|
|
@ -39,6 +39,7 @@ public class RelatedAuditEqualityExpression extends AbstractAtomicExpression {
|
|||
AuditReaderImplementor versionsReader,
|
||||
String entityName,
|
||||
String alias,
|
||||
String componentPrefix,
|
||||
QueryBuilder qb,
|
||||
Parameters parameters) {
|
||||
String propertyName = CriteriaTools.determinePropertyName(
|
||||
|
@ -48,7 +49,12 @@ public class RelatedAuditEqualityExpression extends AbstractAtomicExpression {
|
|||
propertyNameGetter
|
||||
);
|
||||
|
||||
RelationDescription relatedEntity = CriteriaTools.getRelatedEntity( enversService, entityName, propertyName );
|
||||
RelationDescription relatedEntity = CriteriaTools.getRelatedEntity(
|
||||
enversService,
|
||||
entityName,
|
||||
componentPrefix.concat( propertyName )
|
||||
);
|
||||
|
||||
if ( relatedEntity == null ) {
|
||||
throw new AuditException(
|
||||
"This criterion can only be used on a property that is a relation to another property." );
|
||||
|
|
|
@ -40,6 +40,7 @@ public class RelatedAuditInExpression extends AbstractAtomicExpression {
|
|||
AuditReaderImplementor versionsReader,
|
||||
String entityName,
|
||||
String alias,
|
||||
String componentPrefix,
|
||||
QueryBuilder qb,
|
||||
Parameters parameters) {
|
||||
String propertyName = CriteriaTools.determinePropertyName(
|
||||
|
@ -49,7 +50,12 @@ public class RelatedAuditInExpression extends AbstractAtomicExpression {
|
|||
propertyNameGetter
|
||||
);
|
||||
|
||||
RelationDescription relatedEntity = CriteriaTools.getRelatedEntity( enversService, entityName, propertyName );
|
||||
RelationDescription relatedEntity = CriteriaTools.getRelatedEntity(
|
||||
enversService,
|
||||
entityName,
|
||||
componentPrefix.concat( propertyName )
|
||||
);
|
||||
|
||||
if ( relatedEntity == null ) {
|
||||
throw new AuditException(
|
||||
"The criterion can only be used on a property that is a relation to another property." );
|
||||
|
@ -69,7 +75,7 @@ public class RelatedAuditInExpression extends AbstractAtomicExpression {
|
|||
List<QueryParameterData> qpdList = relatedEntity.getIdMapper().mapToQueryParametersFromId( propertyName );
|
||||
if ( qpdList != null ) {
|
||||
QueryParameterData qpd = qpdList.iterator().next();
|
||||
parameters.addWhereWithParams( alias, qpd.getQueryParameterName(), "in (", ids, ")" );
|
||||
parameters.addWhereWithParams( alias, componentPrefix.concat( qpd.getQueryParameterName() ), "in (", ids, ")" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ public class RevisionTypeAuditExpression extends AbstractAtomicExpression {
|
|||
AuditReaderImplementor versionsReader,
|
||||
String entityName,
|
||||
String alias,
|
||||
String componentPrefix,
|
||||
QueryBuilder qb,
|
||||
Parameters parameters) {
|
||||
parameters.addWhereWithParam( alias, enversService.getConfig().getRevisionTypePropertyName(), op, value );
|
||||
|
|
|
@ -11,6 +11,7 @@ import java.util.Locale;
|
|||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.envers.boot.internal.EnversService;
|
||||
import org.hibernate.envers.exception.AuditException;
|
||||
import org.hibernate.envers.internal.entities.ComponentDescription;
|
||||
import org.hibernate.envers.internal.entities.RelationDescription;
|
||||
import org.hibernate.envers.internal.entities.RelationType;
|
||||
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
||||
|
@ -25,6 +26,7 @@ import org.hibernate.type.Type;
|
|||
* @author Adam Warski (adam at warski dot org)
|
||||
*/
|
||||
public class SimpleAuditExpression extends AbstractAtomicExpression {
|
||||
|
||||
private PropertyNameGetter propertyNameGetter;
|
||||
private Object value;
|
||||
private String op;
|
||||
|
@ -42,6 +44,7 @@ public class SimpleAuditExpression extends AbstractAtomicExpression {
|
|||
AuditReaderImplementor versionsReader,
|
||||
String entityName,
|
||||
String alias,
|
||||
String componentPrefix,
|
||||
QueryBuilder qb,
|
||||
Parameters parameters) {
|
||||
String propertyName = CriteriaTools.determinePropertyName(
|
||||
|
@ -51,7 +54,8 @@ public class SimpleAuditExpression extends AbstractAtomicExpression {
|
|||
propertyNameGetter
|
||||
);
|
||||
|
||||
RelationDescription relatedEntity = CriteriaTools.getRelatedEntity( enversService, entityName, propertyName );
|
||||
String prefixedPropertyName = componentPrefix.concat( propertyName );
|
||||
RelationDescription relatedEntity = CriteriaTools.getRelatedEntity( enversService, entityName, prefixedPropertyName );
|
||||
|
||||
if ( relatedEntity == null ) {
|
||||
// HHH-9178 - Add support to component type equality.
|
||||
|
@ -75,14 +79,14 @@ public class SimpleAuditExpression extends AbstractAtomicExpression {
|
|||
final Object componentValue = componentType.getPropertyValue( value, i, session );
|
||||
parameters.addWhereWithParam(
|
||||
alias,
|
||||
propertyName + "_" + componentType.getPropertyNames()[ i ],
|
||||
prefixedPropertyName + "_" + componentType.getPropertyNames()[ i ],
|
||||
op,
|
||||
componentValue
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
parameters.addWhereWithParam( alias, propertyName, op, value );
|
||||
parameters.addWhereWithParam( alias, prefixedPropertyName, op, value );
|
||||
}
|
||||
}
|
||||
else if ( relatedEntity.getRelationType() == RelationType.TO_ONE ) {
|
||||
|
|
|
@ -37,10 +37,18 @@ public class SimpleFunctionAuditExpression implements AuditCriterion {
|
|||
EnversService enversService,
|
||||
AuditReaderImplementor versionsReader,
|
||||
Map<String, String> aliasToEntityNameMap,
|
||||
Map<String, String> aliasToComponentPropertyNameMap,
|
||||
String baseAlias,
|
||||
QueryBuilder qb,
|
||||
Parameters parameters) {
|
||||
parameters.addWhereWithFunction( enversService.getConfig(), aliasToEntityNameMap, function, op, value );
|
||||
parameters.addWhereWithFunction(
|
||||
enversService.getConfig(),
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
function,
|
||||
op,
|
||||
value
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ public abstract class AbstractAuditQuery implements AuditQueryImplementor {
|
|||
protected String versionsEntityName;
|
||||
protected QueryBuilder qb;
|
||||
protected final Map<String, String> aliasToEntityNameMap = new HashMap<>();
|
||||
protected final Map<String, String> aliasToComponentPropertyNameMap = new HashMap<>();
|
||||
|
||||
protected boolean hasOrder;
|
||||
|
||||
|
@ -138,7 +139,14 @@ public abstract class AbstractAuditQuery implements AuditQueryImplementor {
|
|||
String projectionEntityAlias = projection.getAlias( REFERENCED_ENTITY_ALIAS );
|
||||
String projectionEntityName = aliasToEntityNameMap.get( projectionEntityAlias );
|
||||
registerProjection( projectionEntityName, projection );
|
||||
projection.addProjectionToQuery( enversService, versionsReader, aliasToEntityNameMap, REFERENCED_ENTITY_ALIAS, qb );
|
||||
projection.addProjectionToQuery(
|
||||
enversService,
|
||||
versionsReader,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
REFERENCED_ENTITY_ALIAS,
|
||||
qb
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -162,7 +170,18 @@ public abstract class AbstractAuditQuery implements AuditQueryImplementor {
|
|||
orderEntityName,
|
||||
orderData.getPropertyName()
|
||||
);
|
||||
qb.addOrder( orderEntityAlias, propertyName, orderData.isAscending(), orderData.getNullPrecedence() );
|
||||
String componentPrefix = CriteriaTools.determineComponentPropertyPrefix(
|
||||
enversService,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
orderEntityAlias
|
||||
);
|
||||
qb.addOrder(
|
||||
orderEntityAlias,
|
||||
componentPrefix.concat( propertyName ),
|
||||
orderData.isAscending(),
|
||||
orderData.getNullPrecedence()
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -187,6 +206,7 @@ public abstract class AbstractAuditQuery implements AuditQueryImplementor {
|
|||
associationName,
|
||||
joinType,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
REFERENCED_ENTITY_ALIAS,
|
||||
alias
|
||||
);
|
||||
|
@ -312,7 +332,15 @@ public abstract class AbstractAuditQuery implements AuditQueryImplementor {
|
|||
if ( projections.size() == 1 ) {
|
||||
// qr is the value of the projection itself
|
||||
final Pair<String, AuditProjection> projection = projections.get( 0 );
|
||||
result.add( projection.getSecond().convertQueryResult( enversService, entityInstantiator, projection.getFirst(), revision, qr ) );
|
||||
result.add(
|
||||
projection.getSecond().convertQueryResult(
|
||||
enversService,
|
||||
entityInstantiator,
|
||||
projection.getFirst(),
|
||||
revision,
|
||||
qr
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
// qr is an array where each of its components holds the value of corresponding projection
|
||||
|
@ -320,7 +348,13 @@ public abstract class AbstractAuditQuery implements AuditQueryImplementor {
|
|||
Object[] tresults = new Object[qresults.length];
|
||||
for ( int i = 0; i < qresults.length; i++ ) {
|
||||
final Pair<String, AuditProjection> projection = projections.get( i );
|
||||
tresults[i] = projection.getSecond().convertQueryResult( enversService, entityInstantiator, projection.getFirst(), revision, qresults[i] );
|
||||
tresults[i] = projection.getSecond().convertQueryResult(
|
||||
enversService,
|
||||
entityInstantiator,
|
||||
projection.getFirst(),
|
||||
revision,
|
||||
qresults[i]
|
||||
);
|
||||
}
|
||||
result.add( tresults );
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ import org.hibernate.envers.RevisionType;
|
|||
import org.hibernate.envers.boot.internal.EnversService;
|
||||
import org.hibernate.envers.configuration.Configuration;
|
||||
import org.hibernate.envers.exception.AuditException;
|
||||
import org.hibernate.envers.internal.entities.ComponentDescription;
|
||||
import org.hibernate.envers.internal.entities.ComponentDescription.ComponentType;
|
||||
import org.hibernate.envers.internal.entities.RelationDescription;
|
||||
import org.hibernate.envers.internal.entities.RelationType;
|
||||
import org.hibernate.envers.internal.entities.mapper.id.IdMapper;
|
||||
|
@ -51,10 +53,12 @@ public class AuditAssociationQueryImpl<Q extends AuditQueryImplementor>
|
|||
private final JoinType joinType;
|
||||
private final String entityName;
|
||||
private final RelationDescription relationDescription;
|
||||
private final ComponentDescription componentDescription;
|
||||
private final String ownerAlias;
|
||||
private final String ownerEntityName;
|
||||
private final String alias;
|
||||
private final Map<String, String> aliasToEntityNameMap;
|
||||
private final Map<String, String> aliasToComponentPropertyNameMap;
|
||||
private final List<AuditCriterion> criterions = new ArrayList<>();
|
||||
private final Parameters parameters;
|
||||
private final List<AuditAssociationQueryImpl<?>> associationQueries = new ArrayList<>();
|
||||
|
@ -68,6 +72,7 @@ public class AuditAssociationQueryImpl<Q extends AuditQueryImplementor>
|
|||
final String propertyName,
|
||||
final JoinType joinType,
|
||||
final Map<String, String> aliasToEntityNameMap,
|
||||
final Map<String, String> aliasToComponentPropertyNameMap,
|
||||
final String ownerAlias,
|
||||
final String userSuppliedAlias) {
|
||||
this.enversService = enversService;
|
||||
|
@ -77,12 +82,26 @@ public class AuditAssociationQueryImpl<Q extends AuditQueryImplementor>
|
|||
this.joinType = joinType;
|
||||
|
||||
ownerEntityName = aliasToEntityNameMap.get( ownerAlias );
|
||||
final RelationDescription relationDescription = CriteriaTools.getRelatedEntity(
|
||||
this.ownerAlias = ownerAlias;
|
||||
this.alias = userSuppliedAlias == null ? queryBuilder.generateAlias() : userSuppliedAlias;
|
||||
|
||||
String componentPrefix = CriteriaTools.determineComponentPropertyPrefix(
|
||||
enversService,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
ownerAlias
|
||||
);
|
||||
String prefixedPropertyName = componentPrefix.concat( propertyName );
|
||||
|
||||
relationDescription = CriteriaTools.getRelatedEntity(
|
||||
enversService,
|
||||
ownerEntityName,
|
||||
propertyName
|
||||
prefixedPropertyName
|
||||
);
|
||||
if ( relationDescription == null ) {
|
||||
|
||||
componentDescription = CriteriaTools.getComponent( enversService, ownerEntityName, prefixedPropertyName );
|
||||
|
||||
if ( relationDescription == null && componentDescription == null ) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
Locale.ENGLISH,
|
||||
|
@ -92,12 +111,17 @@ public class AuditAssociationQueryImpl<Q extends AuditQueryImplementor>
|
|||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( relationDescription != null ) {
|
||||
this.entityName = relationDescription.getToEntityName();
|
||||
this.relationDescription = relationDescription;
|
||||
this.ownerAlias = ownerAlias;
|
||||
this.alias = userSuppliedAlias == null ? queryBuilder.generateAlias() : userSuppliedAlias;
|
||||
}
|
||||
else {
|
||||
aliasToComponentPropertyNameMap.put( alias, componentDescription.getPropertyName() );
|
||||
this.entityName = ownerEntityName;
|
||||
}
|
||||
aliasToEntityNameMap.put( this.alias, entityName );
|
||||
this.aliasToEntityNameMap = aliasToEntityNameMap;
|
||||
this.aliasToComponentPropertyNameMap = aliasToComponentPropertyNameMap;
|
||||
parameters = queryBuilder.addParameters( this.alias );
|
||||
}
|
||||
|
||||
|
@ -142,6 +166,7 @@ public class AuditAssociationQueryImpl<Q extends AuditQueryImplementor>
|
|||
associationName,
|
||||
joinType,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
this.alias,
|
||||
alias
|
||||
);
|
||||
|
@ -162,7 +187,14 @@ public class AuditAssociationQueryImpl<Q extends AuditQueryImplementor>
|
|||
String projectionEntityAlias = projection.getAlias( alias );
|
||||
String projectionEntityName = aliasToEntityNameMap.get( projectionEntityAlias );
|
||||
registerProjection( projectionEntityName, projection );
|
||||
projection.addProjectionToQuery( enversService, auditReader, aliasToEntityNameMap, alias, queryBuilder );
|
||||
projection.addProjectionToQuery(
|
||||
enversService,
|
||||
auditReader,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
alias,
|
||||
queryBuilder
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -177,7 +209,18 @@ public class AuditAssociationQueryImpl<Q extends AuditQueryImplementor>
|
|||
orderEntityName,
|
||||
orderData.getPropertyName()
|
||||
);
|
||||
queryBuilder.addOrder( orderEntityAlias, propertyName, orderData.isAscending(), orderData.getNullPrecedence() );
|
||||
String componentPrefix = CriteriaTools.determineComponentPropertyPrefix(
|
||||
enversService,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
orderEntityAlias
|
||||
);
|
||||
queryBuilder.addOrder(
|
||||
orderEntityAlias,
|
||||
componentPrefix.concat( propertyName ),
|
||||
orderData.isAscending(),
|
||||
orderData.getNullPrecedence()
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -240,7 +283,31 @@ public class AuditAssociationQueryImpl<Q extends AuditQueryImplementor>
|
|||
}
|
||||
|
||||
protected void addCriterionsToQuery(AuditReaderImplementor versionsReader) {
|
||||
final Configuration configuration = enversService.getConfig();
|
||||
if ( relationDescription != null ) {
|
||||
createEntityJoin( enversService.getConfig() );
|
||||
}
|
||||
else {
|
||||
createComponentJoin( enversService.getConfig() );
|
||||
}
|
||||
|
||||
for ( AuditCriterion criterion : criterions ) {
|
||||
criterion.addToQuery(
|
||||
enversService,
|
||||
versionsReader,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
alias,
|
||||
queryBuilder,
|
||||
parameters
|
||||
);
|
||||
}
|
||||
|
||||
for ( AuditAssociationQueryImpl<?> query : associationQueries ) {
|
||||
query.addCriterionsToQuery( versionsReader );
|
||||
}
|
||||
}
|
||||
|
||||
private void createEntityJoin(Configuration configuration) {
|
||||
boolean targetIsAudited = enversService.getEntitiesConfigurations().isVersioned( entityName );
|
||||
String targetEntityName = entityName;
|
||||
if ( targetIsAudited ) {
|
||||
|
@ -414,22 +481,85 @@ public class AuditAssociationQueryImpl<Q extends AuditQueryImplementor>
|
|||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for ( AuditCriterion criterion : criterions ) {
|
||||
criterion.addToQuery(
|
||||
enversService,
|
||||
versionsReader,
|
||||
aliasToEntityNameMap,
|
||||
private void createComponentJoin(Configuration configuration) {
|
||||
String originalIdPropertyName = configuration.getOriginalIdPropertyName();
|
||||
String revisionPropertyPath = configuration.getRevisionNumberPath();
|
||||
if ( componentDescription.getType() == ComponentType.MANY ) {
|
||||
// join middle_entity
|
||||
Parameters joinConditionParameters = queryBuilder.addJoin(
|
||||
joinType,
|
||||
componentDescription.getAuditMiddleEntityName(),
|
||||
alias,
|
||||
queryBuilder,
|
||||
parameters
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
for ( final AuditAssociationQueryImpl<?> sub : associationQueries ) {
|
||||
sub.addCriterionsToQuery( versionsReader );
|
||||
}
|
||||
String middleOriginalIdPropertyPath = alias + "." + originalIdPropertyName;
|
||||
// join condition: owner.reference_id = middle.id_ref_ing
|
||||
String ownerPrefix = ownerAlias + "." + originalIdPropertyName;
|
||||
MiddleIdData middleIdData = componentDescription.getMiddleIdData();
|
||||
middleIdData.getPrefixedMapper().addIdsEqualToQuery(
|
||||
joinConditionParameters,
|
||||
middleOriginalIdPropertyPath,
|
||||
middleIdData.getOriginalMapper(),
|
||||
ownerPrefix
|
||||
);
|
||||
|
||||
// filter revisions of middle entity
|
||||
Parameters middleParameters = queryBuilder.addParameters( alias );
|
||||
Parameters middleParametersToUse = middleParameters;
|
||||
if ( joinType == JoinType.LEFT ) {
|
||||
middleParametersToUse = middleParameters.addSubParameters( Parameters.OR );
|
||||
middleParametersToUse.addNullRestriction( revisionPropertyPath, true );
|
||||
middleParametersToUse = middleParametersToUse.addSubParameters( Parameters.AND );
|
||||
}
|
||||
configuration.getAuditStrategy().addAssociationAtRevisionRestriction(
|
||||
queryBuilder,
|
||||
middleParametersToUse,
|
||||
revisionPropertyPath,
|
||||
configuration.getRevisionEndFieldName(),
|
||||
true,
|
||||
middleIdData,
|
||||
componentDescription.getAuditMiddleEntityName(),
|
||||
middleOriginalIdPropertyPath,
|
||||
revisionPropertyPath,
|
||||
originalIdPropertyName,
|
||||
alias,
|
||||
true
|
||||
);
|
||||
|
||||
// filter deleted middle entities
|
||||
String middleRevTypePropertyPath = middleOriginalIdPropertyPath + "." + configuration.getRevisionTypePropertyName();
|
||||
if ( joinType == JoinType.LEFT ) {
|
||||
middleParametersToUse = middleParameters.addSubParameters( Parameters.OR );
|
||||
middleParametersToUse.addNullRestriction( middleRevTypePropertyPath, false );
|
||||
}
|
||||
middleParametersToUse.addWhereWithParam( middleRevTypePropertyPath, false, "!=", RevisionType.DEL );
|
||||
}
|
||||
else {
|
||||
// ComponentType.ONE
|
||||
/*
|
||||
* The properties of a single component are directly mapped on the owner entity. Therefore no join would be
|
||||
* required to access those properties (except the case an explicit on-clause has been specified). However,
|
||||
* the user has supplied an alias and may be accessing properties of this component through that alias: If
|
||||
* no join is generated, the 'virtual' alias has to be retranslated to the owning entity alias. To keep
|
||||
* things simple a join on the owning entity itself is generated. The join is cheaper than other audit joins
|
||||
* because we can join on the complete primary key (id + rev) and do not have to range filter on the target
|
||||
* revision number.
|
||||
*/
|
||||
String targetEntityName = configuration.getAuditEntityName( entityName );
|
||||
Parameters joinConditionParameters = queryBuilder.addJoin( joinType, targetEntityName, alias, false );
|
||||
|
||||
// join condition: owner.reference_id = middle.id_reference_id
|
||||
String ownerPrefix = ownerAlias + "." + originalIdPropertyName;
|
||||
String middleOriginalIdPropertyPath = alias + "." + originalIdPropertyName;
|
||||
IdMapper idMapper = enversService.getEntitiesConfigurations().get( entityName ).getIdMapper();
|
||||
idMapper.addIdsEqualToQuery( joinConditionParameters, ownerPrefix, middleOriginalIdPropertyPath );
|
||||
|
||||
// join condition: owner.rev=middle.rev
|
||||
joinConditionParameters.addWhere( ownerAlias, revisionPropertyPath, "=", alias, revisionPropertyPath );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -114,6 +114,7 @@ public class EntitiesAtRevisionQuery extends AbstractAuditQuery {
|
|||
enversService,
|
||||
versionsReader,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
QueryConstants.REFERENCED_ENTITY_ALIAS,
|
||||
qb,
|
||||
qb.getRootParameters()
|
||||
|
|
|
@ -66,6 +66,7 @@ public class EntitiesModifiedAtRevisionQuery extends AbstractAuditQuery {
|
|||
enversService,
|
||||
versionsReader,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
QueryConstants.REFERENCED_ENTITY_ALIAS,
|
||||
qb,
|
||||
qb.getRootParameters()
|
||||
|
|
|
@ -112,6 +112,7 @@ public class RevisionsOfEntityQuery extends AbstractAuditQuery {
|
|||
enversService,
|
||||
versionsReader,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
QueryConstants.REFERENCED_ENTITY_ALIAS,
|
||||
qb,
|
||||
qb.getRootParameters()
|
||||
|
|
|
@ -32,6 +32,7 @@ public interface AuditProjection {
|
|||
EnversService enversService,
|
||||
AuditReaderImplementor auditReader,
|
||||
Map<String, String> aliasToEntityNameMap,
|
||||
Map<String, String> aliasToComponentPropertyNameMap,
|
||||
String baseAlias,
|
||||
QueryBuilder queryBuilder);
|
||||
|
||||
|
|
|
@ -34,8 +34,13 @@ public class EntityAuditProjection implements AuditProjection {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void addProjectionToQuery(EnversService enversService, AuditReaderImplementor auditReader,
|
||||
Map<String, String> aliasToEntityNameMap, String baseAlias, QueryBuilder queryBuilder) {
|
||||
public void addProjectionToQuery(
|
||||
EnversService enversService,
|
||||
AuditReaderImplementor auditReader,
|
||||
Map<String, String> aliasToEntityNameMap,
|
||||
Map<String, String> aliasToComponentPropertyNameMap,
|
||||
String baseAlias,
|
||||
QueryBuilder queryBuilder) {
|
||||
String projectionEntityAlias = getAlias( baseAlias );
|
||||
queryBuilder.addProjection(
|
||||
null,
|
||||
|
|
|
@ -43,6 +43,7 @@ public class PropertyAuditProjection implements AuditProjection {
|
|||
EnversService enversService,
|
||||
AuditReaderImplementor auditReader,
|
||||
Map<String, String> aliasToEntityNameMap,
|
||||
Map<String, String> aliasToComponentPropertyNameMap,
|
||||
String baseAlias,
|
||||
QueryBuilder queryBuilder) {
|
||||
String projectionEntityAlias = getAlias( baseAlias );
|
||||
|
@ -51,16 +52,29 @@ public class PropertyAuditProjection implements AuditProjection {
|
|||
enversService,
|
||||
auditReader,
|
||||
projectionEntityName,
|
||||
propertyNameGetter );
|
||||
propertyNameGetter
|
||||
);
|
||||
String propertyNamePrefix = CriteriaTools.determineComponentPropertyPrefix(
|
||||
enversService,
|
||||
aliasToEntityNameMap,
|
||||
aliasToComponentPropertyNameMap,
|
||||
projectionEntityAlias
|
||||
);
|
||||
queryBuilder.addProjection(
|
||||
function,
|
||||
projectionEntityAlias,
|
||||
propertyName,
|
||||
distinct );
|
||||
propertyNamePrefix.concat( propertyName ),
|
||||
distinct
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object convertQueryResult(EnversService enversService, EntityInstantiator entityInstantiator, String entityName, Number revision, Object value) {
|
||||
public Object convertQueryResult(
|
||||
EnversService enversService,
|
||||
EntityInstantiator entityInstantiator,
|
||||
String entityName,
|
||||
Number revision,
|
||||
Object value) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,336 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.orm.test.envers.integration.query;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.persistence.ElementCollection;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.Embedded;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.criteria.JoinType;
|
||||
|
||||
import org.hibernate.envers.Audited;
|
||||
import org.hibernate.envers.query.AuditEntity;
|
||||
import org.hibernate.orm.test.envers.BaseEnversJPAFunctionalTestCase;
|
||||
import org.hibernate.orm.test.envers.Priority;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Felix Feisst (feisst dot felix at gmail dot com)
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
@TestForIssue(jiraKey = "HHH-11895")
|
||||
public class ComponentQueryTest extends BaseEnversJPAFunctionalTestCase {
|
||||
|
||||
@Embeddable
|
||||
public static class Symbol {
|
||||
|
||||
@ManyToOne
|
||||
private SymbolType type;
|
||||
private String identifier;
|
||||
|
||||
public Symbol() {
|
||||
|
||||
}
|
||||
|
||||
public Symbol(final SymbolType type, final String identifier) {
|
||||
this.type = type;
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
public SymbolType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(SymbolType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
public void setIdentifier(String identifier) {
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Entity(name = "SymbolType")
|
||||
@Audited
|
||||
public static class SymbolType {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Integer id;
|
||||
|
||||
private String name;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Asset")
|
||||
@Audited
|
||||
public static class Asset {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Integer id;
|
||||
|
||||
@Embedded
|
||||
private Symbol singleSymbol;
|
||||
|
||||
@ElementCollection
|
||||
private Set<Symbol> multiSymbols = new HashSet<>();
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Symbol getSingleSymbol() {
|
||||
return singleSymbol;
|
||||
}
|
||||
|
||||
public void setSingleSymbol(Symbol singleSymbol) {
|
||||
this.singleSymbol = singleSymbol;
|
||||
}
|
||||
|
||||
public Set<Symbol> getMultiSymbols() {
|
||||
return multiSymbols;
|
||||
}
|
||||
|
||||
public void setMultiSymbols(Set<Symbol> multiSymbols) {
|
||||
this.multiSymbols = multiSymbols;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[]{ Asset.class, Symbol.class, SymbolType.class };
|
||||
}
|
||||
|
||||
private SymbolType type1;
|
||||
private SymbolType type2;
|
||||
|
||||
private Asset asset1;
|
||||
private Asset asset2;
|
||||
private Asset asset3;
|
||||
|
||||
@Test
|
||||
@Priority(10)
|
||||
public void initData() {
|
||||
EntityManager em = getEntityManager();
|
||||
em.getTransaction().begin();
|
||||
|
||||
type1 = new SymbolType();
|
||||
type1.setName( "T1" );
|
||||
em.persist( type1 );
|
||||
|
||||
type2 = new SymbolType();
|
||||
type2.setName( "T2" );
|
||||
em.persist( type2 );
|
||||
|
||||
asset1 = new Asset();
|
||||
em.persist( asset1 );
|
||||
|
||||
asset2 = new Asset();
|
||||
asset2.setSingleSymbol( new Symbol( type1, "X1" ) );
|
||||
asset2.getMultiSymbols().add( new Symbol( type1, "X" ) );
|
||||
em.persist( asset2 );
|
||||
|
||||
asset3 = new Asset();
|
||||
asset3.setSingleSymbol( new Symbol( type2, "X2" ) );
|
||||
asset3.getMultiSymbols().add( new Symbol( type1, "Y" ) );
|
||||
asset3.getMultiSymbols().add( new Symbol( type2, "X" ) );
|
||||
em.persist( asset3 );
|
||||
|
||||
em.getTransaction().commit();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleSymbolUsingIdentifier() {
|
||||
List actual = getAuditReader().createQuery()
|
||||
.forEntitiesAtRevision( Asset.class, 1 )
|
||||
.traverseRelation( "singleSymbol", JoinType.INNER, "s" )
|
||||
.add( AuditEntity.property( "s", "identifier" ).eq( "X1" ) )
|
||||
.up()
|
||||
.addProjection( AuditEntity.id() )
|
||||
.getResultList();
|
||||
assertEquals( "Expected only asset2 to be returned", Collections.singletonList( asset2.getId() ), actual );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiSymbolUsingIdentifier() {
|
||||
List actual = getAuditReader().createQuery()
|
||||
.forEntitiesAtRevision( Asset.class, 1 )
|
||||
.traverseRelation( "multiSymbols", JoinType.INNER, "s" )
|
||||
.add( AuditEntity.property( "s", "identifier" ).like( "X%" ) )
|
||||
.up()
|
||||
.addProjection( AuditEntity.id() )
|
||||
.addOrder( AuditEntity.id().asc() )
|
||||
.getResultList();
|
||||
List<Integer> expected = new ArrayList<>();
|
||||
Collections.addAll( expected, asset2.getId(), asset3.getId() );
|
||||
assertEquals( "Expected only the ids of the assets with symbol T1", expected, actual );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleSymbolUsingType() {
|
||||
List actual = getAuditReader().createQuery()
|
||||
.forEntitiesAtRevision( Asset.class, 1 )
|
||||
.traverseRelation( "singleSymbol", JoinType.INNER, "s" )
|
||||
.add( AuditEntity.property( "s", "type" ).eq( type1 ) )
|
||||
.up()
|
||||
.addProjection( AuditEntity.id() )
|
||||
.getResultList();
|
||||
assertEquals( "Expected only asset2 to be returned", Collections.singletonList( asset2.getId() ), actual );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiSymbolUsingType() {
|
||||
List actual = getAuditReader().createQuery()
|
||||
.forEntitiesAtRevision( Asset.class, 1 )
|
||||
.traverseRelation( "multiSymbols", JoinType.INNER, "s" )
|
||||
.add( AuditEntity.property( "s", "type" ).eq( type2 ) )
|
||||
.up()
|
||||
.addProjection( AuditEntity.id() )
|
||||
.getResultList();
|
||||
assertEquals( " Expected only asset3 to be returned", Collections.singletonList( asset3.getId() ), actual );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJoinOnSingleComponentAssociation() {
|
||||
List actual = getAuditReader().createQuery()
|
||||
.forEntitiesAtRevision( Asset.class, 1 )
|
||||
.traverseRelation( "singleSymbol", JoinType.INNER, "s" )
|
||||
.traverseRelation( "type", JoinType.INNER, "t" )
|
||||
.add( AuditEntity.property( "t", "name" ).eq( "T1" ) )
|
||||
.up()
|
||||
.up()
|
||||
.addProjection( AuditEntity.id() )
|
||||
.getResultList();
|
||||
assertEquals( "Expected only asset2 to be returned", Collections.singletonList( asset2.getId() ), actual );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJoinOnMultiComponentAssociation() {
|
||||
List actual = getAuditReader().createQuery()
|
||||
.forEntitiesAtRevision( Asset.class, 1 )
|
||||
.traverseRelation( "multiSymbols", JoinType.INNER, "s" )
|
||||
.traverseRelation( "type", JoinType.INNER, "t" )
|
||||
.add( AuditEntity.property( "t", "name" ).eq( "T2" ) )
|
||||
.up()
|
||||
.up()
|
||||
.addProjection( AuditEntity.id() )
|
||||
.getResultList();
|
||||
assertEquals( "Expected only asset3 to be returned", Collections.singletonList( asset3.getId() ), actual );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrderingOnSingleComponent() {
|
||||
List actual = getAuditReader().createQuery()
|
||||
.forEntitiesAtRevision( Asset.class, 1 )
|
||||
.addProjection( AuditEntity.id() )
|
||||
.traverseRelation( "singleSymbol", JoinType.LEFT, "s" )
|
||||
.addOrder( AuditEntity.property( "s", "identifier" ).asc() )
|
||||
.getResultList();
|
||||
List<Integer> expected = new ArrayList<>();
|
||||
Collections.addAll( expected, asset1.getId(), asset2.getId(), asset3.getId() );
|
||||
assertEquals( "Expected all assets in correct order", expected, actual );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrderingOnMultiComponent() {
|
||||
List actual = getAuditReader().createQuery()
|
||||
.forEntitiesAtRevision( Asset.class, 1 )
|
||||
.addProjection( AuditEntity.id() )
|
||||
.traverseRelation( "multiSymbols", JoinType.LEFT, "s" )
|
||||
.traverseRelation( "type", JoinType.LEFT, "t" )
|
||||
.addOrder( AuditEntity.property( "t", "name" ).asc() )
|
||||
.addOrder( AuditEntity.property( "s", "identifier" ).asc() )
|
||||
.getResultList();
|
||||
List<Integer> expected = new ArrayList<>();
|
||||
Collections.addAll( expected, asset1.getId(), asset2.getId(), asset3.getId(), asset3.getId() );
|
||||
assertEquals( "Expected all assets in correct order", expected, actual );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProjectionOnSingleComponentProperty() {
|
||||
List actual = getAuditReader().createQuery()
|
||||
.forEntitiesAtRevision( Asset.class, 1 )
|
||||
.add( AuditEntity.id().eq( asset2.getId() ) )
|
||||
.traverseRelation( "singleSymbol", JoinType.INNER, "s" )
|
||||
.addProjection( AuditEntity.property( "s", "identifier" ) )
|
||||
.getResultList();
|
||||
assertEquals( "Expected the symbol identifier of asset2 to be returned", Collections.singletonList( "X1" ), actual );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProjectionOnMultiComponentProperty() {
|
||||
List actual = getAuditReader().createQuery()
|
||||
.forEntitiesAtRevision( Asset.class, 1 )
|
||||
.add( AuditEntity.id().eq( asset2.getId() ) )
|
||||
.traverseRelation( "multiSymbols", JoinType.INNER, "s" )
|
||||
.addProjection( AuditEntity.property( "s", "identifier" ) )
|
||||
.getResultList();
|
||||
assertEquals( "Expected the symbol identifier of asset2 to be returned", Collections.singletonList( "X" ), actual );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFunctionOnSingleComponentProperty() {
|
||||
List actual = getAuditReader().createQuery()
|
||||
.forEntitiesAtRevision( Asset.class, 1 )
|
||||
.add( AuditEntity.id().eq( asset2.getId() ) )
|
||||
.traverseRelation( "singleSymbol", JoinType.INNER, "s" )
|
||||
.addProjection( AuditEntity.function( "CONCAT", AuditEntity.property( "s", "identifier" ), "Z" ) )
|
||||
.getResultList();
|
||||
assertEquals( "Expecte the symbol identfier of asset2 concatenated with 'Z'", Collections.singletonList( "X1Z" ), actual );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFunctionOnMultiComponentProperty() {
|
||||
List actual = getAuditReader().createQuery()
|
||||
.forEntitiesAtRevision( Asset.class, 1 )
|
||||
.add( AuditEntity.id().eq( asset2.getId() ) )
|
||||
.traverseRelation( "multiSymbols", JoinType.INNER, "s" )
|
||||
.addProjection( AuditEntity.function( "CONCAT", AuditEntity.property( "s", "identifier" ), "Z" ) )
|
||||
.getResultList();
|
||||
assertEquals( "Expecte the symbol identfier of asset2 concatenated with 'Z'", Collections.singletonList( "XZ" ), actual );
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.orm.test.envers.integration.query;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.Embedded;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.criteria.JoinType;
|
||||
|
||||
import org.hibernate.envers.Audited;
|
||||
import org.hibernate.envers.query.AuditEntity;
|
||||
import org.hibernate.orm.test.envers.BaseEnversJPAFunctionalTestCase;
|
||||
import org.hibernate.orm.test.envers.Priority;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Felix Feisst (feisst dot felix at gmail dot com)
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-11895")
|
||||
public class NestedComponentQueryTest extends BaseEnversJPAFunctionalTestCase {
|
||||
|
||||
@Embeddable
|
||||
public static class Component1 {
|
||||
|
||||
private String name1;
|
||||
|
||||
@Embedded
|
||||
private Component2 component2;
|
||||
|
||||
public String getName1() {
|
||||
return name1;
|
||||
}
|
||||
|
||||
public void setName1(String name1) {
|
||||
this.name1 = name1;
|
||||
}
|
||||
|
||||
public Component2 getComponent2() {
|
||||
return component2;
|
||||
}
|
||||
|
||||
public void setComponent2(Component2 component2) {
|
||||
this.component2 = component2;
|
||||
}
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
public static class Component2 {
|
||||
|
||||
private String name2;
|
||||
|
||||
public String getName2() {
|
||||
return name2;
|
||||
}
|
||||
|
||||
public void setName2(String name2) {
|
||||
this.name2 = name2;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "EntityOwner")
|
||||
@Audited
|
||||
public static class EntityOwner {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Integer id;
|
||||
|
||||
@Embedded
|
||||
private Component1 component1;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Component1 getComponent1() {
|
||||
return component1;
|
||||
}
|
||||
|
||||
public void setComponent1(Component1 component1) {
|
||||
this.component1 = component1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[]{ EntityOwner.class, Component1.class, Component2.class };
|
||||
}
|
||||
|
||||
private EntityOwner owner1;
|
||||
private EntityOwner owner2;
|
||||
|
||||
@Test
|
||||
@Priority(10)
|
||||
public void initData() {
|
||||
EntityManager em = getEntityManager();
|
||||
em.getTransaction().begin();
|
||||
owner1 = new EntityOwner();
|
||||
Component1 component1 = new Component1();
|
||||
component1.setName1( "X" );
|
||||
owner1.setComponent1( component1 );
|
||||
Component2 component2 = new Component2();
|
||||
component2.setName2( "Y" );
|
||||
component1.setComponent2( component2 );
|
||||
em.persist( owner1 );
|
||||
owner2 = new EntityOwner();
|
||||
Component1 component12 = new Component1();
|
||||
component12.setName1( "Z" );
|
||||
owner2.setComponent1( component12 );
|
||||
Component2 component22 = new Component2();
|
||||
component22.setName2( "Z" );
|
||||
component12.setComponent2( component22 );
|
||||
em.persist( owner2 );
|
||||
em.getTransaction().commit();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryNestedComponent() {
|
||||
List actual = getAuditReader().createQuery()
|
||||
.forEntitiesAtRevision( EntityOwner.class, 1 )
|
||||
.addProjection( AuditEntity.id() )
|
||||
.traverseRelation( "component1", JoinType.INNER, "c1" )
|
||||
.traverseRelation( "component2", JoinType.INNER, "c2" )
|
||||
.add( AuditEntity.property( "c2", "name2" ).eq( "Y" ) )
|
||||
.getResultList();
|
||||
assertEquals( "Expected owner1 to be returned", Collections.singletonList( owner1.getId() ), actual );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryNestedComponentWithPropertyEquals() {
|
||||
List actual = getAuditReader().createQuery()
|
||||
.forEntitiesAtRevision( EntityOwner.class, 1 )
|
||||
.addProjection( AuditEntity.id() )
|
||||
.traverseRelation( "component1", JoinType.INNER, "c1" )
|
||||
.traverseRelation( "component2", JoinType.INNER, "c2" )
|
||||
.add( AuditEntity.property( "c1", "name1" ).eqProperty( "c2", "name2" ) )
|
||||
.getResultList();
|
||||
assertEquals( "Expected owner2 to be returned", Collections.singletonList( owner2.getId() ), actual );
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue