HHH-13780 Allow NamedQuery to set hint QueryHints.PASS_DISTINCT_THROUGH
This commit is contained in:
parent
63a96e335e
commit
bf473681e4
|
@ -67,6 +67,7 @@ public abstract class QueryBinder {
|
||||||
.setReadOnly( hints.getBoolean( queryName, QueryHints.READ_ONLY ) )
|
.setReadOnly( hints.getBoolean( queryName, QueryHints.READ_ONLY ) )
|
||||||
.setComment( hints.getString( queryName, QueryHints.COMMENT ) )
|
.setComment( hints.getString( queryName, QueryHints.COMMENT ) )
|
||||||
.setParameterTypes( null )
|
.setParameterTypes( null )
|
||||||
|
.setPassDistinctThrough( hints.getPassDistinctThrough( queryName ) )
|
||||||
.createNamedQueryDefinition();
|
.createNamedQueryDefinition();
|
||||||
|
|
||||||
if ( isDefault ) {
|
if ( isDefault ) {
|
||||||
|
@ -108,8 +109,9 @@ public abstract class QueryBinder {
|
||||||
.setReadOnly( hints.getBoolean( queryName, QueryHints.READ_ONLY ) )
|
.setReadOnly( hints.getBoolean( queryName, QueryHints.READ_ONLY ) )
|
||||||
.setComment( hints.getString( queryName, QueryHints.COMMENT ) )
|
.setComment( hints.getString( queryName, QueryHints.COMMENT ) )
|
||||||
.setParameterTypes( null )
|
.setParameterTypes( null )
|
||||||
.setCallable( hints.getBoolean( queryName, QueryHints.CALLABLE ) );
|
.setCallable( hints.getBoolean( queryName, QueryHints.CALLABLE ) )
|
||||||
|
.setPassDistinctThrough( hints.getPassDistinctThrough( queryName ) );
|
||||||
|
|
||||||
if ( !BinderHelper.isEmptyAnnotationValue( resultSetMapping ) ) {
|
if ( !BinderHelper.isEmptyAnnotationValue( resultSetMapping ) ) {
|
||||||
//sql result set usage
|
//sql result set usage
|
||||||
builder.setResultSetRef( resultSetMapping )
|
builder.setResultSetRef( resultSetMapping )
|
||||||
|
|
|
@ -9,6 +9,7 @@ package org.hibernate.cfg.annotations;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import javax.persistence.LockModeType;
|
import javax.persistence.LockModeType;
|
||||||
import javax.persistence.NamedQuery;
|
import javax.persistence.NamedQuery;
|
||||||
import javax.persistence.QueryHint;
|
import javax.persistence.QueryHint;
|
||||||
|
@ -84,21 +85,28 @@ public class QueryHintDefinition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean getPassDistinctThrough(String query) {
|
||||||
|
return doGetBoolean( query, QueryHints.PASS_DISTINCT_THROUGH ).orElse( null );
|
||||||
|
}
|
||||||
|
|
||||||
public boolean getBoolean(String query, String hintName) {
|
public boolean getBoolean(String query, String hintName) {
|
||||||
String value =(String) hintsMap.get( hintName );
|
return doGetBoolean( query, hintName ).orElse( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<Boolean> doGetBoolean(String query, String hintName) {
|
||||||
|
String value = (String) hintsMap.get( hintName );
|
||||||
if ( value == null ) {
|
if ( value == null ) {
|
||||||
return false;
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
if ( value.equalsIgnoreCase( "true" ) ) {
|
if ( value.equalsIgnoreCase( "true" ) ) {
|
||||||
return true;
|
return Optional.of( true );
|
||||||
}
|
}
|
||||||
else if ( value.equalsIgnoreCase( "false" ) ) {
|
else if ( value.equalsIgnoreCase( "false" ) ) {
|
||||||
return false;
|
return Optional.of( false );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new AnnotationException( "Not a boolean in hint: " + query + ":" + hintName );
|
throw new AnnotationException( "Not a boolean in hint: " + query + ":" + hintName );
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getString(String query, String hintName) {
|
public String getString(String query, String hintName) {
|
||||||
|
|
|
@ -33,6 +33,7 @@ public class NamedQueryDefinition implements Serializable {
|
||||||
private final CacheMode cacheMode;
|
private final CacheMode cacheMode;
|
||||||
private final boolean readOnly;
|
private final boolean readOnly;
|
||||||
private final String comment;
|
private final String comment;
|
||||||
|
private final Boolean passDistinctThrough;
|
||||||
|
|
||||||
// added for jpa 2.1
|
// added for jpa 2.1
|
||||||
private final Integer firstResult;
|
private final Integer firstResult;
|
||||||
|
@ -133,7 +134,8 @@ public class NamedQueryDefinition implements Serializable {
|
||||||
comment,
|
comment,
|
||||||
parameterTypes,
|
parameterTypes,
|
||||||
null, // firstResult
|
null, // firstResult
|
||||||
null // maxResults
|
null, // maxResults
|
||||||
|
null // passDistinctThrough
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +153,8 @@ public class NamedQueryDefinition implements Serializable {
|
||||||
String comment,
|
String comment,
|
||||||
Map parameterTypes,
|
Map parameterTypes,
|
||||||
Integer firstResult,
|
Integer firstResult,
|
||||||
Integer maxResults) {
|
Integer maxResults,
|
||||||
|
Boolean passDistinctThrough) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.query = query;
|
this.query = query;
|
||||||
this.cacheable = cacheable;
|
this.cacheable = cacheable;
|
||||||
|
@ -167,6 +170,7 @@ public class NamedQueryDefinition implements Serializable {
|
||||||
|
|
||||||
this.firstResult = firstResult;
|
this.firstResult = firstResult;
|
||||||
this.maxResults = maxResults;
|
this.maxResults = maxResults;
|
||||||
|
this.passDistinctThrough = passDistinctThrough;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
@ -230,6 +234,10 @@ public class NamedQueryDefinition implements Serializable {
|
||||||
return maxResults;
|
return maxResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean getPassDistinctThrough() {
|
||||||
|
return passDistinctThrough;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return getClass().getName() + '(' + name + " [" + query + "])";
|
return getClass().getName() + '(' + name + " [" + query + "])";
|
||||||
|
@ -250,7 +258,8 @@ public class NamedQueryDefinition implements Serializable {
|
||||||
getComment(),
|
getComment(),
|
||||||
getParameterTypes(),
|
getParameterTypes(),
|
||||||
getFirstResult(),
|
getFirstResult(),
|
||||||
getMaxResults()
|
getMaxResults(),
|
||||||
|
getPassDistinctThrough()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ public class NamedQueryDefinitionBuilder {
|
||||||
protected LockOptions lockOptions;
|
protected LockOptions lockOptions;
|
||||||
protected Integer firstResult;
|
protected Integer firstResult;
|
||||||
protected Integer maxResults;
|
protected Integer maxResults;
|
||||||
|
protected Boolean passDistinctThrough;
|
||||||
|
|
||||||
public NamedQueryDefinitionBuilder() {
|
public NamedQueryDefinitionBuilder() {
|
||||||
}
|
}
|
||||||
|
@ -114,6 +115,11 @@ public class NamedQueryDefinitionBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public NamedQueryDefinitionBuilder setPassDistinctThrough(Boolean passDistinctThrough) {
|
||||||
|
this.passDistinctThrough = passDistinctThrough;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public NamedQueryDefinition createNamedQueryDefinition() {
|
public NamedQueryDefinition createNamedQueryDefinition() {
|
||||||
return new NamedQueryDefinition(
|
return new NamedQueryDefinition(
|
||||||
name,
|
name,
|
||||||
|
@ -129,7 +135,8 @@ public class NamedQueryDefinitionBuilder {
|
||||||
comment,
|
comment,
|
||||||
parameterTypes,
|
parameterTypes,
|
||||||
firstResult,
|
firstResult,
|
||||||
maxResults
|
maxResults,
|
||||||
|
passDistinctThrough
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,8 @@ public class NamedSQLQueryDefinition extends NamedQueryDefinition {
|
||||||
null, // resultSetRef
|
null, // resultSetRef
|
||||||
querySpaces,
|
querySpaces,
|
||||||
callable,
|
callable,
|
||||||
queryReturns
|
queryReturns,
|
||||||
|
null // passDistinctThrough
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,7 +141,8 @@ public class NamedSQLQueryDefinition extends NamedQueryDefinition {
|
||||||
resultSetRef,
|
resultSetRef,
|
||||||
querySpaces,
|
querySpaces,
|
||||||
callable,
|
callable,
|
||||||
null // queryReturns
|
null, // queryReturns
|
||||||
|
null // passDistinctThrough
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,7 +163,8 @@ public class NamedSQLQueryDefinition extends NamedQueryDefinition {
|
||||||
String resultSetRef,
|
String resultSetRef,
|
||||||
List<String> querySpaces,
|
List<String> querySpaces,
|
||||||
boolean callable,
|
boolean callable,
|
||||||
NativeSQLQueryReturn[] queryReturns) {
|
NativeSQLQueryReturn[] queryReturns,
|
||||||
|
Boolean passDistinctThrough) {
|
||||||
super(
|
super(
|
||||||
name,
|
name,
|
||||||
query.trim(), /* trim done to workaround stupid oracle bug that cant handle whitespaces before a { in a sp */
|
query.trim(), /* trim done to workaround stupid oracle bug that cant handle whitespaces before a { in a sp */
|
||||||
|
@ -176,7 +179,8 @@ public class NamedSQLQueryDefinition extends NamedQueryDefinition {
|
||||||
comment,
|
comment,
|
||||||
parameterTypes,
|
parameterTypes,
|
||||||
firstResult,
|
firstResult,
|
||||||
maxResults
|
maxResults,
|
||||||
|
passDistinctThrough
|
||||||
);
|
);
|
||||||
this.resultSetRef = resultSetRef;
|
this.resultSetRef = resultSetRef;
|
||||||
this.querySpaces = querySpaces;
|
this.querySpaces = querySpaces;
|
||||||
|
@ -219,7 +223,8 @@ public class NamedSQLQueryDefinition extends NamedQueryDefinition {
|
||||||
getResultSetRef(),
|
getResultSetRef(),
|
||||||
getQuerySpaces(),
|
getQuerySpaces(),
|
||||||
isCallable(),
|
isCallable(),
|
||||||
getQueryReturns()
|
getQueryReturns(),
|
||||||
|
getPassDistinctThrough()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -149,6 +149,11 @@ public class NamedSQLQueryDefinitionBuilder extends NamedQueryDefinitionBuilder
|
||||||
return (NamedSQLQueryDefinitionBuilder) super.setMaxResults( maxResults );
|
return (NamedSQLQueryDefinitionBuilder) super.setMaxResults( maxResults );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NamedSQLQueryDefinitionBuilder setPassDistinctThrough(Boolean passDistinctThrough) {
|
||||||
|
return (NamedSQLQueryDefinitionBuilder) super.setPassDistinctThrough( passDistinctThrough );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NamedSQLQueryDefinition createNamedQueryDefinition() {
|
public NamedSQLQueryDefinition createNamedQueryDefinition() {
|
||||||
return new NamedSQLQueryDefinition(
|
return new NamedSQLQueryDefinition(
|
||||||
|
@ -168,7 +173,8 @@ public class NamedSQLQueryDefinitionBuilder extends NamedQueryDefinitionBuilder
|
||||||
resultSetRef,
|
resultSetRef,
|
||||||
querySpacesCopy(),
|
querySpacesCopy(),
|
||||||
callable,
|
callable,
|
||||||
queryReturns
|
queryReturns,
|
||||||
|
passDistinctThrough
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,7 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.engine.transaction.internal.TransactionImpl;
|
import org.hibernate.engine.transaction.internal.TransactionImpl;
|
||||||
import org.hibernate.engine.transaction.spi.TransactionImplementor;
|
import org.hibernate.engine.transaction.spi.TransactionImplementor;
|
||||||
import org.hibernate.id.uuid.StandardRandomStrategy;
|
import org.hibernate.id.uuid.StandardRandomStrategy;
|
||||||
|
import org.hibernate.jpa.QueryHints;
|
||||||
import org.hibernate.jpa.internal.util.FlushModeTypeHelper;
|
import org.hibernate.jpa.internal.util.FlushModeTypeHelper;
|
||||||
import org.hibernate.jpa.spi.CriteriaQueryTupleTransformer;
|
import org.hibernate.jpa.spi.CriteriaQueryTupleTransformer;
|
||||||
import org.hibernate.jpa.spi.HibernateEntityManagerImplementor;
|
import org.hibernate.jpa.spi.HibernateEntityManagerImplementor;
|
||||||
|
@ -695,6 +696,9 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
|
||||||
if ( nqd.getFlushMode() != null ) {
|
if ( nqd.getFlushMode() != null ) {
|
||||||
query.setHibernateFlushMode( nqd.getFlushMode() );
|
query.setHibernateFlushMode( nqd.getFlushMode() );
|
||||||
}
|
}
|
||||||
|
if ( nqd.getPassDistinctThrough() != null ) {
|
||||||
|
query.setHint( QueryHints.HINT_PASS_DISTINCT_THROUGH, nqd.getPassDistinctThrough() );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -14,6 +14,9 @@ import javax.persistence.Entity;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
import javax.persistence.ManyToOne;
|
import javax.persistence.ManyToOne;
|
||||||
import javax.persistence.OneToMany;
|
import javax.persistence.OneToMany;
|
||||||
|
import javax.persistence.NamedQueries;
|
||||||
|
import javax.persistence.NamedQuery;
|
||||||
|
import javax.persistence.QueryHint;
|
||||||
|
|
||||||
import org.hibernate.boot.SessionFactoryBuilder;
|
import org.hibernate.boot.SessionFactoryBuilder;
|
||||||
import org.hibernate.jpa.QueryHints;
|
import org.hibernate.jpa.QueryHints;
|
||||||
|
@ -34,6 +37,9 @@ import static org.junit.Assert.assertTrue;
|
||||||
@TestForIssue( jiraKey = "HHH-10965" )
|
@TestForIssue( jiraKey = "HHH-10965" )
|
||||||
public class SelectDistinctHqlTest extends BaseNonConfigCoreFunctionalTestCase {
|
public class SelectDistinctHqlTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||||
|
|
||||||
|
private static final String DISTINCT_PASSES_THROUGH_TRUE_NAMED_QUERY = "distinctPassesThroughTrue";
|
||||||
|
private static final String DISTINCT_PASSES_THROUGH_FALSE_NAMED_QUERY = "distinctPassesThroughFalse";
|
||||||
|
private static final String DISTINCT_PASSES_THROUGH_NOT_SPECIFIED_NAMED_QUERY = "distinctPassesThroughNotSpecified";
|
||||||
private SQLStatementInterceptor sqlStatementInterceptor;
|
private SQLStatementInterceptor sqlStatementInterceptor;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -137,7 +143,65 @@ public class SelectDistinctHqlTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue(jiraKey = "HHH-13780")
|
||||||
|
public void testNamedQueryDistinctPassThroughTrue() {
|
||||||
|
doInHibernate( this::sessionFactory, session -> {
|
||||||
|
sqlStatementInterceptor.getSqlQueries().clear();
|
||||||
|
List<Person> persons = session.createNamedQuery( DISTINCT_PASSES_THROUGH_TRUE_NAMED_QUERY, Person.class )
|
||||||
|
.setMaxResults( 5 )
|
||||||
|
.getResultList();
|
||||||
|
assertEquals( 1, persons.size() );
|
||||||
|
String sqlQuery = sqlStatementInterceptor.getSqlQueries().getLast();
|
||||||
|
assertTrue( sqlQuery.contains( " distinct " ) );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue(jiraKey = "HHH-13780")
|
||||||
|
public void testNamedQueryDistinctPassThroughTrueWhenNotSpecified() {
|
||||||
|
doInHibernate( this::sessionFactory, session -> {
|
||||||
|
sqlStatementInterceptor.getSqlQueries().clear();
|
||||||
|
List<Person> persons =
|
||||||
|
session.createNamedQuery( DISTINCT_PASSES_THROUGH_NOT_SPECIFIED_NAMED_QUERY, Person.class )
|
||||||
|
.setMaxResults( 5 )
|
||||||
|
.getResultList();
|
||||||
|
assertEquals( 1, persons.size() );
|
||||||
|
String sqlQuery = sqlStatementInterceptor.getSqlQueries().getLast();
|
||||||
|
assertTrue( sqlQuery.contains( " distinct " ) );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue(jiraKey = "HHH-13780")
|
||||||
|
public void testNamedQueryDistinctPassThroughFalse() {
|
||||||
|
doInHibernate( this::sessionFactory, session -> {
|
||||||
|
sqlStatementInterceptor.getSqlQueries().clear();
|
||||||
|
List<Person> persons =
|
||||||
|
session.createNamedQuery( DISTINCT_PASSES_THROUGH_FALSE_NAMED_QUERY, Person.class )
|
||||||
|
.setMaxResults( 5 )
|
||||||
|
.getResultList();
|
||||||
|
assertEquals( 1, persons.size() );
|
||||||
|
String sqlQuery = sqlStatementInterceptor.getSqlQueries().getLast();
|
||||||
|
assertFalse( sqlQuery.contains( " distinct " ) );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
@Entity(name = "Person")
|
@Entity(name = "Person")
|
||||||
|
@NamedQueries({
|
||||||
|
@NamedQuery(name = DISTINCT_PASSES_THROUGH_TRUE_NAMED_QUERY,
|
||||||
|
query = "select distinct p from Person p left join fetch p.phones",
|
||||||
|
hints = {
|
||||||
|
@QueryHint(name = QueryHints.HINT_PASS_DISTINCT_THROUGH, value = "true")
|
||||||
|
}),
|
||||||
|
@NamedQuery(name = DISTINCT_PASSES_THROUGH_FALSE_NAMED_QUERY,
|
||||||
|
query = "select distinct p from Person p left join fetch p.phones",
|
||||||
|
hints = {
|
||||||
|
@QueryHint(name = QueryHints.HINT_PASS_DISTINCT_THROUGH, value = "false")
|
||||||
|
}),
|
||||||
|
@NamedQuery(name = DISTINCT_PASSES_THROUGH_NOT_SPECIFIED_NAMED_QUERY,
|
||||||
|
query = "select distinct p from Person p left join fetch p.phones")
|
||||||
|
})
|
||||||
public static class Person {
|
public static class Person {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
|
|
Loading…
Reference in New Issue