2011-03-29 8 views
10

मैं इस परीक्षण HQL मिल गया है:हाइबरनेट एचक्यूएल क्यों बाएं जुड़ने पर एक एसक्यूएल विशिष्ट कारण है?

select distinct o from Order o left join fetch o.lineItems 

और यह एक स्पष्ट कारण के बिना एक एसक्यूएल अलग उत्पन्न करता है:

select distinct order0_.id as id61_0_, orderline1_.order_id as order1_62_1_... 

एसक्यूएल resultset हमेशा एक ही है (के साथ और एक के बिना एसक्यूएल विशिष्ट):

order id | order name | orderline id | orderline name 
---------+------------+--------------+--------------- 
     1 | foo  |   1 | foo item 
     1 | foo  |   2 | bar item 
     1 | foo  |   3 | test item 
     2 | empty  |   NULL | NULL 
     3 | bar  |   4 | qwerty item 
     3 | bar  |   5 | asdfgh item 

हाइबरनेट एसक्यूएल को अलग क्यों उत्पन्न करता है? एसक्यूएल विशिष्ट कोई समझ नहीं लेता है और क्वेरी को आवश्यकतानुसार धीमा कर देता है। यह FAQ जो कहा गया है कि सिर्फ परिणाम ट्रांसफार्मर के लिए एक शॉर्टकट है इस मामले में HQL अलग के विपरीत है:

session.createQuery ("आदेश से ओ अलग चयन ओ बाईं o.lineItems लाने में शामिल होने के")।सूची();

ऐसा लगता है कि आप यहां SQL DISTINCT कीवर्ड का उपयोग कर रहे हैं। बेशक, यह एसक्यूएल नहीं है, यह एचक्यूएल है। इस मामले में, यह परिणाम ट्रांसफॉर्मर के लिए सिर्फ एक शॉर्टकट है। हां, अन्य मामलों में एक एचक्यूएल विशिष्ट सीधे एसक्यूएल डिस्टिंट में अनुवाद करेगा। इस मामले में नहीं: आप SQL स्तर पर डुप्लीकेट को फ़िल्टर नहीं कर सकते हैं, किसी उत्पाद की प्रकृति/शामिल होने से इनकार करते हैं - आप डुप्लिकेट चाहते हैं या आपको आवश्यक सभी डेटा नहीं मिलते हैं। हाँ यह "अलग" कीवर्ड का उपयोग करता है लेकिन रास्ते में नहीं मुझे लगता है कि आप के लिए यह उम्मीद कर रहे हैं (या तरीका है कि -

धन्यवाद

+0

सूची या सेट के रूप में कैसे लाइनिटैम मैप किए जाते हैं? सबसे पहले डुप्लिकेट और विशिष्ट समझ में आता है, उत्तरार्द्ध नहीं है। – Osw

+0

सेट, जैसे FAQ में। – cherouvim

+0

मुझे इस मुद्दे का भी सामना करना पड़ा है। और Postgres इस अनावश्यक विशिष्ट को अनुकूलित करने के लिए प्रतीत नहीं होता है, इसलिए यह विशिष्ट निश्चित रूप से Postgres डेटाबेस के लिए निष्पादन योजना को प्रभावित करेगा। मेरा मानना ​​है कि यह हाइबरनेट के साथ बग है। – vbezhenar

उत्तर

3

एसक्यूएल बयान है कि हाइबरनेट उत्पन्न करता है को करीब से देख लो हाइबरनेट एफएक्यू का अर्थ है) यानी "विशिष्ट" या "अद्वितीय" ऑर्डर का एक सेट वापस करने के लिए।

यह अलग-अलग ऑर्डर देने के लिए अलग-अलग कीवर्ड का उपयोग नहीं करता है, क्योंकि उस SQL ​​क्वेरी में यह समझ में नहीं आता है कि आपने जो भी निर्दिष्ट किया है उस पर विचार करने पर विचार करें।

परिणामी एसक्यूएल सेट को अभी भी परिणाम ट्रांसफॉर्मर द्वारा प्रोसेसिंग की आवश्यकता है, स्पष्ट रूप से एसक्यूएल सेट में डुप्लिकेट ऑर्डर हैं। यही कारण है कि वे कहते हैं कि एचक्यूएल अलग-अलग कीवर्ड सीधे SQL विशिष्ट कीवर्ड पर मैप नहीं करता है।

+0

यह एक एसक्यूएल विशिष्ट उत्पन्न करता है (एक परिणाम ट्रान्सफॉर्मर के साथ)। एसक्यूएल विशिष्ट कोई समझ नहीं लेता है और क्वेरी को आवश्यकतानुसार धीमा कर देता है। – cherouvim

+1

शायद यह एक अलग मुद्दा है। यदि आप सोचते हैं कि प्रत्येक जेनरेट की गई एसक्यूएल क्वेरी इष्टतम होनी चाहिए, तो यह बहुत अधिक उम्मीद कर सकता है, यह वास्तव में हाइबरनेट का उद्देश्य नहीं है। –

+1

कृपया प्रश्न फिर से पढ़ें। यह मेरी एकमात्र और सटीक समस्या है। यह दस्तावेज कहता है कि यह क्या विरोधाभास करता है। धन्यवाद। – cherouvim

1

मुझे एक ही समस्या थी और मुझे लगता है कि यह एक हाइबरनेट समस्या है (कोई बग नहीं है क्योंकि कोड विफल नहीं होता है)। हालांकि, मुझे यह सुनिश्चित करने के लिए गहराई से खोदना होगा कि यह एक मुद्दा है।

हाइबरनेट (कम से कम संस्करण 4 में जो संस्करण मैं अपने प्रोजेक्ट पर काम कर रहा हूं, विशेष रूप से 4.3.11) एसपीआई की अवधारणा का उपयोग करता है, लंबी कहानी छोटी: फ्रेमवर्क को बढ़ाने या संशोधित करने के लिए एपीआई की तरह।

मैं वर्गों org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory को बदलने के लिए इस सुविधा का लाभ ले लिया और org.hibernate.hql.internal.ast.QueryTranslatorImpl (इस वर्ग हाइबरनेट और प्रतिनिधियों ने कहा जाता है SQL क्वेरी पैदा करने का काम) (यह जो org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory द्वारा कहा जाता है और उत्पन्न करता है एक आंतरिक वर्ग की तरह है वास्तविक एसक्यूएल क्वेरी)।

org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory के लिए रिप्लेसमेंट:

package org.hibernate.hql.internal.ast; 

import java.util.Map; 
import org.hibernate.engine.query.spi.EntityGraphQueryHint; 
import org.hibernate.engine.spi.SessionFactoryImplementor; 
import org.hibernate.hql.spi.QueryTranslator; 

public class NoDistinctInSQLASTQueryTranslatorFactory extends ASTQueryTranslatorFactory { 

    @Override 
    public QueryTranslator createQueryTranslator(String queryIdentifier, String queryString, Map filters, SessionFactoryImplementor factory, EntityGraphQueryHint entityGraphQueryHint) { 
     return new NoDistinctInSQLQueryTranslatorImpl(queryIdentifier, queryString, filters, factory, entityGraphQueryHint); 
    } 

} 

org.hibernate.hql.internal.ast.QueryTranslatorImpl के लिए रिप्लेसमेंट:

package org.hibernate.hql.internal.ast; 

import java.io.Serializable; 
import java.util.ArrayList; 
import java.util.Collections; 
import java.util.HashMap; 
import java.util.Iterator; 
import java.util.List; 
import java.util.Map; 
import java.util.Set; 

import org.hibernate.HibernateException; 
import org.hibernate.MappingException; 
import org.hibernate.QueryException; 
import org.hibernate.ScrollableResults; 
import org.hibernate.engine.query.spi.EntityGraphQueryHint; 
import org.hibernate.engine.spi.QueryParameters; 
import org.hibernate.engine.spi.RowSelection; 
import org.hibernate.engine.spi.SessionFactoryImplementor; 
import org.hibernate.engine.spi.SessionImplementor; 
import org.hibernate.event.spi.EventSource; 
import org.hibernate.hql.internal.QueryExecutionRequestException; 
import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes; 
import org.hibernate.hql.internal.antlr.HqlTokenTypes; 
import org.hibernate.hql.internal.antlr.SqlTokenTypes; 
import org.hibernate.hql.internal.ast.exec.BasicExecutor; 
import org.hibernate.hql.internal.ast.exec.DeleteExecutor; 
import org.hibernate.hql.internal.ast.exec.MultiTableDeleteExecutor; 
import org.hibernate.hql.internal.ast.exec.MultiTableUpdateExecutor; 
import org.hibernate.hql.internal.ast.exec.StatementExecutor; 
import org.hibernate.hql.internal.ast.tree.AggregatedSelectExpression; 
import org.hibernate.hql.internal.ast.tree.FromElement; 
import org.hibernate.hql.internal.ast.tree.InsertStatement; 
import org.hibernate.hql.internal.ast.tree.QueryNode; 
import org.hibernate.hql.internal.ast.tree.Statement; 
import org.hibernate.hql.internal.ast.util.ASTPrinter; 
import org.hibernate.hql.internal.ast.util.ASTUtil; 
import org.hibernate.hql.internal.ast.util.NodeTraverser; 
import org.hibernate.hql.spi.FilterTranslator; 
import org.hibernate.hql.spi.ParameterTranslations; 
import org.hibernate.internal.CoreMessageLogger; 
import org.hibernate.internal.util.ReflectHelper; 
import org.hibernate.internal.util.StringHelper; 
import org.hibernate.internal.util.collections.IdentitySet; 
import org.hibernate.loader.hql.QueryLoader; 
import org.hibernate.param.ParameterSpecification; 
import org.hibernate.persister.entity.Queryable; 
import org.hibernate.type.Type; 

import org.jboss.logging.Logger; 

import antlr.ANTLRException; 
import antlr.RecognitionException; 
import antlr.TokenStreamException; 
import antlr.collections.AST; 

/** 
* A QueryTranslator that uses an Antlr-based parser. 
* 
* @author Joshua Davis ([email protected]) 
*/ 
public class NoDistinctInSQLQueryTranslatorImpl extends QueryTranslatorImpl implements FilterTranslator { 

    private static final CoreMessageLogger LOG = Logger.getMessageLogger(
      CoreMessageLogger.class, 
      QueryTranslatorImpl.class.getName() 
    ); 

    private SessionFactoryImplementor factory; 

    private final String queryIdentifier; 
    private String hql; 
    private boolean shallowQuery; 
    private Map tokenReplacements; 

    //TODO:this is only needed during compilation .. can we eliminate the instvar? 
    private Map enabledFilters; 

    private boolean compiled; 
    private QueryLoader queryLoader; 
    private StatementExecutor statementExecutor; 

    private Statement sqlAst; 
    private String sql; 

    private ParameterTranslations paramTranslations; 
    private List<ParameterSpecification> collectedParameterSpecifications; 

    private EntityGraphQueryHint entityGraphQueryHint; 

    /** 
    * Creates a new AST-based query translator. 
    * 
    * @param queryIdentifier The query-identifier (used in stats collection) 
    * @param query The hql query to translate 
    * @param enabledFilters Currently enabled filters 
    * @param factory The session factory constructing this translator instance. 
    */ 
    public NoDistinctInSQLQueryTranslatorImpl(
      String queryIdentifier, 
      String query, 
      Map enabledFilters, 
      SessionFactoryImplementor factory) { 
     super(queryIdentifier, query, enabledFilters, factory); 
     this.queryIdentifier = queryIdentifier; 
     this.hql = query; 
     this.compiled = false; 
     this.shallowQuery = false; 
     this.enabledFilters = enabledFilters; 
     this.factory = factory; 
    } 

    public NoDistinctInSQLQueryTranslatorImpl(
      String queryIdentifier, 
      String query, 
      Map enabledFilters, 
      SessionFactoryImplementor factory, 
      EntityGraphQueryHint entityGraphQueryHint) { 
     this(queryIdentifier, query, enabledFilters, factory); 
     this.entityGraphQueryHint = entityGraphQueryHint; 
    } 

    /** 
    * Compile a "normal" query. This method may be called multiple times. 
    * Subsequent invocations are no-ops. 
    * 
    * @param replacements Defined query substitutions. 
    * @param shallow Does this represent a shallow (scalar or entity-id) 
    * select? 
    * @throws QueryException There was a problem parsing the query string. 
    * @throws MappingException There was a problem querying defined mappings. 
    */ 
    @Override 
    public void compile(
      Map replacements, 
      boolean shallow) throws QueryException, MappingException { 
     doCompile(replacements, shallow, null); 
    } 

    /** 
    * Compile a filter. This method may be called multiple times. Subsequent 
    * invocations are no-ops. 
    * 
    * @param collectionRole the role name of the collection used as the basis 
    * for the filter. 
    * @param replacements Defined query substitutions. 
    * @param shallow Does this represent a shallow (scalar or entity-id) 
    * select? 
    * @throws QueryException There was a problem parsing the query string. 
    * @throws MappingException There was a problem querying defined mappings. 
    */ 
    @Override 
    public void compile(
      String collectionRole, 
      Map replacements, 
      boolean shallow) throws QueryException, MappingException { 
     doCompile(replacements, shallow, collectionRole); 
    } 

    /** 
    * Performs both filter and non-filter compiling. 
    * 
    * @param replacements Defined query substitutions. 
    * @param shallow Does this represent a shallow (scalar or entity-id) 
    * select? 
    * @param collectionRole the role name of the collection used as the basis 
    * for the filter, NULL if this is not a filter. 
    */ 
    private synchronized void doCompile(Map replacements, boolean shallow, String collectionRole) { 
     // If the query is already compiled, skip the compilation. 
     if (compiled) { 
      LOG.debug("compile() : The query is already compiled, skipping..."); 
      return; 
     } 

     // Remember the parameters for the compilation. 
     this.tokenReplacements = replacements; 
     if (tokenReplacements == null) { 
      tokenReplacements = new HashMap(); 
     } 
     this.shallowQuery = shallow; 

     try { 
      // PHASE 1 : Parse the HQL into an AST. 
      final HqlParser parser = parse(true); 

      // PHASE 2 : Analyze the HQL AST, and produce an SQL AST. 
      final HqlSqlWalker w = analyze(parser, collectionRole); 

      sqlAst = (Statement) w.getAST(); 

      // at some point the generate phase needs to be moved out of here, 
      // because a single object-level DML might spawn multiple SQL DML 
      // command executions. 
      // 
      // Possible to just move the sql generation for dml stuff, but for 
      // consistency-sake probably best to just move responsiblity for 
      // the generation phase completely into the delegates 
      // (QueryLoader/StatementExecutor) themselves. Also, not sure why 
      // QueryLoader currently even has a dependency on this at all; does 
      // it need it? Ideally like to see the walker itself given to the delegates directly... 
      if (sqlAst.needsExecutor()) { 
       statementExecutor = buildAppropriateStatementExecutor(w); 
      } else { 
       // PHASE 3 : Generate the SQL. 
       generate((QueryNode) sqlAst); 
       queryLoader = new QueryLoader(this, factory, w.getSelectClause()); 
      } 

      compiled = true; 
     } catch (QueryException qe) { 
      if (qe.getQueryString() == null) { 
       throw qe.wrapWithQueryString(hql); 
      } else { 
       throw qe; 
      } 
     } catch (RecognitionException e) { 
      // we do not actually propagate ANTLRExceptions as a cause, so 
      // log it here for diagnostic purposes 
      LOG.trace("Converted antlr.RecognitionException", e); 
      throw QuerySyntaxException.convert(e, hql); 
     } catch (ANTLRException e) { 
      // we do not actually propagate ANTLRExceptions as a cause, so 
      // log it here for diagnostic purposes 
      LOG.trace("Converted antlr.ANTLRException", e); 
      throw new QueryException(e.getMessage(), hql); 
     } 

     //only needed during compilation phase... 
     this.enabledFilters = null; 
    } 

    private void generate(AST sqlAst) throws QueryException, RecognitionException { 
     if (sql == null) { 
      final SqlGenerator gen = new SqlGenerator(factory); 
      gen.statement(sqlAst); 
      sql = gen.getSQL(); 
      //Hack: The distinct operator is removed from the sql 
      //string to avoid executing a distinct query in the db server when 
      //the distinct is used in hql. 
      sql = sql.replace("distinct", ""); 
      if (LOG.isDebugEnabled()) { 
       LOG.debugf("HQL: %s", hql); 
       LOG.debugf("SQL: %s", sql); 
      } 
      gen.getParseErrorHandler().throwQueryException(); 
      collectedParameterSpecifications = gen.getCollectedParameters(); 
     } 
    } 

    private static final ASTPrinter SQL_TOKEN_PRINTER = new ASTPrinter(SqlTokenTypes.class); 

    private HqlSqlWalker analyze(HqlParser parser, String collectionRole) throws QueryException, RecognitionException { 
     final HqlSqlWalker w = new HqlSqlWalker(this, factory, parser, tokenReplacements, collectionRole); 
     final AST hqlAst = parser.getAST(); 

     // Transform the tree. 
     w.statement(hqlAst); 

     if (LOG.isDebugEnabled()) { 
      LOG.debug(SQL_TOKEN_PRINTER.showAsString(w.getAST(), "--- SQL AST ---")); 
     } 

     w.getParseErrorHandler().throwQueryException(); 

     return w; 
    } 

    private HqlParser parse(boolean filter) throws TokenStreamException, RecognitionException { 
     // Parse the query string into an HQL AST. 
     final HqlParser parser = HqlParser.getInstance(hql); 
     parser.setFilter(filter); 

     LOG.debugf("parse() - HQL: %s", hql); 
     parser.statement(); 

     final AST hqlAst = parser.getAST(); 

     final NodeTraverser walker = new NodeTraverser(new JavaConstantConverter()); 
     walker.traverseDepthFirst(hqlAst); 

     showHqlAst(hqlAst); 

     parser.getParseErrorHandler().throwQueryException(); 
     return parser; 
    } 

    private static final ASTPrinter HQL_TOKEN_PRINTER = new ASTPrinter(HqlTokenTypes.class); 

    @Override 
    void showHqlAst(AST hqlAst) { 
     if (LOG.isDebugEnabled()) { 
      LOG.debug(HQL_TOKEN_PRINTER.showAsString(hqlAst, "--- HQL AST ---")); 
     } 
    } 

    private void errorIfDML() throws HibernateException { 
     if (sqlAst.needsExecutor()) { 
      throw new QueryExecutionRequestException("Not supported for DML operations", hql); 
     } 
    } 

    private void errorIfSelect() throws HibernateException { 
     if (!sqlAst.needsExecutor()) { 
      throw new QueryExecutionRequestException("Not supported for select queries", hql); 
     } 
    } 

    @Override 
    public String getQueryIdentifier() { 
     return queryIdentifier; 
    } 

    @Override 
    public Statement getSqlAST() { 
     return sqlAst; 
    } 

    private HqlSqlWalker getWalker() { 
     return sqlAst.getWalker(); 
    } 

    /** 
    * Types of the return values of an <tt>iterate()</tt> style query. 
    * 
    * @return an array of <tt>Type</tt>s. 
    */ 
    @Override 
    public Type[] getReturnTypes() { 
     errorIfDML(); 
     return getWalker().getReturnTypes(); 
    } 

    @Override 
    public String[] getReturnAliases() { 
     errorIfDML(); 
     return getWalker().getReturnAliases(); 
    } 

    @Override 
    public String[][] getColumnNames() { 
     errorIfDML(); 
     return getWalker().getSelectClause().getColumnNames(); 
    } 

    @Override 
    public Set<Serializable> getQuerySpaces() { 
     return getWalker().getQuerySpaces(); 
    } 

    @Override 
    public List list(SessionImplementor session, QueryParameters queryParameters) 
      throws HibernateException { 
     // Delegate to the QueryLoader... 
     errorIfDML(); 

     final QueryNode query = (QueryNode) sqlAst; 
     final boolean hasLimit = queryParameters.getRowSelection() != null && queryParameters.getRowSelection().definesLimits(); 
     final boolean needsDistincting = (query.getSelectClause().isDistinct() || hasLimit) && containsCollectionFetches(); 

     QueryParameters queryParametersToUse; 
     if (hasLimit && containsCollectionFetches()) { 
      LOG.firstOrMaxResultsSpecifiedWithCollectionFetch(); 
      RowSelection selection = new RowSelection(); 
      selection.setFetchSize(queryParameters.getRowSelection().getFetchSize()); 
      selection.setTimeout(queryParameters.getRowSelection().getTimeout()); 
      queryParametersToUse = queryParameters.createCopyUsing(selection); 
     } else { 
      queryParametersToUse = queryParameters; 
     } 

     List results = queryLoader.list(session, queryParametersToUse); 

     if (needsDistincting) { 
      int includedCount = -1; 
      // NOTE : firstRow is zero-based 
      int first = !hasLimit || queryParameters.getRowSelection().getFirstRow() == null 
        ? 0 
        : queryParameters.getRowSelection().getFirstRow(); 
      int max = !hasLimit || queryParameters.getRowSelection().getMaxRows() == null 
        ? -1 
        : queryParameters.getRowSelection().getMaxRows(); 
      List tmp = new ArrayList(); 
      IdentitySet distinction = new IdentitySet(); 
      for (final Object result : results) { 
       if (!distinction.add(result)) { 
        continue; 
       } 
       includedCount++; 
       if (includedCount < first) { 
        continue; 
       } 
       tmp.add(result); 
       // NOTE : (max - 1) because first is zero-based while max is not... 
       if (max >= 0 && (includedCount - first) >= (max - 1)) { 
        break; 
       } 
      } 
      results = tmp; 
     } 

     return results; 
    } 

    /** 
    * Return the query results as an iterator 
    */ 
    @Override 
    public Iterator iterate(QueryParameters queryParameters, EventSource session) 
      throws HibernateException { 
     // Delegate to the QueryLoader... 
     errorIfDML(); 
     return queryLoader.iterate(queryParameters, session); 
    } 

    /** 
    * Return the query results, as an instance of <tt>ScrollableResults</tt> 
    */ 
    @Override 
    public ScrollableResults scroll(QueryParameters queryParameters, SessionImplementor session) 
      throws HibernateException { 
     // Delegate to the QueryLoader... 
     errorIfDML(); 
     return queryLoader.scroll(queryParameters, session); 
    } 

    @Override 
    public int executeUpdate(QueryParameters queryParameters, SessionImplementor session) 
      throws HibernateException { 
     errorIfSelect(); 
     return statementExecutor.execute(queryParameters, session); 
    } 

    /** 
    * The SQL query string to be called; implemented by all subclasses 
    */ 
    @Override 
    public String getSQLString() { 
     return sql; 
    } 

    @Override 
    public List<String> collectSqlStrings() { 
     ArrayList<String> list = new ArrayList<>(); 
     if (isManipulationStatement()) { 
      String[] sqlStatements = statementExecutor.getSqlStatements(); 
      Collections.addAll(list, sqlStatements); 
     } else { 
      list.add(sql); 
     } 
     return list; 
    } 

    // -- Package local methods for the QueryLoader delegate -- 
    @Override 
    public boolean isShallowQuery() { 
     return shallowQuery; 
    } 

    @Override 
    public String getQueryString() { 
     return hql; 
    } 

    @Override 
    public Map getEnabledFilters() { 
     return enabledFilters; 
    } 

    @Override 
    public int[] getNamedParameterLocs(String name) { 
     return getWalker().getNamedParameterLocations(name); 
    } 

    @Override 
    public boolean containsCollectionFetches() { 
     errorIfDML(); 
     List collectionFetches = ((QueryNode) sqlAst).getFromClause().getCollectionFetches(); 
     return collectionFetches != null && collectionFetches.size() > 0; 
    } 

    @Override 
    public boolean isManipulationStatement() { 
     return sqlAst.needsExecutor(); 
    } 

    @Override 
    public void validateScrollability() throws HibernateException { 
     // Impl Note: allows multiple collection fetches as long as the 
     // entire fecthed graph still "points back" to a single 
     // root entity for return 

     errorIfDML(); 

     final QueryNode query = (QueryNode) sqlAst; 

     // If there are no collection fetches, then no further checks are needed 
     List collectionFetches = query.getFromClause().getCollectionFetches(); 
     if (collectionFetches.isEmpty()) { 
      return; 
     } 

     // A shallow query is ok (although technically there should be no fetching here...) 
     if (isShallowQuery()) { 
      return; 
     } 

     // Otherwise, we have a non-scalar select with defined collection fetch(es). 
     // Make sure that there is only a single root entity in the return (no tuples) 
     if (getReturnTypes().length > 1) { 
      throw new HibernateException("cannot scroll with collection fetches and returned tuples"); 
     } 

     FromElement owner = null; 
     for (Object o : query.getSelectClause().getFromElementsForLoad()) { 
      // should be the first, but just to be safe... 
      final FromElement fromElement = (FromElement) o; 
      if (fromElement.getOrigin() == null) { 
       owner = fromElement; 
       break; 
      } 
     } 

     if (owner == null) { 
      throw new HibernateException("unable to locate collection fetch(es) owner for scrollability checks"); 
     } 

     // This is not strictly true. We actually just need to make sure that 
     // it is ordered by root-entity PK and that that order-by comes before 
     // any non-root-entity ordering... 
     AST primaryOrdering = query.getOrderByClause().getFirstChild(); 
     if (primaryOrdering != null) { 
      // TODO : this is a bit dodgy, come up with a better way to check this (plus see above comment) 
      String[] idColNames = owner.getQueryable().getIdentifierColumnNames(); 
      String expectedPrimaryOrderSeq = StringHelper.join(
        ", ", 
        StringHelper.qualify(owner.getTableAlias(), idColNames) 
      ); 
      if (!primaryOrdering.getText().startsWith(expectedPrimaryOrderSeq)) { 
       throw new HibernateException("cannot scroll results with collection fetches which are not ordered primarily by the root entity's PK"); 
      } 
     } 
    } 

    private StatementExecutor buildAppropriateStatementExecutor(HqlSqlWalker walker) { 
     final Statement statement = (Statement) walker.getAST(); 
     switch (walker.getStatementType()) { 
      case HqlSqlTokenTypes.DELETE: { 
       final FromElement fromElement = walker.getFinalFromClause().getFromElement(); 
       final Queryable persister = fromElement.getQueryable(); 
       if (persister.isMultiTable()) { 
        return new MultiTableDeleteExecutor(walker); 
       } else { 
        return new DeleteExecutor(walker, persister); 
       } 
      } 
      case HqlSqlTokenTypes.UPDATE: { 
       final FromElement fromElement = walker.getFinalFromClause().getFromElement(); 
       final Queryable persister = fromElement.getQueryable(); 
       if (persister.isMultiTable()) { 
        // even here, if only properties mapped to the "base table" are referenced 
        // in the set and where clauses, this could be handled by the BasicDelegate. 
        // TODO : decide if it is better performance-wise to doAfterTransactionCompletion that check, or to simply use the MultiTableUpdateDelegate 
        return new MultiTableUpdateExecutor(walker); 
       } else { 
        return new BasicExecutor(walker, persister); 
       } 
      } 
      case HqlSqlTokenTypes.INSERT: 
       return new BasicExecutor(walker, ((InsertStatement) statement).getIntoClause().getQueryable()); 
      default: 
       throw new QueryException("Unexpected statement type"); 
     } 
    } 

    @Override 
    public ParameterTranslations getParameterTranslations() { 
     if (paramTranslations == null) { 
      paramTranslations = new ParameterTranslationsImpl(getWalker().getParameters()); 
     } 
     return paramTranslations; 
    } 

    @Override 
    public List<ParameterSpecification> getCollectedParameterSpecifications() { 
     return collectedParameterSpecifications; 
    } 

    @Override 
    public Class getDynamicInstantiationResultType() { 
     AggregatedSelectExpression aggregation = queryLoader.getAggregatedSelectExpression(); 
     return aggregation == null ? null : aggregation.getAggregationResultType(); 
    } 

    public static class JavaConstantConverter implements NodeTraverser.VisitationStrategy { 

     private AST dotRoot; 

     @Override 
     public void visit(AST node) { 
      if (dotRoot != null) { 
       // we are already processing a dot-structure 
       if (ASTUtil.isSubtreeChild(dotRoot, node)) { 
        return; 
       } 
       // we are now at a new tree level 
       dotRoot = null; 
      } 

      if (node.getType() == HqlTokenTypes.DOT) { 
       dotRoot = node; 
       handleDotStructure(dotRoot); 
      } 
     } 

     private void handleDotStructure(AST dotStructureRoot) { 
      final String expression = ASTUtil.getPathText(dotStructureRoot); 
      final Object constant = ReflectHelper.getConstantValue(expression); 
      if (constant != null) { 
       dotStructureRoot.setFirstChild(null); 
       dotStructureRoot.setType(HqlTokenTypes.JAVA_CONSTANT); 
       dotStructureRoot.setText(expression); 
      } 
     } 
    } 

    @Override 
    public EntityGraphQueryHint getEntityGraphQueryHint() { 
     return entityGraphQueryHint; 
    } 

    @Override 
    public void setEntityGraphQueryHint(EntityGraphQueryHint entityGraphQueryHint) { 
     this.entityGraphQueryHint = entityGraphQueryHint; 
    } 
} 

आप कोड प्रवाह का पालन करें तो आप देखेंगे मैं सिर्फ विधि private void generate(AST sqlAst) throws QueryException, RecognitionException संशोधित और कहा कि इस प्रकार मैंने किया निम्न पंक्तियां:

//Hack: The distinct keywordis removed from the sql string to 
//avoid executing a distinct query in the DBMS when the distinct 
//is used in hql. 
sql = sql.replace("distinct", ""); 

मैं इस कोड के साथ क्या करता हूं जेनरेट की गई SQL क्वेरी से अलग कीवर्ड को निकालना है।

ऊपर कक्षाएं बनाने के बाद, मैं हाइबरनेट विन्यास फ़ाइल में निम्न पंक्ति कहा:

<property name="hibernate.query.factory_class">org.hibernate.hql.internal.ast.NoDistinctInSQLASTQueryTranslatorFactory</property> 

इस लाइन अलग कीवर्ड के बिना HQL प्रश्नों को पार्स और SQL प्रश्नों उत्पन्न करने के लिए अपने कस्टम वर्ग का उपयोग करने के हाइबरनेट बताता है। नोटिस मैंने उसी कस्टम पैकेज में अपनी कस्टम कक्षाएं बनाईं जहां मूल एचक्यूएल पार्सर रहता है।

संबंधित मुद्दे