HHH-2736 query hints for Query and Criteria
This commit is contained in:
parent
6beb5acb4b
commit
742b1b4156
|
@ -25,6 +25,8 @@
|
||||||
package org.hibernate;
|
package org.hibernate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.persistence.QueryHint;
|
||||||
|
|
||||||
import org.hibernate.criterion.CriteriaSpecification;
|
import org.hibernate.criterion.CriteriaSpecification;
|
||||||
import org.hibernate.criterion.Criterion;
|
import org.hibernate.criterion.Criterion;
|
||||||
import org.hibernate.criterion.Order;
|
import org.hibernate.criterion.Order;
|
||||||
|
@ -505,6 +507,18 @@ public interface Criteria extends CriteriaSpecification {
|
||||||
* @return this (for method chaining)
|
* @return this (for method chaining)
|
||||||
*/
|
*/
|
||||||
public Criteria setComment(String comment);
|
public Criteria setComment(String comment);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a DB query hint to the SQL. These differ from JPA's {@link QueryHint}, which is specific to the JPA
|
||||||
|
* implementation and ignores DB vendor-specific hints. Instead, these are intended solely for the vendor-specific
|
||||||
|
* hints, such as Oracle's optimizers. Multiple query hints are supported; the Dialect will determine
|
||||||
|
* concatenation and placement.
|
||||||
|
*
|
||||||
|
* @param hint The database specific query hint to add.
|
||||||
|
* @return this (for method chaining)
|
||||||
|
*/
|
||||||
|
public Criteria addQueryHint(String hint);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Override the flush mode for this particular query.
|
* Override the flush mode for this particular query.
|
||||||
|
|
|
@ -34,6 +34,8 @@ import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.persistence.QueryHint;
|
||||||
|
|
||||||
import org.hibernate.transform.ResultTransformer;
|
import org.hibernate.transform.ResultTransformer;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
|
|
||||||
|
@ -211,6 +213,16 @@ public interface Query extends BasicQueryContract {
|
||||||
* @see #getComment()
|
* @see #getComment()
|
||||||
*/
|
*/
|
||||||
public Query setComment(String comment);
|
public Query setComment(String comment);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a DB query hint to the SQL. These differ from JPA's {@link QueryHint}, which is specific to the JPA
|
||||||
|
* implementation and ignores DB vendor-specific hints. Instead, these are intended solely for the vendor-specific
|
||||||
|
* hints, such as Oracle's optimizers. Multiple query hints are supported; the Dialect will determine
|
||||||
|
* concatenation and placement.
|
||||||
|
*
|
||||||
|
* @param hint The database specific query hint to add.
|
||||||
|
*/
|
||||||
|
public Query addQueryHint(String hint);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the HQL select clause aliases, if any.
|
* Return the HQL select clause aliases, if any.
|
||||||
|
|
|
@ -35,6 +35,7 @@ import java.sql.Types;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -2658,4 +2659,16 @@ public abstract class Dialect implements ConversionContext {
|
||||||
public boolean supportsNotNullUnique() {
|
public boolean supportsNotNullUnique() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply a hint to the query. The entire query is provided, allowing the Dialect full control over the placement
|
||||||
|
* and syntax of the hint. By default, ignore the hint and simply return the query.
|
||||||
|
*
|
||||||
|
* @param query The query to which to apply the hint.
|
||||||
|
* @param hints The hints to apply
|
||||||
|
* @return The modified SQL
|
||||||
|
*/
|
||||||
|
public String getQueryHintString(String query, List<String> hints) {
|
||||||
|
return query;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,9 +27,11 @@ import java.sql.CallableStatement;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Types;
|
import java.sql.Types;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.hibernate.JDBCException;
|
import org.hibernate.JDBCException;
|
||||||
import org.hibernate.QueryTimeoutException;
|
import org.hibernate.QueryTimeoutException;
|
||||||
|
import org.hibernate.annotations.common.util.StringHelper;
|
||||||
import org.hibernate.cfg.Environment;
|
import org.hibernate.cfg.Environment;
|
||||||
import org.hibernate.dialect.function.NoArgSQLFunction;
|
import org.hibernate.dialect.function.NoArgSQLFunction;
|
||||||
import org.hibernate.dialect.function.NvlFunction;
|
import org.hibernate.dialect.function.NvlFunction;
|
||||||
|
@ -582,4 +584,26 @@ public class Oracle8iDialect extends Dialect {
|
||||||
public String getNotExpression( String expression ) {
|
public String getNotExpression( String expression ) {
|
||||||
return "not (" + expression + ")";
|
return "not (" + expression + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getQueryHintString(String sql, List<String> hints) {
|
||||||
|
final String hint = StringHelper.join( ", ", hints.iterator() );
|
||||||
|
|
||||||
|
if ( StringHelper.isEmpty( hint ) ) {
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int pos = sql.indexOf( "select" );
|
||||||
|
if ( pos > -1 ) {
|
||||||
|
final StringBuilder buffer = new StringBuilder( sql.length() + hint.length() + 8 );
|
||||||
|
if ( pos > 0 ) {
|
||||||
|
buffer.append( sql.substring( 0, pos ) );
|
||||||
|
}
|
||||||
|
buffer.append( "select /*+ " ).append( hint ).append( " */" )
|
||||||
|
.append( sql.substring( pos + "select".length() ) );
|
||||||
|
sql = buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,7 @@ public final class QueryParameters {
|
||||||
private boolean cacheable;
|
private boolean cacheable;
|
||||||
private String cacheRegion;
|
private String cacheRegion;
|
||||||
private String comment;
|
private String comment;
|
||||||
|
private List<String> queryHints;
|
||||||
private ScrollMode scrollMode;
|
private ScrollMode scrollMode;
|
||||||
private Serializable[] collectionKeys;
|
private Serializable[] collectionKeys;
|
||||||
private Object optionalObject;
|
private Object optionalObject;
|
||||||
|
@ -101,7 +102,7 @@ public final class QueryParameters {
|
||||||
public QueryParameters(
|
public QueryParameters(
|
||||||
final Type[] positionalParameterTypes,
|
final Type[] positionalParameterTypes,
|
||||||
final Object[] positionalParameterValues) {
|
final Object[] positionalParameterValues) {
|
||||||
this( positionalParameterTypes, positionalParameterValues, null, null, false, false, false, null, null, false, null );
|
this( positionalParameterTypes, positionalParameterValues, null, null, false, false, false, null, null, null, false, null );
|
||||||
}
|
}
|
||||||
|
|
||||||
public QueryParameters(
|
public QueryParameters(
|
||||||
|
@ -127,6 +128,7 @@ public final class QueryParameters {
|
||||||
false,
|
false,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
collectionKeys,
|
collectionKeys,
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
@ -143,6 +145,7 @@ public final class QueryParameters {
|
||||||
final String cacheRegion,
|
final String cacheRegion,
|
||||||
//final boolean forceCacheRefresh,
|
//final boolean forceCacheRefresh,
|
||||||
final String comment,
|
final String comment,
|
||||||
|
final List<String> queryHints,
|
||||||
final boolean isLookupByNaturalKey,
|
final boolean isLookupByNaturalKey,
|
||||||
final ResultTransformer transformer) {
|
final ResultTransformer transformer) {
|
||||||
this(
|
this(
|
||||||
|
@ -156,6 +159,7 @@ public final class QueryParameters {
|
||||||
cacheable,
|
cacheable,
|
||||||
cacheRegion,
|
cacheRegion,
|
||||||
comment,
|
comment,
|
||||||
|
queryHints,
|
||||||
null,
|
null,
|
||||||
transformer
|
transformer
|
||||||
);
|
);
|
||||||
|
@ -174,6 +178,7 @@ public final class QueryParameters {
|
||||||
final String cacheRegion,
|
final String cacheRegion,
|
||||||
//final boolean forceCacheRefresh,
|
//final boolean forceCacheRefresh,
|
||||||
final String comment,
|
final String comment,
|
||||||
|
final List<String> queryHints,
|
||||||
final Serializable[] collectionKeys,
|
final Serializable[] collectionKeys,
|
||||||
ResultTransformer transformer) {
|
ResultTransformer transformer) {
|
||||||
this.positionalParameterTypes = positionalParameterTypes;
|
this.positionalParameterTypes = positionalParameterTypes;
|
||||||
|
@ -185,6 +190,7 @@ public final class QueryParameters {
|
||||||
this.cacheRegion = cacheRegion;
|
this.cacheRegion = cacheRegion;
|
||||||
//this.forceCacheRefresh = forceCacheRefresh;
|
//this.forceCacheRefresh = forceCacheRefresh;
|
||||||
this.comment = comment;
|
this.comment = comment;
|
||||||
|
this.queryHints = queryHints;
|
||||||
this.collectionKeys = collectionKeys;
|
this.collectionKeys = collectionKeys;
|
||||||
this.isReadOnlyInitialized = isReadOnlyInitialized;
|
this.isReadOnlyInitialized = isReadOnlyInitialized;
|
||||||
this.readOnly = readOnly;
|
this.readOnly = readOnly;
|
||||||
|
@ -203,6 +209,7 @@ public final class QueryParameters {
|
||||||
final String cacheRegion,
|
final String cacheRegion,
|
||||||
//final boolean forceCacheRefresh,
|
//final boolean forceCacheRefresh,
|
||||||
final String comment,
|
final String comment,
|
||||||
|
final List<String> queryHints,
|
||||||
final Serializable[] collectionKeys,
|
final Serializable[] collectionKeys,
|
||||||
final Object optionalObject,
|
final Object optionalObject,
|
||||||
final String optionalEntityName,
|
final String optionalEntityName,
|
||||||
|
@ -219,6 +226,7 @@ public final class QueryParameters {
|
||||||
cacheable,
|
cacheable,
|
||||||
cacheRegion,
|
cacheRegion,
|
||||||
comment,
|
comment,
|
||||||
|
queryHints,
|
||||||
collectionKeys,
|
collectionKeys,
|
||||||
transformer
|
transformer
|
||||||
);
|
);
|
||||||
|
@ -322,6 +330,14 @@ public final class QueryParameters {
|
||||||
public void setComment(String comment) {
|
public void setComment(String comment) {
|
||||||
this.comment = comment;
|
this.comment = comment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> getQueryHints() {
|
||||||
|
return queryHints;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setQueryHints(List<String> queryHints) {
|
||||||
|
this.queryHints = queryHints;
|
||||||
|
}
|
||||||
|
|
||||||
public ScrollMode getScrollMode() {
|
public ScrollMode getScrollMode() {
|
||||||
return scrollMode;
|
return scrollMode;
|
||||||
|
@ -561,6 +577,7 @@ public final class QueryParameters {
|
||||||
this.cacheable,
|
this.cacheable,
|
||||||
this.cacheRegion,
|
this.cacheRegion,
|
||||||
this.comment,
|
this.comment,
|
||||||
|
this.queryHints,
|
||||||
this.collectionKeys,
|
this.collectionKeys,
|
||||||
this.optionalObject,
|
this.optionalObject,
|
||||||
this.optionalEntityName,
|
this.optionalEntityName,
|
||||||
|
|
|
@ -101,6 +101,7 @@ public abstract class AbstractQueryImpl implements Query {
|
||||||
private boolean cacheable;
|
private boolean cacheable;
|
||||||
private String cacheRegion;
|
private String cacheRegion;
|
||||||
private String comment;
|
private String comment;
|
||||||
|
private final List<String> queryHints = new ArrayList<String>();
|
||||||
private FlushMode flushMode;
|
private FlushMode flushMode;
|
||||||
private CacheMode cacheMode;
|
private CacheMode cacheMode;
|
||||||
private FlushMode sessionFlushMode;
|
private FlushMode sessionFlushMode;
|
||||||
|
@ -192,6 +193,12 @@ public abstract class AbstractQueryImpl implements Query {
|
||||||
this.comment = comment;
|
this.comment = comment;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Query addQueryHint(String queryHint) {
|
||||||
|
queryHints.add( queryHint );
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer getFirstResult() {
|
public Integer getFirstResult() {
|
||||||
|
@ -987,6 +994,7 @@ public abstract class AbstractQueryImpl implements Query {
|
||||||
cacheable,
|
cacheable,
|
||||||
cacheRegion,
|
cacheRegion,
|
||||||
comment,
|
comment,
|
||||||
|
queryHints,
|
||||||
collectionKey == null ? null : new Serializable[] { collectionKey },
|
collectionKey == null ? null : new Serializable[] { collectionKey },
|
||||||
optionalObject,
|
optionalObject,
|
||||||
optionalEntityName,
|
optionalEntityName,
|
||||||
|
|
|
@ -75,6 +75,7 @@ public class CriteriaImpl implements Criteria, Serializable {
|
||||||
private boolean cacheable;
|
private boolean cacheable;
|
||||||
private String cacheRegion;
|
private String cacheRegion;
|
||||||
private String comment;
|
private String comment;
|
||||||
|
private final List<String> queryHints = new ArrayList<String>();
|
||||||
|
|
||||||
private FlushMode flushMode;
|
private FlushMode flushMode;
|
||||||
private CacheMode cacheMode;
|
private CacheMode cacheMode;
|
||||||
|
@ -345,11 +346,23 @@ public class CriteriaImpl implements Criteria, Serializable {
|
||||||
public String getComment() {
|
public String getComment() {
|
||||||
return comment;
|
return comment;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Criteria setComment(String comment) {
|
public Criteria setComment(String comment) {
|
||||||
this.comment = comment;
|
this.comment = comment;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> getQueryHints() {
|
||||||
|
return queryHints;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Criteria addQueryHint(String queryHint) {
|
||||||
|
queryHints.add( queryHint );
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Criteria setFlushMode(FlushMode flushMode) {
|
public Criteria setFlushMode(FlushMode flushMode) {
|
||||||
this.flushMode = flushMode;
|
this.flushMode = flushMode;
|
||||||
|
@ -666,6 +679,11 @@ public class CriteriaImpl implements Criteria, Serializable {
|
||||||
public Criteria setComment(String comment) {
|
public Criteria setComment(String comment) {
|
||||||
CriteriaImpl.this.setComment(comment);
|
CriteriaImpl.this.setComment(comment);
|
||||||
return this;
|
return this;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public Criteria addQueryHint(String queryHint) {
|
||||||
|
CriteriaImpl.this.addQueryHint( queryHint );
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public Criteria setProjection(Projection projection) {
|
public Criteria setProjection(Projection projection) {
|
||||||
|
|
|
@ -242,6 +242,13 @@ public abstract class Loader {
|
||||||
Dialect dialect,
|
Dialect dialect,
|
||||||
List<AfterLoadAction> afterLoadActions) throws HibernateException {
|
List<AfterLoadAction> afterLoadActions) throws HibernateException {
|
||||||
sql = applyLocks( sql, parameters, dialect, afterLoadActions );
|
sql = applyLocks( sql, parameters, dialect, afterLoadActions );
|
||||||
|
|
||||||
|
// Keep this here, rather than moving to Select. Some Dialects may need the hint to be appended to the very
|
||||||
|
// end or beginning of the finalized SQL statement, so wait until everything is processed.
|
||||||
|
if ( parameters.getQueryHints() != null && parameters.getQueryHints().size() > 0 ) {
|
||||||
|
sql = dialect.getQueryHintString( sql, parameters.getQueryHints() );
|
||||||
|
}
|
||||||
|
|
||||||
return getFactory().getSettings().isCommentsEnabled()
|
return getFactory().getSettings().isCommentsEnabled()
|
||||||
? prependComment( sql, parameters )
|
? prependComment( sql, parameters )
|
||||||
: sql;
|
: sql;
|
||||||
|
@ -1844,7 +1851,7 @@ public abstract class Loader {
|
||||||
* limit parameters.
|
* limit parameters.
|
||||||
*/
|
*/
|
||||||
protected final PreparedStatement prepareQueryStatement(
|
protected final PreparedStatement prepareQueryStatement(
|
||||||
final String sql,
|
String sql,
|
||||||
final QueryParameters queryParameters,
|
final QueryParameters queryParameters,
|
||||||
final LimitHandler limitHandler,
|
final LimitHandler limitHandler,
|
||||||
final boolean scroll,
|
final boolean scroll,
|
||||||
|
@ -1856,7 +1863,7 @@ public abstract class Loader {
|
||||||
boolean useLimitOffset = hasFirstRow && useLimit && limitHandler.supportsLimitOffset();
|
boolean useLimitOffset = hasFirstRow && useLimit && limitHandler.supportsLimitOffset();
|
||||||
boolean callable = queryParameters.isCallable();
|
boolean callable = queryParameters.isCallable();
|
||||||
final ScrollMode scrollMode = getScrollMode( scroll, hasFirstRow, useLimitOffset, queryParameters );
|
final ScrollMode scrollMode = getScrollMode( scroll, hasFirstRow, useLimitOffset, queryParameters );
|
||||||
|
|
||||||
PreparedStatement st = session.getTransactionCoordinator().getJdbcCoordinator().getStatementPreparer().prepareQueryStatement(
|
PreparedStatement st = session.getTransactionCoordinator().getJdbcCoordinator().getStatementPreparer().prepareQueryStatement(
|
||||||
sql,
|
sql,
|
||||||
callable,
|
callable,
|
||||||
|
|
|
@ -355,6 +355,7 @@ public class CriteriaQueryTranslator implements CriteriaQuery {
|
||||||
rootCriteria.getCacheable(),
|
rootCriteria.getCacheable(),
|
||||||
rootCriteria.getCacheRegion(),
|
rootCriteria.getCacheRegion(),
|
||||||
rootCriteria.getComment(),
|
rootCriteria.getComment(),
|
||||||
|
rootCriteria.getQueryHints(),
|
||||||
rootCriteria.isLookupByNaturalKey(),
|
rootCriteria.isLookupByNaturalKey(),
|
||||||
rootCriteria.getResultTransformer()
|
rootCriteria.getResultTransformer()
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,177 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* JBoss, Home of Professional Open Source
|
||||||
|
* Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
|
||||||
|
* as indicated by the @authors tag. All rights reserved.
|
||||||
|
* See the copyright.txt in the distribution for a
|
||||||
|
* full listing of individual contributors.
|
||||||
|
*
|
||||||
|
* This copyrighted material is made available to anyone wishing to use,
|
||||||
|
* modify, copy, or redistribute it subject to the terms and conditions
|
||||||
|
* of the GNU Lesser General Public License, v. 2.1.
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT A
|
||||||
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License,
|
||||||
|
* v.2.1 along with this distribution; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
* MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
package org.hibernate.test.queryhint;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
|
||||||
|
import org.hibernate.Criteria;
|
||||||
|
import org.hibernate.Query;
|
||||||
|
import org.hibernate.Session;
|
||||||
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
|
import org.hibernate.cfg.Configuration;
|
||||||
|
import org.hibernate.criterion.Restrictions;
|
||||||
|
import org.hibernate.dialect.Oracle8iDialect;
|
||||||
|
import org.hibernate.testing.RequiresDialect;
|
||||||
|
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Brett Meyer
|
||||||
|
*/
|
||||||
|
@RequiresDialect( Oracle8iDialect.class )
|
||||||
|
public class QueryHintTest extends BaseCoreFunctionalTestCase {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class<?>[] getAnnotatedClasses() {
|
||||||
|
return new Class<?>[] { Employee.class, Department.class };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(Configuration configuration) {
|
||||||
|
configuration.setProperty( AvailableSettings.DIALECT, QueryHintTestDialect.class.getName() );
|
||||||
|
configuration.setProperty( AvailableSettings.USE_SQL_COMMENTS, "true" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testQueryHint() {
|
||||||
|
Department department = new Department();
|
||||||
|
department.name = "Sales";
|
||||||
|
Employee employee1 = new Employee();
|
||||||
|
employee1.department = department;
|
||||||
|
Employee employee2 = new Employee();
|
||||||
|
employee2.department = department;
|
||||||
|
|
||||||
|
Session s = openSession();
|
||||||
|
s.getTransaction().begin();
|
||||||
|
s.persist( department ); s.persist( employee1 );
|
||||||
|
s.persist( employee2 );
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.clear();
|
||||||
|
|
||||||
|
// test Query w/ a simple Oracle optimizer hint
|
||||||
|
s.getTransaction().begin();
|
||||||
|
Query query = s.createQuery( "FROM QueryHintTest$Employee e WHERE e.department.name = :departmentName" )
|
||||||
|
.addQueryHint( "ALL_ROWS" )
|
||||||
|
.setParameter( "departmentName", "Sales" );
|
||||||
|
List results = query.list();
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.clear();
|
||||||
|
|
||||||
|
assertEquals(results.size(), 2);
|
||||||
|
assertTrue(QueryHintTestDialect.getProcessedSql().contains( "select /*+ ALL_ROWS */"));
|
||||||
|
|
||||||
|
QueryHintTestDialect.resetProcessedSql();
|
||||||
|
|
||||||
|
// test multiple hints
|
||||||
|
s.getTransaction().begin();
|
||||||
|
query = s.createQuery( "FROM QueryHintTest$Employee e WHERE e.department.name = :departmentName" )
|
||||||
|
.addQueryHint( "ALL_ROWS" )
|
||||||
|
.addQueryHint( "USE_CONCAT" )
|
||||||
|
.setParameter( "departmentName", "Sales" );
|
||||||
|
results = query.list();
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.clear();
|
||||||
|
|
||||||
|
assertEquals(results.size(), 2);
|
||||||
|
assertTrue(QueryHintTestDialect.getProcessedSql().contains( "select /*+ ALL_ROWS, USE_CONCAT */"));
|
||||||
|
|
||||||
|
QueryHintTestDialect.resetProcessedSql();
|
||||||
|
|
||||||
|
// ensure the insertion logic can handle a comment appended to the front
|
||||||
|
s.getTransaction().begin();
|
||||||
|
query = s.createQuery( "FROM QueryHintTest$Employee e WHERE e.department.name = :departmentName" )
|
||||||
|
.setComment( "this is a test" )
|
||||||
|
.addQueryHint( "ALL_ROWS" )
|
||||||
|
.setParameter( "departmentName", "Sales" );
|
||||||
|
results = query.list();
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.clear();
|
||||||
|
|
||||||
|
assertEquals(results.size(), 2);
|
||||||
|
assertTrue(QueryHintTestDialect.getProcessedSql().contains( "select /*+ ALL_ROWS */"));
|
||||||
|
|
||||||
|
QueryHintTestDialect.resetProcessedSql();
|
||||||
|
|
||||||
|
// test Criteria
|
||||||
|
s.getTransaction().begin();
|
||||||
|
Criteria criteria = s.createCriteria( Employee.class )
|
||||||
|
.addQueryHint( "ALL_ROWS" )
|
||||||
|
.createCriteria( "department" ).add( Restrictions.eq( "name", "Sales" ) );
|
||||||
|
results = criteria.list();
|
||||||
|
s.getTransaction().commit();
|
||||||
|
s.close();
|
||||||
|
|
||||||
|
assertEquals(results.size(), 2);
|
||||||
|
assertTrue(QueryHintTestDialect.getProcessedSql().contains( "select /*+ ALL_ROWS */"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Since the query hint is added to the SQL during Loader's executeQueryStatement -> preprocessSQL, rather than
|
||||||
|
* early on during the QueryTranslator or QueryLoader initialization, there's not an easy way to check the full SQL
|
||||||
|
* after completely processing it. Instead, use this ridiculous hack to ensure Loader actually calls Dialect.
|
||||||
|
*
|
||||||
|
* TODO: This is terrible. Better ideas?
|
||||||
|
*/
|
||||||
|
public static class QueryHintTestDialect extends Oracle8iDialect {
|
||||||
|
private static String processedSql;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getQueryHintString(String sql, List<String> hints) {
|
||||||
|
processedSql = super.getQueryHintString( sql, hints );
|
||||||
|
return processedSql;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getProcessedSql() {
|
||||||
|
return processedSql;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void resetProcessedSql() {
|
||||||
|
processedSql = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public static class Employee {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
public long id;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
public Department department;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public static class Department {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
public long id;
|
||||||
|
|
||||||
|
public String name;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue