[hibernate-commits] Hibernate SVN: r10851 - in branches/Branch_3_2/Hibernate3: doc/reference/en/modules src/org/hibernate/hql/ast/tree src/org/hibernate/persister/entity src/org/hibernate/tuple/entity test/org/hibernate/test test/org/hibernate/test/idprops test/org/hibernate/test/legacy

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Tue Nov 21 12:38:50 EST 2006


Author: steve.ebersole at jboss.com
Date: 2006-11-21 12:38:43 -0500 (Tue, 21 Nov 2006)
New Revision: 10851

Added:
   branches/Branch_3_2/Hibernate3/test/org/hibernate/test/idprops/
   branches/Branch_3_2/Hibernate3/test/org/hibernate/test/idprops/IdentifierPropertyReferencesTest.java
   branches/Branch_3_2/Hibernate3/test/org/hibernate/test/idprops/LineItem.java
   branches/Branch_3_2/Hibernate3/test/org/hibernate/test/idprops/LineItemPK.java
   branches/Branch_3_2/Hibernate3/test/org/hibernate/test/idprops/Mapping.hbm.xml
   branches/Branch_3_2/Hibernate3/test/org/hibernate/test/idprops/Order.java
   branches/Branch_3_2/Hibernate3/test/org/hibernate/test/idprops/Person.java
Modified:
   branches/Branch_3_2/Hibernate3/doc/reference/en/modules/query_hql.xml
   branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/tree/DotNode.java
   branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/tree/FromElement.java
   branches/Branch_3_2/Hibernate3/src/org/hibernate/persister/entity/AbstractEntityPersister.java
   branches/Branch_3_2/Hibernate3/src/org/hibernate/persister/entity/AbstractPropertyMapping.java
   branches/Branch_3_2/Hibernate3/src/org/hibernate/persister/entity/EntityPersister.java
   branches/Branch_3_2/Hibernate3/src/org/hibernate/tuple/entity/EntityMetamodel.java
   branches/Branch_3_2/Hibernate3/test/org/hibernate/test/legacy/CustomPersister.java
Log:
HHH-1851 : relax special 'id' property handling

Modified: branches/Branch_3_2/Hibernate3/doc/reference/en/modules/query_hql.xml
===================================================================
--- branches/Branch_3_2/Hibernate3/doc/reference/en/modules/query_hql.xml	2006-11-21 16:33:52 UTC (rev 10850)
+++ branches/Branch_3_2/Hibernate3/doc/reference/en/modules/query_hql.xml	2006-11-21 17:38:43 UTC (rev 10851)
@@ -1,4 +1,4 @@
-<chapter id="queryhql">
+<chapter id="queryhql" revision="1">
     <title>HQL: The Hibernate Query Language</title>
     
     <para>
@@ -210,6 +210,42 @@
         <programlisting><![CDATA[from Cat as cat where cat.mate.name like '%s%']]></programlisting>
 	</sect1>
 
+    <sect1 id="queryhql-identifier-property">
+        <title>Refering to identifier property</title>
+
+        <para>
+            There are, generally speaking, 2 ways to refer to an entity's identifier property:
+        </para>
+        <itemizedlist spacing="compact">
+            <listitem>
+                <para>
+                    The special property (lowercase) <literal>id</literal> may be used to reference the identifier
+                    property of an entity <emphasis>provided that entity does not define a non-identifier property
+                    named id</emphasis>.
+                </para>
+            </listitem>
+            <listitem>
+                <para>
+                    If the entity defines a named identifier property, you may use that property name.
+                </para>
+            </listitem>
+        </itemizedlist>
+
+        <para>
+            References to composite identifier properties follow the same naming rules.  If the
+            entity has a non-identifier property named id, the composite identifier property can only
+            be referenced by its defined named; otherwise, the special <literal>id</literal> property
+            can be used to rerference the identifier property.
+        </para>
+
+        <para>
+            Note: this has changed significantly starting in version 3.2.2.  In previous versions,
+            <literal>id</literal> <emphasis>always</emphasis> referred to the identifier property no
+            matter what its actual name.  A ramification of that decision was that non-identifier
+            properties named <literal>id</literal> could never be referenced in Hibernate queries.
+        </para>
+    </sect1>
+
     <sect1 id="queryhql-select">
         <title>The select clause</title>
 
@@ -389,7 +425,7 @@
         
     </sect1>
 
-    <sect1 id="queryhql-where">
+    <sect1 id="queryhql-where" revision="1">
         <title>The where clause</title>
 
         <para>
@@ -448,8 +484,9 @@
 where cat.mate = mate]]></programlisting>
 
         <para>
-            The special property (lowercase) <literal>id</literal> may be used to reference the 
-            unique identifier of an object. (You may also use its property name.)
+            The special property (lowercase) <literal>id</literal> may be used to reference the
+            unique identifier of an object. See <xref linkend="queryhql-identifier-property"/>
+            for more information.
         </para>
 
         <programlisting><![CDATA[from Cat as cat where cat.id = 123
@@ -463,7 +500,8 @@
         <para>
             Properties of composite identifiers may also be used. Suppose <literal>Person</literal> 
             has a composite identifier consisting of <literal>country</literal> and 
-            <literal>medicareNumber</literal>.
+            <literal>medicareNumber</literal>.  Again, see <xref linkend="queryhql-identifier-property"/>
+            for more information regarding referencing identifier properties.
         </para>
 
         <programlisting><![CDATA[from bank.Person person
@@ -477,7 +515,7 @@
         <para>
             Once again, the second query requires no table join.
         </para>
-            
+
         <para>
             Likewise, the special property <literal>class</literal> accesses the discriminator value
             of an instance in the case of polymorphic persistence. A Java class name embedded in the 
@@ -485,17 +523,12 @@
         </para>
 
         <programlisting><![CDATA[from Cat cat where cat.class = DomesticCat]]></programlisting>
-        
+
         <para>
-            You may also specify properties of components or composite user types (and of components 
-            of components, etc). Never try to use a path-expression that ends in a property of component 
-            type (as opposed to a property of a component). For example, if <literal>store.owner</literal>
-            is an entity with a component <literal>address</literal>
+            You may also use components or composite user types, or properties of said
+            component types. See <xref linkend="queryhql-coomponents"/> for more details.
         </para>
 
-        <programlisting><![CDATA[store.owner.address.city    // okay
-store.owner.address         // error!]]></programlisting>
-
         <para>
             An "any" type has the special properties <literal>id</literal> and <literal>class</literal>,
             allowing us to express a join in the following way (where <literal>AuditLog.item</literal>
@@ -869,7 +902,7 @@
 
     </sect1>
     
-    <sect1 id="queryhql-subqueries" revision="2">
+    <sect1 id="queryhql-subqueries" revision="3">
         <title>Subqueries</title>
         
         <para>
@@ -906,33 +939,10 @@
         </para>
 
         <para>
-            For subqueries with more than one expression in the select list, you can use a tuple constructor:
+            Note that subqueries can also utilize <literal>row value constructor</literal> syntax.  See
+            <xref linkend="queryhql-tuple"/> for more details.
         </para>
         
-        <programlisting><![CDATA[from Cat as cat 
-where not ( cat.name, cat.color ) in ( 
-    select cat.name, cat.color from DomesticCat cat 
-)]]></programlisting>
-
-        <para>
-            Note that on some databases (but not Oracle or HSQL), you can use tuple constructors in other
-            contexts, for example when querying components or composite user types:
-        </para>
-
-        <programlisting><![CDATA[from Person where name = ('Gavin', 'A', 'King')]]></programlisting>
-        
-        <para>
-            Which is equivalent to the more verbose:
-        </para>
-        
-        <programlisting><![CDATA[from Person where name.first = 'Gavin' and name.initial = 'A' and name.last = 'King')]]></programlisting>
-
-        <para>
-            There are two good reasons you might not want to do this kind of thing: first, it is not 
-            completely portable between database platforms; second, the query is now dependent upon
-            the ordering of properties in the mapping document.
-        </para>
-        
     </sect1>
 
     <sect1 id="queryhql-examples">
@@ -1147,5 +1157,78 @@
 
     </sect1>
 
+    <sect1 id="queryhql-components">
+        <title>Components</title>
+
+        <para>
+            Components might be used in just about every way that simple value types can be used in HQL
+            queries.  They can appear in the <literal>select</literal> clause:
+        </para>
+
+        <programlisting><![CDATA[select p.name from from Person p]]></programlisting>
+        <programlisting><![CDATA[select p.name.first from from Person p]]></programlisting>
+
+        <para>
+            where the Person's name property is a component.  Components can also be used
+            in the <literal>where</literal> clause:
+        </para>
+
+        <programlisting><![CDATA[from from Person p where p.name = :name]]></programlisting>
+        <programlisting><![CDATA[from from Person p where p.name.first = :firstName]]></programlisting>
+
+        <para>
+            Components can also be used in the <literal>order by</literal> clause:
+        </para>
+
+        <programlisting><![CDATA[from from Person p order by p.name]]></programlisting>
+        <programlisting><![CDATA[from from Person p order by p.name.first]]></programlisting>
+
+        <para>
+            Another common use of components is in <xref linkend="queryhql-tuple">row value constructors</xref>.
+        </para>
+    </sect1>
+
+    <sect1 id="queryhql-tuple">
+        <title>Row value constructor syntax</title>
+
+        <para>
+            HQL supports the use of ANSI SQL <literal>row value constructor</literal> syntax (sometimes
+            called <literal>tuple</literal> syntax), even though the underlying database may not support
+            that notion.  Here we are generally referring to multi-valued comparisons, typically associated
+            with components.  Consider an entity Person which defines a name component:
+        </para>
+
+        <programlisting><![CDATA[from Person p where p.name.first='John' and p.name.last='Jingleheimer-Schmidt']]></programlisting>
+
+        <para>
+            That's valid syntax, although a little verbose.  It be nice to make this a bit more concise and use
+            <literal>row value constructor</literal> syntax:
+        </para>
+
+        <programlisting><![CDATA[from Person p where p.name=('John', 'Jingleheimer-Schmidt')]]></programlisting>
+
+        <para>
+            It can also be useful to specify this in the <literal>select</literal> clause:
+        </para>
+
+        <programlisting><![CDATA[select p.name from from Person p]]></programlisting>
+
+        <para>
+            Another time using <literal>row value constructor</literal> syntax can be beneficial
+            is when using subqueries needing to compare against multiple values:
+        </para>
+
+        <programlisting><![CDATA[from Cat as cat
+where not ( cat.name, cat.color ) in (
+    select cat.name, cat.color from DomesticCat cat
+)]]></programlisting>
+
+        <para>
+            One thing to consider when deciding if you want to use this syntax is that the query will
+            be dependent upon the ordering of the component sub-properties in the metadata.
+        </para>
+
+    </sect1>
+
 </chapter>
 

Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/tree/DotNode.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/tree/DotNode.java	2006-11-21 16:33:52 UTC (rev 10850)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/tree/DotNode.java	2006-11-21 17:38:43 UTC (rev 10851)
@@ -81,9 +81,9 @@
 	private FromElement impliedJoin;
 
 	/**
-	 * Sets the join type for the '.' node (JoinFragment.XXX).
+	 * Sets the join type for this '.' node structure.
 	 *
-	 * @param joinType
+	 * @param joinType The type of join to use.
 	 * @see JoinFragment
 	 */
 	public void setJoinType(int joinType) {
@@ -220,8 +220,7 @@
 	private Type prepareLhs() throws SemanticException {
 		FromReferenceNode lhs = getLhs();
 		lhs.prepareForDot( propertyName );
-		Type propertyType = getDataType();
-		return propertyType;
+		return getDataType();
 	}
 
 	private void dereferenceCollection(CollectionType collectionType, boolean implicitJoin, boolean indexed, String classAlias, AST parent)
@@ -298,7 +297,7 @@
 		//              select clause is part of a scalar query :/ )
 		DotNode parentAsDotNode = null;
 		String property = propertyName;
-		boolean joinIsNeeded = false;
+		final boolean joinIsNeeded;
 
 		if ( isDotNode( parent ) ) {
 			parentAsDotNode = ( DotNode ) parent;
@@ -340,13 +339,11 @@
 		String joinPath = getPath();
 
 		if ( impliedJoin && getWalker().isInFrom() ) {
-			int impliedJoinType = getWalker().getImpliedJoinType();
-			joinType = impliedJoinType;
+			joinType = getWalker().getImpliedJoinType();
 		}
 
 		FromClause currentFromClause = getWalker().getCurrentFromClause();
-		FromElement elem = null;
-		elem = currentFromClause.findJoinByPath( joinPath );
+		FromElement elem = currentFromClause.findJoinByPath( joinPath );
 
 ///////////////////////////////////////////////////////////////////////////////
 //
@@ -420,14 +417,42 @@
 		return impliedJoin;
 	}
 
-	private boolean isReferenceToPrimaryKey(String propertyName, EntityType propertyType) {
-		if ( EntityPersister.ENTITY_ID.equals( propertyName ) ) {
-			// the referenced node text is the special 'id'
-			return propertyType.isReferenceToPrimaryKey();
+	/**
+	 * Is the given property name a reference to the primary key of the associated
+	 * entity construed by the given entity type?
+	 * <p/>
+	 * For example, consider a fragment like order.customer.id
+	 * (where order is a from-element alias).  Here, we'd have:
+	 * propertyName = "id" AND
+	 * owningType = ManyToOneType(Customer)
+	 * and are being asked to determine whether "customer.id" is a reference
+	 * to customer's PK...
+	 *
+	 * @param propertyName The name of the property to check.
+	 * @param owningType The type represeting the entity "owning" the property
+	 * @return True if propertyName references the entity's (owningType->associatedEntity)
+	 * primary key; false otherwise.
+	 */
+	private boolean isReferenceToPrimaryKey(String propertyName, EntityType owningType) {
+		EntityPersister persister = getSessionFactoryHelper()
+				.getFactory()
+				.getEntityPersister( owningType.getAssociatedEntityName() );
+		if ( persister.getEntityMetamodel().hasNonIdentifierPropertyNamedId() ) {
+			// only the identifier property field name can be a reference to the associated entity's PK...
+			return propertyName.equals( persister.getIdentifierPropertyName() ) && owningType.isReferenceToPrimaryKey();
 		}
 		else {
-			String keyPropertyName = getSessionFactoryHelper().getIdentifierOrUniqueKeyPropertyName( propertyType );
-			return keyPropertyName != null && keyPropertyName.equals( propertyName ) && propertyType.isReferenceToPrimaryKey();
+			// here, we have two possibilities:
+			// 		1) the property-name matches the explicitly identifier property name
+			//		2) the property-name matches the implicit 'id' property name
+			if ( EntityPersister.ENTITY_ID.equals( propertyName ) ) {
+				// the referenced node text is the special 'id'
+				return owningType.isReferenceToPrimaryKey();
+			}
+			else {
+				String keyPropertyName = getSessionFactoryHelper().getIdentifierOrUniqueKeyPropertyName( owningType );
+				return keyPropertyName != null && keyPropertyName.equals( propertyName ) && owningType.isReferenceToPrimaryKey();
+			}
 		}
 	}
 

Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/tree/FromElement.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/tree/FromElement.java	2006-11-21 16:33:52 UTC (rev 10850)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/tree/FromElement.java	2006-11-21 17:38:43 UTC (rev 10851)
@@ -5,7 +5,6 @@
 import java.util.List;
 
 import org.hibernate.QueryException;
-import org.hibernate.MappingException;
 import org.hibernate.engine.JoinSequence;
 import org.hibernate.hql.QueryTranslator;
 import org.hibernate.hql.CollectionProperties;
@@ -285,11 +284,19 @@
 			throw new IllegalStateException( "No table alias for node " + this );
 		}
 		String[] cols;
+		String propertyName;
+		if ( getEntityPersister() != null && getEntityPersister().getEntityMetamodel() != null
+				&& getEntityPersister().getEntityMetamodel().hasNonIdentifierPropertyNamedId() ) {
+			propertyName = getEntityPersister().getIdentifierPropertyName();
+		}
+		else {
+			propertyName = EntityPersister.ENTITY_ID;
+		}
 		if ( getWalker().getStatementType() == HqlSqlWalker.SELECT ) {
-			cols = getPropertyMapping( EntityPersister.ENTITY_ID ).toColumns( table, EntityPersister.ENTITY_ID );
+			cols = getPropertyMapping( propertyName ).toColumns( table, propertyName );
 		}
 		else {
-			cols = getPropertyMapping( EntityPersister.ENTITY_ID ).toColumns( EntityPersister.ENTITY_ID );
+			cols = getPropertyMapping( propertyName ).toColumns( propertyName );
 		}
 		String result = StringHelper.join( ", ", cols );
 		return  cols.length == 1 ? result : "(" + result + ")";
@@ -375,13 +382,6 @@
 		return origin;
 	}
 
-	/**
-	 * Returns the type of a property, given it's name (the last part) and the full path.
-	 *
-	 * @param propertyName The last part of the full path to the property.
-	 * @return The type.
-	 * @0param propertyPath The full property path.
-	 */
 	public Type getPropertyType(String propertyName, String propertyPath) {
 		return elementType.getPropertyType( propertyName, propertyPath );
 	}
@@ -422,9 +422,6 @@
 		return filter;
 	}
 
-	/**
-	 * Returns true if the from fragment should be included in the from clause.
-	 */
 	public boolean useFromFragment() {
 		checkInitialized();
 		// If it's not implied or it is implied and it's a many to many join where the target wasn't found.
@@ -472,10 +469,6 @@
 		// Do nothing, eplicit from elements are *always* in the projection list.
 	}
 
-	/**
-	 * Returns true if this element should be in the projection list.
-	 *
-	 */
 	public boolean inProjectionList() {
 		return !isImplied() && isFromOrJoinFragment();
 	}

Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/persister/entity/AbstractEntityPersister.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/persister/entity/AbstractEntityPersister.java	2006-11-21 16:33:52 UTC (rev 10850)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/persister/entity/AbstractEntityPersister.java	2006-11-21 17:38:43 UTC (rev 10851)
@@ -1508,9 +1508,11 @@
 		// ALIASES
 		internalInitSubclassPropertyAliasesMap( null, model.getSubclassPropertyClosureIterator() );
 
-		// aliases for identifier ( alias.id )
-		subclassPropertyAliases.put( ENTITY_ID, getIdentifierAliases() );
-		subclassPropertyColumnNames.put( ENTITY_ID, getIdentifierColumnNames() );
+		// aliases for identifier ( alias.id ); skip if the entity defines a non-id property named 'id'
+		if ( ! entityMetamodel.hasNonIdentifierPropertyNamedId() ) {
+			subclassPropertyAliases.put( ENTITY_ID, getIdentifierAliases() );
+			subclassPropertyColumnNames.put( ENTITY_ID, getIdentifierColumnNames() );
+		}
 
 		// aliases named identifier ( alias.idname )
 		if ( hasIdentifierProperty() ) {
@@ -1527,23 +1529,26 @@
 			String[] idColumnNames = getIdentifierColumnNames();
 
 			for ( int i = 0; i < idPropertyNames.length; i++ ) {
-				subclassPropertyAliases.put(
-						ENTITY_ID + "." + idPropertyNames[i],
-						new String[] { idAliases[i] }
+				if ( entityMetamodel.hasNonIdentifierPropertyNamedId() ) {
+					subclassPropertyAliases.put(
+							ENTITY_ID + "." + idPropertyNames[i],
+							new String[] { idAliases[i] }
 					);
-				subclassPropertyColumnNames.put(
-						ENTITY_ID + "." + getIdentifierPropertyName() + "." + idPropertyNames[i],
-						new String[] { idColumnNames[i] }
+					subclassPropertyColumnNames.put(
+							ENTITY_ID + "." + getIdentifierPropertyName() + "." + idPropertyNames[i],
+							new String[] { idColumnNames[i] }
 					);
-				if (hasIdentifierProperty() && !ENTITY_ID.equals( getIdentifierPropertyName() ) ) {
+				}
+//				if (hasIdentifierProperty() && !ENTITY_ID.equals( getIdentifierPropertyName() ) ) {
+				if ( hasIdentifierProperty() ) {
 					subclassPropertyAliases.put(
 							getIdentifierPropertyName() + "." + idPropertyNames[i],
 							new String[] { idAliases[i] }
-						);
+					);
 					subclassPropertyColumnNames.put(
 							getIdentifierPropertyName() + "." + idPropertyNames[i],
 							new String[] { idColumnNames[i] }
-						);
+					);
 				}
 				else {
 					// embedded composite ids ( alias.idname1, alias.idname2 )
@@ -1554,10 +1559,8 @@
 		}
 
 		if ( entityMetamodel.isPolymorphic() ) {
-			subclassPropertyAliases.put( ENTITY_CLASS,
-					new String[]{getDiscriminatorAlias()} );
-			subclassPropertyColumnNames.put( ENTITY_CLASS,
-					new String[]{getDiscriminatorColumnName()} );
+			subclassPropertyAliases.put( ENTITY_CLASS, new String[] { getDiscriminatorAlias() } );
+			subclassPropertyColumnNames.put( ENTITY_CLASS, new String[] { getDiscriminatorColumnName() } );
 		}
 
 	}
@@ -1672,7 +1675,9 @@
 		if ( entityMetamodel.getIdentifierProperty().isEmbedded() ) {
 			propertyMapping.initPropertyPaths( null, getIdentifierType(), getIdentifierColumnNames(), null, mapping );
 		}
-		propertyMapping.initPropertyPaths( ENTITY_ID, getIdentifierType(), getIdentifierColumnNames(), null, mapping );
+		if ( ! entityMetamodel.hasNonIdentifierPropertyNamedId() ) {
+			propertyMapping.initPropertyPaths( ENTITY_ID, getIdentifierType(), getIdentifierColumnNames(), null, mapping );
+		}
 	}
 
 	private void initDiscriminatorPropertyPath(Mapping mapping) throws MappingException {
@@ -3145,7 +3150,7 @@
 		return factory;
 	}
 
-	protected EntityMetamodel getEntityMetamodel() {
+	public EntityMetamodel getEntityMetamodel() {
 		return entityMetamodel;
 	}
 

Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/persister/entity/AbstractPropertyMapping.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/persister/entity/AbstractPropertyMapping.java	2006-11-21 16:33:52 UTC (rev 10850)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/persister/entity/AbstractPropertyMapping.java	2006-11-21 17:38:43 UTC (rev 10851)
@@ -34,18 +34,14 @@
 
 	public Type toType(String propertyName) throws QueryException {
 		Type type = (Type) typesByPropertyPath.get(propertyName);
-		if (type==null) throwPropertyException(propertyName);
+		if ( type == null ) {
+			throw propertyException( propertyName );
+		}
 		return type;
 	}
 
-	protected final void throwPropertyException(String propertyName) 
-	throws QueryException {
-		throw new QueryException(
-				"could not resolve property: " +
-				propertyName +
-				" of: " +
-				getEntityName()
-			);
+	protected final QueryException propertyException(String propertyName) {
+		return new QueryException( "could not resolve property: " + propertyName + " of: " + getEntityName() );
 	}
 	
 	public String[] getColumnNames(String propertyName) {
@@ -56,11 +52,12 @@
 		return cols;
 	}
 
-	public String[] toColumns(String alias, String propertyName)
-	throws QueryException {
+	public String[] toColumns(String alias, String propertyName) throws QueryException {
 		//TODO: *two* hashmap lookups here is one too many...
 		String[] columns = (String[]) columnsByPropertyPath.get(propertyName);
-		if (columns==null) throwPropertyException(propertyName);
+		if ( columns == null ) {
+			throw propertyException( propertyName );
+		}
 		String[] templates = (String[]) formulaTemplatesByPropertyPath.get(propertyName);
 		String[] result = new String[columns.length];
 		for ( int i=0; i<columns.length; i++ ) {
@@ -77,7 +74,9 @@
 	public String[] toColumns(String propertyName)
 	throws QueryException {
 		String[] columns = (String[]) columnsByPropertyPath.get(propertyName);
-		if (columns==null) throwPropertyException(propertyName);
+		if ( columns == null ) {
+			throw propertyException( propertyName );
+		}
 		String[] templates = (String[]) formulaTemplatesByPropertyPath.get(propertyName);
 		String[] result = new String[columns.length];
 		for ( int i=0; i<columns.length; i++ ) {
@@ -165,18 +164,20 @@
 			final String path, 
 			final EntityType etype, 
 			final String[] columns, 
-			final Mapping factory)
-	throws MappingException {
+			final Mapping factory) throws MappingException {
 
-		Type idtype = etype.getIdentifierOrUniqueKeyType(factory);
+		Type idtype = etype.getIdentifierOrUniqueKeyType( factory );
+		String idPropName = etype.getIdentifierOrUniqueKeyPropertyName(factory);
+		boolean hasNonIdentifierPropertyNamedId = hasNonIdentifierPropertyNamedId( etype, factory );
 
 		if ( etype.isReferenceToPrimaryKey() ) {
-			String idpath1 = extendPath(path, EntityPersister.ENTITY_ID);
-			addPropertyPath(idpath1, idtype, columns, null);
-			initPropertyPaths(idpath1, idtype, columns, null, factory);
+			if ( !hasNonIdentifierPropertyNamedId ) {
+				String idpath1 = extendPath(path, EntityPersister.ENTITY_ID);
+				addPropertyPath(idpath1, idtype, columns, null);
+				initPropertyPaths(idpath1, idtype, columns, null, factory);
+			}
 		}
 
-		String idPropName = etype.getIdentifierOrUniqueKeyPropertyName(factory);
 		if (idPropName!=null) {
 			String idpath2 = extendPath(path, idPropName);
 			addPropertyPath(idpath2, idtype, columns, null);
@@ -184,6 +185,18 @@
 		}
 	}
 
+	private boolean hasNonIdentifierPropertyNamedId(final EntityType entityType, final Mapping factory) {
+		// TODO : would be great to have a Mapping#hasNonIdentifierPropertyNamedId method
+		// I don't believe that Mapping#getReferencedPropertyType accounts for the identifier property; so
+		// if it returns for a property named 'id', then we should have a non-id field named id
+		try {
+			return factory.getReferencedPropertyType( entityType.getAssociatedEntityName(), EntityPersister.ENTITY_ID ) != null;
+		}
+		catch( MappingException e ) {
+			return false;
+		}
+	}
+
 	protected void initComponentPropertyPaths(
 			final String path, 
 			final AbstractComponentType type, 

Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/persister/entity/EntityPersister.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/persister/entity/EntityPersister.java	2006-11-21 16:33:52 UTC (rev 10850)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/persister/entity/EntityPersister.java	2006-11-21 17:38:43 UTC (rev 10851)
@@ -8,6 +8,7 @@
 import org.hibernate.LockMode;
 import org.hibernate.MappingException;
 import org.hibernate.EntityMode;
+import org.hibernate.tuple.entity.EntityMetamodel;
 import org.hibernate.cache.CacheConcurrencyStrategy;
 import org.hibernate.cache.OptimisticCacheSource;
 import org.hibernate.cache.entry.CacheEntryStructure;
@@ -72,8 +73,15 @@
 	 * @return The name of the entity which this persister maps.
 	 */
 	public String getEntityName();
-	
+
 	/**
+	 * Retrieve the underlying entity metamodel instance...
+	 *
+	 *@return The metamodel
+	 */
+	public EntityMetamodel getEntityMetamodel();
+
+	/**
 	 * Determine whether the given name represents a subclass entity
 	 * (or this entity itself) of the entity mapped by this persister.
 	 *

Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/tuple/entity/EntityMetamodel.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/tuple/entity/EntityMetamodel.java	2006-11-21 16:33:52 UTC (rev 10850)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/tuple/entity/EntityMetamodel.java	2006-11-21 17:38:43 UTC (rev 10851)
@@ -75,6 +75,7 @@
 	private final boolean hasCollections;
 	private final boolean hasMutableProperties; 
 	private final boolean hasLazyProperties;
+	private final boolean hasNonIdentifierPropertyNamedId;
 
 	private final int[] naturalIdPropertyNumbers;
 
@@ -151,6 +152,7 @@
 		boolean foundCascade = false;
 		boolean foundCollection = false;
 		boolean foundMutable = false;
+		boolean foundNonIdentifierPropertyNamedId = false;
 		
 		while ( iter.hasNext() ) {
 			Property prop = ( Property ) iter.next();
@@ -167,6 +169,10 @@
 				naturalIdNumbers.add( new Integer(i) );
 			}
 
+			if ( "id".equals( prop.getName() ) ) {
+				foundNonIdentifierPropertyNamedId = true;
+			}
+
 			// temporary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 			boolean lazy = prop.isLazy() && lazyAvailable;
 			if ( lazy ) hasLazy = true;
@@ -215,9 +221,12 @@
 		}
 
 		hasCascades = foundCascade;
+		hasNonIdentifierPropertyNamedId = foundNonIdentifierPropertyNamedId;
 		versionPropertyIndex = tempVersionProperty;
 		hasLazyProperties = hasLazy;
-		if (hasLazyProperties) log.info("lazy property fetching available for: " + name);
+		if ( hasLazyProperties ) {
+			log.info( "lazy property fetching available for: " + name );
+		}
 
 		lazy = persistentClass.isLazy() && (
 				// TODO: this disables laziness even in non-pojo entity modes:
@@ -371,6 +380,10 @@
 		return hasMutableProperties;
 	}
 
+	public boolean hasNonIdentifierPropertyNamedId() {
+		return hasNonIdentifierPropertyNamedId;
+	}
+
 	public boolean hasLazyProperties() {
 		return hasLazyProperties;
 	}

Added: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/idprops/IdentifierPropertyReferencesTest.java
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/idprops/IdentifierPropertyReferencesTest.java	2006-11-21 16:33:52 UTC (rev 10850)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/idprops/IdentifierPropertyReferencesTest.java	2006-11-21 17:38:43 UTC (rev 10851)
@@ -0,0 +1,187 @@
+package org.hibernate.test.idprops;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.test.TestCase;
+import org.hibernate.Session;
+import org.hibernate.Query;
+import org.hibernate.Criteria;
+import org.hibernate.criterion.Projections;
+import org.hibernate.criterion.Restrictions;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class IdentifierPropertyReferencesTest extends TestCase {
+	public IdentifierPropertyReferencesTest(String name) {
+		super( name );
+	}
+
+	protected String[] getMappings() {
+		return new String[] { "idprops/Mapping.hbm.xml" };
+	}
+
+	public static Test suite() {
+		return new TestSuite( IdentifierPropertyReferencesTest.class );
+	}
+
+	public void testHqlIdPropertyReferences() {
+		Session s = openSession();
+		s.beginTransaction();
+		Person p = new Person( new Long(1), "steve", 123 );
+		s.save( p );
+		Order o = new Order( new Long(1), p );
+		LineItem l = new LineItem( o, "my-product", 2 );
+		l.setId( "456" );
+		s.save( o );
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+
+		long count = extractCount( s, "select count(*) from Person p where p.id = 123" );
+		assertEquals( "Person by id prop (non-identifier)", 1, count );
+		count = extractCount( s, "select count(*) from Person p where p.pk = 1" );
+		assertEquals( "Person by pk prop (identifier)", 1, count );
+
+		count = extractCount( s, "select count(*) from Order o where o.id = 1" );
+		assertEquals( "Order by number prop (named identifier)", 1, count );
+		count = extractCount( s, "select count(*) from Order o where o.number = 1" );
+		assertEquals( "Order by id prop (virtual identifier)", 1, count );
+
+		count = extractCount( s, "select count(*) from LineItem l where l.id = '456'" );
+		assertEquals( "LineItem by id prop (non-identifier", 1, count );
+
+		if ( getDialect().supportsRowValueConstructorSyntax() ) {
+			Query q = s.createQuery( "select count(*) from LineItem l where l.pk = (:order, :product)" )
+					.setEntity( "order", o )
+					.setString( "product", "my-product" );
+			count = extractCount( q );
+			assertEquals( "LineItem by pk prop (named composite identifier", 1, count );
+		}
+
+		count = extractCount( s, "select count(*) from Order o where o.orderee.id = 1" );
+		assertEquals( 0, count );
+		count = extractCount( s, "select count(*) from Order o where o.orderee.pk = 1" );
+		assertEquals( 1, count );
+		count = extractCount( s, "select count(*) from Order o where o.orderee.id = 123" );
+		assertEquals( 1, count );
+
+		count = extractCount( s, "select count(*) from LineItem l where l.pk.order.id = 1" );
+		assertEquals( 1, count );
+		count = extractCount( s, "select count(*) from LineItem l where l.pk.order.number = 1" );
+		assertEquals( 1, count );
+		count = extractCount( s, "select count(*) from LineItem l where l.pk.order.orderee.pk = 1" );
+		assertEquals( 1, count );
+
+		s.delete( o );
+		s.delete( p );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	public void testCriteriaIdPropertyReferences() {
+		Session s = openSession();
+		s.beginTransaction();
+		Person p = new Person( new Long(1), "steve", 123 );
+		s.save( p );
+		Order o = new Order( new Long(1), p );
+		LineItem l = new LineItem( o, "my-product", 2 );
+		l.setId( "456" );
+		s.save( o );
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+
+		Criteria crit = s.createCriteria( Person.class );
+		crit.setProjection( Projections.rowCount() );
+		crit.add( Restrictions.eq( "id", new Integer(123) ) );
+		long count = extractCount( crit );
+		assertEquals( "Person by id prop (non-identifier)", 1, count );
+
+		crit = s.createCriteria( Person.class );
+		crit.setProjection( Projections.rowCount() );
+		crit.add( Restrictions.eq( "pk", new Long(1) ) );
+		count = extractCount( crit );
+		assertEquals( "Person by pk prop (identifier)", 1, count );
+
+		crit = s.createCriteria( Order.class );
+		crit.setProjection( Projections.rowCount() );
+		crit.add(  Restrictions.eq( "number", new Long(1) ) );
+		count = extractCount( crit );
+		assertEquals( "Order by number prop (named identifier)", 1, count );
+
+		crit = s.createCriteria( Order.class );
+		crit.setProjection( Projections.rowCount() );
+		crit.add(  Restrictions.eq( "id", new Long(1) ) );
+		count = extractCount( crit );
+		assertEquals( "Order by id prop (virtual identifier)", 1, count );
+
+		crit = s.createCriteria( LineItem.class );
+		crit.setProjection( Projections.rowCount() );
+		crit.add(  Restrictions.eq( "id", "456" ) );
+		count = extractCount( crit );
+		assertEquals( "LineItem by id prop (non-identifier", 1, count );
+
+		if ( getDialect().supportsRowValueConstructorSyntax() ) {
+			crit = s.createCriteria( LineItem.class );
+			crit.setProjection( Projections.rowCount() );
+			crit.add( Restrictions.eq( "pk", new LineItemPK( o, "my-product" ) ) );
+			count = extractCount( crit );
+			assertEquals( "LineItem by pk prop (named composite identifier)", 1, count );
+		}
+
+		crit = s.createCriteria( Order.class );
+		crit.setProjection( Projections.rowCount() );
+		crit.createAlias( "orderee", "p" ).add( Restrictions.eq( "p.id", new Integer(1) ) );
+		count = extractCount( crit );
+		assertEquals( 0, count );
+
+		crit = s.createCriteria( Order.class );
+		crit.setProjection( Projections.rowCount() );
+		crit.createAlias( "orderee", "p" ).add( Restrictions.eq( "p.pk", new Long(1) ) );
+		count = extractCount( crit );
+		assertEquals( 1, count );
+
+		crit = s.createCriteria( Order.class );
+		crit.setProjection( Projections.rowCount() );
+		crit.createAlias( "orderee", "p" ).add( Restrictions.eq( "p.id", new Integer(123) ) );
+		count = extractCount( crit );
+		assertEquals( 1, count );
+
+		crit = s.createCriteria( LineItem.class );
+		crit.setProjection( Projections.rowCount() );
+		crit.add( Restrictions.eq( "pk.order.id", new Long(1) ) );
+		count = extractCount( crit );
+		assertEquals( 1, count );
+
+		crit = s.createCriteria( LineItem.class );
+		crit.setProjection( Projections.rowCount() );
+		crit.add( Restrictions.eq( "pk.order.number", new Long(1) ) );
+		count = extractCount( crit );
+		assertEquals( 1, count );
+
+		s.delete( o );
+		s.delete( p );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	private long extractCount(Session s, String hql) {
+		return extractCount( s.createQuery( hql ) );
+	}
+
+	private long extractCount(Query query) {
+		return ( ( Long ) query.list().get( 0 ) ).longValue();
+	}
+
+	private long extractCount(Criteria crit) {
+		return ( ( Integer ) crit.list().get( 0 ) ).intValue();
+	}
+}

Added: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/idprops/LineItem.java
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/idprops/LineItem.java	2006-11-21 16:33:52 UTC (rev 10850)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/idprops/LineItem.java	2006-11-21 17:38:43 UTC (rev 10851)
@@ -0,0 +1,49 @@
+package org.hibernate.test.idprops;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class LineItem {
+	private LineItemPK pk;
+	private int quantity;
+	private String id;
+
+	public LineItem() {
+	}
+
+	public LineItem(LineItemPK pk, int quantity) {
+		this.pk = pk;
+		this.quantity = quantity;
+		this.pk.getOrder().getLineItems().add( this );
+	}
+
+	public LineItem(Order order, String productCode, int quantity) {
+		this( new LineItemPK( order, productCode ), quantity );
+	}
+
+	public LineItemPK getPk() {
+		return pk;
+	}
+
+	public void setPk(LineItemPK pk) {
+		this.pk = pk;
+	}
+
+	public int getQuantity() {
+		return quantity;
+	}
+
+	public void setQuantity(int quantity) {
+		this.quantity = quantity;
+	}
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+}

Added: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/idprops/LineItemPK.java
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/idprops/LineItemPK.java	2006-11-21 16:33:52 UTC (rev 10850)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/idprops/LineItemPK.java	2006-11-21 17:38:43 UTC (rev 10851)
@@ -0,0 +1,64 @@
+package org.hibernate.test.idprops;
+
+import java.io.Serializable;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class LineItemPK implements Serializable {
+	private Order order;
+	private String productCode;
+
+	public LineItemPK() {
+	}
+
+	public LineItemPK(Order order, String productCode) {
+		this.order = order;
+		this.productCode = productCode;
+	}
+
+	public Order getOrder() {
+		return order;
+	}
+
+	public void setOrder(Order order) {
+		this.order = order;
+	}
+
+	public String getProductCode() {
+		return productCode;
+	}
+
+	public void setProductCode(String productCode) {
+		this.productCode = productCode;
+	}
+
+	public boolean equals(Object o) {
+		if ( this == o ) {
+			return true;
+		}
+		if ( o == null || getClass() != o.getClass() ) {
+			return false;
+		}
+
+		LineItemPK that = ( LineItemPK ) o;
+
+		if ( !order.equals( that.order ) ) {
+			return false;
+		}
+		if ( !productCode.equals( that.productCode ) ) {
+			return false;
+		}
+
+		return true;
+	}
+
+	public int hashCode() {
+		int result;
+		result = order.hashCode();
+		result = 31 * result + productCode.hashCode();
+		return result;
+	}
+}

Added: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/idprops/Mapping.hbm.xml
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/idprops/Mapping.hbm.xml	2006-11-21 16:33:52 UTC (rev 10850)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/idprops/Mapping.hbm.xml	2006-11-21 17:38:43 UTC (rev 10851)
@@ -0,0 +1,48 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
+
+<hibernate-mapping package="org.hibernate.test.idprops">
+
+    <!--
+        Person has an identitifer property named something other than 'id';
+        additionally, it has a non-identitifer property named 'id'
+    -->
+    <class name="Person" table="T_ID_PERSON">
+        <id name="pk" column="PK" type="long">
+            <generator class="assigned"/>
+        </id>
+        <property name="name" column="NAME" type="string"/>
+        <property name="id" column="ID_NON_ID" type="int" />
+    </class>
+
+    <!--
+        Order has an identitifer property named something other than 'id';
+        it has no non-identitifer property named 'id'
+    -->
+    <class name="Order" table="T_ID_ORDER">
+        <id name="number" column="PK" type="long">
+            <generator class="assigned"/>
+        </id>
+        <property name="placed" column="ORDR_DT" type="timestamp"/>
+        <many-to-one name="orderee" class="Person" column="ORDEREE"/>
+        <set name="lineItems" cascade="all-delete-orphan" lazy="true" inverse="true">
+            <key column="ORDR_ID"/>
+            <one-to-many class="LineItem"/>
+        </set>
+    </class>
+
+    <!--
+        LineItem has a composite identitifer property named something other than 'id';
+        additionally, it has a non-identitifer property named 'id'
+    -->
+    <class name="LineItem" table="T_ID_LINE_ITEM">
+        <composite-id class="LineItemPK" name="pk">
+            <key-many-to-one name="order" class="Order" column="ORDR_ID" />
+            <key-property name="productCode" column="PROD_CODE" type="string"/>
+        </composite-id>
+        <property name="quantity" column="QTY" type="int"/>
+        <property name="id" column="ID_NON_ID2" type="string"/>
+    </class>
+
+</hibernate-mapping>
\ No newline at end of file

Added: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/idprops/Order.java
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/idprops/Order.java	2006-11-21 16:33:52 UTC (rev 10850)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/idprops/Order.java	2006-11-21 17:38:43 UTC (rev 10851)
@@ -0,0 +1,60 @@
+package org.hibernate.test.idprops;
+
+import java.util.Date;
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class Order {
+	private Long number;
+	private Date placed;
+	private Person orderee;
+
+	private Set lineItems = new HashSet();
+
+	public Order() {
+	}
+
+	public Order(Long number, Person orderee) {
+		this.number = number;
+		this.orderee = orderee;
+		this.placed = new Date();
+	}
+
+	public Long getNumber() {
+		return number;
+	}
+
+	public void setNumber(Long number) {
+		this.number = number;
+	}
+
+	public Date getPlaced() {
+		return placed;
+	}
+
+	public void setPlaced(Date placed) {
+		this.placed = placed;
+	}
+
+	public Person getOrderee() {
+		return orderee;
+	}
+
+	public void setOrderee(Person orderee) {
+		this.orderee = orderee;
+	}
+
+
+	public Set getLineItems() {
+		return lineItems;
+	}
+
+	public void setLineItems(Set lineItems) {
+		this.lineItems = lineItems;
+	}
+}

Added: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/idprops/Person.java
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/idprops/Person.java	2006-11-21 16:33:52 UTC (rev 10850)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/idprops/Person.java	2006-11-21 17:38:43 UTC (rev 10851)
@@ -0,0 +1,45 @@
+package org.hibernate.test.idprops;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class Person {
+	private Long pk;
+	private String name;
+	private int id;
+
+	public Person() {
+	}
+
+	public Person(Long pk, String name, int id) {
+		this.pk = pk;
+		this.name = name;
+		this.id = id;
+	}
+
+	public Long getPk() {
+		return pk;
+	}
+
+	public void setPk(Long pk) {
+		this.pk = pk;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public int getId() {
+		return id;
+	}
+
+	public void setId(int id) {
+		this.id = id;
+	}
+}

Modified: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/legacy/CustomPersister.java
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/legacy/CustomPersister.java	2006-11-21 16:33:52 UTC (rev 10850)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/legacy/CustomPersister.java	2006-11-21 17:38:43 UTC (rev 10851)
@@ -11,6 +11,7 @@
 import org.hibernate.HibernateException;
 import org.hibernate.LockMode;
 import org.hibernate.MappingException;
+import org.hibernate.tuple.entity.EntityMetamodel;
 import org.hibernate.cache.CacheConcurrencyStrategy;
 import org.hibernate.cache.entry.CacheEntryStructure;
 import org.hibernate.cache.entry.UnstructuredCacheEntry;
@@ -616,4 +617,8 @@
 	public Comparator getVersionComparator() {
 		return null;
 	}
+
+	public EntityMetamodel getEntityMetamodel() {
+		return null;
+	}
 }




More information about the hibernate-commits mailing list