HHH-6766 - Criteria API multicolumn subqueries
This commit is contained in:
parent
c26030a27e
commit
fed759f8e5
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package org.hibernate.test.criteria;
|
||||
|
||||
/**
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
public class Man extends Person {
|
||||
}
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package org.hibernate.test.criteria;
|
||||
|
||||
/**
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
public class Woman extends Person {
|
||||
}
|
Loading…
Reference in New Issue