HHH-11735 Support traversal of to-many-associations in audit queries.
This commit is contained in:
parent
b384b37f39
commit
bb09222102
|
@ -178,7 +178,7 @@ public class MiddleTableCollectionMetadataGenerator extends AbstractCollectionMe
|
|||
addMapper( context, commonCollectionMapperData, elementComponentData, indexComponentData );
|
||||
|
||||
// Storing information about this relation.
|
||||
storeMiddleEntityRelationInformation( context, mappedBy );
|
||||
storeMiddleEntityRelationInformation( context, mappedBy, referencingIdData, referencedPrefix, auditMiddleEntityName );
|
||||
}
|
||||
|
||||
private String getMiddleTableName(CollectionMetadataContext context) {
|
||||
|
@ -231,20 +231,42 @@ public class MiddleTableCollectionMetadataGenerator extends AbstractCollectionMe
|
|||
return getMetadataBuildingContext().getMetadataCollector().getEntityBinding( context.getReferencedEntityName() );
|
||||
}
|
||||
|
||||
private void storeMiddleEntityRelationInformation(CollectionMetadataContext context, String mappedBy) {
|
||||
private void storeMiddleEntityRelationInformation(
|
||||
CollectionMetadataContext context,
|
||||
String mappedBy,
|
||||
MiddleIdData referencingIdData,
|
||||
String referencedPrefix,
|
||||
String auditMiddleEntityName) {
|
||||
// Only if this is a relation (when there is a referenced entity).
|
||||
if ( context.getReferencedEntityName() != null ) {
|
||||
final IdMappingData referencedIdMapping = getReferencedIdMappingData(
|
||||
context.getReferencingEntityName(),
|
||||
context.getReferencedEntityName(),
|
||||
context.getPropertyAuditingData(),
|
||||
true
|
||||
);
|
||||
final MiddleIdData referencedIdData = createMiddleIdData(
|
||||
referencedIdMapping,
|
||||
referencedPrefix + "_",
|
||||
context.getReferencedEntityName()
|
||||
);
|
||||
if ( context.getCollection().isInverse() ) {
|
||||
context.getReferencingEntityConfiguration().addToManyMiddleNotOwningRelation(
|
||||
context.getPropertyName(),
|
||||
mappedBy,
|
||||
context.getReferencedEntityName()
|
||||
context.getReferencedEntityName(),
|
||||
referencingIdData,
|
||||
referencedIdData,
|
||||
auditMiddleEntityName
|
||||
);
|
||||
}
|
||||
else {
|
||||
context.getReferencingEntityConfiguration().addToManyMiddleRelation(
|
||||
context.getPropertyName(),
|
||||
context.getReferencedEntityName()
|
||||
context.getReferencedEntityName(),
|
||||
referencingIdData,
|
||||
referencedIdData,
|
||||
auditMiddleEntityName
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.util.Map;
|
|||
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;
|
||||
import org.hibernate.envers.internal.entities.mapper.relation.MiddleIdData;
|
||||
|
||||
/**
|
||||
* Runtime representation of an entity that may or may not be audited.
|
||||
|
@ -109,13 +110,21 @@ public class EntityConfiguration {
|
|||
idMapper,
|
||||
fakeBidirectionalRelationMapper,
|
||||
fakeBidirectionalRelationIndexMapper,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
indexed
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public void addToManyMiddleRelation(String fromPropertyName, String toEntityName) {
|
||||
public void addToManyMiddleRelation(
|
||||
String fromPropertyName,
|
||||
String toEntityName,
|
||||
MiddleIdData referencingIdData,
|
||||
MiddleIdData referencedIdData,
|
||||
String auditMiddleEntityName) {
|
||||
relations.put(
|
||||
fromPropertyName,
|
||||
RelationDescription.toMany(
|
||||
|
@ -126,13 +135,22 @@ public class EntityConfiguration {
|
|||
null,
|
||||
null,
|
||||
null,
|
||||
referencingIdData,
|
||||
referencedIdData,
|
||||
auditMiddleEntityName,
|
||||
true,
|
||||
false
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public void addToManyMiddleNotOwningRelation(String fromPropertyName, String mappedByPropertyName, String toEntityName) {
|
||||
public void addToManyMiddleNotOwningRelation(
|
||||
String fromPropertyName,
|
||||
String mappedByPropertyName,
|
||||
String toEntityName,
|
||||
MiddleIdData referencingIdData,
|
||||
MiddleIdData referencedIdData,
|
||||
String auditMiddleEntityName) {
|
||||
relations.put(
|
||||
fromPropertyName,
|
||||
RelationDescription.toMany(
|
||||
|
@ -143,6 +161,9 @@ public class EntityConfiguration {
|
|||
null,
|
||||
null,
|
||||
null,
|
||||
referencingIdData,
|
||||
referencedIdData,
|
||||
auditMiddleEntityName,
|
||||
true,
|
||||
false
|
||||
)
|
||||
|
|
|
@ -8,6 +8,7 @@ package org.hibernate.envers.internal.entities;
|
|||
|
||||
import org.hibernate.envers.internal.entities.mapper.PropertyMapper;
|
||||
import org.hibernate.envers.internal.entities.mapper.id.IdMapper;
|
||||
import org.hibernate.envers.internal.entities.mapper.relation.MiddleIdData;
|
||||
|
||||
/**
|
||||
* @author Adam Warski (adam at warski dot org)
|
||||
|
@ -22,6 +23,9 @@ public class RelationDescription {
|
|||
private final IdMapper idMapper;
|
||||
private final PropertyMapper fakeBidirectionalRelationMapper;
|
||||
private final PropertyMapper fakeBidirectionalRelationIndexMapper;
|
||||
private final MiddleIdData referencingIdData;
|
||||
private final MiddleIdData referencedIdData;
|
||||
private final String auditMiddleEntityName;
|
||||
private final boolean insertable;
|
||||
private final boolean indexed;
|
||||
private boolean bidirectional;
|
||||
|
@ -38,7 +42,7 @@ public class RelationDescription {
|
|||
boolean ignoreNotFound) {
|
||||
return new RelationDescription(
|
||||
fromPropertyName, relationType, toEntityName, mappedByPropertyName, idMapper,
|
||||
fakeBidirectionalRelationMapper, fakeBidirectionalRelationIndexMapper, insertable, ignoreNotFound, false
|
||||
fakeBidirectionalRelationMapper, fakeBidirectionalRelationIndexMapper, null, null, null, insertable, ignoreNotFound, false
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -50,6 +54,9 @@ public class RelationDescription {
|
|||
IdMapper idMapper,
|
||||
PropertyMapper fakeBidirectionalRelationMapper,
|
||||
PropertyMapper fakeBidirectionalRelationIndexMapper,
|
||||
MiddleIdData referencingIdData,
|
||||
MiddleIdData referencedIdData,
|
||||
String auditMiddleEntityName,
|
||||
boolean insertable,
|
||||
boolean indexed) {
|
||||
// Envers populates collections by executing dedicated queries. Special handling of
|
||||
|
@ -58,7 +65,7 @@ public class RelationDescription {
|
|||
// Therefore assigning false to ignoreNotFound.
|
||||
return new RelationDescription(
|
||||
fromPropertyName, relationType, toEntityName, mappedByPropertyName, idMapper, fakeBidirectionalRelationMapper,
|
||||
fakeBidirectionalRelationIndexMapper, insertable, false, indexed
|
||||
fakeBidirectionalRelationIndexMapper, referencingIdData, referencedIdData, auditMiddleEntityName, insertable, false, indexed
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -70,6 +77,9 @@ public class RelationDescription {
|
|||
IdMapper idMapper,
|
||||
PropertyMapper fakeBidirectionalRelationMapper,
|
||||
PropertyMapper fakeBidirectionalRelationIndexMapper,
|
||||
MiddleIdData referencingIdData,
|
||||
MiddleIdData referencedIdData,
|
||||
String auditMiddleEntityName,
|
||||
boolean insertable,
|
||||
boolean ignoreNotFound,
|
||||
boolean indexed) {
|
||||
|
@ -81,6 +91,9 @@ public class RelationDescription {
|
|||
this.idMapper = idMapper;
|
||||
this.fakeBidirectionalRelationMapper = fakeBidirectionalRelationMapper;
|
||||
this.fakeBidirectionalRelationIndexMapper = fakeBidirectionalRelationIndexMapper;
|
||||
this.referencingIdData = referencingIdData;
|
||||
this.referencedIdData = referencedIdData;
|
||||
this.auditMiddleEntityName = auditMiddleEntityName;
|
||||
this.insertable = insertable;
|
||||
this.indexed = indexed;
|
||||
this.bidirectional = false;
|
||||
|
@ -118,6 +131,18 @@ public class RelationDescription {
|
|||
return fakeBidirectionalRelationIndexMapper;
|
||||
}
|
||||
|
||||
public MiddleIdData getReferencingIdData() {
|
||||
return referencingIdData;
|
||||
}
|
||||
|
||||
public MiddleIdData getReferencedIdData() {
|
||||
return referencedIdData;
|
||||
}
|
||||
|
||||
public String getAuditMiddleEntityName() {
|
||||
return auditMiddleEntityName;
|
||||
}
|
||||
|
||||
public boolean isInsertable() {
|
||||
return insertable;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ package org.hibernate.envers.query.criteria.internal;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.envers.boot.internal.EnversService;
|
||||
|
@ -48,13 +49,20 @@ public abstract class CriteriaTools {
|
|||
return null;
|
||||
}
|
||||
|
||||
if ( relationDesc.getRelationType() == RelationType.TO_ONE ) {
|
||||
if ( relationDesc.getRelationType() == RelationType.TO_ONE
|
||||
|| relationDesc.getRelationType() == RelationType.TO_MANY_MIDDLE
|
||||
|| relationDesc.getRelationType() == RelationType.TO_MANY_NOT_OWNING
|
||||
|| relationDesc.getRelationType() == RelationType.TO_MANY_MIDDLE_NOT_OWNING ) {
|
||||
return relationDesc;
|
||||
}
|
||||
|
||||
throw new AuditException(
|
||||
"This type of relation (" + entityName + "." + propertyName +
|
||||
") isn't supported and can't be used in queries."
|
||||
String.format(
|
||||
Locale.ENGLISH,
|
||||
"This type of relation (%s.%s) isn't supported and can't be used in queries.",
|
||||
entityName,
|
||||
propertyName
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,12 @@
|
|||
*/
|
||||
package org.hibernate.envers.query.criteria.internal;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.hibernate.envers.boot.internal.EnversService;
|
||||
import org.hibernate.envers.exception.AuditException;
|
||||
import org.hibernate.envers.internal.entities.RelationDescription;
|
||||
import org.hibernate.envers.internal.entities.RelationType;
|
||||
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
||||
import org.hibernate.envers.internal.tools.query.Parameters;
|
||||
import org.hibernate.envers.internal.tools.query.QueryBuilder;
|
||||
|
@ -43,8 +47,18 @@ public class NotNullAuditExpression extends AbstractAtomicExpression {
|
|||
if ( relatedEntity == null ) {
|
||||
parameters.addNotNullRestriction( alias, propertyName );
|
||||
}
|
||||
else {
|
||||
else if ( relatedEntity.getRelationType() == RelationType.TO_ONE ) {
|
||||
relatedEntity.getIdMapper().addIdEqualsToQuery( parameters, null, alias, null, false );
|
||||
}
|
||||
else {
|
||||
throw new AuditException(
|
||||
String.format(
|
||||
Locale.ENGLISH,
|
||||
"This type of relation (%s.%s) can't be used with not null restrictions.",
|
||||
entityName,
|
||||
propertyName
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,8 +6,12 @@
|
|||
*/
|
||||
package org.hibernate.envers.query.criteria.internal;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.hibernate.envers.boot.internal.EnversService;
|
||||
import org.hibernate.envers.exception.AuditException;
|
||||
import org.hibernate.envers.internal.entities.RelationDescription;
|
||||
import org.hibernate.envers.internal.entities.RelationType;
|
||||
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
||||
import org.hibernate.envers.internal.tools.query.Parameters;
|
||||
import org.hibernate.envers.internal.tools.query.QueryBuilder;
|
||||
|
@ -43,8 +47,18 @@ public class NullAuditExpression extends AbstractAtomicExpression {
|
|||
if ( relatedEntity == null ) {
|
||||
parameters.addNullRestriction( alias, propertyName );
|
||||
}
|
||||
else {
|
||||
else if ( relatedEntity.getRelationType() == RelationType.TO_ONE ) {
|
||||
relatedEntity.getIdMapper().addIdEqualsToQuery( parameters, null, alias, null, true );
|
||||
}
|
||||
else {
|
||||
throw new AuditException(
|
||||
String.format(
|
||||
Locale.ENGLISH,
|
||||
"This type of relation (%s.%s) can't be used with null restrictions",
|
||||
entityName,
|
||||
propertyName
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,13 +6,15 @@
|
|||
*/
|
||||
package org.hibernate.envers.query.criteria.internal;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.hibernate.envers.boot.internal.EnversService;
|
||||
import org.hibernate.envers.exception.AuditException;
|
||||
import org.hibernate.envers.internal.entities.RelationDescription;
|
||||
import org.hibernate.envers.internal.entities.RelationType;
|
||||
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
||||
import org.hibernate.envers.internal.tools.query.Parameters;
|
||||
import org.hibernate.envers.internal.tools.query.QueryBuilder;
|
||||
import org.hibernate.envers.query.criteria.AuditCriterion;
|
||||
import org.hibernate.envers.query.internal.property.PropertyNameGetter;
|
||||
|
||||
/**
|
||||
|
@ -49,7 +51,16 @@ public class RelatedAuditEqualityExpression extends AbstractAtomicExpression {
|
|||
RelationDescription relatedEntity = CriteriaTools.getRelatedEntity( enversService, entityName, propertyName );
|
||||
if ( relatedEntity == null ) {
|
||||
throw new AuditException(
|
||||
"This criterion can only be used on a property that is a relation to another property."
|
||||
"This criterion can only be used on a property that is a relation to another property." );
|
||||
}
|
||||
else if ( relatedEntity.getRelationType() != RelationType.TO_ONE ) {
|
||||
throw new AuditException(
|
||||
String.format(
|
||||
Locale.ENGLISH,
|
||||
"This type of relation (%s.%s) can't be used with related equality restrictions",
|
||||
entityName,
|
||||
propertyName
|
||||
)
|
||||
);
|
||||
}
|
||||
relatedEntity.getIdMapper().addIdEqualsToQuery( parameters, id, alias, null, equals );
|
||||
|
|
|
@ -7,15 +7,16 @@
|
|||
package org.hibernate.envers.query.criteria.internal;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.hibernate.envers.boot.internal.EnversService;
|
||||
import org.hibernate.envers.exception.AuditException;
|
||||
import org.hibernate.envers.internal.entities.RelationDescription;
|
||||
import org.hibernate.envers.internal.entities.RelationType;
|
||||
import org.hibernate.envers.internal.entities.mapper.id.QueryParameterData;
|
||||
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
||||
import org.hibernate.envers.internal.tools.query.Parameters;
|
||||
import org.hibernate.envers.internal.tools.query.QueryBuilder;
|
||||
import org.hibernate.envers.query.criteria.AuditCriterion;
|
||||
import org.hibernate.envers.query.internal.property.PropertyNameGetter;
|
||||
|
||||
/**
|
||||
|
@ -51,7 +52,16 @@ public class RelatedAuditInExpression extends AbstractAtomicExpression {
|
|||
RelationDescription relatedEntity = CriteriaTools.getRelatedEntity( enversService, entityName, propertyName );
|
||||
if ( relatedEntity == null ) {
|
||||
throw new AuditException(
|
||||
"The criterion can only be used on a property that is a relation to another property."
|
||||
"The criterion can only be used on a property that is a relation to another property." );
|
||||
}
|
||||
else if ( relatedEntity.getRelationType() != RelationType.TO_ONE ) {
|
||||
throw new AuditException(
|
||||
String.format(
|
||||
Locale.ENGLISH,
|
||||
"This type of relation (%s.%s) can't be used with related in restrictions",
|
||||
entityName,
|
||||
propertyName
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,10 +6,13 @@
|
|||
*/
|
||||
package org.hibernate.envers.query.criteria.internal;
|
||||
|
||||
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.RelationDescription;
|
||||
import org.hibernate.envers.internal.entities.RelationType;
|
||||
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
||||
import org.hibernate.envers.internal.tools.query.Parameters;
|
||||
import org.hibernate.envers.internal.tools.query.QueryBuilder;
|
||||
|
@ -59,7 +62,13 @@ public class SimpleAuditExpression extends AbstractAtomicExpression {
|
|||
final Type type = getPropertyType( session, entityName, propertyName );
|
||||
if ( type != null && type.isComponentType() ) {
|
||||
if ( !"=".equals( op ) && !"<>".equals( op ) ) {
|
||||
throw new AuditException( "Component-based criterion is not supported for op: " + op );
|
||||
throw new AuditException(
|
||||
String.format(
|
||||
Locale.ENGLISH,
|
||||
"Component-based criterion is not supported for op: %s",
|
||||
op
|
||||
)
|
||||
);
|
||||
}
|
||||
final ComponentType componentType = (ComponentType) type;
|
||||
for ( int i = 0; i < componentType.getPropertyNames().length; i++ ) {
|
||||
|
@ -76,16 +85,30 @@ public class SimpleAuditExpression extends AbstractAtomicExpression {
|
|||
parameters.addWhereWithParam( alias, propertyName, op, value );
|
||||
}
|
||||
}
|
||||
else {
|
||||
else if ( relatedEntity.getRelationType() == RelationType.TO_ONE ) {
|
||||
if ( !"=".equals( op ) && !"<>".equals( op ) ) {
|
||||
throw new AuditException(
|
||||
"This type of operation: " + op + " (" + entityName + "." + propertyName +
|
||||
") isn't supported and can't be used in queries."
|
||||
String.format(
|
||||
Locale.ENGLISH,
|
||||
"This type of operation: %s (%s.%s) isn't supported and can't be used in queries.",
|
||||
op,
|
||||
entityName,
|
||||
propertyName
|
||||
)
|
||||
);
|
||||
}
|
||||
Object id = relatedEntity.getIdMapper().mapToIdFromEntity( value );
|
||||
relatedEntity.getIdMapper().addIdEqualsToQuery( parameters, id, alias, null, "=".equals( op ) );
|
||||
}
|
||||
else {
|
||||
throw new AuditException(
|
||||
String.format(
|
||||
"This type of relation (%s.%s) can't be used in audit query restrictions.",
|
||||
entityName,
|
||||
propertyName
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,6 +9,7 @@ package org.hibernate.envers.query.internal.impl;
|
|||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import jakarta.persistence.NoResultException;
|
||||
import jakarta.persistence.NonUniqueResultException;
|
||||
|
@ -18,10 +19,12 @@ import org.hibernate.CacheMode;
|
|||
import org.hibernate.FlushMode;
|
||||
import org.hibernate.Incubating;
|
||||
import org.hibernate.LockMode;
|
||||
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.RelationDescription;
|
||||
import org.hibernate.envers.internal.entities.RelationType;
|
||||
import org.hibernate.envers.internal.entities.mapper.id.IdMapper;
|
||||
import org.hibernate.envers.internal.entities.mapper.relation.MiddleIdData;
|
||||
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
||||
|
@ -47,8 +50,9 @@ public class AuditAssociationQueryImpl<Q extends AuditQueryImplementor>
|
|||
private final QueryBuilder queryBuilder;
|
||||
private final JoinType joinType;
|
||||
private final String entityName;
|
||||
private final IdMapper ownerAssociationIdMapper;
|
||||
private final RelationDescription relationDescription;
|
||||
private final String ownerAlias;
|
||||
private final String ownerEntityName;
|
||||
private final String alias;
|
||||
private final Map<String, String> aliasToEntityNameMap;
|
||||
private final List<AuditCriterion> criterions = new ArrayList<>();
|
||||
|
@ -72,17 +76,24 @@ public class AuditAssociationQueryImpl<Q extends AuditQueryImplementor>
|
|||
this.queryBuilder = queryBuilder;
|
||||
this.joinType = joinType;
|
||||
|
||||
String ownerEntityName = aliasToEntityNameMap.get( ownerAlias );
|
||||
ownerEntityName = aliasToEntityNameMap.get( ownerAlias );
|
||||
final RelationDescription relationDescription = CriteriaTools.getRelatedEntity(
|
||||
enversService,
|
||||
ownerEntityName,
|
||||
propertyName
|
||||
);
|
||||
if ( relationDescription == null ) {
|
||||
throw new IllegalArgumentException( "Property " + propertyName + " of entity " + ownerEntityName + " is not a valid association for queries" );
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
Locale.ENGLISH,
|
||||
"Property %s of entity %s is not a valid association for queries",
|
||||
propertyName,
|
||||
ownerEntityName
|
||||
)
|
||||
);
|
||||
}
|
||||
this.entityName = relationDescription.getToEntityName();
|
||||
this.ownerAssociationIdMapper = relationDescription.getIdMapper();
|
||||
this.relationDescription = relationDescription;
|
||||
this.ownerAlias = ownerAlias;
|
||||
this.alias = userSuppliedAlias == null ? queryBuilder.generateAlias() : userSuppliedAlias;
|
||||
aliasToEntityNameMap.put( this.alias, entityName );
|
||||
|
@ -241,26 +252,154 @@ public class AuditAssociationQueryImpl<Q extends AuditQueryImplementor>
|
|||
}
|
||||
|
||||
protected void addCriterionsToQuery(AuditReaderImplementor versionsReader) {
|
||||
if ( enversService.getEntitiesConfigurations().isVersioned( entityName ) ) {
|
||||
Configuration configuration = enversService.getConfig();
|
||||
String auditEntityName = configuration.getAuditEntityName( entityName );
|
||||
Parameters joinConditionParameters = queryBuilder.addJoin( joinType, auditEntityName, alias, false );
|
||||
final Configuration configuration = enversService.getConfig();
|
||||
boolean targetIsAudited = enversService.getEntitiesConfigurations().isVersioned( entityName );
|
||||
String targetEntityName = entityName;
|
||||
if ( targetIsAudited ) {
|
||||
targetEntityName = configuration.getAuditEntityName( entityName );
|
||||
}
|
||||
String originalIdPropertyName = configuration.getOriginalIdPropertyName();
|
||||
String revisionPropertyPath = configuration.getRevisionNumberPath();
|
||||
|
||||
if ( relationDescription.getRelationType() == RelationType.TO_ONE ) {
|
||||
Parameters joinConditionParameters = queryBuilder.addJoin( joinType, targetEntityName, alias, false );
|
||||
|
||||
// owner.reference_id = target.originalId.id
|
||||
String originalIdPropertyName = configuration.getOriginalIdPropertyName();
|
||||
IdMapper idMapperTarget = enversService.getEntitiesConfigurations().get( entityName ).getIdMapper();
|
||||
final String prefix = alias.concat( "." ).concat( originalIdPropertyName );
|
||||
ownerAssociationIdMapper.addIdsEqualToQuery(
|
||||
IdMapper idMapperTarget;
|
||||
String prefix;
|
||||
if ( targetIsAudited ) {
|
||||
idMapperTarget = enversService.getEntitiesConfigurations().get( entityName ).getIdMapper();
|
||||
prefix = alias.concat( "." ).concat( originalIdPropertyName );
|
||||
}
|
||||
else {
|
||||
idMapperTarget = enversService.getEntitiesConfigurations()
|
||||
.getNotVersionEntityConfiguration( entityName )
|
||||
.getIdMapper();
|
||||
prefix = alias;
|
||||
}
|
||||
relationDescription.getIdMapper().addIdsEqualToQuery(
|
||||
joinConditionParameters,
|
||||
ownerAlias,
|
||||
idMapperTarget,
|
||||
prefix
|
||||
);
|
||||
}
|
||||
else if ( relationDescription.getRelationType() == RelationType.TO_MANY_NOT_OWNING ) {
|
||||
if ( !targetIsAudited ) {
|
||||
throw new AuditException(
|
||||
String.format(
|
||||
Locale.ENGLISH,
|
||||
"Cannot build queries for relation type %s to non audited target entities",
|
||||
relationDescription.getRelationType()
|
||||
)
|
||||
);
|
||||
}
|
||||
Parameters joinConditionParameters = queryBuilder.addJoin( joinType, targetEntityName, alias, false );
|
||||
|
||||
// owner.originalId.id = target.reference_id
|
||||
IdMapper idMapperOwner = enversService.getEntitiesConfigurations().get( ownerEntityName ).getIdMapper();
|
||||
String prefix = ownerAlias.concat( "." ).concat( originalIdPropertyName );
|
||||
relationDescription.getIdMapper().addIdsEqualToQuery(
|
||||
joinConditionParameters,
|
||||
alias,
|
||||
idMapperOwner,
|
||||
prefix );
|
||||
}
|
||||
else if ( relationDescription.getRelationType() == RelationType.TO_MANY_MIDDLE
|
||||
|| relationDescription.getRelationType() == RelationType.TO_MANY_MIDDLE_NOT_OWNING ) {
|
||||
if ( !targetIsAudited && relationDescription.getRelationType() == RelationType.TO_MANY_MIDDLE_NOT_OWNING ) {
|
||||
throw new AuditException(
|
||||
String.format(
|
||||
Locale.ENGLISH,
|
||||
"Cannot build queries for relation type %s to non audited target entities",
|
||||
relationDescription.getRelationType()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
String middleEntityAlias = queryBuilder.generateAlias();
|
||||
|
||||
// join middle_entity
|
||||
Parameters joinConditionParametersMiddle = queryBuilder.addJoin(
|
||||
joinType,
|
||||
relationDescription.getAuditMiddleEntityName(),
|
||||
middleEntityAlias,
|
||||
false
|
||||
);
|
||||
|
||||
// join target_entity
|
||||
Parameters joinConditionParametersTarget = queryBuilder.addJoin( joinType, targetEntityName, alias, false );
|
||||
|
||||
Parameters middleParameters = queryBuilder.addParameters( middleEntityAlias );
|
||||
String middleOriginalIdPropertyPath = middleEntityAlias + "." + originalIdPropertyName;
|
||||
|
||||
// join condition: owner.reference_id = middle.id_ref_ing
|
||||
String ownerPrefix = ownerAlias + "." + originalIdPropertyName;
|
||||
MiddleIdData referencingIdData = relationDescription.getReferencingIdData();
|
||||
referencingIdData.getPrefixedMapper().addIdsEqualToQuery(
|
||||
joinConditionParametersMiddle,
|
||||
middleOriginalIdPropertyPath,
|
||||
referencingIdData.getOriginalMapper(),
|
||||
ownerPrefix
|
||||
);
|
||||
|
||||
// join condition: middle.id_ref_ed = target.id
|
||||
String targetPrefix = alias;
|
||||
if ( targetIsAudited ) {
|
||||
targetPrefix = alias + "." + originalIdPropertyName;
|
||||
}
|
||||
MiddleIdData referencedIdData = relationDescription.getReferencedIdData();
|
||||
referencedIdData.getPrefixedMapper().addIdsEqualToQuery(
|
||||
joinConditionParametersTarget,
|
||||
middleOriginalIdPropertyPath,
|
||||
referencedIdData.getOriginalMapper(),
|
||||
targetPrefix
|
||||
);
|
||||
|
||||
// filter revisions of middle entity
|
||||
Parameters middleParametersToUse = middleParameters;
|
||||
if ( joinType == JoinType.LEFT ) {
|
||||
middleParametersToUse = middleParameters.addSubParameters( Parameters.OR );
|
||||
middleParametersToUse.addNullRestriction( revisionPropertyPath, true );
|
||||
middleParametersToUse = middleParametersToUse.addSubParameters( Parameters.AND );
|
||||
}
|
||||
|
||||
enversService.getAuditStrategy().addAssociationAtRevisionRestriction(
|
||||
queryBuilder,
|
||||
middleParametersToUse,
|
||||
revisionPropertyPath,
|
||||
configuration.getRevisionEndFieldName(),
|
||||
true,
|
||||
referencingIdData,
|
||||
relationDescription.getAuditMiddleEntityName(),
|
||||
middleOriginalIdPropertyPath,
|
||||
revisionPropertyPath,
|
||||
originalIdPropertyName,
|
||||
middleEntityAlias,
|
||||
true
|
||||
);
|
||||
|
||||
// filter deleted middle entities
|
||||
if ( joinType == JoinType.LEFT ) {
|
||||
middleParametersToUse = middleParameters.addSubParameters( Parameters.OR );
|
||||
middleParametersToUse.addNullRestriction( configuration.getRevisionTypePropertyName(), true );
|
||||
}
|
||||
middleParametersToUse.addWhereWithParam( configuration.getRevisionTypePropertyName(), true, "!=", RevisionType.DEL );
|
||||
}
|
||||
else {
|
||||
throw new AuditException(
|
||||
String.format(
|
||||
Locale.ENGLISH,
|
||||
"Cannot build queries for relation type %s",
|
||||
relationDescription.getRelationType()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( targetIsAudited ) {
|
||||
// filter revision of target entity
|
||||
Parameters parametersToUse = parameters;
|
||||
String revisionPropertyPath = configuration.getRevisionNumberPath();
|
||||
if (joinType == JoinType.LEFT) {
|
||||
if ( joinType == JoinType.LEFT ) {
|
||||
parametersToUse = parameters.addSubParameters( Parameters.OR );
|
||||
parametersToUse.addNullRestriction( revisionPropertyPath, true );
|
||||
parametersToUse = parametersToUse.addSubParameters( Parameters.AND );
|
||||
|
@ -270,7 +409,7 @@ public class AuditAssociationQueryImpl<Q extends AuditQueryImplementor>
|
|||
enversService.getEntitiesConfigurations().get( entityName ).getIdMappingData(),
|
||||
null,
|
||||
entityName,
|
||||
enversService.getEntitiesConfigurations().isVersioned( entityName )
|
||||
true
|
||||
);
|
||||
enversService.getAuditStrategy().addEntityAtRevisionRestriction(
|
||||
configuration,
|
||||
|
@ -287,19 +426,6 @@ public class AuditAssociationQueryImpl<Q extends AuditQueryImplementor>
|
|||
true
|
||||
);
|
||||
}
|
||||
else {
|
||||
Parameters joinConditionParameters = queryBuilder.addJoin( joinType, entityName, alias, false );
|
||||
// owner.reference_id = target.id
|
||||
final IdMapper idMapperTarget = enversService.getEntitiesConfigurations()
|
||||
.getNotVersionEntityConfiguration( entityName )
|
||||
.getIdMapper();
|
||||
ownerAssociationIdMapper.addIdsEqualToQuery(
|
||||
joinConditionParameters,
|
||||
ownerAlias,
|
||||
idMapperTarget,
|
||||
alias
|
||||
);
|
||||
}
|
||||
|
||||
for ( AuditCriterion criterion : criterions ) {
|
||||
criterion.addToQuery(
|
||||
|
|
|
@ -16,7 +16,6 @@ import java.util.Set;
|
|||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.envers.exception.AuditException;
|
||||
import org.hibernate.envers.query.AuditEntity;
|
||||
import org.hibernate.orm.test.envers.BaseEnversFunctionalTestCase;
|
||||
import org.hibernate.orm.test.envers.Priority;
|
||||
|
@ -371,28 +370,6 @@ public class AuditedDynamicComponentsAdvancedCasesTest extends BaseEnversFunctio
|
|||
|
||||
assertTyping( IllegalArgumentException.class, e );
|
||||
}
|
||||
|
||||
try {
|
||||
getAuditReader().createQuery()
|
||||
.forEntitiesAtRevision( AdvancedEntity.class, 1 )
|
||||
.add(
|
||||
AuditEntity.property( "dynamicConfiguration_" + INTERNAL_MAP_WITH_MANY_TO_MANY )
|
||||
.eq( entity.getDynamicConfiguration().get( INTERNAL_MAP_WITH_MANY_TO_MANY ) )
|
||||
)
|
||||
.getResultList();
|
||||
Assert.fail();
|
||||
}
|
||||
catch ( Exception e ) {
|
||||
if ( getSession().getTransaction().isActive() ) {
|
||||
getSession().getTransaction().rollback();
|
||||
}
|
||||
|
||||
assertTyping( AuditException.class, e );
|
||||
Assert.assertEquals(
|
||||
"This type of relation (org.hibernate.orm.test.envers.integration.components.dynamic.AdvancedEntity.dynamicConfiguration_internalMapWithEntities) isn't supported and can't be used in queries.",
|
||||
e.getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -148,7 +148,8 @@ public class SanityCheckTest extends BaseEnversFunctionalTestCase {
|
|||
catch ( Exception e ) {
|
||||
assertTyping( AuditException.class, e );
|
||||
assertEquals(
|
||||
"This type of relation (org.hibernate.orm.test.envers.integration.components.dynamic.PlainEntity.component_manyToManyList) isn't supported and can't be used in queries.",
|
||||
"This type of relation (org.hibernate.orm.test.envers.integration.components.dynamic.PlainEntity." +
|
||||
"component_manyToManyList) can't be used in audit query restrictions.",
|
||||
e.getMessage()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,496 @@
|
|||
/*
|
||||
* 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 static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinTable;
|
||||
import jakarta.persistence.ManyToMany;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.criteria.JoinType;
|
||||
|
||||
import org.hibernate.envers.AuditJoinTable;
|
||||
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-11735")
|
||||
public class AssociationToManyJoinQueryTest extends BaseEnversJPAFunctionalTestCase {
|
||||
|
||||
private EntityA aEmpty;
|
||||
private EntityA aOneToMany;
|
||||
private EntityA aManyToMany;
|
||||
private EntityA aBidiOneToManyInverse;
|
||||
private EntityA aBidiManyToManyOwning;
|
||||
private EntityA aBidiManyToManyInverse;
|
||||
private EntityB b1;
|
||||
private EntityB b2;
|
||||
private EntityB b3;
|
||||
private EntityC c1;
|
||||
private EntityC c2;
|
||||
private EntityC c3;
|
||||
|
||||
@Entity(name = "EntityA")
|
||||
@Audited
|
||||
public static class EntityA {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@OneToMany
|
||||
@AuditJoinTable(name = "entitya_onetomany_entityb_aud")
|
||||
private Set<EntityB> bOneToMany = new HashSet<>();
|
||||
|
||||
@ManyToMany
|
||||
@JoinTable(name = "entitya_manytomany_entityb")
|
||||
private Set<EntityB> bManyToMany = new HashSet<>();
|
||||
|
||||
@OneToMany(mappedBy = "bidiAManyToOneOwning")
|
||||
private Set<EntityC> bidiCOneToManyInverse = new HashSet<>();
|
||||
|
||||
@ManyToMany
|
||||
@AuditJoinTable(name = "entitya_entityc_bidi_aud")
|
||||
private Set<EntityC> bidiCManyToManyOwning = new HashSet<>();
|
||||
|
||||
@ManyToMany(mappedBy = "bidiAManyToManyOwning")
|
||||
private Set<EntityC> bidiCManyToManyInverse = new HashSet<>();
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Set<EntityB> getbOneToMany() {
|
||||
return bOneToMany;
|
||||
}
|
||||
|
||||
public void setbOneToMany(Set<EntityB> bOneToMany) {
|
||||
this.bOneToMany = bOneToMany;
|
||||
}
|
||||
|
||||
public Set<EntityB> getbManyToMany() {
|
||||
return bManyToMany;
|
||||
}
|
||||
|
||||
public void setbManyToMany(Set<EntityB> bManyToMany) {
|
||||
this.bManyToMany = bManyToMany;
|
||||
}
|
||||
|
||||
public Set<EntityC> getBidiCOneToManyInverse() {
|
||||
return bidiCOneToManyInverse;
|
||||
}
|
||||
|
||||
public void setBidiCOneToManyInverse(Set<EntityC> bidiCOneToManyInverse) {
|
||||
this.bidiCOneToManyInverse = bidiCOneToManyInverse;
|
||||
}
|
||||
|
||||
public Set<EntityC> getBidiCManyToManyOwning() {
|
||||
return bidiCManyToManyOwning;
|
||||
}
|
||||
|
||||
public void setBidiCManyToManyOwning(Set<EntityC> bidiCManyToManyOwning) {
|
||||
this.bidiCManyToManyOwning = bidiCManyToManyOwning;
|
||||
}
|
||||
|
||||
public Set<EntityC> getBidiCManyToManyInverse() {
|
||||
return bidiCManyToManyInverse;
|
||||
}
|
||||
|
||||
public void setBidiCManyToManyInverse(Set<EntityC> bidiCManyToManyInverse) {
|
||||
this.bidiCManyToManyInverse = bidiCManyToManyInverse;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Entity(name = "EntityB")
|
||||
@Audited
|
||||
public static class EntityB {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "EntityC")
|
||||
@Audited
|
||||
public static class EntityC {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@ManyToOne
|
||||
private EntityA bidiAManyToOneOwning;
|
||||
|
||||
@ManyToMany
|
||||
@JoinTable(name = "entityc_entitya_bidi")
|
||||
private Set<EntityA> bidiAManyToManyOwning = new HashSet<>();
|
||||
|
||||
@ManyToMany(mappedBy = "bidiCManyToManyOwning")
|
||||
private Set<EntityA> bidiAManyToManyInverse = new HashSet<>();
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public EntityA getBidiAManyToOneOwning() {
|
||||
return bidiAManyToOneOwning;
|
||||
}
|
||||
|
||||
public void setBidiAManyToOneOwning(EntityA bidiAManyToOneOwning) {
|
||||
this.bidiAManyToOneOwning = bidiAManyToOneOwning;
|
||||
}
|
||||
|
||||
public Set<EntityA> getBidiAManyToManyOwning() {
|
||||
return bidiAManyToManyOwning;
|
||||
}
|
||||
|
||||
public void setBidiAManyToManyOwning(Set<EntityA> bidiAManyToManyOwning) {
|
||||
this.bidiAManyToManyOwning = bidiAManyToManyOwning;
|
||||
}
|
||||
|
||||
public Set<EntityA> getBidiAManyToManyInverse() {
|
||||
return bidiAManyToManyInverse;
|
||||
}
|
||||
|
||||
public void setBidiAManyToManyInverse(Set<EntityA> bidiAManyToManyInverse) {
|
||||
this.bidiAManyToManyInverse = bidiAManyToManyInverse;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[]{ EntityA.class, EntityB.class, EntityC.class };
|
||||
}
|
||||
|
||||
@Test
|
||||
@Priority(10)
|
||||
public void initData() {
|
||||
// revision 1:
|
||||
EntityManager em = getEntityManager();
|
||||
em.getTransaction().begin();
|
||||
b1 = new EntityB();
|
||||
b1.setId( 21L );
|
||||
b1.setName( "B1" );
|
||||
em.persist( b1 );
|
||||
b2 = new EntityB();
|
||||
b2.setId( 22L );
|
||||
b2.setName( "B2" );
|
||||
em.persist( b2 );
|
||||
b3 = new EntityB();
|
||||
b3.setId( 23L );
|
||||
b3.setName( "B3" );
|
||||
em.persist( b3 );
|
||||
c1 = new EntityC();
|
||||
c1.setId( 31L );
|
||||
c1.setName( "C1" );
|
||||
em.persist( c1 );
|
||||
c2 = new EntityC();
|
||||
c2.setId( 32L );
|
||||
c2.setName( "C2" );
|
||||
em.persist( c2 );
|
||||
c3 = new EntityC();
|
||||
c3.setId( 33L );
|
||||
c3.setName( "C3" );
|
||||
em.persist( c3 );
|
||||
aEmpty = new EntityA();
|
||||
aEmpty.setId( 1L );
|
||||
aEmpty.setName( "aEmpty" );
|
||||
em.persist( aEmpty );
|
||||
aOneToMany = new EntityA();
|
||||
aOneToMany.setId( 2L );
|
||||
aOneToMany.setName( "aOneToMany" );
|
||||
aOneToMany.getbOneToMany().add( b1 );
|
||||
aOneToMany.getbOneToMany().add( b3 );
|
||||
em.persist( aOneToMany );
|
||||
aManyToMany = new EntityA();
|
||||
aManyToMany.setId( 3L );
|
||||
aManyToMany.setName( "aManyToMany" );
|
||||
aManyToMany.getbManyToMany().add( b1 );
|
||||
aManyToMany.getbManyToMany().add( b3 );
|
||||
em.persist( aManyToMany );
|
||||
aBidiOneToManyInverse = new EntityA();
|
||||
aBidiOneToManyInverse.setId( 4L );
|
||||
aBidiOneToManyInverse.setName( "aBidiOneToManyInverse" );
|
||||
aBidiOneToManyInverse.getBidiCOneToManyInverse().add( c1 );
|
||||
c1.setBidiAManyToOneOwning( aBidiOneToManyInverse );
|
||||
aBidiOneToManyInverse.getBidiCOneToManyInverse().add( c3 );
|
||||
c3.setBidiAManyToOneOwning( aBidiOneToManyInverse );
|
||||
em.persist( aBidiOneToManyInverse );
|
||||
aBidiManyToManyOwning = new EntityA();
|
||||
aBidiManyToManyOwning.setId( 5L );
|
||||
aBidiManyToManyOwning.setName( "aBidiManyToManyOwning" );
|
||||
aBidiManyToManyOwning.getBidiCManyToManyOwning().add( c1 );
|
||||
c1.getBidiAManyToManyInverse().add( aBidiManyToManyOwning );
|
||||
aBidiManyToManyOwning.getBidiCManyToManyOwning().add( c3 );
|
||||
c3.getBidiAManyToManyInverse().add( aBidiManyToManyOwning );
|
||||
em.persist( aBidiManyToManyOwning );
|
||||
aBidiManyToManyInverse = new EntityA();
|
||||
aBidiManyToManyInverse.setId( 6L );
|
||||
aBidiManyToManyInverse.setName( "aBidiManyToManyInverse" );
|
||||
aBidiManyToManyInverse.getBidiCManyToManyInverse().add( c1 );
|
||||
c1.getBidiAManyToManyOwning().add( aBidiManyToManyInverse );
|
||||
aBidiManyToManyInverse.getBidiCManyToManyInverse().add( c3 );
|
||||
c3.getBidiAManyToManyOwning().add( aBidiManyToManyInverse );
|
||||
em.persist( aBidiManyToManyInverse );
|
||||
em.getTransaction().commit();
|
||||
|
||||
// revision 2:
|
||||
em.getTransaction().begin();
|
||||
aOneToMany.getbOneToMany().remove( b1 );
|
||||
aOneToMany.getbOneToMany().add( b2 );
|
||||
aManyToMany.getbManyToMany().remove( b1 );
|
||||
aManyToMany.getbManyToMany().add( b2 );
|
||||
aBidiOneToManyInverse.getBidiCOneToManyInverse().remove( c1 );
|
||||
c1.setBidiAManyToOneOwning( null );
|
||||
aBidiOneToManyInverse.getBidiCOneToManyInverse().add( c2 );
|
||||
c2.setBidiAManyToOneOwning( aBidiOneToManyInverse );
|
||||
aBidiManyToManyOwning.getBidiCManyToManyOwning().remove( c1 );
|
||||
c1.getBidiAManyToManyInverse().remove( aBidiManyToManyOwning );
|
||||
aBidiManyToManyOwning.getBidiCManyToManyOwning().add( c2 );
|
||||
c2.getBidiAManyToManyInverse().add( aBidiManyToManyOwning );
|
||||
aBidiManyToManyInverse.getBidiCManyToManyInverse().remove( c1 );
|
||||
c1.getBidiAManyToManyOwning().remove( aBidiManyToManyInverse );
|
||||
aBidiManyToManyInverse.getBidiCManyToManyInverse().add( c2 );
|
||||
c2.getBidiAManyToManyOwning().add( aBidiManyToManyInverse );
|
||||
em.getTransaction().commit();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneToManyInnerJoin() {
|
||||
List<?> list1 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation( "bOneToMany", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "B1" ) ).getResultList();
|
||||
assertEquals( "Expected exactly one entity", 1, list1.size() );
|
||||
EntityA entityA1 = (EntityA) list1.get( 0 );
|
||||
assertEquals( "Expected the correct entity to be resolved", aOneToMany.getId(), entityA1.getId() );
|
||||
|
||||
List<?> list2 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation( "bOneToMany", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "B2" ) ).getResultList();
|
||||
assertTrue( "Expected no entities to be returned, since B2 has been added in revision 2", list2.isEmpty() );
|
||||
|
||||
List<?> list3 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 2 ).traverseRelation( "bOneToMany", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "B2" ) ).getResultList();
|
||||
assertEquals( "Expected exactly one entity", 1, list3.size() );
|
||||
EntityA entityA3 = (EntityA) list3.get( 0 );
|
||||
assertEquals( "Expected the correct entity to be resolved", aOneToMany.getId(), entityA3.getId() );
|
||||
|
||||
List<?> list4 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 2 ).traverseRelation( "bOneToMany", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "B1" ) ).getResultList();
|
||||
assertTrue( "Expected no entities to be returned, since B1 has been removed in revision 2", list4.isEmpty() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneToManyLeftJoin() {
|
||||
List<?> list = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation( "bOneToMany", JoinType.LEFT, "b" ).up()
|
||||
.add( AuditEntity.or( AuditEntity.property( "name" ).eq( "aEmpty" ), AuditEntity.property( "b", "name" ).eq( "B1" ) ) )
|
||||
.getResultList();
|
||||
assertTrue( "Expected the correct entities to be resolved", listContainsIds( list, aEmpty.getId(), aOneToMany.getId() ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testManyToManyInnerJoin() {
|
||||
List<?> list1 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation( "bManyToMany", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "B1" ) ).getResultList();
|
||||
assertEquals( "Expected exactly one entity", 1, list1.size() );
|
||||
EntityA entityA1 = (EntityA) list1.get( 0 );
|
||||
assertEquals( "Expected the correct entity to be resolved", aManyToMany.getId(), entityA1.getId() );
|
||||
|
||||
List<?> list2 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation( "bManyToMany", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "B2" ) ).getResultList();
|
||||
assertTrue( "Expected no entities to be returned, since B2 has been added in revision 2", list2.isEmpty() );
|
||||
|
||||
List<?> list3 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 2 ).traverseRelation( "bManyToMany", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "B2" ) ).getResultList();
|
||||
assertEquals( "Expected exactly one entity", 1, list3.size() );
|
||||
EntityA entityA3 = (EntityA) list3.get( 0 );
|
||||
assertEquals( "Expected the correct entity to be resolved", aManyToMany.getId(), entityA3.getId() );
|
||||
|
||||
List<?> list4 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 2 ).traverseRelation( "bManyToMany", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "B1" ) ).getResultList();
|
||||
assertTrue( "Expected no entities to be returned, since B1 has been removed in revision 2", list4.isEmpty() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testManyToManyLeftJoin() {
|
||||
List<?> list = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation( "bManyToMany", JoinType.LEFT, "b" ).up()
|
||||
.add( AuditEntity.or( AuditEntity.property( "name" ).eq( "aEmpty" ), AuditEntity.property( "b", "name" ).eq( "B1" ) ) )
|
||||
.getResultList();
|
||||
assertTrue( "Expected the correct entities to be resolved", listContainsIds( list, aEmpty.getId(), aManyToMany.getId() ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBidiOneToManyInnerJoin() {
|
||||
List<?> list1 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation( "bidiCOneToManyInverse", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "C1" ) ).getResultList();
|
||||
assertEquals( "Expected exactly one entity", 1, list1.size() );
|
||||
EntityA entityA1 = (EntityA) list1.get( 0 );
|
||||
assertEquals( "Expected the correct entity to be resolved", aBidiOneToManyInverse.getId(), entityA1.getId() );
|
||||
|
||||
List<?> list2 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation( "bidiCOneToManyInverse", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "C2" ) ).getResultList();
|
||||
assertTrue( "Expected no entities to be returned, since C2 has been added in revision 2", list2.isEmpty() );
|
||||
|
||||
List<?> list3 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 2 ).traverseRelation( "bidiCOneToManyInverse", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "C2" ) ).getResultList();
|
||||
assertEquals( "Expected exactly one entity", 1, list3.size() );
|
||||
EntityA entityA3 = (EntityA) list3.get( 0 );
|
||||
assertEquals( "Expected the correct entity to be resolved", aBidiOneToManyInverse.getId(), entityA3.getId() );
|
||||
|
||||
List<?> list4 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 2 ).traverseRelation( "bidiCOneToManyInverse", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "C1" ) ).getResultList();
|
||||
assertTrue( "Expected no entities to be returned, since C1 has been removed in revision 2", list4.isEmpty() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBidiOneToManyLeftJoin() {
|
||||
List<?> list = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation( "bidiCOneToManyInverse", JoinType.LEFT, "c" )
|
||||
.up()
|
||||
.add( AuditEntity.or( AuditEntity.property( "name" ).eq( "aEmpty" ), AuditEntity.property( "c", "name" ).eq( "C1" ) ) )
|
||||
.getResultList();
|
||||
assertTrue( "Expected the correct entities to be resolved", listContainsIds( list, aEmpty.getId(), aBidiOneToManyInverse.getId() ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBidiManyToManyOwningInnerJoin() {
|
||||
List<?> list1 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation( "bidiCManyToManyOwning", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "C1" ) ).getResultList();
|
||||
assertEquals( "Expected exactly one entity", 1, list1.size() );
|
||||
EntityA entityA1 = (EntityA) list1.get( 0 );
|
||||
assertEquals( "Expected the correct entity to be resolved", aBidiManyToManyOwning.getId(), entityA1.getId() );
|
||||
|
||||
List<?> list2 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation( "bidiCManyToManyOwning", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "C2" ) ).getResultList();
|
||||
assertTrue( "Expected no entities to be returned, since C2 has been added in revision 2", list2.isEmpty() );
|
||||
|
||||
List<?> list3 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 2 ).traverseRelation( "bidiCManyToManyOwning", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "C2" ) ).getResultList();
|
||||
assertEquals( "Expected exactly one entity", 1, list3.size() );
|
||||
EntityA entityA3 = (EntityA) list3.get( 0 );
|
||||
assertEquals( "Expected the correct entity to be resolved", aBidiManyToManyOwning.getId(), entityA3.getId() );
|
||||
|
||||
List<?> list4 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 2 ).traverseRelation( "bidiCManyToManyOwning", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "C1" ) ).getResultList();
|
||||
assertTrue( "Expected no entities to be returned, since C1 has been removed in revision 2", list4.isEmpty() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBidiManyToManyOwningLeftJoin() {
|
||||
List<?> list = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation( "bidiCManyToManyOwning", JoinType.LEFT, "c" )
|
||||
.up()
|
||||
.add( AuditEntity.or( AuditEntity.property( "name" ).eq( "aEmpty" ), AuditEntity.property( "c", "name" ).eq( "C1" ) ) )
|
||||
.getResultList();
|
||||
assertTrue( "Expected the correct entities to be resolved", listContainsIds( list, aEmpty.getId(), aBidiManyToManyOwning.getId() ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBidiManyToManyInverseInnerJoin() {
|
||||
List<?> list1 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation( "bidiCManyToManyInverse", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "C1" ) ).getResultList();
|
||||
assertEquals( "Expected exactly one entity", 1, list1.size() );
|
||||
EntityA entityA1 = (EntityA) list1.get( 0 );
|
||||
assertEquals( "Expected the correct entity to be resolved", aBidiManyToManyInverse.getId(), entityA1.getId() );
|
||||
|
||||
List<?> list2 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation( "bidiCManyToManyInverse", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "C2" ) ).getResultList();
|
||||
assertTrue( "Expected no entities to be returned, since C2 has been added in revision 2", list2.isEmpty() );
|
||||
|
||||
List<?> list3 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 2 ).traverseRelation( "bidiCManyToManyInverse", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "C2" ) ).getResultList();
|
||||
assertEquals( "Expected exactly one entity", 1, list3.size() );
|
||||
EntityA entityA3 = (EntityA) list3.get( 0 );
|
||||
assertEquals( "Expected the correct entity to be resolved", aBidiManyToManyInverse.getId(), entityA3.getId() );
|
||||
|
||||
List<?> list4 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 2 ).traverseRelation( "bidiCManyToManyInverse", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "C1" ) ).getResultList();
|
||||
assertTrue( "Expected no entities to be returned, since C1 has been removed in revision 2", list4.isEmpty() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBidiManyToManyInverseLeftJoin() {
|
||||
List<?> list = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation( "bidiCManyToManyInverse", JoinType.LEFT, "c" )
|
||||
.up()
|
||||
.add( AuditEntity.or( AuditEntity.property( "name" ).eq( "aEmpty" ), AuditEntity.property( "c", "name" ).eq( "C1" ) ) )
|
||||
.getResultList();
|
||||
assertTrue( "Expected the correct entities to be resolved", listContainsIds( list, aEmpty.getId(), aBidiManyToManyInverse.getId() ) );
|
||||
}
|
||||
|
||||
private boolean listContainsIds(final Collection<?> entities, final long... ids) {
|
||||
final Set<Long> idSet = new HashSet<>();
|
||||
for ( final Object entity : entities ) {
|
||||
idSet.add( ( (EntityA) entity ).getId() );
|
||||
}
|
||||
boolean result = true;
|
||||
for ( final long id : ids ) {
|
||||
if ( !idSet.contains( id ) ) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,508 @@
|
|||
/*
|
||||
* 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 static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinTable;
|
||||
import jakarta.persistence.ManyToMany;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.criteria.JoinType;
|
||||
|
||||
import org.hibernate.envers.AuditJoinTable;
|
||||
import org.hibernate.envers.Audited;
|
||||
import org.hibernate.envers.RelationTargetAuditMode;
|
||||
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-11735")
|
||||
public class AssociationToNotAuditedManyJoinQueryTest extends BaseEnversJPAFunctionalTestCase {
|
||||
|
||||
private EntityA aEmpty;
|
||||
private EntityA aOneToMany;
|
||||
private EntityA aManyToMany;
|
||||
private EntityA aBidiOneToManyInverse;
|
||||
private EntityA aBidiManyToManyOwning;
|
||||
private EntityA aBidiManyToManyInverse;
|
||||
private EntityB b1;
|
||||
private EntityB b2;
|
||||
private EntityB b3;
|
||||
private EntityC c1;
|
||||
private EntityC c2;
|
||||
private EntityC c3;
|
||||
|
||||
@Entity(name = "EntityA")
|
||||
@Audited
|
||||
public static class EntityA {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@OneToMany
|
||||
@AuditJoinTable(name = "entitya_onetomany_entityb_aud")
|
||||
@Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
|
||||
private Set<EntityB> bOneToMany = new HashSet<>();
|
||||
|
||||
@ManyToMany
|
||||
@Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
|
||||
@JoinTable(name = "entitya_manytomany_entityb")
|
||||
private Set<EntityB> bManyToMany = new HashSet<>();
|
||||
|
||||
// @OneToMany(mappedBy="bidiAManyToOneOwning")
|
||||
// @Audited(targetAuditMode=RelationTargetAuditMode.NOT_AUDITED)
|
||||
// private Set<EntityC> bidiCOneToManyInverse = new HashSet<>();
|
||||
|
||||
@ManyToMany
|
||||
@Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
|
||||
@AuditJoinTable(name = "entitya_entityc_bidi_aud")
|
||||
private Set<EntityC> bidiCManyToManyOwning = new HashSet<>();
|
||||
|
||||
// @ManyToMany(mappedBy="bidiAManyToManyOwning")
|
||||
// @Audited(targetAuditMode=RelationTargetAuditMode.NOT_AUDITED)
|
||||
// private Set<EntityC> bidiCManyToManyInverse = new HashSet<>();
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Set<EntityB> getbOneToMany() {
|
||||
return bOneToMany;
|
||||
}
|
||||
|
||||
public void setbOneToMany(Set<EntityB> bOneToMany) {
|
||||
this.bOneToMany = bOneToMany;
|
||||
}
|
||||
|
||||
public Set<EntityB> getbManyToMany() {
|
||||
return bManyToMany;
|
||||
}
|
||||
|
||||
public void setbManyToMany(Set<EntityB> bManyToMany) {
|
||||
this.bManyToMany = bManyToMany;
|
||||
}
|
||||
|
||||
// public Set<EntityC> getBidiCOneToManyInverse() {
|
||||
// return bidiCOneToManyInverse;
|
||||
// }
|
||||
//
|
||||
//
|
||||
//
|
||||
// public void setBidiCOneToManyInverse(Set<EntityC> bidiCOneToManyInverse) {
|
||||
// this.bidiCOneToManyInverse = bidiCOneToManyInverse;
|
||||
// }
|
||||
|
||||
public Set<EntityC> getBidiCManyToManyOwning() {
|
||||
return bidiCManyToManyOwning;
|
||||
}
|
||||
|
||||
public void setBidiCManyToManyOwning(Set<EntityC> bidiCManyToManyOwning) {
|
||||
this.bidiCManyToManyOwning = bidiCManyToManyOwning;
|
||||
}
|
||||
|
||||
// public Set<EntityC> getBidiCManyToManyInverse() {
|
||||
// return bidiCManyToManyInverse;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// public void setBidiCManyToManyInverse(Set<EntityC> bidiCManyToManyInverse) {
|
||||
// this.bidiCManyToManyInverse = bidiCManyToManyInverse;
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@Entity(name = "EntityB")
|
||||
public static class EntityB {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "EntityC")
|
||||
public static class EntityC {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
// @ManyToOne
|
||||
// private EntityA bidiAManyToOneOwning;
|
||||
|
||||
// @ManyToMany
|
||||
// @JoinTable(name="entityc_entitya_bidi")
|
||||
// private Set<EntityA> bidiAManyToManyOwning = new HashSet<>();
|
||||
|
||||
@ManyToMany(mappedBy = "bidiCManyToManyOwning")
|
||||
private Set<EntityA> bidiAManyToManyInverse = new HashSet<>();
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
// public EntityA getBidiAManyToOneOwning() {
|
||||
// return bidiAManyToOneOwning;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// public void setBidiAManyToOneOwning(EntityA bidiAManyToOneOwning) {
|
||||
// this.bidiAManyToOneOwning = bidiAManyToOneOwning;
|
||||
// }
|
||||
|
||||
// public Set<EntityA> getBidiAManyToManyOwning() {
|
||||
// return bidiAManyToManyOwning;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// public void setBidiAManyToManyOwning(Set<EntityA> bidiAManyToManyOwning) {
|
||||
// this.bidiAManyToManyOwning = bidiAManyToManyOwning;
|
||||
// }
|
||||
|
||||
public Set<EntityA> getBidiAManyToManyInverse() {
|
||||
return bidiAManyToManyInverse;
|
||||
}
|
||||
|
||||
public void setBidiAManyToManyInverse(Set<EntityA> bidiAManyToManyInverse) {
|
||||
this.bidiAManyToManyInverse = bidiAManyToManyInverse;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[]{ EntityA.class, EntityB.class, EntityC.class };
|
||||
}
|
||||
|
||||
@Test
|
||||
@Priority(10)
|
||||
public void initData() {
|
||||
// revision 1:
|
||||
EntityManager em = getEntityManager();
|
||||
em.getTransaction().begin();
|
||||
b1 = new EntityB();
|
||||
b1.setId( 21L );
|
||||
b1.setName( "B1" );
|
||||
em.persist( b1 );
|
||||
b2 = new EntityB();
|
||||
b2.setId( 22L );
|
||||
b2.setName( "B2" );
|
||||
em.persist( b2 );
|
||||
b3 = new EntityB();
|
||||
b3.setId( 23L );
|
||||
b3.setName( "B3" );
|
||||
em.persist( b3 );
|
||||
c1 = new EntityC();
|
||||
c1.setId( 31L );
|
||||
c1.setName( "C1" );
|
||||
em.persist( c1 );
|
||||
c2 = new EntityC();
|
||||
c2.setId( 32L );
|
||||
c2.setName( "C2" );
|
||||
em.persist( c2 );
|
||||
c3 = new EntityC();
|
||||
c3.setId( 33L );
|
||||
c3.setName( "C3" );
|
||||
em.persist( c3 );
|
||||
aEmpty = new EntityA();
|
||||
aEmpty.setId( 1L );
|
||||
aEmpty.setName( "aEmpty" );
|
||||
em.persist( aEmpty );
|
||||
aOneToMany = new EntityA();
|
||||
aOneToMany.setId( 2L );
|
||||
aOneToMany.setName( "aOneToMany" );
|
||||
aOneToMany.getbOneToMany().add( b1 );
|
||||
aOneToMany.getbOneToMany().add( b3 );
|
||||
em.persist( aOneToMany );
|
||||
aManyToMany = new EntityA();
|
||||
aManyToMany.setId( 3L );
|
||||
aManyToMany.setName( "aManyToMany" );
|
||||
aManyToMany.getbManyToMany().add( b1 );
|
||||
aManyToMany.getbManyToMany().add( b3 );
|
||||
em.persist( aManyToMany );
|
||||
aBidiOneToManyInverse = new EntityA();
|
||||
aBidiOneToManyInverse.setId( 4L );
|
||||
aBidiOneToManyInverse.setName( "aBidiOneToManyInverse" );
|
||||
// aBidiOneToManyInverse.getBidiCOneToManyInverse().add( c1 );
|
||||
// c1.setBidiAManyToOneOwning( aBidiOneToManyInverse );
|
||||
// aBidiOneToManyInverse.getBidiCOneToManyInverse().add( c3 );
|
||||
// c3.setBidiAManyToOneOwning( aBidiOneToManyInverse );
|
||||
em.persist( aBidiOneToManyInverse );
|
||||
aBidiManyToManyOwning = new EntityA();
|
||||
aBidiManyToManyOwning.setId( 5L );
|
||||
aBidiManyToManyOwning.setName( "aBidiManyToManyOwning" );
|
||||
aBidiManyToManyOwning.getBidiCManyToManyOwning().add( c1 );
|
||||
c1.getBidiAManyToManyInverse().add( aBidiManyToManyOwning );
|
||||
aBidiManyToManyOwning.getBidiCManyToManyOwning().add( c3 );
|
||||
c3.getBidiAManyToManyInverse().add( aBidiManyToManyOwning );
|
||||
em.persist( aBidiManyToManyOwning );
|
||||
aBidiManyToManyInverse = new EntityA();
|
||||
aBidiManyToManyInverse.setId( 6L );
|
||||
aBidiManyToManyInverse.setName( "aBidiManyToManyInverse" );
|
||||
// aBidiManyToManyInverse.getBidiCManyToManyInverse().add( c1 );
|
||||
// c1.getBidiAManyToManyOwning().add( aBidiManyToManyInverse );
|
||||
// aBidiManyToManyInverse.getBidiCManyToManyInverse().add( c3 );
|
||||
// c3.getBidiAManyToManyOwning().add( aBidiManyToManyInverse );
|
||||
em.persist( aBidiManyToManyInverse );
|
||||
em.getTransaction().commit();
|
||||
|
||||
// revision 2:
|
||||
em.getTransaction().begin();
|
||||
aOneToMany.getbOneToMany().remove( b1 );
|
||||
aOneToMany.getbOneToMany().add( b2 );
|
||||
aManyToMany.getbManyToMany().remove( b1 );
|
||||
aManyToMany.getbManyToMany().add( b2 );
|
||||
// aBidiOneToManyInverse.getBidiCOneToManyInverse().remove( c1 );
|
||||
// c1.setBidiAManyToOneOwning( null );
|
||||
// aBidiOneToManyInverse.getBidiCOneToManyInverse().add( c2 );
|
||||
// c2.setBidiAManyToOneOwning( aBidiOneToManyInverse );
|
||||
aBidiManyToManyOwning.getBidiCManyToManyOwning().remove( c1 );
|
||||
c1.getBidiAManyToManyInverse().remove( aBidiManyToManyOwning );
|
||||
aBidiManyToManyOwning.getBidiCManyToManyOwning().add( c2 );
|
||||
c2.getBidiAManyToManyInverse().add( aBidiManyToManyOwning );
|
||||
// aBidiManyToManyInverse.getBidiCManyToManyInverse().remove( c1 );
|
||||
// c1.getBidiAManyToManyOwning().remove( aBidiManyToManyInverse );
|
||||
// aBidiManyToManyInverse.getBidiCManyToManyInverse().add( c2 );
|
||||
// c2.getBidiAManyToManyOwning().add( aBidiManyToManyInverse );
|
||||
em.getTransaction().commit();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneToManyInnerJoin() {
|
||||
List<?> list1 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation( "bOneToMany", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "B1" ) ).getResultList();
|
||||
assertEquals( "Expected exactly one entity", 1, list1.size() );
|
||||
EntityA entityA1 = (EntityA) list1.get( 0 );
|
||||
assertEquals( "Expected the correct entity to be resolved", aOneToMany.getId(), entityA1.getId() );
|
||||
|
||||
List<?> list2 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation( "bOneToMany", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "B2" ) ).getResultList();
|
||||
assertTrue( "Expected no entities to be returned, since B2 has been added in revision 2", list2.isEmpty() );
|
||||
|
||||
List<?> list3 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 2 ).traverseRelation( "bOneToMany", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "B2" ) ).getResultList();
|
||||
assertEquals( "Expected exactly one entity", 1, list3.size() );
|
||||
EntityA entityA3 = (EntityA) list3.get( 0 );
|
||||
assertEquals( "Expected the correct entity to be resolved", aOneToMany.getId(), entityA3.getId() );
|
||||
|
||||
List<?> list4 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 2 ).traverseRelation( "bOneToMany", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "B1" ) ).getResultList();
|
||||
assertTrue( "Expected no entities to be returned, since B1 has been removed in revision 2", list4.isEmpty() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneToManyLeftJoin() {
|
||||
List<?> list = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation( "bOneToMany", JoinType.LEFT, "b" ).up()
|
||||
.add( AuditEntity.or( AuditEntity.property( "name" ).eq( "aEmpty" ), AuditEntity.property( "b", "name" ).eq( "B1" ) ) )
|
||||
.getResultList();
|
||||
assertTrue( "Expected the correct entities to be resolved", listContainsIds( list, aEmpty.getId(), aOneToMany.getId() ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testManyToManyInnerJoin() {
|
||||
List<?> list1 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation( "bManyToMany", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "B1" ) ).getResultList();
|
||||
assertEquals( "Expected exactly one entity", 1, list1.size() );
|
||||
EntityA entityA1 = (EntityA) list1.get( 0 );
|
||||
assertEquals( "Expected the correct entity to be resolved", aManyToMany.getId(), entityA1.getId() );
|
||||
|
||||
List<?> list2 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation( "bManyToMany", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "B2" ) ).getResultList();
|
||||
assertTrue( "Expected no entities to be returned, since B2 has been added in revision 2", list2.isEmpty() );
|
||||
|
||||
List<?> list3 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 2 ).traverseRelation( "bManyToMany", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "B2" ) ).getResultList();
|
||||
assertEquals( "Expected exactly one entity", 1, list3.size() );
|
||||
EntityA entityA3 = (EntityA) list3.get( 0 );
|
||||
assertEquals( "Expected the correct entity to be resolved", aManyToMany.getId(), entityA3.getId() );
|
||||
|
||||
List<?> list4 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 2 ).traverseRelation( "bManyToMany", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "B1" ) ).getResultList();
|
||||
assertTrue( "Expected no entities to be returned, since B1 has been removed in revision 2", list4.isEmpty() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testManyToManyLeftJoin() {
|
||||
List<?> list = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation( "bManyToMany", JoinType.LEFT, "b" ).up()
|
||||
.add( AuditEntity.or( AuditEntity.property( "name" ).eq( "aEmpty" ), AuditEntity.property( "b", "name" ).eq( "B1" ) ) )
|
||||
.getResultList();
|
||||
assertTrue( "Expected the correct entities to be resolved", listContainsIds( list, aEmpty.getId(), aManyToMany.getId() ) );
|
||||
}
|
||||
|
||||
// @Test
|
||||
// public void testBidiOneToManyInnerJoin() {
|
||||
// List<?> list1 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation(
|
||||
// "bidiCOneToManyInverse", JoinType.INNER ).add( AuditEntity.property( "name" ).eq( "C1" ) ).getResultList();
|
||||
// assertEquals("Expected exactly one entity", 1, list1.size());
|
||||
// EntityA entityA1 = (EntityA) list1.get( 0 );
|
||||
// assertEquals("Expected the correct entity to be resolved", aBidiOneToManyInverse.getId(), entityA1.getId());
|
||||
//
|
||||
// List<?> list2 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation(
|
||||
// "bidiCOneToManyInverse", JoinType.INNER ).add( AuditEntity.property( "name" ).eq( "C2" ) ).getResultList();
|
||||
// assertTrue("Expected no entities to be returned, since C2 has been added in revision 2", list2.isEmpty());
|
||||
//
|
||||
// List<?> list3 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 2 ).traverseRelation(
|
||||
// "bidiCOneToManyInverse", JoinType.INNER ).add( AuditEntity.property( "name" ).eq( "C2" ) ).getResultList();
|
||||
// assertEquals("Expected exactly one entity", 1, list3.size());
|
||||
// EntityA entityA3 = (EntityA) list3.get( 0 );
|
||||
// assertEquals("Expected the correct entity to be resolved", aBidiOneToManyInverse.getId(), entityA3.getId());
|
||||
//
|
||||
// List<?> list4 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 2 ).traverseRelation(
|
||||
// "bidiCOneToManyInverse", JoinType.INNER ).add( AuditEntity.property( "name" ).eq( "C1" ) ).getResultList();
|
||||
// assertTrue("Expected no entities to be returned, since C1 has been removed in revision 2", list4.isEmpty());
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testBidiOneToManyLeftJoin() {
|
||||
// List<?> list = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation(
|
||||
// "bidiCOneToManyInverse", JoinType.LEFT, "c" ).up()
|
||||
// .add( AuditEntity.or( AuditEntity.property( "name" ).eq( "aEmpty" ), AuditEntity.property( "c", "name" ).eq( "C1"
|
||||
// ) ) )
|
||||
// .getResultList();
|
||||
// assertTrue("Expected the correct entities to be resolved", listContainsIds(list, aEmpty.getId(),
|
||||
// aBidiOneToManyInverse.getId()));
|
||||
// }
|
||||
|
||||
@Test
|
||||
public void testBidiManyToManyOwningInnerJoin() {
|
||||
List<?> list1 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation( "bidiCManyToManyOwning", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "C1" ) ).getResultList();
|
||||
assertEquals( "Expected exactly one entity", 1, list1.size() );
|
||||
EntityA entityA1 = (EntityA) list1.get( 0 );
|
||||
assertEquals( "Expected the correct entity to be resolved", aBidiManyToManyOwning.getId(), entityA1.getId() );
|
||||
|
||||
List<?> list2 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation( "bidiCManyToManyOwning", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "C2" ) ).getResultList();
|
||||
assertTrue( "Expected no entities to be returned, since C2 has been added in revision 2", list2.isEmpty() );
|
||||
|
||||
List<?> list3 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 2 ).traverseRelation( "bidiCManyToManyOwning", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "C2" ) ).getResultList();
|
||||
assertEquals( "Expected exactly one entity", 1, list3.size() );
|
||||
EntityA entityA3 = (EntityA) list3.get( 0 );
|
||||
assertEquals( "Expected the correct entity to be resolved", aBidiManyToManyOwning.getId(), entityA3.getId() );
|
||||
|
||||
List<?> list4 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 2 ).traverseRelation( "bidiCManyToManyOwning", JoinType.INNER )
|
||||
.add( AuditEntity.property( "name" ).eq( "C1" ) ).getResultList();
|
||||
assertTrue( "Expected no entities to be returned, since C1 has been removed in revision 2", list4.isEmpty() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBidiManyToManyOwningLeftJoin() {
|
||||
List<?> list = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation( "bidiCManyToManyOwning", JoinType.LEFT, "c" )
|
||||
.up()
|
||||
.add( AuditEntity.or( AuditEntity.property( "name" ).eq( "aEmpty" ), AuditEntity.property( "c", "name" ).eq( "C1" ) ) )
|
||||
.getResultList();
|
||||
assertTrue( "Expected the correct entities to be resolved", listContainsIds( list, aEmpty.getId(), aBidiManyToManyOwning.getId() ) );
|
||||
}
|
||||
|
||||
// @Test
|
||||
// public void testBidiManyToManyInverseInnerJoin() {
|
||||
// List<?> list1 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation(
|
||||
// "bidiCManyToManyInverse", JoinType.INNER ).add( AuditEntity.property( "name" ).eq( "C1" ) ).getResultList();
|
||||
// assertEquals("Expected exactly one entity", 1, list1.size());
|
||||
// EntityA entityA1 = (EntityA) list1.get( 0 );
|
||||
// assertEquals("Expected the correct entity to be resolved", aBidiManyToManyInverse.getId(), entityA1.getId());
|
||||
//
|
||||
// List<?> list2 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation(
|
||||
// "bidiCManyToManyInverse", JoinType.INNER ).add( AuditEntity.property( "name" ).eq( "C2" ) ).getResultList();
|
||||
// assertTrue("Expected no entities to be returned, since C2 has been added in revision 2", list2.isEmpty());
|
||||
//
|
||||
// List<?> list3 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 2 ).traverseRelation(
|
||||
// "bidiCManyToManyInverse", JoinType.INNER ).add( AuditEntity.property( "name" ).eq( "C2" ) ).getResultList();
|
||||
// assertEquals("Expected exactly one entity", 1, list3.size());
|
||||
// EntityA entityA3 = (EntityA) list3.get( 0 );
|
||||
// assertEquals("Expected the correct entity to be resolved", aBidiManyToManyInverse.getId(), entityA3.getId());
|
||||
//
|
||||
// List<?> list4 = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 2 ).traverseRelation(
|
||||
// "bidiCManyToManyInverse", JoinType.INNER ).add( AuditEntity.property( "name" ).eq( "C1" ) ).getResultList();
|
||||
// assertTrue("Expected no entities to be returned, since C1 has been removed in revision 2", list4.isEmpty());
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testBidiManyToManyInverseLeftJoin() {
|
||||
// List<?> list = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 ).traverseRelation(
|
||||
// "bidiCManyToManyInverse", JoinType.LEFT, "c" ).up()
|
||||
// .add( AuditEntity.or( AuditEntity.property( "name" ).eq( "aEmpty" ), AuditEntity.property( "c", "name" ).eq( "C1"
|
||||
// ) ) )
|
||||
// .getResultList();
|
||||
// assertTrue("Expected the correct entities to be resolved", listContainsIds(list, aEmpty.getId(),
|
||||
// aBidiManyToManyInverse.getId()));
|
||||
// }
|
||||
|
||||
private boolean listContainsIds(final Collection<?> entities, final long... ids) {
|
||||
final Set<Long> idSet = new HashSet<>();
|
||||
for ( final Object entity : entities ) {
|
||||
idSet.add( ( (EntityA) entity ).getId() );
|
||||
}
|
||||
boolean result = true;
|
||||
for ( final long id : ids ) {
|
||||
if ( !idSet.contains( id ) ) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue