HHH-13111 Restore support of criteria subqueries in select clauses
When the JPA query compliance mode is disabled (the default), it should be possible to include subqueries in select clauses of a criteria query. This was previously accepted due to a bug. After having fixed HHH-13001, we started to throw an IllegalStateException.
This commit is contained in:
parent
a89a9beeb0
commit
25554375f2
|
@ -258,8 +258,12 @@ public class CriteriaSubqueryImpl<T> extends ExpressionImpl<T> implements Subque
|
|||
|
||||
@Override
|
||||
public String render(RenderingContext renderingContext) {
|
||||
if ( renderingContext.getClauseStack().getCurrent() == Clause.SELECT ) {
|
||||
throw new IllegalStateException( "Subquery cannot occur in select clause" );
|
||||
if ( criteriaBuilder().getEntityManagerFactory().getSessionFactoryOptions().getJpaCompliance()
|
||||
.isJpaQueryComplianceEnabled() &&
|
||||
renderingContext.getClauseStack().getCurrent() == Clause.SELECT ) {
|
||||
throw new IllegalStateException(
|
||||
"The JPA specification does not support subqueries in select clauses. " +
|
||||
"Please disable the JPA query compliance if you want to use this feature." );
|
||||
}
|
||||
|
||||
StringBuilder subqueryBuffer = new StringBuilder( "(" );
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* 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.jpa.test.criteria.subquery;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.CollectionTable;
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.MapKeyColumn;
|
||||
import javax.persistence.OneToMany;
|
||||
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
import org.junit.Before;
|
||||
|
||||
public abstract class AbstractSubqueryInSelectClauseTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[]{ Person.class, Document.class };
|
||||
}
|
||||
|
||||
@Before
|
||||
public void initData() {
|
||||
doInJPA( this::entityManagerFactory, em -> {
|
||||
Person p1 = new Person();
|
||||
Person p2 = new Person();
|
||||
Document d = new Document();
|
||||
|
||||
p1.getLocalized().put( 1, "p1.1" );
|
||||
p1.getLocalized().put( 2, "p1.2" );
|
||||
p2.getLocalized().put( 1, "p2.1" );
|
||||
p2.getLocalized().put( 2, "p2.2" );
|
||||
|
||||
d.getContacts().put( 1, p1 );
|
||||
d.getContacts().put( 2, p2 );
|
||||
|
||||
em.persist( p1 );
|
||||
em.persist( p2 );
|
||||
em.persist( d );
|
||||
} );
|
||||
}
|
||||
|
||||
@Entity(name = "Document")
|
||||
public static class Document {
|
||||
|
||||
private Integer id;
|
||||
|
||||
private Map<Integer, Person> contacts = new HashMap<Integer, Person>();
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@OneToMany
|
||||
@CollectionTable
|
||||
@MapKeyColumn(name = "position")
|
||||
public Map<Integer, Person> getContacts() {
|
||||
return contacts;
|
||||
}
|
||||
|
||||
public void setContacts(Map<Integer, Person> contacts) {
|
||||
this.contacts = contacts;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Person")
|
||||
public static class Person {
|
||||
|
||||
private Integer id;
|
||||
|
||||
private Map<Integer, String> localized = new HashMap<Integer, String>();
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@ElementCollection
|
||||
public Map<Integer, String> getLocalized() {
|
||||
return localized;
|
||||
}
|
||||
|
||||
public void setLocalized(Map<Integer, String> localized) {
|
||||
this.localized = localized;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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.jpa.test.criteria.subquery;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.Tuple;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Join;
|
||||
import javax.persistence.criteria.JoinType;
|
||||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.criteria.Subquery;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.junit.Test;
|
||||
|
||||
@TestForIssue(jiraKey = "HHH-13111")
|
||||
public class SubqueryInSelectClauseJpaComplianceTest extends AbstractSubqueryInSelectClauseTest {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
protected void addConfigOptions(Map options) {
|
||||
options.put( AvailableSettings.JPA_QUERY_COMPLIANCE, true );
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testSubqueryInSelectClause() {
|
||||
doInJPA( this::entityManagerFactory, em -> {
|
||||
CriteriaBuilder cb = em.getCriteriaBuilder();
|
||||
CriteriaQuery<Tuple> query = cb.createTupleQuery();
|
||||
Root<Document> document = query.from( Document.class );
|
||||
Join<?, ?> contacts = document.join( "contacts", JoinType.LEFT );
|
||||
|
||||
Subquery<Long> personCount = query.subquery( Long.class );
|
||||
Root<Person> person = personCount.from( Person.class );
|
||||
personCount.select( cb.count( person ) ).where( cb.equal( contacts.get( "id" ), person.get( "id" ) ) );
|
||||
|
||||
query.multiselect( document.get( "id" ), personCount.getSelection() );
|
||||
|
||||
em.createQuery( query ).getResultList();
|
||||
} );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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.jpa.test.criteria.subquery;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.Tuple;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Join;
|
||||
import javax.persistence.criteria.JoinType;
|
||||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.criteria.Subquery;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.junit.Test;
|
||||
|
||||
@TestForIssue(jiraKey = "HHH-13111")
|
||||
public class SubqueryInSelectClauseTest extends AbstractSubqueryInSelectClauseTest {
|
||||
|
||||
@Test
|
||||
public void testSubqueryInSelectClause() {
|
||||
doInJPA( this::entityManagerFactory, em -> {
|
||||
CriteriaBuilder cb = em.getCriteriaBuilder();
|
||||
CriteriaQuery<Tuple> query = cb.createTupleQuery();
|
||||
Root<Document> document = query.from( Document.class );
|
||||
Join<?, ?> contacts = document.join( "contacts", JoinType.LEFT );
|
||||
|
||||
Subquery<Long> personCount = query.subquery( Long.class );
|
||||
Root<Person> person = personCount.from( Person.class );
|
||||
personCount.select( cb.count( person ) ).where( cb.equal( contacts.get( "id" ), person.get( "id" ) ) );
|
||||
|
||||
query.multiselect( document.get( "id" ), personCount.getSelection() );
|
||||
|
||||
List<?> l = em.createQuery( query ).getResultList();
|
||||
assertEquals( 2, l.size() );
|
||||
} );
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue