Add property for disabling subquery join rewrites and handle mysql quoted identifiers
This commit is contained in:
parent
9e8fd60e26
commit
556aa265c0
|
@ -61,55 +61,7 @@ import org.hibernate.tuple.entity.EntityTuplizerFactory;
|
|||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import static org.hibernate.cfg.AvailableSettings.ACQUIRE_CONNECTIONS;
|
||||
import static org.hibernate.cfg.AvailableSettings.ALLOW_JTA_TRANSACTION_ACCESS;
|
||||
import static org.hibernate.cfg.AvailableSettings.AUTO_CLOSE_SESSION;
|
||||
import static org.hibernate.cfg.AvailableSettings.AUTO_EVICT_COLLECTION_CACHE;
|
||||
import static org.hibernate.cfg.AvailableSettings.AUTO_SESSION_EVENTS_LISTENER;
|
||||
import static org.hibernate.cfg.AvailableSettings.BATCH_FETCH_STYLE;
|
||||
import static org.hibernate.cfg.AvailableSettings.BATCH_VERSIONED_DATA;
|
||||
import static org.hibernate.cfg.AvailableSettings.CACHE_REGION_PREFIX;
|
||||
import static org.hibernate.cfg.AvailableSettings.CHECK_NULLABILITY;
|
||||
import static org.hibernate.cfg.AvailableSettings.CONNECTION_HANDLING;
|
||||
import static org.hibernate.cfg.AvailableSettings.CUSTOM_ENTITY_DIRTINESS_STRATEGY;
|
||||
import static org.hibernate.cfg.AvailableSettings.DEFAULT_BATCH_FETCH_SIZE;
|
||||
import static org.hibernate.cfg.AvailableSettings.DEFAULT_ENTITY_MODE;
|
||||
import static org.hibernate.cfg.AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS;
|
||||
import static org.hibernate.cfg.AvailableSettings.FLUSH_BEFORE_COMPLETION;
|
||||
import static org.hibernate.cfg.AvailableSettings.GENERATE_STATISTICS;
|
||||
import static org.hibernate.cfg.AvailableSettings.HQL_BULK_ID_STRATEGY;
|
||||
import static org.hibernate.cfg.AvailableSettings.INTERCEPTOR;
|
||||
import static org.hibernate.cfg.AvailableSettings.JDBC_TIME_ZONE;
|
||||
import static org.hibernate.cfg.AvailableSettings.JPAQL_STRICT_COMPLIANCE;
|
||||
import static org.hibernate.cfg.AvailableSettings.JTA_TRACK_BY_THREAD;
|
||||
import static org.hibernate.cfg.AvailableSettings.LOG_SESSION_METRICS;
|
||||
import static org.hibernate.cfg.AvailableSettings.MAX_FETCH_DEPTH;
|
||||
import static org.hibernate.cfg.AvailableSettings.MULTI_TENANT_IDENTIFIER_RESOLVER;
|
||||
import static org.hibernate.cfg.AvailableSettings.ORDER_INSERTS;
|
||||
import static org.hibernate.cfg.AvailableSettings.ORDER_UPDATES;
|
||||
import static org.hibernate.cfg.AvailableSettings.PREFER_USER_TRANSACTION;
|
||||
import static org.hibernate.cfg.AvailableSettings.PROCEDURE_NULL_PARAM_PASSING;
|
||||
import static org.hibernate.cfg.AvailableSettings.QUERY_CACHE_FACTORY;
|
||||
import static org.hibernate.cfg.AvailableSettings.QUERY_STARTUP_CHECKING;
|
||||
import static org.hibernate.cfg.AvailableSettings.QUERY_SUBSTITUTIONS;
|
||||
import static org.hibernate.cfg.AvailableSettings.RELEASE_CONNECTIONS;
|
||||
import static org.hibernate.cfg.AvailableSettings.SESSION_FACTORY_NAME;
|
||||
import static org.hibernate.cfg.AvailableSettings.SESSION_FACTORY_NAME_IS_JNDI;
|
||||
import static org.hibernate.cfg.AvailableSettings.SESSION_SCOPED_INTERCEPTOR;
|
||||
import static org.hibernate.cfg.AvailableSettings.STATEMENT_BATCH_SIZE;
|
||||
import static org.hibernate.cfg.AvailableSettings.STATEMENT_FETCH_SIZE;
|
||||
import static org.hibernate.cfg.AvailableSettings.STATEMENT_INSPECTOR;
|
||||
import static org.hibernate.cfg.AvailableSettings.USE_DIRECT_REFERENCE_CACHE_ENTRIES;
|
||||
import static org.hibernate.cfg.AvailableSettings.USE_GET_GENERATED_KEYS;
|
||||
import static org.hibernate.cfg.AvailableSettings.USE_IDENTIFIER_ROLLBACK;
|
||||
import static org.hibernate.cfg.AvailableSettings.USE_MINIMAL_PUTS;
|
||||
import static org.hibernate.cfg.AvailableSettings.USE_QUERY_CACHE;
|
||||
import static org.hibernate.cfg.AvailableSettings.USE_SCROLLABLE_RESULTSET;
|
||||
import static org.hibernate.cfg.AvailableSettings.USE_SECOND_LEVEL_CACHE;
|
||||
import static org.hibernate.cfg.AvailableSettings.USE_SQL_COMMENTS;
|
||||
import static org.hibernate.cfg.AvailableSettings.USE_STRUCTURED_CACHE;
|
||||
import static org.hibernate.cfg.AvailableSettings.WRAP_RESULT_SETS;
|
||||
import static org.hibernate.cfg.AvailableSettings.ALLOW_UPDATE_OUTSIDE_TRANSACTION;
|
||||
import static org.hibernate.cfg.AvailableSettings.*;
|
||||
import static org.hibernate.engine.config.spi.StandardConverters.BOOLEAN;
|
||||
import static org.hibernate.jpa.AvailableSettings.DISCARD_PC_ON_CLOSE;
|
||||
|
||||
|
@ -571,6 +523,7 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
|
|||
private boolean strictJpaQueryLanguageCompliance;
|
||||
private boolean namedQueryStartupCheckingEnabled;
|
||||
private final boolean procedureParameterNullPassingEnabled;
|
||||
private final boolean collectionJoinSubqueryRewriteEnabled;
|
||||
|
||||
// Caching
|
||||
private boolean secondLevelCacheEnabled;
|
||||
|
@ -690,6 +643,7 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
|
|||
this.strictJpaQueryLanguageCompliance = cfgService.getSetting( JPAQL_STRICT_COMPLIANCE, BOOLEAN, false );
|
||||
this.namedQueryStartupCheckingEnabled = cfgService.getSetting( QUERY_STARTUP_CHECKING, BOOLEAN, true );
|
||||
this.procedureParameterNullPassingEnabled = cfgService.getSetting( PROCEDURE_NULL_PARAM_PASSING, BOOLEAN, false );
|
||||
this.collectionJoinSubqueryRewriteEnabled = cfgService.getSetting( COLLECTION_JOIN_SUBQUERY, BOOLEAN, true );
|
||||
|
||||
this.secondLevelCacheEnabled = cfgService.getSetting( USE_SECOND_LEVEL_CACHE, BOOLEAN, true );
|
||||
this.queryCacheEnabled = cfgService.getSetting( USE_QUERY_CACHE, BOOLEAN, false );
|
||||
|
@ -1086,6 +1040,11 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
|
|||
return procedureParameterNullPassingEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCollectionJoinSubqueryRewriteEnabled() {
|
||||
return collectionJoinSubqueryRewriteEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecondLevelCacheEnabled() {
|
||||
return secondLevelCacheEnabled;
|
||||
|
@ -1391,6 +1350,11 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
|
|||
return options.isProcedureParameterNullPassingEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCollectionJoinSubqueryRewriteEnabled() {
|
||||
return options.isCollectionJoinSubqueryRewriteEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecondLevelCacheEnabled() {
|
||||
return options.isSecondLevelCacheEnabled();
|
||||
|
|
|
@ -96,6 +96,7 @@ public class SessionFactoryOptionsImpl implements SessionFactoryOptions {
|
|||
private final boolean strictJpaQueryLanguageCompliance;
|
||||
private final boolean namedQueryStartupCheckingEnabled;
|
||||
private final boolean procedureParameterNullPassingEnabled;
|
||||
private final boolean collectionJoinSubqueryRewriteEnabled;
|
||||
|
||||
// Caching
|
||||
private final boolean secondLevelCacheEnabled;
|
||||
|
@ -173,6 +174,7 @@ public class SessionFactoryOptionsImpl implements SessionFactoryOptions {
|
|||
this.strictJpaQueryLanguageCompliance = state.isStrictJpaQueryLanguageCompliance();
|
||||
this.namedQueryStartupCheckingEnabled = state.isNamedQueryStartupCheckingEnabled();
|
||||
this.procedureParameterNullPassingEnabled = state.isProcedureParameterNullPassingEnabled();
|
||||
this.collectionJoinSubqueryRewriteEnabled = state.isCollectionJoinSubqueryRewriteEnabled();
|
||||
|
||||
this.secondLevelCacheEnabled = state.isSecondLevelCacheEnabled();
|
||||
this.queryCacheEnabled = state.isQueryCacheEnabled();
|
||||
|
@ -372,6 +374,11 @@ public class SessionFactoryOptionsImpl implements SessionFactoryOptions {
|
|||
return procedureParameterNullPassingEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCollectionJoinSubqueryRewriteEnabled() {
|
||||
return collectionJoinSubqueryRewriteEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAllowOutOfTransactionUpdateOperations() {
|
||||
return allowOutOfTransactionUpdateOperations;
|
||||
|
|
|
@ -117,6 +117,8 @@ public interface SessionFactoryOptionsState {
|
|||
|
||||
boolean isProcedureParameterNullPassingEnabled();
|
||||
|
||||
boolean isCollectionJoinSubqueryRewriteEnabled();
|
||||
|
||||
boolean isSecondLevelCacheEnabled();
|
||||
|
||||
boolean isQueryCacheEnabled();
|
||||
|
|
|
@ -214,6 +214,11 @@ public class AbstractDelegatingSessionFactoryOptions implements SessionFactoryOp
|
|||
return delegate.isProcedureParameterNullPassingEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCollectionJoinSubqueryRewriteEnabled() {
|
||||
return delegate.isCollectionJoinSubqueryRewriteEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAllowOutOfTransactionUpdateOperations() {
|
||||
return delegate.isAllowOutOfTransactionUpdateOperations();
|
||||
|
|
|
@ -202,6 +202,8 @@ public interface SessionFactoryOptions {
|
|||
|
||||
boolean isProcedureParameterNullPassingEnabled();
|
||||
|
||||
boolean isCollectionJoinSubqueryRewriteEnabled();
|
||||
|
||||
boolean isAllowOutOfTransactionUpdateOperations();
|
||||
|
||||
boolean isReleaseResourcesOnCloseEnabled();
|
||||
|
|
|
@ -1548,4 +1548,14 @@ public interface AvailableSettings {
|
|||
* @since 5.2
|
||||
*/
|
||||
String ALLOW_UPDATE_OUTSIDE_TRANSACTION = "hibernate.allow_update_outside_transaction";
|
||||
|
||||
/**
|
||||
* Setting which indicates whether or not the new JOINS over collection tables should be rewritten to subqueries.
|
||||
* <p/>
|
||||
* Default is {@code true}. Existing applications may want to disable this (set it {@code false}) for
|
||||
* upgrade compatibility.
|
||||
*
|
||||
* @since 5.2
|
||||
*/
|
||||
String COLLECTION_JOIN_SUBQUERY = "hibernate.collection_join_subquery";
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.hibernate.type.AssociationType;
|
|||
*/
|
||||
public class JoinSequence {
|
||||
private final SessionFactoryImplementor factory;
|
||||
private final boolean collectionJoinSubquery;
|
||||
|
||||
private final StringBuilder conditions = new StringBuilder();
|
||||
private final List<Join> joins = new ArrayList<Join>();
|
||||
|
@ -52,6 +53,7 @@ public class JoinSequence {
|
|||
*/
|
||||
public JoinSequence(SessionFactoryImplementor factory) {
|
||||
this.factory = factory;
|
||||
this.collectionJoinSubquery = factory.getSessionFactoryOptions().isCollectionJoinSubqueryRewriteEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -192,7 +194,8 @@ public class JoinSequence {
|
|||
last = rootJoinable;
|
||||
}
|
||||
else if (
|
||||
withClauseFragment != null
|
||||
collectionJoinSubquery
|
||||
&& withClauseFragment != null
|
||||
&& joins.size() > 1
|
||||
&& withClauseFragment.contains( withClauseJoinAlias )
|
||||
&& ( first = ( iter = joins.iterator() ).next() ).joinType == JoinType.LEFT_OUTER_JOIN
|
||||
|
@ -208,6 +211,7 @@ public class JoinSequence {
|
|||
"([a-zA-Z0-9_]+) | " + // Normal identifiers
|
||||
"('[a-zA-Z0-9_]+' ((''[a-zA-Z0-9_]+)+')?) | " + // Single quoted identifiers
|
||||
"(\"[a-zA-Z0-9_]+\" ((\"\"[a-zA-Z0-9_]+)+\")?) | " + // Double quoted identifiers
|
||||
"(`[a-zA-Z0-9_]+` ((``[a-zA-Z0-9_]+)+`)?) | " + // MySQL quoted identifiers
|
||||
"(\\[[a-zA-Z0-9_\\s]+\\])" + // MSSQL quoted identifiers
|
||||
")"
|
||||
);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.hibernate.test.hql;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -15,6 +16,7 @@ import org.hibernate.Query;
|
|||
import org.hibernate.QueryException;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.Transaction;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.hql.internal.ast.InvalidWithClauseException;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
|
@ -220,6 +222,29 @@ public class WithClauseTest extends BaseCoreFunctionalTestCase {
|
|||
data.cleanup();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11157")
|
||||
public void testWithClauseAsNonSubqueryWithKey() {
|
||||
rebuildSessionFactory( Collections.singletonMap( AvailableSettings.COLLECTION_JOIN_SUBQUERY, "false" ) );
|
||||
|
||||
TestData data = new TestData();
|
||||
data.prepare();
|
||||
|
||||
Session s = openSession();
|
||||
Transaction txn = s.beginTransaction();
|
||||
|
||||
// Since family has a join table, we will first left join all family members and then do the WITH clause on the target entity table join
|
||||
// Normally this produces 2 results which is wrong and can only be circumvented by converting the join table and target entity table join to a subquery
|
||||
List list = s.createQuery("from Human h left join h.family as f with key(f) like 'son1' where h.description = 'father'")
|
||||
.list();
|
||||
assertEquals("subquery rewriting of join table was not disabled", 2, list.size());
|
||||
|
||||
txn.commit();
|
||||
s.close();
|
||||
|
||||
data.cleanup();
|
||||
}
|
||||
|
||||
private class TestData {
|
||||
public void prepare() {
|
||||
Session session = openSession();
|
||||
|
|
|
@ -9,10 +9,7 @@ package org.hibernate.testing.junit4;
|
|||
import java.io.InputStream;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.*;
|
||||
import javax.persistence.SharedCacheMode;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
|
@ -98,8 +95,17 @@ public abstract class BaseCoreFunctionalTestCase extends BaseUnitTestCase {
|
|||
@BeforeClassOnce
|
||||
@SuppressWarnings( {"UnusedDeclaration"})
|
||||
protected void buildSessionFactory() {
|
||||
buildSessionFactory( Collections.emptyMap() );
|
||||
}
|
||||
|
||||
protected void buildSessionFactory(Map<String, String> properties) {
|
||||
// for now, build the configuration to get all the property settings
|
||||
configuration = constructAndConfigureConfiguration();
|
||||
if ( properties != null && !properties.isEmpty() ) {
|
||||
for ( Map.Entry<String, String> entry : properties.entrySet() ) {
|
||||
configuration.setProperty( entry.getKey(), entry.getValue() );
|
||||
}
|
||||
}
|
||||
BootstrapServiceRegistry bootRegistry = buildBootstrapServiceRegistry();
|
||||
serviceRegistry = buildServiceRegistry( bootRegistry, configuration );
|
||||
// this is done here because Configuration does not currently support 4.0 xsd
|
||||
|
@ -109,6 +115,10 @@ public abstract class BaseCoreFunctionalTestCase extends BaseUnitTestCase {
|
|||
}
|
||||
|
||||
protected void rebuildSessionFactory() {
|
||||
rebuildSessionFactory( Collections.emptyMap() );
|
||||
}
|
||||
|
||||
protected void rebuildSessionFactory(Map<String, String> properties) {
|
||||
if ( sessionFactory == null ) {
|
||||
return;
|
||||
}
|
||||
|
@ -122,7 +132,7 @@ public abstract class BaseCoreFunctionalTestCase extends BaseUnitTestCase {
|
|||
catch (Exception ignore) {
|
||||
}
|
||||
|
||||
buildSessionFactory();
|
||||
buildSessionFactory( properties );
|
||||
}
|
||||
|
||||
protected Configuration buildConfiguration() {
|
||||
|
|
Loading…
Reference in New Issue