HHH-16899 refinements to the CriteriaDefinition API
This commit is contained in:
parent
f3eb0ec770
commit
6b7d5bae3d
|
@ -61,6 +61,7 @@ import org.hibernate.query.Query;
|
|||
import org.hibernate.query.QueryTypeMismatchException;
|
||||
import org.hibernate.query.SelectionQuery;
|
||||
import org.hibernate.query.UnknownNamedQueryException;
|
||||
import org.hibernate.query.criteria.CriteriaDefinition;
|
||||
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
|
||||
import org.hibernate.query.criteria.JpaCriteriaInsertSelect;
|
||||
import org.hibernate.query.hql.spi.SqmQueryImplementor;
|
||||
|
@ -768,8 +769,13 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
|
|||
|
||||
@Override
|
||||
public <R> SelectionQuery<R> createSelectionQuery(CriteriaQuery<R> criteria) {
|
||||
SqmUtil.verifyIsSelectStatement( (SqmStatement<?>) criteria, null );
|
||||
return new SqmSelectionQueryImpl<>( (SqmSelectStatement<R>) criteria, criteria.getResultType(), this );
|
||||
if ( criteria instanceof CriteriaDefinition ) {
|
||||
return ((CriteriaDefinition<R>) criteria).createSelectionQuery(this);
|
||||
}
|
||||
else {
|
||||
SqmUtil.verifyIsSelectStatement( (SqmStatement<?>) criteria, null );
|
||||
return new SqmSelectionQueryImpl<>( (SqmSelectStatement<R>) criteria, criteria.getResultType(), this );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1307,25 +1313,29 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
|
|||
@Override
|
||||
public <T> QueryImplementor<T> createQuery(CriteriaQuery<T> criteriaQuery) {
|
||||
checkOpen();
|
||||
|
||||
try {
|
||||
final SqmSelectStatement<T> selectStatement = (SqmSelectStatement<T>) criteriaQuery;
|
||||
if ( ! ( selectStatement.getQueryPart() instanceof SqmQueryGroup ) ) {
|
||||
final SqmQuerySpec<T> querySpec = selectStatement.getQuerySpec();
|
||||
if ( querySpec.getSelectClause().getSelections().isEmpty() ) {
|
||||
if ( querySpec.getFromClause().getRoots().size() == 1 ) {
|
||||
querySpec.getSelectClause().setSelection( querySpec.getFromClause().getRoots().get(0) );
|
||||
if ( criteriaQuery instanceof CriteriaDefinition ) {
|
||||
return (QueryImplementor<T>) ((CriteriaDefinition<T>) criteriaQuery).createSelectionQuery(this);
|
||||
}
|
||||
else {
|
||||
try {
|
||||
final SqmSelectStatement<T> selectStatement = (SqmSelectStatement<T>) criteriaQuery;
|
||||
if ( ! ( selectStatement.getQueryPart() instanceof SqmQueryGroup ) ) {
|
||||
final SqmQuerySpec<T> querySpec = selectStatement.getQuerySpec();
|
||||
if ( querySpec.getSelectClause().getSelections().isEmpty() ) {
|
||||
if ( querySpec.getFromClause().getRoots().size() == 1 ) {
|
||||
querySpec.getSelectClause().setSelection( querySpec.getFromClause().getRoots().get(0) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return createCriteriaQuery( selectStatement, criteriaQuery.getResultType() );
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
if ( getSessionFactory().getJpaMetamodel().getJpaCompliance().isJpaTransactionComplianceEnabled() ) {
|
||||
markForRollbackOnly();
|
||||
return createCriteriaQuery( selectStatement, criteriaQuery.getResultType() );
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
if ( getSessionFactory().getJpaMetamodel().getJpaCompliance().isJpaTransactionComplianceEnabled() ) {
|
||||
markForRollbackOnly();
|
||||
}
|
||||
throw getExceptionConverter().convert( e );
|
||||
}
|
||||
throw getExceptionConverter().convert( e );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,11 +6,15 @@
|
|||
*/
|
||||
package org.hibernate.query.criteria;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.EntityManagerFactory;
|
||||
import jakarta.persistence.TypedQuery;
|
||||
import jakarta.persistence.criteria.*;
|
||||
import jakarta.persistence.metamodel.EntityType;
|
||||
import org.hibernate.Incubating;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.SharedSessionContract;
|
||||
import org.hibernate.query.QueryProducer;
|
||||
import org.hibernate.query.SelectionQuery;
|
||||
import org.hibernate.query.criteria.spi.HibernateCriteriaBuilderDelegate;
|
||||
import org.hibernate.query.sqm.FetchClauseType;
|
||||
|
@ -22,7 +26,7 @@ import java.util.function.Function;
|
|||
|
||||
/**
|
||||
* A utility class that makes it easier to build {@linkplain CriteriaQuery criteria queries}.
|
||||
* From within an implementation of the {@link #define()} method, all operations of the
|
||||
* From within an initializer block of a (usually anonymous) subclass, all operations of the
|
||||
* {@link CriteriaBuilder} and {@link CriteriaQuery} may be called without the need for
|
||||
* specifying the target object.
|
||||
* <p>
|
||||
|
@ -30,17 +34,16 @@ import java.util.function.Function;
|
|||
* <pre>
|
||||
* sessionFactory.inTransaction(session -> {
|
||||
* List<Book> books
|
||||
* = new CriteriaDefinition<>(sessionFactory, Book.class) {
|
||||
* public void define() {
|
||||
* var book = from(Book.class);
|
||||
* where(like(book.get(Book_.title), "%Hibernate%"));
|
||||
* orderBy(desc(book.get(Book_.publicationDate)), asc(book.get(Book_.isbn)));
|
||||
* book.fetch(Book_.authors);
|
||||
* }
|
||||
* }
|
||||
* = new CriteriaDefinition<>(sessionFactory, Book.class) {{
|
||||
* var book = from(Book.class);
|
||||
* where(like(book.get(Book_.title), "%Hibernate%"));
|
||||
* orderBy(desc(book.get(Book_.publicationDate)), asc(book.get(Book_.isbn)));
|
||||
* book.fetch(Book_.authors);
|
||||
* }}
|
||||
* .createSelectionQuery(session)
|
||||
* .setMaxResults(10)
|
||||
* .getResultList();
|
||||
* ...
|
||||
* });
|
||||
* </pre>
|
||||
* <p>
|
||||
|
@ -49,20 +52,45 @@ import java.util.function.Function;
|
|||
* sessionFactory.inTransaction(session -> {
|
||||
* List<Book> books
|
||||
* = new CriteriaDefinition<>(sessionFactory, Book.class,
|
||||
* "from Book left join fetch authors where type = BOOK") {
|
||||
* public void define() {
|
||||
* var book = (JpaRoot<Book>) getSelection();
|
||||
* where(getRestriction(), like(book.get(Book_.title), "%Hibernate%"));
|
||||
* orderBy(desc(book.get(Book_.publicationDate)), asc(book.get(Book_.isbn)));
|
||||
* }
|
||||
* }
|
||||
* "from Book left join fetch authors where type = BOOK") {{
|
||||
* var book = (JpaRoot<Book>) getSelection();
|
||||
* where(getRestriction(), like(book.get(Book_.title), "%Hibernate%"));
|
||||
* orderBy(desc(book.get(Book_.publicationDate)), asc(book.get(Book_.isbn)));
|
||||
* }}
|
||||
* .createSelectionQuery(session)
|
||||
* .getResultList();
|
||||
* ...
|
||||
* });
|
||||
* </pre>
|
||||
* For queries which don't change between executions, the {@code CriteriaDefinition} may be
|
||||
* safely built and cached at startup:
|
||||
* <pre>
|
||||
* // build and cache the query
|
||||
* static final CriteriaQuery<Book> bookQuery =
|
||||
* new CriteriaDefinition<>(sessionFactory, Book.class) {{
|
||||
* var book = from(Book.class);
|
||||
* where(like(book.get(Book_.title), "%Hibernate%"));
|
||||
* orderBy(desc(book.get(Book_.publicationDate)), asc(book.get(Book_.isbn)));
|
||||
* book.fetch(Book_.authors);
|
||||
* }};
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* // execute it in a session
|
||||
* sessionFactory.inTransaction(session -> {
|
||||
* List<Book> books =
|
||||
* session.createQuery(bookQuery)
|
||||
* .setMaxResults(10)
|
||||
* .getResultList();
|
||||
* ...
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* @param <R> the query result type
|
||||
*
|
||||
* @since 6.3
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
@Incubating
|
||||
public abstract class CriteriaDefinition<R>
|
||||
|
@ -74,38 +102,64 @@ public abstract class CriteriaDefinition<R>
|
|||
public CriteriaDefinition(SessionFactory factory, Class<R> resultType) {
|
||||
super( factory.getCriteriaBuilder() );
|
||||
query = createQuery( resultType );
|
||||
define();
|
||||
}
|
||||
|
||||
public CriteriaDefinition(SessionFactory factory, Class<R> resultType, String baseHql) {
|
||||
super( factory.getCriteriaBuilder() );
|
||||
query = createQuery( baseHql, resultType );
|
||||
define();
|
||||
}
|
||||
|
||||
public CriteriaDefinition(SessionFactory factory, CriteriaQuery<R> baseQuery) {
|
||||
super( factory.getCriteriaBuilder() );
|
||||
query = (JpaCriteriaQuery<R>) baseQuery;
|
||||
define();
|
||||
}
|
||||
|
||||
public CriteriaDefinition(Session session, Class<R> resultType) {
|
||||
this( session.getSessionFactory(), resultType );
|
||||
public CriteriaDefinition(EntityManagerFactory factory, Class<R> resultType) {
|
||||
super( factory.getCriteriaBuilder() );
|
||||
query = createQuery( resultType );
|
||||
}
|
||||
|
||||
public CriteriaDefinition(Session session, Class<R> resultType, String baseHql) {
|
||||
this( session.getSessionFactory(), resultType, baseHql );
|
||||
public CriteriaDefinition(EntityManagerFactory factory, Class<R> resultType, String baseHql) {
|
||||
super( factory.getCriteriaBuilder() );
|
||||
query = createQuery( baseHql, resultType );
|
||||
}
|
||||
|
||||
public CriteriaDefinition(Session session, CriteriaQuery<R> baseQuery) {
|
||||
this( session.getSessionFactory(), baseQuery );
|
||||
public CriteriaDefinition(EntityManagerFactory factory, CriteriaQuery<R> baseQuery) {
|
||||
super( factory.getCriteriaBuilder() );
|
||||
query = (JpaCriteriaQuery<R>) baseQuery;
|
||||
}
|
||||
|
||||
public SelectionQuery<R> createSelectionQuery(Session session) {
|
||||
return session.createSelectionQuery( query );
|
||||
public CriteriaDefinition(SharedSessionContract session, Class<R> resultType) {
|
||||
this( session.getFactory(), resultType );
|
||||
}
|
||||
|
||||
public abstract void define();
|
||||
public CriteriaDefinition(SharedSessionContract session, Class<R> resultType, String baseHql) {
|
||||
this( session.getFactory(), resultType, baseHql );
|
||||
}
|
||||
|
||||
public CriteriaDefinition(SharedSessionContract session, CriteriaQuery<R> baseQuery) {
|
||||
this( session.getFactory(), baseQuery );
|
||||
}
|
||||
|
||||
public CriteriaDefinition(EntityManager entityManager, Class<R> resultType) {
|
||||
this( entityManager.getEntityManagerFactory(), resultType );
|
||||
}
|
||||
|
||||
public CriteriaDefinition(EntityManager entityManager, Class<R> resultType, String baseHql) {
|
||||
this( entityManager.getEntityManagerFactory(), resultType, baseHql );
|
||||
}
|
||||
|
||||
public CriteriaDefinition(EntityManager entityManager, CriteriaQuery<R> baseQuery) {
|
||||
this( entityManager.getEntityManagerFactory(), baseQuery );
|
||||
}
|
||||
|
||||
public SelectionQuery<R> createSelectionQuery(QueryProducer session) {
|
||||
return session.createQuery( query );
|
||||
}
|
||||
|
||||
public TypedQuery<R> createQuery(EntityManager entityManager) {
|
||||
return entityManager.createQuery( query );
|
||||
}
|
||||
|
||||
@Override
|
||||
public JpaCriteriaQuery<R> select(Selection<? extends R> selection) {
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import org.hibernate.Incubating;
|
||||
import org.hibernate.query.SortDirection;
|
||||
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
|
||||
|
@ -76,6 +77,10 @@ public class HibernateCriteriaBuilderDelegate implements HibernateCriteriaBuilde
|
|||
this.criteriaBuilder = criteriaBuilder;
|
||||
}
|
||||
|
||||
public HibernateCriteriaBuilderDelegate(CriteriaBuilder criteriaBuilder) {
|
||||
this.criteriaBuilder = (HibernateCriteriaBuilder) criteriaBuilder;
|
||||
}
|
||||
|
||||
protected HibernateCriteriaBuilder getCriteriaBuilder() {
|
||||
return criteriaBuilder;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
package org.hibernate.orm.test.jpa.criteria.query;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.EntityManagerFactory;
|
||||
import jakarta.persistence.Id;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.query.criteria.CriteriaDefinition;
|
||||
import org.hibernate.query.criteria.JpaRoot;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.Jpa;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -15,6 +17,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|||
|
||||
@SessionFactory
|
||||
@DomainModel(annotatedClasses = CriteriaDefinitionTest.Message.class)
|
||||
@Jpa(annotatedClasses = CriteriaDefinitionTest.Message.class)
|
||||
public class CriteriaDefinitionTest {
|
||||
|
||||
@Test void test(SessionFactoryScope scope) {
|
||||
|
@ -23,45 +26,83 @@ public class CriteriaDefinitionTest {
|
|||
s.persist( new Message(2L, "bye") );
|
||||
});
|
||||
|
||||
SessionFactoryImplementor sessionFactory = scope.getSessionFactory();
|
||||
var factory = scope.getSessionFactory();
|
||||
|
||||
var query1 = new CriteriaDefinition<>(sessionFactory, Object[].class) {
|
||||
public void define() {
|
||||
var m = from(Message.class);
|
||||
select(array(m.get("id"), m.get("text")));
|
||||
where(like(m.get("text"), "hell%"), m.get("id").equalTo(1));
|
||||
orderBy(asc(m.get("id")));
|
||||
}
|
||||
};
|
||||
var query1 = new CriteriaDefinition<>(factory, Object[].class) {{
|
||||
var message = from(Message.class);
|
||||
select(array(message.get("id"), message.get("text")));
|
||||
where(like(message.get("text"), "hell%"), message.get("id").equalTo(1));
|
||||
orderBy(asc(message.get("id")));
|
||||
}};
|
||||
|
||||
var query2 = new CriteriaDefinition<>(sessionFactory, Message.class) {
|
||||
public void define() {
|
||||
var m = from(Message.class);
|
||||
where(like(m.get("text"), "hell%"), m.get("id").equalTo(1));
|
||||
orderBy(asc(m.get("id")));
|
||||
}
|
||||
};
|
||||
var query2 = new CriteriaDefinition<>(factory, Message.class) {{
|
||||
var message = from(Message.class);
|
||||
where(like(message.get("text"), "hell%"), message.get("id").equalTo(1));
|
||||
orderBy(asc(message.get("id")));
|
||||
}};
|
||||
|
||||
var query3 = new CriteriaDefinition<>(sessionFactory, Message.class, "from Msg") {
|
||||
public void define() {
|
||||
var message = (JpaRoot<Message>) getSelection();
|
||||
where(ilike(message.get("text"), "%e%"));
|
||||
orderBy(asc(message.get("text")));
|
||||
}
|
||||
};
|
||||
var query3 = new CriteriaDefinition<>(factory, Message.class, "from Msg") {{
|
||||
var message = (JpaRoot<Message>) getSelection();
|
||||
where(ilike(message.get("text"), "%e%"));
|
||||
orderBy(asc(message.get("text")));
|
||||
}};
|
||||
|
||||
scope.inSession(session -> {
|
||||
var idAndText = query1.createSelectionQuery(session).getSingleResult();
|
||||
var idAndText = session.createSelectionQuery(query1).getSingleResult();
|
||||
assertNotNull(idAndText);
|
||||
assertEquals(1L,idAndText[0]);
|
||||
assertEquals("hello",idAndText[1]);
|
||||
|
||||
var message = query2.createSelectionQuery(session).getSingleResult();
|
||||
var message = session.createSelectionQuery(query2).getSingleResult();
|
||||
assertNotNull(message);
|
||||
assertEquals(1L,message.id);
|
||||
assertEquals("hello",message.text);
|
||||
|
||||
var messages = query3.createSelectionQuery(session).getResultList();
|
||||
var messages = session.createSelectionQuery(query3).getResultList();
|
||||
assertEquals(2,messages.size());
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Test void test(EntityManagerFactoryScope scope) {
|
||||
scope.inTransaction( s -> {
|
||||
s.persist( new Message(1L, "hello") );
|
||||
s.persist( new Message(2L, "bye") );
|
||||
});
|
||||
|
||||
EntityManagerFactory factory = scope.getEntityManagerFactory();
|
||||
|
||||
var query1 = new CriteriaDefinition<>(factory, Object[].class) {{
|
||||
var message = from(Message.class);
|
||||
select(array(message.get("id"), message.get("text")));
|
||||
where(like(message.get("text"), "hell%"), message.get("id").equalTo(1));
|
||||
orderBy(asc(message.get("id")));
|
||||
}};
|
||||
|
||||
var query2 = new CriteriaDefinition<>(factory, Message.class) {{
|
||||
var message = from(Message.class);
|
||||
where(like(message.get("text"), "hell%"), message.get("id").equalTo(1));
|
||||
orderBy(asc(message.get("id")));
|
||||
}};
|
||||
|
||||
var query3 = new CriteriaDefinition<>(factory, Message.class, "from Msg") {{
|
||||
var message = (JpaRoot<Message>) getSelection();
|
||||
where(ilike(message.get("text"), "%e%"));
|
||||
orderBy(asc(message.get("text")));
|
||||
}};
|
||||
|
||||
scope.inTransaction(entityManager -> {
|
||||
var idAndText = entityManager.createQuery(query1).getSingleResult();
|
||||
assertNotNull(idAndText);
|
||||
assertEquals(1L,idAndText[0]);
|
||||
assertEquals("hello",idAndText[1]);
|
||||
|
||||
var message = entityManager.createQuery(query2).getSingleResult();
|
||||
assertNotNull(message);
|
||||
assertEquals(1L,message.id);
|
||||
assertEquals("hello",message.text);
|
||||
|
||||
var messages = entityManager.createQuery(query3).getResultList();
|
||||
assertEquals(2,messages.size());
|
||||
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue