HHH-8070 - Added IN criteria support to AuditRelatedId.

This commit is contained in:
Chris Cranford 2016-05-10 20:46:14 -05:00
parent 6871824f4d
commit 383972d6c2
6 changed files with 442 additions and 15 deletions

View File

@ -6,15 +6,16 @@
*/
package org.hibernate.envers.query.criteria;
import org.hibernate.envers.query.criteria.internal.RelatedAuditExpression;
import org.hibernate.envers.query.criteria.internal.RelatedAuditEqualityExpression;
import org.hibernate.envers.query.criteria.internal.RelatedAuditInExpression;
import org.hibernate.envers.query.internal.property.PropertyNameGetter;
/**
* Create restrictions on an id of an entity related to an audited entity.
*
* @author Adam Warski (adam at warski dot org)
* @author Chris Cranford
*/
@SuppressWarnings({"JavaDoc"})
public class AuditRelatedId {
private final PropertyNameGetter propertyNameGetter;
@ -23,16 +24,32 @@ public class AuditRelatedId {
}
/**
* Apply an "equal" constraint
* Applies an "equals" criteria predicate.
*
* @param id the value to test equality with
* @return the criterion.
*/
public AuditCriterion eq(Object id) {
return new RelatedAuditExpression( propertyNameGetter, id, true );
return new RelatedAuditEqualityExpression( propertyNameGetter, id, true );
}
/**
* Apply a "not equal" constraint
* Applies a "not equals" criteria predicate.
*
* @param id the value to test inequality with
* @return the criterion
*/
public AuditCriterion ne(Object id) {
return new RelatedAuditExpression( propertyNameGetter, id, false );
return new RelatedAuditEqualityExpression( propertyNameGetter, id, false );
}
/**
* Applies an "in" criteria predicate.
*
* @param values the values to test with
* @return the criterion
*/
public AuditCriterion in(Object[] values) {
return new RelatedAuditInExpression( propertyNameGetter, values );
}
}

View File

@ -16,14 +16,15 @@ import org.hibernate.envers.query.criteria.AuditCriterion;
import org.hibernate.envers.query.internal.property.PropertyNameGetter;
/**
* @author Adam Warski (adam at warski dot org)
* @author Chris Cranford
* @since 5.2
*/
public class RelatedAuditExpression implements AuditCriterion {
public class RelatedAuditEqualityExpression implements AuditCriterion {
private final PropertyNameGetter propertyNameGetter;
private final Object id;
private final boolean equals;
public RelatedAuditExpression(PropertyNameGetter propertyNameGetter, Object id, boolean equals) {
public RelatedAuditEqualityExpression(PropertyNameGetter propertyNameGetter, Object id, boolean equals) {
this.propertyNameGetter = propertyNameGetter;
this.id = id;
this.equals = equals;
@ -45,15 +46,11 @@ public class RelatedAuditExpression implements AuditCriterion {
);
RelationDescription relatedEntity = CriteriaTools.getRelatedEntity( enversService, entityName, propertyName );
if ( relatedEntity == null ) {
throw new AuditException(
"This criterion can only be used on a property that is " +
"a relation to another property."
"This criterion can only be used on a property that is a relation to another property."
);
}
else {
relatedEntity.getIdMapper().addIdEqualsToQuery( parameters, id, null, equals );
}
relatedEntity.getIdMapper().addIdEqualsToQuery( parameters, id, null, equals );
}
}

View File

@ -0,0 +1,62 @@
/*
* 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.List;
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.mapper.id.QueryParameterData;
import org.hibernate.envers.internal.reader.AuditReaderImplementor;
import org.hibernate.envers.internal.tools.query.Parameters;
import org.hibernate.envers.internal.tools.query.QueryBuilder;
import org.hibernate.envers.query.criteria.AuditCriterion;
import org.hibernate.envers.query.internal.property.PropertyNameGetter;
/**
* @author Chris Cranford
* @since 5.2
*/
public class RelatedAuditInExpression implements AuditCriterion {
private final PropertyNameGetter propertyNameGetter;
private final Object[] ids;
public RelatedAuditInExpression(PropertyNameGetter propertyNameGetter, Object[] ids) {
this.propertyNameGetter = propertyNameGetter;
this.ids = ids;
}
public void addToQuery(EnversService enversService,
AuditReaderImplementor versionsReader,
String entityName,
String alias,
QueryBuilder qb,
Parameters parameters) {
String propertyName = CriteriaTools.determinePropertyName(
enversService,
versionsReader,
entityName,
propertyNameGetter
);
RelationDescription relatedEntity = CriteriaTools.getRelatedEntity( enversService, entityName, propertyName );
if ( relatedEntity == null ) {
throw new AuditException(
"The criterion can only be used on a property that is a relation to another property."
);
}
// todo: should this throw an error if qpdList is null? is it possible?
List<QueryParameterData> qpdList = relatedEntity.getIdMapper().mapToQueryParametersFromId( propertyName );
if ( qpdList != null ) {
QueryParameterData qpd = qpdList.iterator().next();
parameters.addWhereWithParams( qpd.getQueryParameterName(), "in (", ids, ")" );
}
}
}

View File

@ -0,0 +1,145 @@
/*
* 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.onetomany.relatedid;
import java.util.List;
import javax.persistence.EntityManager;
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.envers.test.tools.TestTools;
import org.hibernate.testing.TestForIssue;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* @author Chris Cranford
*/
@TestForIssue(jiraKey = "HHH-8070")
public class AuditRelatedIdInTest extends BaseEnversJPAFunctionalTestCase {
private Integer company1Id;
private Integer company2Id;
private Integer company3Id;
private Integer employee1Id;
private Integer employee2Id;
private Integer employee3Id;
private Integer employee4Id;
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[]{
Company.class,
Employee.class
};
}
@Test
@Priority(10)
public void initData() {
EntityManager em = getOrCreateEntityManager();
try {
// Revision 1
Company company1 = new Company( "COMPANY1" );
Company company2 = new Company( "COMPANY2" );
Employee employee1 = new Employee( "Employee1", company1 );
Employee employee2 = new Employee( "Employee2", company2 );
Employee employee3 = new Employee( "Employee3", company2 );
em.getTransaction().begin();
em.persist( company1 );
em.persist( company2 );
em.persist( employee1 );
em.persist( employee2 );
em.persist( employee3 );
em.getTransaction().commit();
// cache ids
company1Id = company1.getId();
company2Id = company2.getId();
employee1Id = employee1.getId();
employee2Id = employee2.getId();
employee3Id = employee3.getId();
// Revision 2
em.getTransaction().begin();
employee2 = em.find( Employee.class, employee2.getId() );
employee2.setCompany( company1 );
company2 = em.find( Company.class, company2.getId() );
company2.setName( "COMPANY2-CHANGED" );
em.merge( employee2 );
em.merge( company2 );
em.getTransaction().commit();
// Revision 3
Company company3 = new Company( "COMPANY3" );
Employee employee4 = new Employee( "Employee4", company3 );
em.getTransaction().begin();
em.persist( company3 );
em.persist( employee4 );
em.getTransaction().commit();
// cache ids
employee4Id = employee4.getId();
company3Id = company3.getId();
}
finally {
em.close();
}
}
@Test
public void testRevisionCounts() {
// companies
assertEquals( 1, getAuditReader().getRevisions( Company.class, company1Id ).size() );
assertEquals( 2, getAuditReader().getRevisions( Company.class, company2Id ).size() );
assertEquals( 1, getAuditReader().getRevisions( Company.class, company3Id ).size() );
// employees
assertEquals( 1, getAuditReader().getRevisions( Employee.class, employee1Id ).size() );
assertEquals( 2, getAuditReader().getRevisions( Employee.class, employee2Id ).size() );
assertEquals( 1, getAuditReader().getRevisions( Employee.class, employee3Id ).size() );
assertEquals( 1, getAuditReader().getRevisions( Employee.class, employee4Id ).size() );
}
@Test
public void testCompany1EmployeeIn() {
AuditQuery auditQuery = getAuditReader().createQuery().forRevisionsOfEntity( Employee.class, true, true );
auditQuery.add( AuditEntity.relatedId( "company" ).in( new Integer[]{ company1Id } ) );
final List<Employee> results = auditQuery.getResultList();
assertEquals( 2, results.size() );
final Employee employee1 = makeEmployee( employee1Id, "Employee1", company1Id, "COMPANY1" );
final Employee employee2 = makeEmployee( employee2Id, "Employee2", company1Id, "COMPANY1" );
assertEquals( results, TestTools.makeList( employee1, employee2 ) );
}
@Test
public void testCompany2EmployeeIn() {
AuditQuery auditQuery = getAuditReader().createQuery().forRevisionsOfEntity( Employee.class, true, true );
auditQuery.add( AuditEntity.relatedId( "company" ).in( new Integer[]{ company2Id } ) );
final List<Employee> results = auditQuery.getResultList();
assertEquals( 2, results.size() );
final Employee employee1 = makeEmployee( employee2Id, "Employee2", company2Id, "COMPANY2" );
final Employee employee2 = makeEmployee( employee3Id, "Employee3", company2Id, "COMPANY2" );
assertEquals( results, TestTools.makeList( employee1, employee2 ) );
}
@Test
public void testCompany3EmployeeIn() {
AuditQuery auditQuery = getAuditReader().createQuery().forRevisionsOfEntity( Employee.class, true, true );
auditQuery.add( AuditEntity.relatedId( "company" ).in( new Integer[]{ company3Id } ) );
final List<Employee> results = auditQuery.getResultList();
assertEquals( 1, results.size() );
final Employee employee = makeEmployee( employee4Id, "Employee4", company3Id, "COMPANY3" );
assertEquals( results, TestTools.makeList( employee ) );
}
private Employee makeEmployee(Integer employeeId, String employeeName, Integer companyId, String companyName) {
return new Employee( employeeId, employeeName, new Company( companyId, companyName ) );
}
}

View File

@ -0,0 +1,101 @@
/*
* 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.onetomany.relatedid;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import org.hibernate.envers.Audited;
/**
* @author Chris Cranford
*/
@Entity
@Audited
public class Company {
@Id
@GeneratedValue
private Integer id;
private String name;
@OneToMany
private List<Employee> employees = new ArrayList<Employee>();
Company() {
}
public Company(String name) {
this.name = name;
}
public Company(Integer id, String name) {
this.id = id;
this.name = name;
}
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 List<Employee> getEmployees() {
return employees;
}
public void setEmployees(List<Employee> employees) {
this.employees = employees;
}
@Override
public int hashCode() {
int result;
result = ( id != null ? id.hashCode() : 0 );
result = 31 * result + ( name != null ? name.hashCode() : 0 );
return result;
}
@Override
public boolean equals(Object object) {
if ( object == this ) {
return true;
}
if ( !( object instanceof Company) ) {
return false;
}
Company that = (Company) object;
if ( getId() != null ? !getId().equals( that.getId() ) : that.getId() != null ) {
return false;
}
if ( getName() != null ? !getName().equals( that.getName() ) : that.getName() != null ) {
return false;
}
return true;
}
@Override
public String toString() {
return "Company{" +
"id=" + id +
", name='" + name + '\'' + '}';
}
}

View File

@ -0,0 +1,105 @@
/*
* 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.onetomany.relatedid;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import org.hibernate.envers.Audited;
/**
* @author Chris Cranford
*/
@Entity
@Audited
public class Employee {
@Id
@GeneratedValue
private Integer id;
private String name;
@ManyToOne
private Company company;
Employee() {
}
public Employee(String name, Company company) {
this( null, name, company );
}
public Employee(Integer id, String name, Company company) {
this.id = id;
this.name = name;
this.company = company;
}
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 Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
@Override
public int hashCode() {
int result;
result = ( id != null ? id.hashCode() : 0 );
result = 31 * result + ( name != null ? name.hashCode() : 0 );
result = 31 * result + ( company != null ? company.hashCode() : 0 );
return result;
}
@Override
public boolean equals(Object object) {
if ( object == this ) {
return true;
}
if ( !( object instanceof Employee) ) {
return false;
}
Employee that = (Employee) object;
if ( getId() != null ? !getId().equals( that.getId() ) : that.getId() != null ) {
return false;
}
if ( getName() != null ? !getName().equals( that.getName() ) : that.getName() != null ) {
return false;
}
if ( getCompany() != null ? !getCompany().equals( that.getCompany() ) : that.getCompany() != null ) {
return false;
}
return true;
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
", company=" + company +
'}';
}
}