HHH-11573 Query based on type expressions

This commit is contained in:
Felix Feisst 2017-03-16 21:02:08 +01:00 committed by Chris Cranford
parent 9d75e6d620
commit 1eec41a136
4 changed files with 212 additions and 1 deletions

View File

@ -482,6 +482,11 @@ public class Parameters {
expressions.add( expression.toString() ); expressions.add( expression.toString() );
} }
public void addEntityTypeRestriction(String alias, String entityName) {
String expression = String.format( "type(%s) = %s", alias, entityName );
expressions.add( expression );
}
private void append(StringBuilder sb, String toAppend, MutableBoolean isFirst) { private void append(StringBuilder sb, String toAppend, MutableBoolean isFirst) {
if ( !isFirst.isSet() ) { if ( !isFirst.isSet() ) {
sb.append( " " ).append( connective ).append( " " ); sb.append( " " ).append( connective ).append( " " );

View File

@ -11,6 +11,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import org.hibernate.envers.RevisionType; import org.hibernate.envers.RevisionType;
import org.hibernate.envers.internal.tools.EntityTools;
import org.hibernate.envers.query.criteria.AuditConjunction; import org.hibernate.envers.query.criteria.AuditConjunction;
import org.hibernate.envers.query.criteria.AuditCriterion; import org.hibernate.envers.query.criteria.AuditCriterion;
import org.hibernate.envers.query.criteria.AuditDisjunction; import org.hibernate.envers.query.criteria.AuditDisjunction;
@ -18,6 +19,7 @@ import org.hibernate.envers.query.criteria.AuditFunction;
import org.hibernate.envers.query.criteria.AuditId; import org.hibernate.envers.query.criteria.AuditId;
import org.hibernate.envers.query.criteria.AuditProperty; import org.hibernate.envers.query.criteria.AuditProperty;
import org.hibernate.envers.query.criteria.AuditRelatedId; import org.hibernate.envers.query.criteria.AuditRelatedId;
import org.hibernate.envers.query.criteria.internal.EntityTypeAuditExpression;
import org.hibernate.envers.query.criteria.internal.LogicalAuditExpression; import org.hibernate.envers.query.criteria.internal.LogicalAuditExpression;
import org.hibernate.envers.query.criteria.internal.NotAuditExpression; import org.hibernate.envers.query.criteria.internal.NotAuditExpression;
import org.hibernate.envers.query.internal.property.EntityPropertyName; import org.hibernate.envers.query.internal.property.EntityPropertyName;
@ -192,7 +194,7 @@ public class AuditEntity {
* <li>AuditEntity.function("concat", AuditEntity.function("upper", AuditEntity.property("prop1")), * <li>AuditEntity.function("concat", AuditEntity.function("upper", AuditEntity.property("prop1")),
* AuditEntity.function("substring", AuditEntity.property("prop2"), 1, 2))</li> * AuditEntity.function("substring", AuditEntity.property("prop2"), 1, 2))</li>
* </ul> * </ul>
* *
* @param function the name of the function * @param function the name of the function
* @param arguments the arguments of the function. A function argument can either be * @param arguments the arguments of the function. A function argument can either be
* <ul> * <ul>
@ -207,4 +209,44 @@ public class AuditEntity {
Collections.addAll( argumentList, arguments ); Collections.addAll( argumentList, arguments );
return new AuditFunction( function, argumentList ); return new AuditFunction( function, argumentList );
} }
/**
* Adds a restriction for the type of the current entity.
*
* @param the entity type to restrict the current alias to
*/
public static AuditCriterion entityType(final Class<?> type) {
return entityType( null, type );
}
/**
* Adds a restriction for the type of the current entity.
*
* @param entityName the entity name to restrict the current alias to
*/
public static AuditCriterion entityType(final String entityName) {
return entityType( null, entityName );
}
/**
* Adds a restriction for the type of the entity of the specified alias.
*
* @param alias the alias to restrict. If null is specified, the current alias is used
* @param type the entity type to restrict the alias to
*/
public static AuditCriterion entityType(final String alias, final Class<?> type) {
Class<?> unproxiedType = EntityTools.getTargetClassIfProxied( type );
return entityType( alias, unproxiedType.getName() );
}
/**
* Adds a restriction for the type of the entity of the specified alias.
*
* @param alias the alias to restrict. If null is specified, the current alias is used
* @param entityName the entity name to restrict the alias to
* @return
*/
public static AuditCriterion entityType(final String alias, final String entityName) {
return new EntityTypeAuditExpression( alias, entityName );
}
} }

View File

@ -0,0 +1,47 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.envers.query.criteria.internal;
import java.util.Map;
import org.hibernate.envers.boot.internal.EnversService;
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
import org.hibernate.envers.internal.tools.query.Parameters;
import org.hibernate.envers.internal.tools.query.QueryBuilder;
import org.hibernate.envers.query.criteria.AuditCriterion;
/**
* @author Felix Feisst (feisst dot felix at gmail dot com)
*/
public class EntityTypeAuditExpression implements AuditCriterion {
private String alias;
private String entityName;
public EntityTypeAuditExpression(
String alias,
String entityName) {
this.alias = alias;
this.entityName = entityName;
}
@Override
public void addToQuery(
EnversService enversService,
AuditReaderImplementor auditReader,
Map<String, String> aliasToEntityNameMap,
String baseAlias,
QueryBuilder qb,
Parameters parameters) {
String effectiveAlias = alias == null ? baseAlias : alias;
String effectiveEntityName = entityName;
if ( enversService.getEntitiesConfigurations().isVersioned( effectiveEntityName ) ) {
effectiveEntityName = enversService.getConfig().getAuditEntityName( effectiveEntityName );
}
parameters.addEntityTypeRestriction( effectiveAlias, effectiveEntityName );
}
}

View File

@ -0,0 +1,117 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.envers.test.integration.query;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityManager;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.Inheritance;
import jakarta.persistence.InheritanceType;
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.junit.Test;
/**
* @author Felix Feisst (feisst dot felix at gmail dot com)
*/
public class EntityTypeQueryTest extends BaseEnversJPAFunctionalTestCase {
@Entity
@Audited
@Inheritance(strategy = InheritanceType.JOINED)
public static class EntityA {
@Id
@GeneratedValue
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
@Audited
public static class EntityB extends EntityA {
}
private EntityA a1;
private EntityA a2;
private EntityB b1;
private EntityB b2;
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[]{ EntityA.class, EntityB.class };
}
@Test
@Priority(10)
public void initData() {
EntityManager em = getEntityManager();
em.getTransaction().begin();
a1 = new EntityA();
a1.setName( "a1" );
em.persist( a1 );
a2 = new EntityA();
a2.setName( "a2" );
em.persist( a2 );
b1 = new EntityB();
b1.setName( "b1" );
em.persist( b1 );
b2 = new EntityB();
b2.setName( "b2" );
em.persist( b2 );
em.getTransaction().commit();
}
@Test
public void testRestrictToSubType() {
List<?> list = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 )
.add( AuditEntity.entityType( EntityB.class ) ).addProjection( AuditEntity.property( "name" ) ).getResultList();
assertEquals( "Expected only entities of type EntityB to be selected", list( "b1", "b2" ), list );
}
@Test
public void testRestrictToSuperType() {
List<?> list = getAuditReader().createQuery().forEntitiesAtRevision( EntityA.class, 1 )
.add( AuditEntity.entityType( EntityA.class ) ).addProjection( AuditEntity.property( "name" ) ).getResultList();
assertEquals( "Expected only entities of type EntityA to be selected", list( "a1", "a2" ), list );
}
private List<String> list(final String... elements) {
final List<String> result = new ArrayList<>();
Collections.addAll( result, elements );
return result;
}
}