Various fixes from Search integration testing

- do not cache Criteria query plans (added Trello card to revisit)
  - support for StatelessSession in JUnit 5 extensions (SessionFactoryScope)
This commit is contained in:
Steve Ebersole 2020-10-30 12:04:52 -05:00
parent 23f64fc675
commit 4402843d1c
9 changed files with 225 additions and 150 deletions

View File

@ -17,6 +17,7 @@ import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.Work;
import org.hibernate.procedure.ProcedureCall;
import org.hibernate.query.QueryProducer;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
/**
* Contract methods shared between {@link Session} and {@link StatelessSession}.
@ -153,7 +154,7 @@ public interface SharedSessionContract extends QueryProducer, Serializable {
* @return an instance of CriteriaBuilder
* @throws IllegalStateException if the StatelessSession has been closed
*/
CriteriaBuilder getCriteriaBuilder();
HibernateCriteriaBuilder getCriteriaBuilder();
@Override
<T> org.hibernate.query.Query<T> createQuery(String queryString, Class<T> resultType);

View File

@ -18,7 +18,6 @@ import javax.persistence.EntityManagerFactory;
import javax.persistence.FlushModeType;
import javax.persistence.LockModeType;
import javax.persistence.StoredProcedureQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaDelete;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.CriteriaUpdate;
@ -54,6 +53,7 @@ import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.Work;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.procedure.ProcedureCall;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.query.spi.QueryImplementor;
import org.hibernate.query.spi.ScrollableResultsImplementor;
import org.hibernate.query.sql.spi.NativeQueryImplementor;
@ -432,7 +432,7 @@ public class SessionDelegatorBaseImpl implements SessionImplementor {
}
@Override
public CriteriaBuilder getCriteriaBuilder() {
public HibernateCriteriaBuilder getCriteriaBuilder() {
return delegate.getCriteriaBuilder();
}

View File

@ -15,7 +15,6 @@ import java.util.TimeZone;
import java.util.UUID;
import javax.persistence.FlushModeType;
import javax.persistence.TransactionRequiredException;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaDelete;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.CriteriaUpdate;
@ -61,6 +60,7 @@ import org.hibernate.procedure.ProcedureCall;
import org.hibernate.procedure.internal.ProcedureCallImpl;
import org.hibernate.procedure.spi.NamedCallableQueryMemento;
import org.hibernate.query.Query;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.query.hql.spi.HqlQueryImplementor;
import org.hibernate.query.hql.spi.NamedHqlQueryMemento;
import org.hibernate.query.named.NamedResultSetMappingMemento;
@ -908,7 +908,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
}
@Override
public CriteriaBuilder getCriteriaBuilder() {
public HibernateCriteriaBuilder getCriteriaBuilder() {
checkOpen();
return getFactory().getCriteriaBuilder();
}

View File

@ -72,6 +72,11 @@ public class QuerySqmImpl<R>
extends AbstractQuery<R>
implements HqlQueryImplementor<R>, ExecutionContext {
/**
* The value used for {@link #getQueryString} for Criteria-based queries
*/
public static final String CRITERIA_HQL_STRING = "<criteria>";
private final String hqlString;
private final SqmStatement sqmStatement;
private final Class resultType;
@ -212,7 +217,7 @@ public class QuerySqmImpl<R>
throw new IllegalArgumentException( "Non-select queries cannot be typed" );
}
this.hqlString = "<criteria>";
this.hqlString = CRITERIA_HQL_STRING;
this.sqmStatement = sqmStatement;
this.resultType = resultType;

View File

@ -45,6 +45,13 @@ public class SqmInterpretationsKey implements QueryInterpretationCache.Key {
private static boolean isCacheable(QuerySqmImpl<?> query) {
assert query.getQueryOptions().getAppliedGraph() != null;
if ( QuerySqmImpl.CRITERIA_HQL_STRING.equals( query.getQueryString() ) ) {
// for now at least, skip caching Criteria-based plans
// - especially wrt parameters atm; this works with HQL because the parameters
// are part of the query string; with Criteria, they are not.
return false;
}
if ( query.getSession().getLoadQueryInfluencers().hasEnabledFilters() ) {
// At the moment we cannot cache query plan if there is filter enabled.
return false;

View File

@ -25,12 +25,13 @@ import org.hibernate.metamodel.model.domain.MapPersistentAttribute;
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.hql.spi.SqmCreationState;
import org.hibernate.query.sqm.IllegalPathUsageException;
import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.hql.spi.SqmCreationState;
import org.hibernate.query.sqm.tree.expression.AbstractSqmExpression;
import org.hibernate.query.sqm.tree.expression.SqmExpression;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
/**
@ -217,24 +218,32 @@ public abstract class AbstractSqmPath<T> extends AbstractSqmExpression<T> implem
@Override
@SuppressWarnings("unchecked")
public SqmPath get(String attributeName) {
public SqmPath<?> get(String attributeName) {
// todo (6.0) : this is similar to the idea of creating an SqmExpression for a Navigable
// should make these stylistically consistent, either -
// 1) add `Navigable#createCriteriaExpression` (ala, the exist `#createSqmExpression`)
// 2) remove `Navigable#createSqmExpression` and use the approach used here instead.
if ( getReferencedPathSource().getSqmPathType() instanceof BasicType ) {
throw new IllegalStateException( "Cannot resolve path `" + attributeName + "` relative to a basic-valued path: `" + getNavigablePath() + "`" );
}
return resolvePath(
attributeName,
(pathSource, name) -> {
final SqmPathSource subNavigable = getReferencedPathSource().findSubPathSource( attributeName );
final SqmPathSource<?> subNavigable = getReferencedPathSource().findSubPathSource( attributeName );
if ( subNavigable == null ) {
throw new IllegalArgumentException( "Could not resolve attribute named `" + attributeName + "` relative to `" + getNavigablePath() + "`" );
}
if ( subNavigable instanceof SingularPersistentAttribute ) {
return createSingularPath( (SingularPersistentAttribute) subNavigable );
return createSingularPath( (SingularPersistentAttribute<?,?>) subNavigable );
}
else {
assert subNavigable instanceof PluralPersistentAttribute;
return createPluralPath( (PluralPersistentAttribute) subNavigable );
return createPluralPath( (PluralPersistentAttribute<?,?,?>) subNavigable );
}
}
);

View File

@ -6,175 +6,153 @@
*/
package org.hibernate.orm.test.query.criteria;
import java.util.List;
import java.util.Map;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Root;
import org.hibernate.IrrelevantEntity;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.query.criteria.JpaCriteriaQuery;
import org.hibernate.query.criteria.JpaRoot;
import org.hibernate.query.spi.QueryParameterImplementor;
import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Test;
import org.hibernate.testing.orm.domain.gambit.BasicEntity;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Test;
/**
* @author Steve Ebersole
*/
public class BasicCriteriaExecutionTests extends BaseNonConfigCoreFunctionalTestCase {
@Override
protected Class[] getAnnotatedClasses() {
return new Class[] { BasicEntity.class };
}
@DomainModel( annotatedClasses = BasicEntity.class )
@SessionFactory
public class BasicCriteriaExecutionTests {
@Test
public void testExecutingBasicCriteriaQuery() {
final CriteriaBuilder criteriaBuilder = sessionFactory().getCriteriaBuilder();
public void testExecutingBasicCriteriaQuery(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
final HibernateCriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
final CriteriaQuery<Object> criteria = criteriaBuilder.createQuery();
final Root<BasicEntity> root = criteria.from( BasicEntity.class );
criteria.select( root );
inSession(
session -> session.createQuery( criteria ).list()
session.createQuery( criteria ).list();
}
);
}
@Test
public void testIt() {
inSession(
public void testIt(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
final HibernateCriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
CriteriaQuery<BasicEntity> criteria = criteriaBuilder.createQuery( BasicEntity.class );
criteria.from( BasicEntity.class );
List<BasicEntity> results = session.createQuery( criteria ).list();
session.createQuery( criteria ).list();
}
);
}
@Test
public void testExecutingBasicCriteriaQueryInStatelessSession() {
final CriteriaBuilder criteriaBuilder = sessionFactory().getCriteriaBuilder();
public void testExecutingBasicCriteriaQueryInStatelessSession(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
final HibernateCriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
final CriteriaQuery<Object> criteria = criteriaBuilder.createQuery();
final Root<BasicEntity> root = criteria.from( BasicEntity.class );
criteria.select( root );
inStatelessSession(
session -> session.createQuery( criteria ).list()
session.createQuery( criteria ).list();
}
);
}
@Test
public void testExecutingBasicCriteriaQueryLiteralPredicate() {
final CriteriaBuilder criteriaBuilder = sessionFactory().getCriteriaBuilder();
public void testExecutingBasicCriteriaQueryLiteralPredicate(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
final HibernateCriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
final CriteriaQuery<Object> criteria = criteriaBuilder.createQuery();
final Root<BasicEntity> root = criteria.from( BasicEntity.class );
criteria.select( root );
criteria.where(
criteriaBuilder.equal(
criteriaBuilder.literal( 1 ),
criteriaBuilder.literal( 1 )
)
);
criteria.where( criteriaBuilder.equal( criteriaBuilder.literal( 1 ), criteriaBuilder.literal( 1 ) ) );
inSession(
session -> session.createQuery( criteria ).list()
session.createQuery( criteria ).list();
}
);
}
@Test
public void testExecutingBasicCriteriaQueryLiteralPredicateInStatelessSession() {
final CriteriaBuilder criteriaBuilder = sessionFactory().getCriteriaBuilder();
public void testExecutingBasicCriteriaQueryLiteralPredicateInStatelessSession(SessionFactoryScope scope) {
scope.inStatelessTransaction(
session -> {
final HibernateCriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
final CriteriaQuery<Object> criteria = criteriaBuilder.createQuery();
final Root<BasicEntity> root = criteria.from( BasicEntity.class );
criteria.select( root );
criteria.where( criteriaBuilder.equal( criteriaBuilder.literal( 1 ), criteriaBuilder.literal( 1 ) ) );
inStatelessSession(
session -> session.createQuery( criteria ).list()
criteria.where(
criteriaBuilder.equal(
criteriaBuilder.literal( 1 ),
criteriaBuilder.literal( 1 )
)
);
session.createQuery( criteria ).list();
}
);
}
@Test
public void testExecutingBasicCriteriaQueryParameterPredicate() {
final CriteriaBuilder criteriaBuilder = sessionFactory().getCriteriaBuilder();
public void testExecutingBasicCriteriaQueryParameterPredicate(SessionFactoryScope scope) {
scope.inStatelessTransaction(
session -> {
final HibernateCriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
final CriteriaQuery<Object> criteria = criteriaBuilder.createQuery();
final Root<BasicEntity> root = criteria.from( BasicEntity.class );
criteria.select( root );
Map<QueryParameterImplementor,Map<SqmParameter, List<JdbcParameter>>> parameterResolutionMap;
final ParameterExpression<Integer> param = criteriaBuilder.parameter( Integer.class );
criteria.where( criteriaBuilder.equal( param, param ) );
inSession(
session -> session.createQuery( criteria ).setParameter( param, 1 ).list()
session.createQuery( criteria ).setParameter( param, 1 ).list();
}
);
}
@Test
public void testExecutingBasicCriteriaQueryParameterPredicateInStatelessSession() {
final CriteriaBuilder criteriaBuilder = sessionFactory().getCriteriaBuilder();
public void testExecutingBasicCriteriaQueryParameterPredicateInStatelessSession(SessionFactoryScope scope) {
scope.inStatelessTransaction(
session -> {
final HibernateCriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
final CriteriaQuery<Object> criteria = criteriaBuilder.createQuery();
final Root<BasicEntity> root = criteria.from( BasicEntity.class );
criteria.select( root );
final ParameterExpression<Integer> param = criteriaBuilder.parameter( Integer.class );
criteria.where( criteriaBuilder.equal( param, param ) );
inStatelessSession(
session -> session.createQuery( criteria ).setParameter( param, 1 ).list()
session.createQuery( criteria ).setParameter( param, 1 ).list();
}
);
}
@Test
public void testCriteriaEntityJoin() {
final HibernateCriteriaBuilder criteriaBuilder = sessionFactory().getCriteriaBuilder();
public void testCriteriaEntityJoin(SessionFactoryScope scope) {
scope.inStatelessTransaction(
session -> {
final HibernateCriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
final JpaCriteriaQuery<Object> criteria = criteriaBuilder.createQuery();
final JpaRoot<BasicEntity> root = criteria.from( BasicEntity.class );
root.join( BasicEntity.class );
criteria.select( root );
inSession(
session -> session.createQuery( criteria ).list()
session.createQuery( criteria ).list();
}
);
}
@Entity(name = "BasicEntity")
public static class BasicEntity {
@Id
@GeneratedValue
private Integer id;
}
}

View File

@ -14,6 +14,7 @@ import java.util.function.Function;
import org.hibernate.Interceptor;
import org.hibernate.SessionFactoryObserver;
import org.hibernate.StatelessSession;
import org.hibernate.Transaction;
import org.hibernate.boot.SessionFactoryBuilder;
import org.hibernate.boot.registry.StandardServiceRegistry;
@ -231,6 +232,25 @@ public class SessionFactoryExtension
this.sessionFactory = createSessionFactory();
}
@Override
public SessionFactoryImplementor getSessionFactory() {
if ( sessionFactory == null ) {
sessionFactory = createSessionFactory();
}
return sessionFactory;
}
@Override
public MetadataImplementor getMetadataImplementor() {
return modelScope.getDomainModel();
}
@Override
public StatementInspector getStatementInspector() {
return getSessionFactory().getSessionFactoryOptions().getStatementInspector();
}
@Override
public void close() {
if ( ! active ) {
@ -259,15 +279,6 @@ public class SessionFactoryExtension
}
}
@Override
public SessionFactoryImplementor getSessionFactory() {
if ( sessionFactory == null ) {
sessionFactory = createSessionFactory();
}
return sessionFactory;
}
private SessionFactoryImplementor createSessionFactory() {
if ( ! active ) {
throw new IllegalStateException( "SessionFactoryScope is no longer active" );
@ -415,13 +426,76 @@ public class SessionFactoryExtension
}
@Override
public MetadataImplementor getMetadataImplementor() {
return modelScope.getDomainModel();
public void inStatelessSession(Consumer<StatelessSession> action) {
log.trace( "#inStatelessSession(Consumer)" );
try ( final StatelessSession statelessSession = getSessionFactory().openStatelessSession(); ) {
log.trace( "StatelessSession opened, calling action" );
action.accept( statelessSession );
}
finally {
log.trace( "StatelessSession close - auto-close block" );
}
}
@Override
public StatementInspector getStatementInspector() {
return getSessionFactory().getSessionFactoryOptions().getStatementInspector();
public void inStatelessTransaction(Consumer<StatelessSession> action) {
log.trace( "#inStatelessTransaction(Consumer)" );
try ( final StatelessSession statelessSession = getSessionFactory().openStatelessSession(); ) {
log.trace( "StatelessSession opened, calling action" );
inStatelessTransaction( statelessSession, action );
}
finally {
log.trace( "StatelessSession close - auto-close block" );
}
}
@Override
public void inStatelessTransaction(StatelessSession session, Consumer<StatelessSession> action) {
log.trace( "inStatelessTransaction(StatelessSession,Consumer)" );
final Transaction txn = session.beginTransaction();
log.trace( "Started transaction" );
try {
log.trace( "Calling action in txn" );
action.accept( session );
log.trace( "Called action - in txn" );
if ( !txn.getRollbackOnly() ) {
log.trace( "Committing transaction" );
txn.commit();
log.trace( "Committed transaction" );
}
else {
try {
log.trace( "Rollback transaction marked for rollback only" );
txn.rollback();
}
catch (Exception e) {
log.error( "Rollback failure", e );
}
}
}
catch (Exception e) {
log.tracef(
"Error calling action: %s (%s) - rolling back",
e.getClass().getName(),
e.getMessage()
);
try {
txn.rollback();
}
catch (Exception ignore) {
log.trace( "Was unable to roll back transaction" );
// really nothing else we can do here - the attempt to
// rollback already failed and there is nothing else
// to clean up.
}
throw e;
}
}
}
}

View File

@ -9,6 +9,7 @@ package org.hibernate.testing.orm.junit;
import java.util.function.Consumer;
import java.util.function.Function;
import org.hibernate.StatelessSession;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
@ -19,18 +20,18 @@ import org.hibernate.resource.jdbc.spi.StatementInspector;
*/
public interface SessionFactoryScope {
SessionFactoryImplementor getSessionFactory();
MetadataImplementor getMetadataImplementor();
StatementInspector getStatementInspector();
void inSession(Consumer<SessionImplementor> action);
void inTransaction(Consumer<SessionImplementor> action);
void inTransaction(SessionImplementor session, Consumer<SessionImplementor> action);
<T> T fromSession(Function<SessionImplementor, T> action);
<T> T fromTransaction(Function<SessionImplementor, T> action);
<T> T fromTransaction(SessionImplementor session, Function<SessionImplementor, T> action);
MetadataImplementor getMetadataImplementor();
StatementInspector getStatementInspector();
void inStatelessSession(Consumer<StatelessSession> action);
void inStatelessTransaction(Consumer<StatelessSession> action);
void inStatelessTransaction(StatelessSession session, Consumer<StatelessSession> action);
}