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;
|
package org.hibernate.envers.query.criteria.internal;
|
||||||
|
|
||||||
|
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;
|
||||||
|
@ -14,6 +15,9 @@ 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.criteria.AuditCriterion;
|
||||||
import org.hibernate.envers.query.internal.property.PropertyNameGetter;
|
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)
|
* @author Adam Warski (adam at warski dot org)
|
||||||
|
@ -46,7 +50,29 @@ public class SimpleAuditExpression implements AuditCriterion {
|
||||||
RelationDescription relatedEntity = CriteriaTools.getRelatedEntity( enversService, entityName, propertyName );
|
RelationDescription relatedEntity = CriteriaTools.getRelatedEntity( enversService, entityName, propertyName );
|
||||||
|
|
||||||
if ( relatedEntity == null ) {
|
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 {
|
else {
|
||||||
if ( !"=".equals( op ) && !"<>".equals( op ) ) {
|
if ( !"=".equals( op ) && !"<>".equals( op ) ) {
|
||||||
|
@ -55,10 +81,28 @@ public class SimpleAuditExpression implements AuditCriterion {
|
||||||
") isn't supported and can't be used in queries."
|
") isn't supported and can't be used in queries."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Object id = relatedEntity.getIdMapper().mapToIdFromEntity( value );
|
Object id = relatedEntity.getIdMapper().mapToIdFromEntity( value );
|
||||||
|
|
||||||
relatedEntity.getIdMapper().addIdEqualsToQuery( parameters, id, null, "=".equals( op ) );
|
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