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 );
|
addMapper( context, commonCollectionMapperData, elementComponentData, indexComponentData );
|
||||||
|
|
||||||
// Storing information about this relation.
|
// Storing information about this relation.
|
||||||
storeMiddleEntityRelationInformation( context, mappedBy );
|
storeMiddleEntityRelationInformation( context, mappedBy, referencingIdData, referencedPrefix, auditMiddleEntityName );
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getMiddleTableName(CollectionMetadataContext context) {
|
private String getMiddleTableName(CollectionMetadataContext context) {
|
||||||
|
@ -231,20 +231,42 @@ public class MiddleTableCollectionMetadataGenerator extends AbstractCollectionMe
|
||||||
return getMetadataBuildingContext().getMetadataCollector().getEntityBinding( context.getReferencedEntityName() );
|
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).
|
// Only if this is a relation (when there is a referenced entity).
|
||||||
if ( context.getReferencedEntityName() != null ) {
|
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() ) {
|
if ( context.getCollection().isInverse() ) {
|
||||||
context.getReferencingEntityConfiguration().addToManyMiddleNotOwningRelation(
|
context.getReferencingEntityConfiguration().addToManyMiddleNotOwningRelation(
|
||||||
context.getPropertyName(),
|
context.getPropertyName(),
|
||||||
mappedBy,
|
mappedBy,
|
||||||
context.getReferencedEntityName()
|
context.getReferencedEntityName(),
|
||||||
|
referencingIdData,
|
||||||
|
referencedIdData,
|
||||||
|
auditMiddleEntityName
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
context.getReferencingEntityConfiguration().addToManyMiddleRelation(
|
context.getReferencingEntityConfiguration().addToManyMiddleRelation(
|
||||||
context.getPropertyName(),
|
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.ExtendedPropertyMapper;
|
||||||
import org.hibernate.envers.internal.entities.mapper.PropertyMapper;
|
import org.hibernate.envers.internal.entities.mapper.PropertyMapper;
|
||||||
import org.hibernate.envers.internal.entities.mapper.id.IdMapper;
|
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.
|
* Runtime representation of an entity that may or may not be audited.
|
||||||
|
@ -109,13 +110,21 @@ public class EntityConfiguration {
|
||||||
idMapper,
|
idMapper,
|
||||||
fakeBidirectionalRelationMapper,
|
fakeBidirectionalRelationMapper,
|
||||||
fakeBidirectionalRelationIndexMapper,
|
fakeBidirectionalRelationIndexMapper,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
true,
|
true,
|
||||||
indexed
|
indexed
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addToManyMiddleRelation(String fromPropertyName, String toEntityName) {
|
public void addToManyMiddleRelation(
|
||||||
|
String fromPropertyName,
|
||||||
|
String toEntityName,
|
||||||
|
MiddleIdData referencingIdData,
|
||||||
|
MiddleIdData referencedIdData,
|
||||||
|
String auditMiddleEntityName) {
|
||||||
relations.put(
|
relations.put(
|
||||||
fromPropertyName,
|
fromPropertyName,
|
||||||
RelationDescription.toMany(
|
RelationDescription.toMany(
|
||||||
|
@ -126,13 +135,22 @@ public class EntityConfiguration {
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
referencingIdData,
|
||||||
|
referencedIdData,
|
||||||
|
auditMiddleEntityName,
|
||||||
true,
|
true,
|
||||||
false
|
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(
|
relations.put(
|
||||||
fromPropertyName,
|
fromPropertyName,
|
||||||
RelationDescription.toMany(
|
RelationDescription.toMany(
|
||||||
|
@ -143,6 +161,9 @@ public class EntityConfiguration {
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
referencingIdData,
|
||||||
|
referencedIdData,
|
||||||
|
auditMiddleEntityName,
|
||||||
true,
|
true,
|
||||||
false
|
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.PropertyMapper;
|
||||||
import org.hibernate.envers.internal.entities.mapper.id.IdMapper;
|
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)
|
* @author Adam Warski (adam at warski dot org)
|
||||||
|
@ -22,6 +23,9 @@ public class RelationDescription {
|
||||||
private final IdMapper idMapper;
|
private final IdMapper idMapper;
|
||||||
private final PropertyMapper fakeBidirectionalRelationMapper;
|
private final PropertyMapper fakeBidirectionalRelationMapper;
|
||||||
private final PropertyMapper fakeBidirectionalRelationIndexMapper;
|
private final PropertyMapper fakeBidirectionalRelationIndexMapper;
|
||||||
|
private final MiddleIdData referencingIdData;
|
||||||
|
private final MiddleIdData referencedIdData;
|
||||||
|
private final String auditMiddleEntityName;
|
||||||
private final boolean insertable;
|
private final boolean insertable;
|
||||||
private final boolean indexed;
|
private final boolean indexed;
|
||||||
private boolean bidirectional;
|
private boolean bidirectional;
|
||||||
|
@ -38,7 +42,7 @@ public class RelationDescription {
|
||||||
boolean ignoreNotFound) {
|
boolean ignoreNotFound) {
|
||||||
return new RelationDescription(
|
return new RelationDescription(
|
||||||
fromPropertyName, relationType, toEntityName, mappedByPropertyName, idMapper,
|
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,
|
IdMapper idMapper,
|
||||||
PropertyMapper fakeBidirectionalRelationMapper,
|
PropertyMapper fakeBidirectionalRelationMapper,
|
||||||
PropertyMapper fakeBidirectionalRelationIndexMapper,
|
PropertyMapper fakeBidirectionalRelationIndexMapper,
|
||||||
|
MiddleIdData referencingIdData,
|
||||||
|
MiddleIdData referencedIdData,
|
||||||
|
String auditMiddleEntityName,
|
||||||
boolean insertable,
|
boolean insertable,
|
||||||
boolean indexed) {
|
boolean indexed) {
|
||||||
// Envers populates collections by executing dedicated queries. Special handling of
|
// Envers populates collections by executing dedicated queries. Special handling of
|
||||||
|
@ -58,7 +65,7 @@ public class RelationDescription {
|
||||||
// Therefore assigning false to ignoreNotFound.
|
// Therefore assigning false to ignoreNotFound.
|
||||||
return new RelationDescription(
|
return new RelationDescription(
|
||||||
fromPropertyName, relationType, toEntityName, mappedByPropertyName, idMapper, fakeBidirectionalRelationMapper,
|
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,
|
IdMapper idMapper,
|
||||||
PropertyMapper fakeBidirectionalRelationMapper,
|
PropertyMapper fakeBidirectionalRelationMapper,
|
||||||
PropertyMapper fakeBidirectionalRelationIndexMapper,
|
PropertyMapper fakeBidirectionalRelationIndexMapper,
|
||||||
|
MiddleIdData referencingIdData,
|
||||||
|
MiddleIdData referencedIdData,
|
||||||
|
String auditMiddleEntityName,
|
||||||
boolean insertable,
|
boolean insertable,
|
||||||
boolean ignoreNotFound,
|
boolean ignoreNotFound,
|
||||||
boolean indexed) {
|
boolean indexed) {
|
||||||
|
@ -81,6 +91,9 @@ public class RelationDescription {
|
||||||
this.idMapper = idMapper;
|
this.idMapper = idMapper;
|
||||||
this.fakeBidirectionalRelationMapper = fakeBidirectionalRelationMapper;
|
this.fakeBidirectionalRelationMapper = fakeBidirectionalRelationMapper;
|
||||||
this.fakeBidirectionalRelationIndexMapper = fakeBidirectionalRelationIndexMapper;
|
this.fakeBidirectionalRelationIndexMapper = fakeBidirectionalRelationIndexMapper;
|
||||||
|
this.referencingIdData = referencingIdData;
|
||||||
|
this.referencedIdData = referencedIdData;
|
||||||
|
this.auditMiddleEntityName = auditMiddleEntityName;
|
||||||
this.insertable = insertable;
|
this.insertable = insertable;
|
||||||
this.indexed = indexed;
|
this.indexed = indexed;
|
||||||
this.bidirectional = false;
|
this.bidirectional = false;
|
||||||
|
@ -118,6 +131,18 @@ public class RelationDescription {
|
||||||
return fakeBidirectionalRelationIndexMapper;
|
return fakeBidirectionalRelationIndexMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MiddleIdData getReferencingIdData() {
|
||||||
|
return referencingIdData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MiddleIdData getReferencedIdData() {
|
||||||
|
return referencedIdData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAuditMiddleEntityName() {
|
||||||
|
return auditMiddleEntityName;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isInsertable() {
|
public boolean isInsertable() {
|
||||||
return insertable;
|
return insertable;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ package org.hibernate.envers.query.criteria.internal;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.envers.boot.internal.EnversService;
|
import org.hibernate.envers.boot.internal.EnversService;
|
||||||
|
@ -48,13 +49,20 @@ public abstract class CriteriaTools {
|
||||||
return null;
|
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;
|
return relationDesc;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new AuditException(
|
throw new AuditException(
|
||||||
"This type of relation (" + entityName + "." + propertyName +
|
String.format(
|
||||||
") isn't supported and can't be used in queries."
|
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;
|
package org.hibernate.envers.query.criteria.internal;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import org.hibernate.envers.boot.internal.EnversService;
|
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.RelationDescription;
|
||||||
|
import org.hibernate.envers.internal.entities.RelationType;
|
||||||
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
||||||
import org.hibernate.envers.internal.tools.query.Parameters;
|
import org.hibernate.envers.internal.tools.query.Parameters;
|
||||||
import org.hibernate.envers.internal.tools.query.QueryBuilder;
|
import org.hibernate.envers.internal.tools.query.QueryBuilder;
|
||||||
|
@ -43,8 +47,18 @@ public class NotNullAuditExpression extends AbstractAtomicExpression {
|
||||||
if ( relatedEntity == null ) {
|
if ( relatedEntity == null ) {
|
||||||
parameters.addNotNullRestriction( alias, propertyName );
|
parameters.addNotNullRestriction( alias, propertyName );
|
||||||
}
|
}
|
||||||
else {
|
else if ( relatedEntity.getRelationType() == RelationType.TO_ONE ) {
|
||||||
relatedEntity.getIdMapper().addIdEqualsToQuery( parameters, null, alias, null, false );
|
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;
|
package org.hibernate.envers.query.criteria.internal;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import org.hibernate.envers.boot.internal.EnversService;
|
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.RelationDescription;
|
||||||
|
import org.hibernate.envers.internal.entities.RelationType;
|
||||||
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
||||||
import org.hibernate.envers.internal.tools.query.Parameters;
|
import org.hibernate.envers.internal.tools.query.Parameters;
|
||||||
import org.hibernate.envers.internal.tools.query.QueryBuilder;
|
import org.hibernate.envers.internal.tools.query.QueryBuilder;
|
||||||
|
@ -43,8 +47,18 @@ public class NullAuditExpression extends AbstractAtomicExpression {
|
||||||
if ( relatedEntity == null ) {
|
if ( relatedEntity == null ) {
|
||||||
parameters.addNullRestriction( alias, propertyName );
|
parameters.addNullRestriction( alias, propertyName );
|
||||||
}
|
}
|
||||||
else {
|
else if ( relatedEntity.getRelationType() == RelationType.TO_ONE ) {
|
||||||
relatedEntity.getIdMapper().addIdEqualsToQuery( parameters, null, alias, null, true );
|
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;
|
package org.hibernate.envers.query.criteria.internal;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import org.hibernate.envers.boot.internal.EnversService;
|
import org.hibernate.envers.boot.internal.EnversService;
|
||||||
import org.hibernate.envers.exception.AuditException;
|
import org.hibernate.envers.exception.AuditException;
|
||||||
import org.hibernate.envers.internal.entities.RelationDescription;
|
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.reader.AuditReaderImplementor;
|
||||||
import org.hibernate.envers.internal.tools.query.Parameters;
|
import org.hibernate.envers.internal.tools.query.Parameters;
|
||||||
import org.hibernate.envers.internal.tools.query.QueryBuilder;
|
import org.hibernate.envers.internal.tools.query.QueryBuilder;
|
||||||
import org.hibernate.envers.query.criteria.AuditCriterion;
|
|
||||||
import org.hibernate.envers.query.internal.property.PropertyNameGetter;
|
import org.hibernate.envers.query.internal.property.PropertyNameGetter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -49,7 +51,16 @@ public class RelatedAuditEqualityExpression extends AbstractAtomicExpression {
|
||||||
RelationDescription relatedEntity = CriteriaTools.getRelatedEntity( enversService, entityName, propertyName );
|
RelationDescription relatedEntity = CriteriaTools.getRelatedEntity( enversService, entityName, propertyName );
|
||||||
if ( relatedEntity == null ) {
|
if ( relatedEntity == null ) {
|
||||||
throw new AuditException(
|
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 );
|
relatedEntity.getIdMapper().addIdEqualsToQuery( parameters, id, alias, null, equals );
|
||||||
|
|
|
@ -7,15 +7,16 @@
|
||||||
package org.hibernate.envers.query.criteria.internal;
|
package org.hibernate.envers.query.criteria.internal;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import org.hibernate.envers.boot.internal.EnversService;
|
import org.hibernate.envers.boot.internal.EnversService;
|
||||||
import org.hibernate.envers.exception.AuditException;
|
import org.hibernate.envers.exception.AuditException;
|
||||||
import org.hibernate.envers.internal.entities.RelationDescription;
|
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.entities.mapper.id.QueryParameterData;
|
||||||
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
||||||
import org.hibernate.envers.internal.tools.query.Parameters;
|
import org.hibernate.envers.internal.tools.query.Parameters;
|
||||||
import org.hibernate.envers.internal.tools.query.QueryBuilder;
|
import org.hibernate.envers.internal.tools.query.QueryBuilder;
|
||||||
import org.hibernate.envers.query.criteria.AuditCriterion;
|
|
||||||
import org.hibernate.envers.query.internal.property.PropertyNameGetter;
|
import org.hibernate.envers.query.internal.property.PropertyNameGetter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,7 +52,16 @@ public class RelatedAuditInExpression extends AbstractAtomicExpression {
|
||||||
RelationDescription relatedEntity = CriteriaTools.getRelatedEntity( enversService, entityName, propertyName );
|
RelationDescription relatedEntity = CriteriaTools.getRelatedEntity( enversService, entityName, propertyName );
|
||||||
if ( relatedEntity == null ) {
|
if ( relatedEntity == null ) {
|
||||||
throw new AuditException(
|
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;
|
package org.hibernate.envers.query.criteria.internal;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import org.hibernate.engine.spi.SessionImplementor;
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
import org.hibernate.envers.boot.internal.EnversService;
|
import org.hibernate.envers.boot.internal.EnversService;
|
||||||
import org.hibernate.envers.exception.AuditException;
|
import org.hibernate.envers.exception.AuditException;
|
||||||
import org.hibernate.envers.internal.entities.RelationDescription;
|
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.reader.AuditReaderImplementor;
|
||||||
import org.hibernate.envers.internal.tools.query.Parameters;
|
import org.hibernate.envers.internal.tools.query.Parameters;
|
||||||
import org.hibernate.envers.internal.tools.query.QueryBuilder;
|
import org.hibernate.envers.internal.tools.query.QueryBuilder;
|
||||||
|
@ -59,7 +62,13 @@ public class SimpleAuditExpression extends AbstractAtomicExpression {
|
||||||
final Type type = getPropertyType( session, entityName, propertyName );
|
final Type type = getPropertyType( session, entityName, propertyName );
|
||||||
if ( type != null && type.isComponentType() ) {
|
if ( type != null && type.isComponentType() ) {
|
||||||
if ( !"=".equals( op ) && !"<>".equals( op ) ) {
|
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;
|
final ComponentType componentType = (ComponentType) type;
|
||||||
for ( int i = 0; i < componentType.getPropertyNames().length; i++ ) {
|
for ( int i = 0; i < componentType.getPropertyNames().length; i++ ) {
|
||||||
|
@ -76,16 +85,30 @@ public class SimpleAuditExpression extends AbstractAtomicExpression {
|
||||||
parameters.addWhereWithParam( alias, propertyName, op, value );
|
parameters.addWhereWithParam( alias, propertyName, op, value );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else if ( relatedEntity.getRelationType() == RelationType.TO_ONE ) {
|
||||||
if ( !"=".equals( op ) && !"<>".equals( op ) ) {
|
if ( !"=".equals( op ) && !"<>".equals( op ) ) {
|
||||||
throw new AuditException(
|
throw new AuditException(
|
||||||
"This type of operation: " + op + " (" + entityName + "." + propertyName +
|
String.format(
|
||||||
") isn't supported and can't be used in queries."
|
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 );
|
Object id = relatedEntity.getIdMapper().mapToIdFromEntity( value );
|
||||||
relatedEntity.getIdMapper().addIdEqualsToQuery( parameters, id, alias, null, "=".equals( op ) );
|
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.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import jakarta.persistence.NoResultException;
|
import jakarta.persistence.NoResultException;
|
||||||
import jakarta.persistence.NonUniqueResultException;
|
import jakarta.persistence.NonUniqueResultException;
|
||||||
|
@ -18,10 +19,12 @@ import org.hibernate.CacheMode;
|
||||||
import org.hibernate.FlushMode;
|
import org.hibernate.FlushMode;
|
||||||
import org.hibernate.Incubating;
|
import org.hibernate.Incubating;
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
|
import org.hibernate.envers.RevisionType;
|
||||||
import org.hibernate.envers.boot.internal.EnversService;
|
import org.hibernate.envers.boot.internal.EnversService;
|
||||||
import org.hibernate.envers.configuration.Configuration;
|
import org.hibernate.envers.configuration.Configuration;
|
||||||
import org.hibernate.envers.exception.AuditException;
|
import org.hibernate.envers.exception.AuditException;
|
||||||
import org.hibernate.envers.internal.entities.RelationDescription;
|
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.id.IdMapper;
|
||||||
import org.hibernate.envers.internal.entities.mapper.relation.MiddleIdData;
|
import org.hibernate.envers.internal.entities.mapper.relation.MiddleIdData;
|
||||||
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
|
||||||
|
@ -47,8 +50,9 @@ public class AuditAssociationQueryImpl<Q extends AuditQueryImplementor>
|
||||||
private final QueryBuilder queryBuilder;
|
private final QueryBuilder queryBuilder;
|
||||||
private final JoinType joinType;
|
private final JoinType joinType;
|
||||||
private final String entityName;
|
private final String entityName;
|
||||||
private final IdMapper ownerAssociationIdMapper;
|
private final RelationDescription relationDescription;
|
||||||
private final String ownerAlias;
|
private final String ownerAlias;
|
||||||
|
private final String ownerEntityName;
|
||||||
private final String alias;
|
private final String alias;
|
||||||
private final Map<String, String> aliasToEntityNameMap;
|
private final Map<String, String> aliasToEntityNameMap;
|
||||||
private final List<AuditCriterion> criterions = new ArrayList<>();
|
private final List<AuditCriterion> criterions = new ArrayList<>();
|
||||||
|
@ -72,17 +76,24 @@ public class AuditAssociationQueryImpl<Q extends AuditQueryImplementor>
|
||||||
this.queryBuilder = queryBuilder;
|
this.queryBuilder = queryBuilder;
|
||||||
this.joinType = joinType;
|
this.joinType = joinType;
|
||||||
|
|
||||||
String ownerEntityName = aliasToEntityNameMap.get( ownerAlias );
|
ownerEntityName = aliasToEntityNameMap.get( ownerAlias );
|
||||||
final RelationDescription relationDescription = CriteriaTools.getRelatedEntity(
|
final RelationDescription relationDescription = CriteriaTools.getRelatedEntity(
|
||||||
enversService,
|
enversService,
|
||||||
ownerEntityName,
|
ownerEntityName,
|
||||||
propertyName
|
propertyName
|
||||||
);
|
);
|
||||||
if ( relationDescription == null ) {
|
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.entityName = relationDescription.getToEntityName();
|
||||||
this.ownerAssociationIdMapper = relationDescription.getIdMapper();
|
this.relationDescription = relationDescription;
|
||||||
this.ownerAlias = ownerAlias;
|
this.ownerAlias = ownerAlias;
|
||||||
this.alias = userSuppliedAlias == null ? queryBuilder.generateAlias() : userSuppliedAlias;
|
this.alias = userSuppliedAlias == null ? queryBuilder.generateAlias() : userSuppliedAlias;
|
||||||
aliasToEntityNameMap.put( this.alias, entityName );
|
aliasToEntityNameMap.put( this.alias, entityName );
|
||||||
|
@ -241,26 +252,154 @@ public class AuditAssociationQueryImpl<Q extends AuditQueryImplementor>
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void addCriterionsToQuery(AuditReaderImplementor versionsReader) {
|
protected void addCriterionsToQuery(AuditReaderImplementor versionsReader) {
|
||||||
if ( enversService.getEntitiesConfigurations().isVersioned( entityName ) ) {
|
final Configuration configuration = enversService.getConfig();
|
||||||
Configuration configuration = enversService.getConfig();
|
boolean targetIsAudited = enversService.getEntitiesConfigurations().isVersioned( entityName );
|
||||||
String auditEntityName = configuration.getAuditEntityName( entityName );
|
String targetEntityName = entityName;
|
||||||
Parameters joinConditionParameters = queryBuilder.addJoin( joinType, auditEntityName, alias, false );
|
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
|
// owner.reference_id = target.originalId.id
|
||||||
String originalIdPropertyName = configuration.getOriginalIdPropertyName();
|
IdMapper idMapperTarget;
|
||||||
IdMapper idMapperTarget = enversService.getEntitiesConfigurations().get( entityName ).getIdMapper();
|
String prefix;
|
||||||
final String prefix = alias.concat( "." ).concat( originalIdPropertyName );
|
if ( targetIsAudited ) {
|
||||||
ownerAssociationIdMapper.addIdsEqualToQuery(
|
idMapperTarget = enversService.getEntitiesConfigurations().get( entityName ).getIdMapper();
|
||||||
|
prefix = alias.concat( "." ).concat( originalIdPropertyName );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
idMapperTarget = enversService.getEntitiesConfigurations()
|
||||||
|
.getNotVersionEntityConfiguration( entityName )
|
||||||
|
.getIdMapper();
|
||||||
|
prefix = alias;
|
||||||
|
}
|
||||||
|
relationDescription.getIdMapper().addIdsEqualToQuery(
|
||||||
joinConditionParameters,
|
joinConditionParameters,
|
||||||
ownerAlias,
|
ownerAlias,
|
||||||
idMapperTarget,
|
idMapperTarget,
|
||||||
prefix
|
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
|
// filter revision of target entity
|
||||||
Parameters parametersToUse = parameters;
|
Parameters parametersToUse = parameters;
|
||||||
String revisionPropertyPath = configuration.getRevisionNumberPath();
|
if ( joinType == JoinType.LEFT ) {
|
||||||
if (joinType == JoinType.LEFT) {
|
|
||||||
parametersToUse = parameters.addSubParameters( Parameters.OR );
|
parametersToUse = parameters.addSubParameters( Parameters.OR );
|
||||||
parametersToUse.addNullRestriction( revisionPropertyPath, true );
|
parametersToUse.addNullRestriction( revisionPropertyPath, true );
|
||||||
parametersToUse = parametersToUse.addSubParameters( Parameters.AND );
|
parametersToUse = parametersToUse.addSubParameters( Parameters.AND );
|
||||||
|
@ -270,7 +409,7 @@ public class AuditAssociationQueryImpl<Q extends AuditQueryImplementor>
|
||||||
enversService.getEntitiesConfigurations().get( entityName ).getIdMappingData(),
|
enversService.getEntitiesConfigurations().get( entityName ).getIdMappingData(),
|
||||||
null,
|
null,
|
||||||
entityName,
|
entityName,
|
||||||
enversService.getEntitiesConfigurations().isVersioned( entityName )
|
true
|
||||||
);
|
);
|
||||||
enversService.getAuditStrategy().addEntityAtRevisionRestriction(
|
enversService.getAuditStrategy().addEntityAtRevisionRestriction(
|
||||||
configuration,
|
configuration,
|
||||||
|
@ -287,19 +426,6 @@ public class AuditAssociationQueryImpl<Q extends AuditQueryImplementor>
|
||||||
true
|
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 ) {
|
for ( AuditCriterion criterion : criterions ) {
|
||||||
criterion.addToQuery(
|
criterion.addToQuery(
|
||||||
|
|
|
@ -16,7 +16,6 @@ import java.util.Set;
|
||||||
|
|
||||||
import org.hibernate.Session;
|
import org.hibernate.Session;
|
||||||
import org.hibernate.cfg.AvailableSettings;
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
import org.hibernate.envers.exception.AuditException;
|
|
||||||
import org.hibernate.envers.query.AuditEntity;
|
import org.hibernate.envers.query.AuditEntity;
|
||||||
import org.hibernate.orm.test.envers.BaseEnversFunctionalTestCase;
|
import org.hibernate.orm.test.envers.BaseEnversFunctionalTestCase;
|
||||||
import org.hibernate.orm.test.envers.Priority;
|
import org.hibernate.orm.test.envers.Priority;
|
||||||
|
@ -371,28 +370,6 @@ public class AuditedDynamicComponentsAdvancedCasesTest extends BaseEnversFunctio
|
||||||
|
|
||||||
assertTyping( IllegalArgumentException.class, e );
|
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
|
@Test
|
||||||
|
|
|
@ -148,7 +148,8 @@ public class SanityCheckTest extends BaseEnversFunctionalTestCase {
|
||||||
catch ( Exception e ) {
|
catch ( Exception e ) {
|
||||||
assertTyping( AuditException.class, e );
|
assertTyping( AuditException.class, e );
|
||||||
assertEquals(
|
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()
|
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