HHH-6766 - Criteria API multicolumn subqueries

This commit is contained in:
Lukasz Antoniak 2011-10-22 16:30:43 +02:00 committed by Strong Liu
parent c26030a27e
commit fed759f8e5
7 changed files with 235 additions and 1 deletions

View File

@ -0,0 +1,28 @@
package org.hibernate.criterion;
import org.hibernate.Criteria;
import org.hibernate.internal.util.StringHelper;
/**
* A comparison between several properties value in the outer query and the result of a multicolumn subquery.
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
public class PropertiesSubqueryExpression extends SubqueryExpression {
private final String[] propertyNames;
protected PropertiesSubqueryExpression(String[] propertyNames, String op, DetachedCriteria dc) {
super( op, null, dc );
this.propertyNames = propertyNames;
}
@Override
protected String toLeftSqlString(Criteria criteria, CriteriaQuery outerQuery) {
StringBuilder left = new StringBuilder( "(" );
final String[] sqlColumnNames = new String[propertyNames.length];
for ( int i = 0; i < sqlColumnNames.length; ++i ) {
sqlColumnNames[i] = outerQuery.getColumn( criteria, propertyNames[i] );
}
left.append( StringHelper.join( ", ", sqlColumnNames ) );
return left.append( ")" ).toString();
}
}

View File

@ -33,6 +33,7 @@ package org.hibernate.criterion;
* @see Projection
* @see org.hibernate.Criteria
* @author Gavin King
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
public class Subqueries {
@ -59,6 +60,22 @@ public class Subqueries {
public static Criterion propertyEq(String propertyName, DetachedCriteria dc) {
return new PropertySubqueryExpression(propertyName, "=", null, dc);
}
public static Criterion propertiesEq(String[] propertyNames, DetachedCriteria dc) {
return new PropertiesSubqueryExpression(propertyNames, "=", dc);
}
public static Criterion propertiesNotEq(String[] propertyNames, DetachedCriteria dc) {
return new PropertiesSubqueryExpression(propertyNames, "<>", dc);
}
public static Criterion propertiesIn(String[] propertyNames, DetachedCriteria dc) {
return new PropertiesSubqueryExpression(propertyNames, "in", dc);
}
public static Criterion propertiesNotIn(String[] propertyNames, DetachedCriteria dc) {
return new PropertiesSubqueryExpression(propertyNames, "not in", dc);
}
public static Criterion propertyNe(String propertyName, DetachedCriteria dc) {
return new PropertySubqueryExpression(propertyName, "<>", null, dc);

View File

@ -59,6 +59,9 @@ import org.hibernate.test.hql.Animal;
import org.hibernate.test.hql.Reptile;
import org.hibernate.testing.SkipForDialect;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.hibernate.testing.DialectChecks;
import org.hibernate.testing.RequiresDialectFeature;
import org.hibernate.testing.TestForIssue;
import org.hibernate.transform.Transformers;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.Type;
@ -72,11 +75,14 @@ import static org.junit.Assert.fail;
/**
* @author Gavin King
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
public class CriteriaQueryTest extends BaseCoreFunctionalTestCase {
@Override
public String[] getMappings() {
return new String[] { "criteria/Enrolment.hbm.xml","criteria/Foo.hbm.xml", "hql/Animal.hbm.xml" };
return new String[] {
"criteria/Enrolment.hbm.xml", "criteria/Foo.hbm.xml", "hql/Animal.hbm.xml", "criteria/Person.hbm.xml"
};
}
@Override
@ -1720,6 +1726,85 @@ public class CriteriaQueryTest extends BaseCoreFunctionalTestCase {
}
@Test
@TestForIssue( jiraKey = "HHH-6766" )
public void testMultiplePropertiesSubquery() {
Session session = openSession();
Transaction tx = session.beginTransaction();
Man lukasz = new Man();
lukasz.setName( "Lukasz" );
lukasz.setHeight( 170 );
lukasz.setWeight( 60 );
session.persist( lukasz );
Man robert = new Man();
robert.setName( "Robert" );
robert.setHeight( 170 );
robert.setWeight( 78 );
session.persist( robert );
Woman kinga = new Woman();
kinga.setName( "Kinga" );
kinga.setHeight( 170 );
kinga.setWeight( 60 );
session.persist( kinga );
tx.commit();
session.close();
session = openSession();
tx = session.beginTransaction();
DetachedCriteria sizeQuery = DetachedCriteria.forClass( Man.class ).setProjection(
Projections.projectionList().add( Projections.property( "weight" ) )
.add( Projections.property( "height" ) ) )
.add( Restrictions.eq( "name", "Lukasz" )
);
List result = session.createCriteria( Woman.class )
.add( Subqueries.propertiesEq( new String[] { "weight", "height" }, sizeQuery ) )
.list();
assertEquals( 1, result.size() );
assertEquals( kinga, result.get( 0 ) );
if (getDialect().supportsRowValueConstructorSyntaxInInList()) {
result = session.createCriteria( Woman.class )
.add( Subqueries.propertiesIn( new String[] { "weight", "height" }, sizeQuery ) )
.list();
assertEquals( 1, result.size() );
assertEquals( kinga, result.get( 0 ) );
}
tx.commit();
session.close();
session = openSession();
tx = session.beginTransaction();
sizeQuery = DetachedCriteria.forClass( Man.class ).setProjection(
Projections.projectionList().add( Projections.property( "weight" ) )
.add( Projections.property( "height" ) ) )
.add( Restrictions.ne( "name", "Lukasz" )
);
result = session.createCriteria( Woman.class )
.add( Subqueries.propertiesNotEq( new String[] { "weight", "height" }, sizeQuery ) )
.list();
assertEquals( 1, result.size() );
assertEquals( kinga, result.get( 0 ) );
if (getDialect().supportsRowValueConstructorSyntaxInInList()) {
result = session.createCriteria( Woman.class )
.add( Subqueries.propertiesNotIn( new String[] { "weight", "height" }, sizeQuery ) )
.list();
assertEquals( 1, result.size() );
assertEquals( kinga, result.get( 0 ) );
}
tx.commit();
session.close();
}
@Test
public void testCriteriaCollectionOfComponent() {
Session session = openSession();

View File

@ -0,0 +1,7 @@
package org.hibernate.test.criteria;
/**
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
public class Man extends Person {
}

View File

@ -0,0 +1,20 @@
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.hibernate.test.criteria">
<class name="Person">
<id name="id" type="long">
<generator class="sequence"/>
</id>
<property name="name" type="string"/>
<property name="weight" type="integer"/>
<property name="height" type="integer"/>
<union-subclass name="Man"/>
<union-subclass name="Woman"/>
</class>
</hibernate-mapping>

View File

@ -0,0 +1,70 @@
package org.hibernate.test.criteria;
/**
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
public abstract class Person {
private Long id;
private String name;
private Integer weight;
private Integer height;
@Override
public boolean equals(Object o) {
if ( this == o ) return true;
if ( o == null || getClass() != o.getClass() ) return false;
Person person = (Person) o;
if ( height != null ? !height.equals( person.height ) : person.height != null ) return false;
if ( id != null ? !id.equals( person.id ) : person.id != null ) return false;
if ( name != null ? !name.equals( person.name ) : person.name != null ) return false;
if ( weight != null ? !weight.equals( person.weight ) : person.weight != null ) return false;
return true;
}
@Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + ( name != null ? name.hashCode() : 0 );
result = 31 * result + ( weight != null ? weight.hashCode() : 0 );
result = 31 * result + ( height != null ? height.hashCode() : 0 );
return result;
}
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 Integer getWeight() {
return weight;
}
public void setWeight(Integer weight) {
this.weight = weight;
}
public Integer getHeight() {
return height;
}
public void setHeight(Integer height) {
this.height = height;
}
}

View File

@ -0,0 +1,7 @@
package org.hibernate.test.criteria;
/**
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
public class Woman extends Person {
}