[hibernate-issues] [Hibernate-JIRA] Commented: (HHH-5645) Criteria.createAlias with specified criterion results in wrong parameters passed into SQL statement

Fekete Kamosh (JIRA) noreply at atlassian.com
Mon Sep 12 16:40:06 EDT 2011


    [ http://opensource.atlassian.com/projects/hibernate/browse/HHH-5645?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=43502#comment-43502 ] 

Fekete Kamosh commented on HHH-5645:
------------------------------------

Hi,
I can offer some temporary solution for this. It is based on Hibernate version 3.5.6.
It might be OK if you use method org.hibernate.Criteria.list() to get results of aliased entities. 
I tried this "patched code" on the test I provided for this fault and it seems fine. 


You need to alter implementation of two Hibernate classes. I would recommend downloading hibernate sources and copy these two into independent project -> change code -> compile them (use hibernate3.jar in classpath to enable compiling) -> create jar file (with two altered classes) -> add this jar file into classpath before standard hibernate3.jar

org.hibernate.loader.criteria.CriteriaQueryTranslator
change method 
public List list(SessionImplementor session) 
	throws HibernateException {
		return list( session, translator.getQueryParameters(), querySpaces, resultTypes );

	}


to
  public List list(SessionImplementor session) 
  throws HibernateException {
    // Query parameters for subqueries are sorted by order specified in userAliases 
    // This is temporary patch for Hibernate fault HHH-5645
    // http://opensource.atlassian.com/projects/hibernate/browse/HHH-5645
    return list( session, translator.getQueryParameters(userAliases), querySpaces, resultTypes );
  }


org.hibernate.loader.criteria.CriteriaQueryTranslator
add 
import java.util.Collection;


change method
	public QueryParameters getQueryParameters() {
		LockOptions lockOptions = new LockOptions();
		RowSelection selection = new RowSelection();
		selection.setFirstRow( rootCriteria.getFirstResult() );
		selection.setMaxRows( rootCriteria.getMaxResults() );
		selection.setTimeout( rootCriteria.getTimeout() );
		selection.setFetchSize( rootCriteria.getFetchSize() );

		Iterator iter = rootCriteria.getLockModes().entrySet().iterator();
		while ( iter.hasNext() ) {
			Map.Entry me = ( Map.Entry ) iter.next();
			final Criteria subcriteria = getAliasedCriteria( ( String ) me.getKey() );
			lockOptions.setAliasSpecificLockMode( getSQLAlias( subcriteria ), (LockMode)me.getValue() );
		}
		List values = new ArrayList();
		List types = new ArrayList();
		iter = rootCriteria.iterateSubcriteria();
		while ( iter.hasNext() ) {
			CriteriaImpl.Subcriteria subcriteria = ( CriteriaImpl.Subcriteria ) iter.next();
			LockMode lm = subcriteria.getLockMode();
			if ( lm != null ) {
				lockOptions.setAliasSpecificLockMode( getSQLAlias( subcriteria ), lm );
			}
			if ( subcriteria.getWithClause() != null )
			{
				TypedValue[] tv = subcriteria.getWithClause().getTypedValues( subcriteria, this );
				for ( int i = 0; i < tv.length; i++ ) {
					values.add( tv[i].getValue() );
					types.add( tv[i].getType() );
				}
			}
		}

		// Type and value gathering for the WHERE clause needs to come AFTER lock mode gathering,
		// because the lock mode gathering loop now contains join clauses which can contain
		// parameter bindings (as in the HQL WITH clause).
		iter = rootCriteria.iterateExpressionEntries();
		while ( iter.hasNext() ) {
			CriteriaImpl.CriterionEntry ce = ( CriteriaImpl.CriterionEntry ) iter.next();
			TypedValue[] tv = ce.getCriterion().getTypedValues( ce.getCriteria(), this );
			for ( int i = 0; i < tv.length; i++ ) {
				values.add( tv[i].getValue() );
				types.add( tv[i].getType() );
			}
		}

		Object[] valueArray = values.toArray();
		Type[] typeArray = ArrayHelper.toTypeArray( types );
		return new QueryParameters(
				typeArray,
		        valueArray,
		        lockOptions,
		        selection,
		        rootCriteria.isReadOnlyInitialized(),
		        ( rootCriteria.isReadOnlyInitialized() ? rootCriteria.isReadOnly() : false ),
		        rootCriteria.getCacheable(),
		        rootCriteria.getCacheRegion(),
		        rootCriteria.getComment(),
		        rootCriteria.isLookupByNaturalKey(),
		        rootCriteria.getResultTransformer()
		);
	}

to new implementation and add new overloaded method getQueryParameters

  public QueryParameters getQueryParameters() {
    return getQueryParameters(null);
  }
  
  /**
   * Overloaded {@link #getQueryParameters()} with possibility to pass
   *  aliases in the order they appear in resulting SQL
   * @param orderedAliases Ordered aliases as they appear in resulting SQL 
   * @return Query parameters with ordered parameters
   */
  public QueryParameters getQueryParameters(String[] orderedAliases) {
    LockOptions lockOptions = new LockOptions();
    RowSelection selection = new RowSelection();
    selection.setFirstRow( rootCriteria.getFirstResult() );
    selection.setMaxRows( rootCriteria.getMaxResults() );
    selection.setTimeout( rootCriteria.getTimeout() );
    selection.setFetchSize( rootCriteria.getFetchSize() );

    Iterator iter = rootCriteria.getLockModes().entrySet().iterator();
    while ( iter.hasNext() ) {
      Map.Entry me = ( Map.Entry ) iter.next();
      final Criteria subcriteria = getAliasedCriteria( ( String ) me.getKey() );
      lockOptions.setAliasSpecificLockMode( getSQLAlias( subcriteria ), (LockMode)me.getValue() );
    }
    List values = new ArrayList();
    List types = new ArrayList();
    // If order of aliases is specified, sort subcriterias by sorted aliases
    iter = orderedAliases == null ? rootCriteria.iterateSubcriteria() : iterSortedSubcriteria(rootCriteria.iterateSubcriteria(), orderedAliases);
    while ( iter.hasNext() ) {
      CriteriaImpl.Subcriteria subcriteria = ( CriteriaImpl.Subcriteria ) iter.next();
      LockMode lm = subcriteria.getLockMode();
      if ( lm != null ) {
        lockOptions.setAliasSpecificLockMode( getSQLAlias( subcriteria ), lm );
      }
      if ( subcriteria.getWithClause() != null )
      {
        TypedValue[] tv = subcriteria.getWithClause().getTypedValues( subcriteria, this );
        for ( int i = 0; i < tv.length; i++ ) {
          values.add( tv[i].getValue() );
          types.add( tv[i].getType() );
        }
      }
    }

    // Type and value gathering for the WHERE clause needs to come AFTER lock mode gathering,
    // because the lock mode gathering loop now contains join clauses which can contain
    // parameter bindings (as in the HQL WITH clause).
    iter = rootCriteria.iterateExpressionEntries();
    while ( iter.hasNext() ) {
      CriteriaImpl.CriterionEntry ce = ( CriteriaImpl.CriterionEntry ) iter.next();
      TypedValue[] tv = ce.getCriterion().getTypedValues( ce.getCriteria(), this );
      for ( int i = 0; i < tv.length; i++ ) {
        values.add( tv[i].getValue() );
        types.add( tv[i].getType() );
      }
    }

    Object[] valueArray = values.toArray();
    Type[] typeArray = ArrayHelper.toTypeArray( types );
    return new QueryParameters(
        typeArray,
            valueArray,
            lockOptions,
            selection,
            rootCriteria.isReadOnlyInitialized(),
            ( rootCriteria.isReadOnlyInitialized() ? rootCriteria.isReadOnly() : false ),
            rootCriteria.getCacheable(),
            rootCriteria.getCacheRegion(),
            rootCriteria.getComment(),
            rootCriteria.isLookupByNaturalKey(),
            rootCriteria.getResultTransformer()
    );
  }



add new method
  /**
   * Returns iterator of sorted subcriteria. 
   * Subcriteria with aliases specified in <code>orderedAliases</code> go first 
   * @param iterSubcriteria Subcriteria iterator
   * @param orderedAliases Ordered names of aliases
   * @return Subcriteria iterator ordered by specified aliases
   */
  private Iterator iterSortedSubcriteria(Iterator iterSubcriteria, final String[] orderedAliases) {
    // Fill collection used only as temporary container
    Collection tempContainer = new ArrayList();
    // Fill with data of iterator
    while(iterSubcriteria.hasNext()) {
      tempContainer.add(iterSubcriteria.next());
    }
    
    // Resulting subcriteria
    List res = new ArrayList();
    
    for(String orderedAlias :orderedAliases) {
      // Lets find subcriteria with actual alias
      Iterator i = tempContainer.iterator();
      while(i.hasNext()) {
        Object obj = i.next();    
        if(obj instanceof CriteriaImpl.Subcriteria) {
          CriteriaImpl.Subcriteria subcriteria = (CriteriaImpl.Subcriteria) obj;
          if(orderedAlias.equals(subcriteria.getAlias())) {
            // Subcriteria found by its alias, lets remember it in resulting list
            res.add(subcriteria);
            // Remove subcriteria from temporary container
            i.remove();
          }
        }       
      }
    }
    // Add rest of criteria (which were not found using alias name) into result
    // , they will be added to the end of list 
    res.addAll(tempContainer); 
    
    return res.iterator();
  }



My code on local machine is little bit messy and this is why I do not post ideal patch. In addition I do not know what is your current version of Hibernate, sorry. 

It is a pitty that Hibernate guys do not care about reported bugs :-(

Fekete 

> Criteria.createAlias with specified criterion results in wrong parameters passed into SQL statement
> ---------------------------------------------------------------------------------------------------
>
>                 Key: HHH-5645
>                 URL: http://opensource.atlassian.com/projects/hibernate/browse/HHH-5645
>             Project: Hibernate Core
>          Issue Type: Bug
>          Components: core
>    Affects Versions: 3.5.6
>         Environment: Windows XP SP2, Hibernate 3.5.6, JRE 1.6.0_14, PostgreSQL (tested also with Sybase ASE)
>            Reporter: Fekete Kamosh
>         Attachments: aliases_test.zip
>
>
> Suppose to have tables A, B, C. 
> Relationships: A(one)=>B(many); B(one)=>C(many)
> Table A is used as main entity. 
> For tables B and C lets *establish aliases* using method 
>   _public Criteria createAlias(String associationPath, String alias, int joinType, *Criterion withClause*) throws HibernateException;_
> Each created alias has addition Criterion to force Hibernate generate clause _"on column = column and (other_criterion_condition)"._
> If aliases are created in *order B, C* everything is OK:
> Resulting SQL: 
> _SELECT this_.table_a_character AS y0_
> FROM   table_a this_
>        LEFT OUTER JOIN table_b tablebalia1_
>          ON this_.table_a_id = tablebalia1_.table_a_id
>             AND ( tablebalia1_.table_b_date = ? )
>        LEFT OUTER JOIN table_c tablecalia2_
>          ON tablebalia1_.table_b_id = tablecalia2_.table_b_id
>             AND ( tablecalia2_.table_c_boolean = ? )
> WHERE  this_.table_a_character = ?_  
> Passed parameters: 
> *[Sun Oct 10 22:09:31 CEST 2010, false, c]*
> But if aliases are created in *order C, B error occurs* as resulting SQL remains the same as already shown, but 
> Passed parameters are:
> *[false, Sun Oct 10 22:12:13 CEST 2010, c]*
> which causes SQL exception, because there are passed *parameters in wrong order to SQL statement*: 
> 43843 [main] WARN org.hibernate.util.JDBCExceptionReporter - SQL Error: 0, SQLState: 42883
> 43843 [main] ERROR org.hibernate.util.JDBCExceptionReporter - ERROR: operator does not exist: date = boolean
> *Test example* (database tables, entities, hibernate.cfg and test file) *attached.*

--
This message is automatically generated by JIRA.
For more information on JIRA, see: http://www.atlassian.com/software/jira

        


More information about the hibernate-issues mailing list