HHH-9178 - Fix QueryException when querying audited entities with embeddables.
This commit is contained in:
parent
69b8202c75
commit
a67b42ca64
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.envers.query.criteria.internal;
|
||||
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.envers.boot.internal.EnversService;
|
||||
import org.hibernate.envers.exception.AuditException;
|
||||
import org.hibernate.envers.internal.entities.RelationDescription;
|
||||
|
@ -14,6 +15,9 @@ import org.hibernate.envers.internal.tools.query.Parameters;
|
|||
import org.hibernate.envers.internal.tools.query.QueryBuilder;
|
||||
import org.hibernate.envers.query.criteria.AuditCriterion;
|
||||
import org.hibernate.envers.query.internal.property.PropertyNameGetter;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.type.ComponentType;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
/**
|
||||
* @author Adam Warski (adam at warski dot org)
|
||||
|
@ -46,7 +50,29 @@ public class SimpleAuditExpression implements AuditCriterion {
|
|||
RelationDescription relatedEntity = CriteriaTools.getRelatedEntity( enversService, entityName, propertyName );
|
||||
|
||||
if ( relatedEntity == null ) {
|
||||
parameters.addWhereWithParam( propertyName, op, value );
|
||||
// HHH-9178 - Add support to component type equality.
|
||||
// This basically will allow = and <> operators to perform component-based equality checks.
|
||||
// Any other operator for a component type will not be supported.
|
||||
// Non-component types will continue to behave normally.
|
||||
final SessionImplementor session = versionsReader.getSessionImplementor();
|
||||
final Type type = getPropertyType( session, entityName, propertyName );
|
||||
if ( type != null && type.isComponentType() ) {
|
||||
if ( !"=".equals( op ) && !"<>".equals( op ) ) {
|
||||
throw new AuditException( "Component-based criterion is not supported for op: " + op );
|
||||
}
|
||||
final ComponentType componentType = (ComponentType) type;
|
||||
for ( int i = 0; i < componentType.getPropertyNames().length; i++ ) {
|
||||
final Object componentValue = componentType.getPropertyValue( value, i, session );
|
||||
parameters.addWhereWithParam(
|
||||
propertyName + "_" + componentType.getPropertyNames()[ i ],
|
||||
op,
|
||||
componentValue
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
parameters.addWhereWithParam( propertyName, op, value );
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( !"=".equals( op ) && !"<>".equals( op ) ) {
|
||||
|
@ -55,10 +81,28 @@ public class SimpleAuditExpression implements AuditCriterion {
|
|||
") isn't supported and can't be used in queries."
|
||||
);
|
||||
}
|
||||
|
||||
Object id = relatedEntity.getIdMapper().mapToIdFromEntity( value );
|
||||
|
||||
relatedEntity.getIdMapper().addIdEqualsToQuery( parameters, id, null, "=".equals( op ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the property type of a given property in the specified entity.
|
||||
*
|
||||
* @param session the session
|
||||
* @param entityName the entity name
|
||||
* @param propertyName the property name
|
||||
* @return the property type of the property or {@code null} if the property name isn't found.
|
||||
*/
|
||||
private Type getPropertyType(SessionImplementor session, String entityName, String propertyName) {
|
||||
// rather than rely on QueryException from calling getPropertyType(), this allows a non-failure way
|
||||
// to determine whether to return null or lookup the value safely.
|
||||
final EntityPersister persister = session.getSessionFactory().getMetamodel().entityPersister( entityName );
|
||||
for ( String name : persister.getPropertyNames() ) {
|
||||
if ( name.equals( propertyName ) ) {
|
||||
return persister.getPropertyType( propertyName );
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* 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.embeddables;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
import org.hibernate.envers.exception.AuditException;
|
||||
import org.hibernate.envers.query.AuditEntity;
|
||||
import org.hibernate.envers.query.AuditQuery;
|
||||
import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase;
|
||||
import org.hibernate.envers.test.Priority;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Test which supports using {@link AuditEntity} to test equality/inequality
|
||||
* between embeddable components.
|
||||
*
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-9178")
|
||||
public class EmbeddableQuery extends BaseEnversJPAFunctionalTestCase {
|
||||
private Integer personId;
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] { Person.class, NameInfo.class };
|
||||
}
|
||||
|
||||
@Test
|
||||
@Priority(10)
|
||||
public void initData() {
|
||||
EntityManager em = getOrCreateEntityManager();
|
||||
try {
|
||||
// Revision 1
|
||||
em.getTransaction().begin();
|
||||
NameInfo ni = new NameInfo( "John", "Doe" );
|
||||
Person person1 = new Person( "JDOE", ni );
|
||||
em.persist( person1 );
|
||||
em.getTransaction().commit();
|
||||
|
||||
// Revision 2
|
||||
em.getTransaction().begin();
|
||||
person1 = em.find( Person.class, person1.getId() );
|
||||
person1.getNameInfo().setFirstName( "Jane" );
|
||||
em.merge( person1 );
|
||||
em.getTransaction().commit();
|
||||
|
||||
// Revision 3
|
||||
em.getTransaction().begin();
|
||||
person1 = em.find( Person.class, person1.getId() );
|
||||
person1.setName( "JDOE2" );
|
||||
em.merge( person1 );
|
||||
em.getTransaction().commit();
|
||||
|
||||
personId = person1.getId();
|
||||
}
|
||||
finally {
|
||||
em.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRevisionCounts() {
|
||||
assertEquals( 3, getAuditReader().getRevisions( Person.class, personId ).size() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuditQueryUsingEmbeddableEquals() {
|
||||
final NameInfo nameInfo = new NameInfo( "John", "Doe" );
|
||||
final AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision( Person.class, 1 );
|
||||
query.add( AuditEntity.property( "nameInfo" ).eq( nameInfo ) );
|
||||
List<?> results = query.getResultList();
|
||||
assertEquals( 1, results.size() );
|
||||
final Person person = (Person) results.get( 0 );
|
||||
assertEquals( nameInfo, person.getNameInfo() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuditQueryUsingEmbeddableNotEquals() {
|
||||
final NameInfo nameInfo = new NameInfo( "Jane", "Doe" );
|
||||
final AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision( Person.class, 1 );
|
||||
query.add( AuditEntity.property( "nameInfo" ).ne( nameInfo ) );
|
||||
assertEquals( 0, query.getResultList().size() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuditQueryUsingEmbeddableNonEqualityCheck() {
|
||||
try {
|
||||
final NameInfo nameInfo = new NameInfo( "John", "Doe" );
|
||||
final AuditQuery query = getAuditReader().createQuery().forEntitiesAtRevision( Person.class, 1 );
|
||||
query.add( AuditEntity.property( "nameInfo" ).le( nameInfo ) );
|
||||
}
|
||||
catch ( Exception ex ) {
|
||||
assertTyping( AuditException.class, ex );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* 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.embeddables;
|
||||
|
||||
import javax.persistence.Embeddable;
|
||||
|
||||
/**
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
@Embeddable
|
||||
public class NameInfo {
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
|
||||
NameInfo() {
|
||||
|
||||
}
|
||||
|
||||
public NameInfo(String firstName, String lastName) {
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
public void setFirstName(String firstName) {
|
||||
this.firstName = firstName;
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
|
||||
public void setLastName(String lastName) {
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
result = ( firstName != null ? firstName.hashCode() : 0 );
|
||||
result = result * 31 + ( lastName != null ? lastName.hashCode() : 0 );
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if ( obj == this ) {
|
||||
return true;
|
||||
}
|
||||
if ( !( obj instanceof NameInfo ) ) {
|
||||
return false;
|
||||
}
|
||||
NameInfo that = (NameInfo) obj;
|
||||
if ( firstName != null ? !firstName.equals( that.firstName) : that.firstName != null ) {
|
||||
return false;
|
||||
}
|
||||
if ( lastName != null ? !lastName.equals( that.lastName) : that.lastName != null ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NameInfo{" +
|
||||
"firstName='" + firstName + '\'' +
|
||||
", lastName='" + lastName + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* 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.embeddables;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import org.hibernate.envers.Audited;
|
||||
|
||||
/**
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
@Entity
|
||||
@Audited
|
||||
public class Person {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Integer id;
|
||||
private String name;
|
||||
private NameInfo nameInfo;
|
||||
|
||||
Person() {
|
||||
|
||||
}
|
||||
|
||||
public Person(String name, NameInfo nameInfo) {
|
||||
this.name = name;
|
||||
this.nameInfo = nameInfo;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public NameInfo getNameInfo() {
|
||||
return nameInfo;
|
||||
}
|
||||
|
||||
public void setNameInfo(NameInfo nameInfo) {
|
||||
this.nameInfo = nameInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
result = ( id != null ? id.hashCode() : 0 );
|
||||
result = 31 * result + ( name != null ? name.hashCode() : 0 );
|
||||
result = 31 * result + ( nameInfo != null ? nameInfo.hashCode() : 0 );
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if ( this == obj ) {
|
||||
return true;
|
||||
}
|
||||
if ( !( obj instanceof Person ) ) {
|
||||
return false;
|
||||
}
|
||||
Person that = (Person) obj;
|
||||
if ( id != null ? !id.equals( that.id ) : that.id != null ) {
|
||||
return false;
|
||||
}
|
||||
if ( name != null ? !name.equals( that.name) : that.name != null ) {
|
||||
return false;
|
||||
}
|
||||
if ( nameInfo != null ? !nameInfo.equals( that.nameInfo ) : that.nameInfo != null ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Person{" +
|
||||
"id=" + id +
|
||||
", name='" + name + '\'' +
|
||||
", nameInfo=" + nameInfo +
|
||||
'}';
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue