Author: steve.ebersole(a)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;
+ }
}