Author: steve.ebersole(a)jboss.com
Date: 2006-11-21 12:39:14 -0500 (Tue, 21 Nov 2006)
New Revision: 10852
Added:
trunk/Hibernate3/test/org/hibernate/test/idprops/
trunk/Hibernate3/test/org/hibernate/test/idprops/IdentifierPropertyReferencesTest.java
trunk/Hibernate3/test/org/hibernate/test/idprops/LineItem.java
trunk/Hibernate3/test/org/hibernate/test/idprops/LineItemPK.java
trunk/Hibernate3/test/org/hibernate/test/idprops/Mapping.hbm.xml
trunk/Hibernate3/test/org/hibernate/test/idprops/Order.java
trunk/Hibernate3/test/org/hibernate/test/idprops/Person.java
Modified:
trunk/Hibernate3/doc/reference/en/modules/query_hql.xml
trunk/Hibernate3/src/org/hibernate/hql/ast/tree/DotNode.java
trunk/Hibernate3/src/org/hibernate/hql/ast/tree/FromElement.java
trunk/Hibernate3/src/org/hibernate/persister/entity/AbstractEntityPersister.java
trunk/Hibernate3/src/org/hibernate/persister/entity/AbstractPropertyMapping.java
trunk/Hibernate3/src/org/hibernate/persister/entity/EntityPersister.java
trunk/Hibernate3/src/org/hibernate/tuple/entity/EntityMetamodel.java
trunk/Hibernate3/test/org/hibernate/test/legacy/CustomPersister.java
Log:
HHH-1851 : relax special 'id' property handling
Modified: trunk/Hibernate3/doc/reference/en/modules/query_hql.xml
===================================================================
--- trunk/Hibernate3/doc/reference/en/modules/query_hql.xml 2006-11-21 17:38:43 UTC (rev
10851)
+++ trunk/Hibernate3/doc/reference/en/modules/query_hql.xml 2006-11-21 17:39:14 UTC (rev
10852)
@@ -1,6 +1,6 @@
-<chapter id="queryhql">
+<chapter id="queryhql" revision="1">
<title>HQL: The Hibernate Query Language</title>
-
+
<para>
Hibernate is equipped with an extremely powerful query language that (quite
intentionally)
looks very much like SQL. But don't be fooled by the syntax; HQL is fully
object-oriented,
@@ -20,12 +20,12 @@
<literal>foo.barSet</literal> is not
<literal>foo.BARSET</literal>.
</para>
-
+
<para>
- This manual uses lowercase HQL keywords. Some users find queries with
uppercase keywords
+ This manual uses lowercase HQL keywords. Some users find queries with
uppercase keywords
more readable, but we find this convention ugly when embedded in Java code.
</para>
-
+
</sect1>
<sect1 id="queryhql-from">
@@ -34,17 +34,17 @@
<para>
The simplest possible Hibernate query is of the form:
</para>
-
+
<programlisting><![CDATA[from eg.Cat]]></programlisting>
-
+
<para>
which simply returns all instances of the class
<literal>eg.Cat</literal>.
We don't usually need to qualify the class name, since
<literal>auto-import</literal>
is the default. So we almost always just write:
</para>
-
+
<programlisting><![CDATA[from Cat]]></programlisting>
-
+
<para>
Most of the time, you will need to assign an
<emphasis>alias</emphasis>, since
you will want to refer to the <literal>Cat</literal> in other
parts of the
@@ -58,33 +58,33 @@
instances, so we could use that alias later in the query. The
<literal>as</literal>
keyword is optional; we could also write:
</para>
-
+
<programlisting><![CDATA[from Cat cat]]></programlisting>
-
+
<para>
Multiple classes may appear, resulting in a cartesian product or
"cross" join.
</para>
-
+
<programlisting><![CDATA[from Formula,
Parameter]]></programlisting>
<programlisting><![CDATA[from Formula as form, Parameter as
param]]></programlisting>
-
+
<para>
It is considered good practice to name query aliases using an initial
lowercase,
consistent with Java naming standards for local variables
(eg. <literal>domesticCat</literal>).
</para>
-
+
</sect1>
<sect1 id="queryhql-joins" revision="2">
<title>Associations and joins</title>
<para>
- We may also assign aliases to associated entities, or even to elements of a
+ We may also assign aliases to associated entities, or even to elements of a
collection of values, using a <literal>join</literal>.
</para>
- <programlisting><![CDATA[from Cat as cat
+ <programlisting><![CDATA[from Cat as cat
inner join cat.mate as mate
left outer join cat.kittens as kitten]]></programlisting>
@@ -118,50 +118,50 @@
</para>
</listitem>
</itemizedlist>
-
+
<para>
- The <literal>inner join</literal>, <literal>left outer
join</literal> and
+ The <literal>inner join</literal>, <literal>left outer
join</literal> and
<literal>right outer join</literal> constructs may be
abbreviated.
</para>
- <programlisting><![CDATA[from Cat as cat
+ <programlisting><![CDATA[from Cat as cat
join cat.mate as mate
left join cat.kittens as kitten]]></programlisting>
-
+
<para>
You may supply extra join conditions using the HQL
<literal>with</literal>
keyword.
</para>
- <programlisting><![CDATA[from Cat as cat
- left join cat.kittens as kitten
+ <programlisting><![CDATA[from Cat as cat
+ left join cat.kittens as kitten
with kitten.bodyWeight > 10.0]]></programlisting>
<para>
- In addition, a "fetch" join allows associations or collections of
values to be
- initialized along with their parent objects, using a single select. This is
particularly
+ In addition, a "fetch" join allows associations or collections of
values to be
+ initialized along with their parent objects, using a single select. This is
particularly
useful in the case of a collection. It effectively overrides the outer join
and
lazy declarations of the mapping file for associations and collections. See
<xref linkend="performance-fetching"/> for more information.
</para>
-
- <programlisting><![CDATA[from Cat as cat
+
+ <programlisting><![CDATA[from Cat as cat
inner join fetch cat.mate
left join fetch cat.kittens]]></programlisting>
-
+
<para>
- A fetch join does not usually need to assign an alias, because the associated
objects
- should not be used in the <literal>where</literal> clause (or any
other clause). Also,
- the associated objects are not returned directly in the query results.
Instead, they may
+ A fetch join does not usually need to assign an alias, because the associated
objects
+ should not be used in the <literal>where</literal> clause (or any
other clause). Also,
+ the associated objects are not returned directly in the query results.
Instead, they may
be accessed via the parent object. The only reason we might need an alias is
if we are
recursively join fetching a further collection:
</para>
-
- <programlisting><![CDATA[from Cat as cat
+
+ <programlisting><![CDATA[from Cat as cat
inner join fetch cat.mate
left join fetch cat.kittens child
left join fetch child.kittens]]></programlisting>
-
+
<para>
Note that the <literal>fetch</literal> construct may not be used
in queries called using
<literal>iterate()</literal> (though
<literal>scroll()</literal> can be used). Nor should
@@ -171,18 +171,18 @@
you'd expect.
Nor may <literal>fetch</literal> be used together with an ad hoc
<literal>with</literal> condition.
It is possible to create a cartesian product by join fetching more than one
collection in a
- query, so take care in this case. Join fetching multiple collection roles
also sometimes gives
- unexpected results for bag mappings, so be careful about how you formulate
your queries in this
- case. Finally, note that <literal>full join fetch</literal> and
<literal>right join fetch</literal>
+ query, so take care in this case. Join fetching multiple collection roles
also sometimes gives
+ unexpected results for bag mappings, so be careful about how you formulate
your queries in this
+ case. Finally, note that <literal>full join fetch</literal> and
<literal>right join fetch</literal>
are not meaningful.
</para>
-
+
<para>
If you are using property-level lazy fetching (with bytecode
instrumentation), it is
possible to force Hibernate to fetch the lazy properties immediately (in the
first
query) using <literal>fetch all properties</literal>.
</para>
-
+
<programlisting><![CDATA[from Document fetch all properties order by
name]]></programlisting>
<programlisting><![CDATA[from Document doc fetch all properties where
lower(doc.name) like '%cats%']]></programlisting>
@@ -210,16 +210,52 @@
<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>
<para>
- The <literal>select</literal> clause picks which objects and
properties to return in
+ The <literal>select</literal> clause picks which objects and
properties to return in
the query result set. Consider:
</para>
- <programlisting><![CDATA[select mate
-from Cat as cat
+ <programlisting><![CDATA[select mate
+from Cat as cat
inner join cat.mate as mate]]></programlisting>
<para>
@@ -239,37 +275,37 @@
<programlisting><![CDATA[select cust.name.firstName from Customer as
cust]]></programlisting>
<para>
- Queries may return multiple objects and/or properties as an array of type
+ Queries may return multiple objects and/or properties as an array of type
<literal>Object[]</literal>,
</para>
- <programlisting><![CDATA[select mother, offspr, mate.name
+ <programlisting><![CDATA[select mother, offspr, mate.name
from DomesticCat as mother
inner join mother.mate as mate
left outer join mother.kittens as offspr]]></programlisting>
-
+
<para>
or as a <literal>List</literal>,
</para>
-
+
<programlisting><![CDATA[select new list(mother, offspr, mate.name)
from DomesticCat as mother
inner join mother.mate as mate
left outer join mother.kittens as offspr]]></programlisting>
-
+
<para>
or as an actual typesafe Java object,
</para>
-
+
<programlisting><![CDATA[select new Family(mother, mate, offspr)
from DomesticCat as mother
join mother.mate as mate
left join mother.kittens as offspr]]></programlisting>
-
+
<para>
assuming that the class <literal>Family</literal> has an
appropriate constructor.
</para>
-
+
<para>
You may assign aliases to selected expressions using
<literal>as</literal>:
</para>
@@ -280,14 +316,14 @@
<para>
This is most useful when used together with <literal>select new
map</literal>:
</para>
-
+
<programlisting><![CDATA[select new map( max(bodyWeight) as max,
min(bodyWeight) as min, count(*) as n )
from Cat cat]]></programlisting>
<para>
This query returns a <literal>Map</literal> from aliases to
selected values.
</para>
-
+
</sect1>
<sect1 id="queryhql-aggregation">
@@ -302,11 +338,11 @@
<!-- NO LONGER SUPPORTED
<para>
- Collections may also appear inside aggregate functions in the
<literal>select</literal>
+ Collections may also appear inside aggregate functions in the
<literal>select</literal>
clause.
</para>
- <programlisting><![CDATA[select cat, count( elements(cat.kittens) )
+ <programlisting><![CDATA[select cat, count( elements(cat.kittens) )
from Cat cat group by cat]]></programlisting>
-->
@@ -331,21 +367,21 @@
</para>
</listitem>
</itemizedlist>
-
+
<para>
You may use arithmetic operators, concatenation, and recognized SQL
functions
in the select clause:
</para>
-
- <programlisting><![CDATA[select cat.weight + sum(kitten.weight)
-from Cat cat
+
+ <programlisting><![CDATA[select cat.weight + sum(kitten.weight)
+from Cat cat
join cat.kittens kitten
group by cat.id, cat.weight]]></programlisting>
-
+
<programlisting><![CDATA[select firstName||' '||initial||'
'||upper(lastName) from Person]]></programlisting>
-
+
<para>
- The <literal>distinct</literal> and
<literal>all</literal> keywords may be used and
+ The <literal>distinct</literal> and
<literal>all</literal> keywords may be used and
have the same semantics as in SQL.
</para>
@@ -354,7 +390,7 @@
select count(distinct cat.name), count(cat) from Cat cat]]></programlisting>
</sect1>
-
+
<sect1 id="queryhql-polymorphism">
<title>Polymorphic queries</title>
@@ -366,30 +402,30 @@
<para>
returns instances not only of <literal>Cat</literal>, but also of
subclasses like
- <literal>DomesticCat</literal>. Hibernate queries may name
<emphasis>any</emphasis> Java
- class or interface in the <literal>from</literal> clause. The
query will return instances
- of all persistent classes that extend that class or implement the interface.
The following
+ <literal>DomesticCat</literal>. Hibernate queries may name
<emphasis>any</emphasis> Java
+ class or interface in the <literal>from</literal> clause. The
query will return instances
+ of all persistent classes that extend that class or implement the interface.
The following
query would return all persistent objects:
</para>
-
+
<programlisting><![CDATA[from java.lang.Object
o]]></programlisting>
-
+
<para>
The interface <literal>Named</literal> might be implemented by
various persistent
classes:
</para>
-
+
<programlisting><![CDATA[from Named n, Named m where n.name =
m.name]]></programlisting>
-
+
<para>
Note that these last two queries will require more than one SQL
<literal>SELECT</literal>. This
means that the <literal>order by</literal> clause does not
correctly order the whole result set.
(It also means you can't call these queries using
<literal>Query.scroll()</literal>.)
</para>
-
+
</sect1>
- <sect1 id="queryhql-where">
+ <sect1 id="queryhql-where" revision="1">
<title>The where clause</title>
<para>
@@ -398,18 +434,18 @@
</para>
<programlisting><![CDATA[from Cat where
name='Fritz']]></programlisting>
-
+
<para>
If there is an alias, use a qualified property name:
</para>
-
+
<programlisting><![CDATA[from Cat as cat where
cat.name='Fritz']]></programlisting>
<para>
returns instances of <literal>Cat</literal> named
'Fritz'.
</para>
- <programlisting><![CDATA[select foo
+ <programlisting><![CDATA[select foo
from Foo foo, Bar bar
where foo.startDate = bar.date]]></programlisting>
@@ -429,7 +465,7 @@
something like
</para>
- <programlisting><![CDATA[from Foo foo
+ <programlisting><![CDATA[from Foo foo
where foo.bar.baz.customer.address.city is not null]]></programlisting>
<para>
@@ -437,19 +473,20 @@
</para>
<para>
- The <literal>=</literal> operator may be used to compare not only
properties, but also
+ The <literal>=</literal> operator may be used to compare not only
properties, but also
instances:
</para>
<programlisting><![CDATA[from Cat cat, Cat rival where cat.mate =
rival.mate]]></programlisting>
- <programlisting><![CDATA[select cat, mate
+ <programlisting><![CDATA[select cat, mate
from Cat cat, Cat mate
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
@@ -461,55 +498,51 @@
</para>
<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>.
+ 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>. Again, see <xref
linkend="queryhql-identifier-property"/>
+ for more information regarding referencing identifier properties.
</para>
<programlisting><![CDATA[from bank.Person person
-where person.id.country = 'AU'
+where person.id.country = 'AU'
and person.id.medicareNumber = 123456]]></programlisting>
<programlisting><![CDATA[from bank.Account account
-where account.owner.id.country = 'AU'
+where account.owner.id.country = 'AU'
and account.owner.id.medicareNumber = 123456]]></programlisting>
<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
+ of an instance in the case of polymorphic persistence. A Java class name
embedded in the
where clause will be translated to its discriminator value.
</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>
is a property mapped with
<literal><any></literal>).
</para>
-
- <programlisting><![CDATA[from AuditLog log, Payment payment
+
+ <programlisting><![CDATA[from AuditLog log, Payment payment
where log.item.class = 'Payment' and log.item.id =
payment.id]]></programlisting>
-
+
<para>
Notice that <literal>log.item.class</literal> and
<literal>payment.class</literal>
would refer to the values of completely different database columns in the
above query.
</para>
-
+
</sect1>
<sect1 id="queryhql-expressions">
@@ -550,14 +583,14 @@
<literal>is not null</literal>,
<literal>is empty</literal>,
<literal>is not empty</literal>,
- <literal>member of</literal> and
+ <literal>member of</literal> and
<literal>not member of</literal>
</para>
</listitem>
<listitem>
<para>
"Simple" case, <literal>case ... when ... then ... else
... end</literal>, and
- "searched" case, <literal>case when ... then ... else
... end</literal>
+ "searched" case, <literal>case when ... then ... else
... end</literal>
</para>
</listitem>
<listitem>
@@ -573,8 +606,8 @@
</listitem>
<listitem>
<para>
- <literal>second(...)</literal>,
<literal>minute(...)</literal>,
- <literal>hour(...)</literal>, <literal>day(...)</literal>,
+ <literal>second(...)</literal>,
<literal>minute(...)</literal>,
+ <literal>hour(...)</literal>, <literal>day(...)</literal>,
<literal>month(...)</literal>,
<literal>year(...)</literal>,
</para>
</listitem>
@@ -592,14 +625,14 @@
<listitem>
<para>
<literal>str()</literal> for converting numeric or
temporal values to a
- readable string
+ readable string
</para>
</listitem>
<listitem>
<para>
<literal>cast(... as ...)</literal>, where the second
argument is the name of
- a Hibernate type, and <literal>extract(... from
...)</literal> if ANSI
- <literal>cast()</literal> and
<literal>extract()</literal> is supported by
+ a Hibernate type, and <literal>extract(... from
...)</literal> if ANSI
+ <literal>cast()</literal> and
<literal>extract()</literal> is supported by
the underlying database
</para>
</listitem>
@@ -611,15 +644,15 @@
</listitem>
<listitem>
<para>
- HQL functions that take collection-valued path expressions:
<literal>size(),
- minelement(), maxelement(), minindex(), maxindex()</literal>,
along with the
+ HQL functions that take collection-valued path expressions:
<literal>size(),
+ minelement(), maxelement(), minindex(), maxindex()</literal>,
along with the
special <literal>elements()</literal> and
<literal>indices</literal> functions
which may be quantified using <literal>some, all, exists, any,
in</literal>.
</para>
</listitem>
<listitem>
<para>
- Any database-supported SQL scalar function like
<literal>sign()</literal>,
+ Any database-supported SQL scalar function like
<literal>sign()</literal>,
<literal>trunc()</literal>,
<literal>rtrim()</literal>, <literal>sin()</literal>
</para>
</listitem>
@@ -663,7 +696,7 @@
<programlisting><![CDATA[from DomesticCat cat where cat.name not in (
'Foo', 'Bar', 'Baz' )]]></programlisting>
<para>
- Likewise, <literal>is null</literal> and <literal>is not
null</literal> may be used to test
+ Likewise, <literal>is null</literal> and <literal>is not
null</literal> may be used to test
for null values.
</para>
@@ -692,20 +725,20 @@
<para>
For indexed collections, you may refer to the minimum and maximum indices
using
- <literal>minindex</literal> and
<literal>maxindex</literal> functions. Similarly,
- you may refer to the minimum and maximum elements of a collection of basic
type
+ <literal>minindex</literal> and
<literal>maxindex</literal> functions. Similarly,
+ you may refer to the minimum and maximum elements of a collection of basic
type
using the <literal>minelement</literal> and
<literal>maxelement</literal>
functions.
</para>
-
+
<programlisting><![CDATA[from Calendar cal where
maxelement(cal.holidays) > current_date]]></programlisting>
-
+
<programlisting><![CDATA[from Order order where maxindex(order.items)
> 100]]></programlisting>
<programlisting><![CDATA[from Order order where minelement(order.items)
> 10000]]></programlisting>
-
+
<para>
- The SQL functions <literal>any, some, all, exists, in</literal>
are supported when passed the element
+ The SQL functions <literal>any, some, all, exists, in</literal>
are supported when passed the element
or index set of a collection (<literal>elements</literal> and
<literal>indices</literal> functions)
or the result of a subquery (see below).
</para>
@@ -728,12 +761,12 @@
<literal>minelement</literal>,
<literal>maxelement</literal> - may only be used in
the where clause in Hibernate3.
</para>
-
+
<para>
Elements of indexed collections (arrays, lists, maps) may be referred to by
index (in a where clause only):
</para>
-
+
<programlisting><![CDATA[from Order order where order.items[0].id =
1234]]></programlisting>
<programlisting><![CDATA[select person from Person person, Calendar
calendar
@@ -749,16 +782,16 @@
<para>
The expression inside <literal>[]</literal> may even be an
arithmetic expression.
</para>
-
+
<programlisting><![CDATA[select item from Item item, Order order
where order.items[ size(order.items) - 1 ] = item]]></programlisting>
-
+
<para>
- HQL also provides the built-in <literal>index()</literal>
function, for elements
+ HQL also provides the built-in <literal>index()</literal>
function, for elements
of a one-to-many association or collection of values.
</para>
- <programlisting><![CDATA[select item, index(item) from Order order
+ <programlisting><![CDATA[select item, index(item) from Order order
join order.items item
where index(item) < 5]]></programlisting>
@@ -769,7 +802,7 @@
<programlisting><![CDATA[from DomesticCat cat where upper(cat.name) like
'FRI%']]></programlisting>
<para>
- If you are not yet convinced by all this, think how much longer and less
readable the
+ If you are not yet convinced by all this, think how much longer and less
readable the
following query would be in SQL:
</para>
@@ -816,7 +849,7 @@
order by cat.name asc, cat.weight desc, cat.birthdate]]></programlisting>
<para>
- The optional <literal>asc</literal> or
<literal>desc</literal> indicate ascending or descending order
+ The optional <literal>asc</literal> or
<literal>desc</literal> indicate ascending or descending order
respectively.
</para>
</sect1>
@@ -828,11 +861,11 @@
A query that returns aggregate values may be grouped by any property of a
returned class or components:
</para>
- <programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
+ <programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
from Cat cat
group by cat.color]]></programlisting>
- <programlisting><![CDATA[select foo.id, avg(name), max(name)
+ <programlisting><![CDATA[select foo.id, avg(name), max(name)
from Foo foo join foo.names name
group by foo.id]]></programlisting>
@@ -840,14 +873,14 @@
A <literal>having</literal> clause is also allowed.
</para>
- <programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
+ <programlisting><![CDATA[select cat.color, sum(cat.weight), count(cat)
from Cat cat
-group by cat.color
+group by cat.color
having cat.color in (eg.Color.TABBY, eg.Color.BLACK)]]></programlisting>
<para>
SQL functions and aggregate functions are allowed in the
<literal>having</literal>
- and <literal>order by</literal> clauses, if supported by the
underlying database
+ and <literal>order by</literal> clauses, if supported by the
underlying database
(eg. not in MySQL).
</para>
@@ -868,37 +901,37 @@
</para>
</sect1>
-
- <sect1 id="queryhql-subqueries" revision="2">
+
+ <sect1 id="queryhql-subqueries" revision="3">
<title>Subqueries</title>
-
+
<para>
For databases that support subselects, Hibernate supports subqueries within
queries. A subquery must
be surrounded by parentheses (often by an SQL aggregate function call). Even
correlated subqueries
(subqueries that refer to an alias in the outer query) are allowed.
</para>
- <programlisting><![CDATA[from Cat as fatcat
-where fatcat.weight > (
- select avg(cat.weight) from DomesticCat cat
+ <programlisting><![CDATA[from Cat as fatcat
+where fatcat.weight > (
+ select avg(cat.weight) from DomesticCat cat
)]]></programlisting>
- <programlisting><![CDATA[from DomesticCat as cat
-where cat.name = some (
- select name.nickName from Name as name
+ <programlisting><![CDATA[from DomesticCat as cat
+where cat.name = some (
+ select name.nickName from Name as name
)]]></programlisting>
-
- <programlisting><![CDATA[from Cat as cat
-where not exists (
- from Cat as mate where mate.mate = cat
+
+ <programlisting><![CDATA[from Cat as cat
+where not exists (
+ from Cat as mate where mate.mate = cat
)]]></programlisting>
- <programlisting><![CDATA[from DomesticCat as cat
-where cat.name not in (
- select name.nickName from Name as name
+ <programlisting><![CDATA[from DomesticCat as cat
+where cat.name not in (
+ select name.nickName from Name as name
)]]></programlisting>
- <programlisting><![CDATA[select cat.id, (select max(kit.weight) from
cat.kitten kit)
+ <programlisting><![CDATA[select cat.id, (select max(kit.weight) from
cat.kitten kit)
from Cat as cat]]></programlisting>
<para>
@@ -906,53 +939,30 @@
</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">
<title>HQL examples</title>
-
+
<para>
Hibernate queries can be quite powerful and complex. In fact, the power of
the query language
is one of Hibernate's main selling points. Here are some example queries
very similar to queries
that I used on a recent project. Note that most queries you will write are
much simpler than these!
</para>
-
+
<para>
- The following query returns the order id, number of items and total value of
the order for all
- unpaid orders for a particular customer and given minimum total value,
ordering the results by
- total value. In determining the prices, it uses the current catalog. The
resulting SQL query,
+ The following query returns the order id, number of items and total value of
the order for all
+ unpaid orders for a particular customer and given minimum total value,
ordering the results by
+ total value. In determining the prices, it uses the current catalog. The
resulting SQL query,
against the <literal>ORDER</literal>,
<literal>ORDER_LINE</literal>, <literal>PRODUCT</literal>,
<literal>CATALOG</literal> and
<literal>PRICE</literal> tables has four inner joins and an
(uncorrelated) subselect.
</para>
-
+
<programlisting><![CDATA[select order.id, sum(price.amount),
count(item)
from Order as order
join order.lineItems as item
@@ -964,19 +974,19 @@
and price.product = product
and catalog.effectiveDate < sysdate
and catalog.effectiveDate >= all (
- select cat.effectiveDate
+ select cat.effectiveDate
from Catalog as cat
where cat.effectiveDate < sysdate
)
group by order
having sum(price.amount) > :minAmount
order by sum(price.amount) desc]]></programlisting>
-
+
<para>
- What a monster! Actually, in real life, I'm not very keen on subqueries,
so my query was
+ What a monster! Actually, in real life, I'm not very keen on subqueries,
so my query was
really more like this:
</para>
-
+
<programlisting><![CDATA[select order.id, sum(price.amount),
count(item)
from Order as order
join order.lineItems as item
@@ -990,24 +1000,24 @@
group by order
having sum(price.amount) > :minAmount
order by sum(price.amount) desc]]></programlisting>
-
+
<para>
The next query counts the number of payments in each status, excluding all
payments in the
- <literal>AWAITING_APPROVAL</literal> status where the most recent
status change was made by the
- current user. It translates to an SQL query with two inner joins and a
correlated subselect
- against the <literal>PAYMENT</literal>,
<literal>PAYMENT_STATUS</literal> and
+ <literal>AWAITING_APPROVAL</literal> status where the most recent
status change was made by the
+ current user. It translates to an SQL query with two inner joins and a
correlated subselect
+ against the <literal>PAYMENT</literal>,
<literal>PAYMENT_STATUS</literal> and
<literal>PAYMENT_STATUS_CHANGE</literal> tables.
</para>
- <programlisting><![CDATA[select count(payment), status.name
-from Payment as payment
+ <programlisting><![CDATA[select count(payment), status.name
+from Payment as payment
join payment.currentStatus as status
join payment.statusChanges as statusChange
where payment.status.name <> PaymentStatus.AWAITING_APPROVAL
or (
- statusChange.timeStamp = (
- select max(change.timeStamp)
- from PaymentStatusChange change
+ statusChange.timeStamp = (
+ select max(change.timeStamp)
+ from PaymentStatusChange change
where change.payment = payment
)
and statusChange.user <> :currentUser
@@ -1016,11 +1026,11 @@
order by status.sortOrder]]></programlisting>
<para>
- If I would have mapped the <literal>statusChanges</literal>
collection as a list, instead of a set,
+ If I would have mapped the <literal>statusChanges</literal>
collection as a list, instead of a set,
the query would have been much simpler to write.
</para>
-
- <programlisting><![CDATA[select count(payment), status.name
+
+ <programlisting><![CDATA[select count(payment), status.name
from Payment as payment
join payment.currentStatus as status
where payment.status.name <> PaymentStatus.AWAITING_APPROVAL
@@ -1031,9 +1041,9 @@
<para>
The next query uses the MS SQL Server <literal>isNull()</literal>
function to return all
the accounts and unpaid payments for the organization to which the current
user belongs.
- It translates to an SQL query with three inner joins, an outer join and a
subselect against
+ It translates to an SQL query with three inner joins, an outer join and a
subselect against
the <literal>ACCOUNT</literal>,
<literal>PAYMENT</literal>, <literal>PAYMENT_STATUS</literal>,
- <literal>ACCOUNT_TYPE</literal>,
<literal>ORGANIZATION</literal> and
+ <literal>ACCOUNT_TYPE</literal>,
<literal>ORGANIZATION</literal> and
<literal>ORG_USER</literal> tables.
</para>
@@ -1082,7 +1092,7 @@
</para>
<programlisting><![CDATA[select usr.id, usr.name
-from User as usr
+from User as usr
left join usr.messages as msg
group by usr.id, usr.name
order by count(msg)]]></programlisting>
@@ -1135,7 +1145,7 @@
<para>
Collection elements may be ordered or grouped using a query filter:
</para>
-
+
<programlisting><![CDATA[Collection orderedCollection = s.filter(
collection, "order by this.amount" );
Collection counts = s.filter( collection, "select this.type, count(this) group by
this.type" );]]></programlisting>
@@ -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: trunk/Hibernate3/src/org/hibernate/hql/ast/tree/DotNode.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/hql/ast/tree/DotNode.java 2006-11-21 17:38:43 UTC
(rev 10851)
+++ trunk/Hibernate3/src/org/hibernate/hql/ast/tree/DotNode.java 2006-11-21 17:39:14 UTC
(rev 10852)
@@ -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: trunk/Hibernate3/src/org/hibernate/hql/ast/tree/FromElement.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/hql/ast/tree/FromElement.java 2006-11-21 17:38:43
UTC (rev 10851)
+++ trunk/Hibernate3/src/org/hibernate/hql/ast/tree/FromElement.java 2006-11-21 17:39:14
UTC (rev 10852)
@@ -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:
trunk/Hibernate3/src/org/hibernate/persister/entity/AbstractEntityPersister.java
===================================================================
---
trunk/Hibernate3/src/org/hibernate/persister/entity/AbstractEntityPersister.java 2006-11-21
17:38:43 UTC (rev 10851)
+++
trunk/Hibernate3/src/org/hibernate/persister/entity/AbstractEntityPersister.java 2006-11-21
17:39:14 UTC (rev 10852)
@@ -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:
trunk/Hibernate3/src/org/hibernate/persister/entity/AbstractPropertyMapping.java
===================================================================
---
trunk/Hibernate3/src/org/hibernate/persister/entity/AbstractPropertyMapping.java 2006-11-21
17:38:43 UTC (rev 10851)
+++
trunk/Hibernate3/src/org/hibernate/persister/entity/AbstractPropertyMapping.java 2006-11-21
17:39:14 UTC (rev 10852)
@@ -34,20 +34,16 @@
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) throws
QueryException {
+ return new QueryException( "could not resolve property: " + propertyName +
" of: " + getEntityName() );
}
-
+
public String[] getColumnNames(String propertyName) {
String[] cols = (String[]) columnsByPropertyPath.get(propertyName);
if (cols==null) {
@@ -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++ ) {
@@ -74,10 +71,11 @@
return result;
}
- public String[] toColumns(String propertyName)
- throws QueryException {
+ 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++ ) {
@@ -100,28 +98,28 @@
}
/*protected void initPropertyPaths(
- final String path,
- final Type type,
- final String[] columns,
- final String[] formulaTemplates,
+ final String path,
+ final Type type,
+ final String[] columns,
+ final String[] formulaTemplates,
final Mapping factory)
throws MappingException {
//addFormulaPropertyPath(path, type, formulaTemplates);
initPropertyPaths(path, type, columns, formulaTemplates, factory);
}*/
-
+
protected void initPropertyPaths(
- final String path,
- final Type type,
- String[] columns,
- final String[] formulaTemplates,
+ final String path,
+ final Type type,
+ String[] columns,
+ final String[] formulaTemplates,
final Mapping factory)
throws MappingException {
-
+
if ( columns.length!=type.getColumnSpan(factory) ) {
- throw new MappingException(
- "broken column mapping for: " + path +
- " of: " + getEntityName()
+ throw new MappingException(
+ "broken column mapping for: " + path +
+ " of: " + getEntityName()
);
}
@@ -147,12 +145,12 @@
AbstractComponentType actype = (AbstractComponentType) type;
initComponentPropertyPaths( path, actype, columns, formulaTemplates, factory );
if ( actype.isEmbedded() ) {
- initComponentPropertyPaths(
- path==null ? null : StringHelper.qualifier(path),
- actype,
- columns,
- formulaTemplates,
- factory
+ initComponentPropertyPaths(
+ path==null ? null : StringHelper.qualifier(path),
+ actype,
+ columns,
+ formulaTemplates,
+ factory
);
}
}
@@ -162,21 +160,23 @@
}
protected void initIdentifierPropertyPaths(
- final String path,
- final EntityType etype,
- final String[] columns,
- final Mapping factory)
- throws MappingException {
+ final String path,
+ final EntityType etype,
+ final String[] columns,
+ 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,10 +184,22 @@
}
}
+ 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,
- final String[] columns,
+ final String path,
+ final AbstractComponentType type,
+ final String[] columns,
String[] formulaTemplates, final Mapping factory)
throws MappingException {
@@ -199,7 +211,7 @@
try {
int length = types[i].getColumnSpan(factory);
String[] columnSlice = ArrayHelper.slice(columns, begin, length);
- String[] formulaSlice = formulaTemplates==null ?
+ String[] formulaSlice = formulaTemplates==null ?
null : ArrayHelper.slice(formulaTemplates, begin, length);
initPropertyPaths(subpath, types[i], columnSlice, formulaSlice, factory);
begin+=length;
Modified: trunk/Hibernate3/src/org/hibernate/persister/entity/EntityPersister.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/persister/entity/EntityPersister.java 2006-11-21
17:38:43 UTC (rev 10851)
+++ trunk/Hibernate3/src/org/hibernate/persister/entity/EntityPersister.java 2006-11-21
17:39:14 UTC (rev 10852)
@@ -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;
@@ -74,6 +75,13 @@
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: trunk/Hibernate3/src/org/hibernate/tuple/entity/EntityMetamodel.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/tuple/entity/EntityMetamodel.java 2006-11-21
17:38:43 UTC (rev 10851)
+++ trunk/Hibernate3/src/org/hibernate/tuple/entity/EntityMetamodel.java 2006-11-21
17:39:14 UTC (rev 10852)
@@ -40,7 +40,7 @@
* @author Steve Ebersole
*/
public class EntityMetamodel implements Serializable {
-
+
private static final Log log = LogFactory.getLog(EntityMetamodel.class);
private static final int NO_VERSION_INDX = -66;
@@ -73,12 +73,13 @@
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private final Map propertyIndexes = new HashMap();
private final boolean hasCollections;
- private final boolean hasMutableProperties;
+ private final boolean hasMutableProperties;
private final boolean hasLazyProperties;
+ private final boolean hasNonIdentifierPropertyNamedId;
private final int[] naturalIdPropertyNumbers;
- private boolean lazy; //not final, proxy factory creation may fail
+ private boolean lazy; //not final because proxy factory creation can fail
private final boolean hasCascades;
private final boolean mutable;
private final boolean isAbstract;
@@ -95,7 +96,7 @@
private final Set subclassEntityNames = new HashSet();
private final EntityEntityModeToTuplizerMapping tuplizerMapping;
-
+
public EntityTuplizer getTuplizer(EntityMode entityMode) {
return (EntityTuplizer) tuplizerMapping.getTuplizer( entityMode );
}
@@ -103,7 +104,7 @@
public EntityTuplizer getTuplizerOrNull(EntityMode entityMode) {
return ( EntityTuplizer ) tuplizerMapping.getTuplizerOrNull( entityMode );
}
-
+
public EntityMode guessEntityMode(Object object) {
return tuplizerMapping.guessEntityMode( object );
}
@@ -151,7 +152,8 @@
boolean foundCascade = false;
boolean foundCollection = false;
boolean foundMutable = false;
-
+ boolean foundNonIdentifierPropertyNamedId = false;
+
while ( iter.hasNext() ) {
Property prop = ( Property ) iter.next();
@@ -162,11 +164,15 @@
else {
properties[i] = PropertyFactory.buildStandardProperty( prop, lazyAvailable );
}
-
+
if ( prop.isNaturalIdentifier() ) {
naturalIdNumbers.add( new Integer(i) );
}
+ if ( "id".equals( prop.getName() ) ) {
+ foundNonIdentifierPropertyNamedId = true;
+ }
+
// temporary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
boolean lazy = prop.isLazy() && lazyAvailable;
if ( lazy ) hasLazy = true;
@@ -181,7 +187,7 @@
propertyUpdateGeneration[i] = properties[i].isUpdateGenerated();
propertyVersionability[i] = properties[i].isVersionable();
nonlazyPropertyUpdateability[i] = properties[i].isUpdateable() && !lazy;
- propertyCheckability[i] = propertyUpdateability[i] ||
+ propertyCheckability[i] = propertyUpdateability[i] ||
( propertyTypes[i].isAssociationType() && ( (AssociationType)
propertyTypes[i] ).isAlwaysDirtyChecked() );
cascadeStyles[i] = properties[i].getCascadeStyle();
@@ -198,7 +204,7 @@
if ( indicatesCollection( properties[i].getType() ) ) {
foundCollection = true;
}
-
+
if ( propertyTypes[i].isMutable() && propertyCheckability[i] ) {
foundMutable = true;
}
@@ -206,7 +212,7 @@
mapPropertyToIndex(prop, i);
i++;
}
-
+
if (naturalIdNumbers.size()==0) {
naturalIdPropertyNumbers = null;
}
@@ -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:
@@ -275,22 +284,22 @@
Iterator iter = ( (Component) prop.getValue() ).getPropertyIterator();
while ( iter.hasNext() ) {
Property subprop = (Property) iter.next();
- propertyIndexes.put(
- prop.getName() + '.' + subprop.getName(),
- new Integer(i)
+ propertyIndexes.put(
+ prop.getName() + '.' + subprop.getName(),
+ new Integer(i)
);
}
}
}
-
+
public int[] getNaturalIdentifierProperties() {
return naturalIdPropertyNumbers;
}
-
+
public boolean hasNaturalIdentifier() {
return naturalIdPropertyNumbers!=null;
}
-
+
public Set getSubclassEntityNames() {
return subclassEntityNames;
}
@@ -358,7 +367,7 @@
}
return index.intValue();
}
-
+
public Integer getPropertyIndexOrNull(String propertyName) {
return (Integer) propertyIndexes.get( propertyName );
}
@@ -366,11 +375,15 @@
public boolean hasCollections() {
return hasCollections;
}
-
+
public boolean hasMutableProperties() {
return hasMutableProperties;
}
+ public boolean hasNonIdentifierPropertyNamedId() {
+ return hasNonIdentifierPropertyNamedId;
+ }
+
public boolean hasLazyProperties() {
return hasLazyProperties;
}
@@ -434,11 +447,11 @@
public boolean isAbstract() {
return isAbstract;
}
-
+
public String toString() {
return "EntityMetamodel(" + name + ':' +
ArrayHelper.toString(properties) + ')';
}
-
+
// temporary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public String[] getPropertyNames() {
return propertyNames;
Added:
trunk/Hibernate3/test/org/hibernate/test/idprops/IdentifierPropertyReferencesTest.java
===================================================================
---
trunk/Hibernate3/test/org/hibernate/test/idprops/IdentifierPropertyReferencesTest.java 2006-11-21
17:38:43 UTC (rev 10851)
+++
trunk/Hibernate3/test/org/hibernate/test/idprops/IdentifierPropertyReferencesTest.java 2006-11-21
17:39:14 UTC (rev 10852)
@@ -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: trunk/Hibernate3/test/org/hibernate/test/idprops/LineItem.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/idprops/LineItem.java 2006-11-21 17:38:43 UTC
(rev 10851)
+++ trunk/Hibernate3/test/org/hibernate/test/idprops/LineItem.java 2006-11-21 17:39:14 UTC
(rev 10852)
@@ -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: trunk/Hibernate3/test/org/hibernate/test/idprops/LineItemPK.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/idprops/LineItemPK.java 2006-11-21 17:38:43
UTC (rev 10851)
+++ trunk/Hibernate3/test/org/hibernate/test/idprops/LineItemPK.java 2006-11-21 17:39:14
UTC (rev 10852)
@@ -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: trunk/Hibernate3/test/org/hibernate/test/idprops/Mapping.hbm.xml
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/idprops/Mapping.hbm.xml 2006-11-21 17:38:43
UTC (rev 10851)
+++ trunk/Hibernate3/test/org/hibernate/test/idprops/Mapping.hbm.xml 2006-11-21 17:39:14
UTC (rev 10852)
@@ -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: trunk/Hibernate3/test/org/hibernate/test/idprops/Order.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/idprops/Order.java 2006-11-21 17:38:43 UTC
(rev 10851)
+++ trunk/Hibernate3/test/org/hibernate/test/idprops/Order.java 2006-11-21 17:39:14 UTC
(rev 10852)
@@ -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: trunk/Hibernate3/test/org/hibernate/test/idprops/Person.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/idprops/Person.java 2006-11-21 17:38:43 UTC
(rev 10851)
+++ trunk/Hibernate3/test/org/hibernate/test/idprops/Person.java 2006-11-21 17:39:14 UTC
(rev 10852)
@@ -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: trunk/Hibernate3/test/org/hibernate/test/legacy/CustomPersister.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/legacy/CustomPersister.java 2006-11-21
17:38:43 UTC (rev 10851)
+++ trunk/Hibernate3/test/org/hibernate/test/legacy/CustomPersister.java 2006-11-21
17:39:14 UTC (rev 10852)
@@ -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;
@@ -76,7 +77,7 @@
public String getEntityName() {
return Custom.class.getName();
}
-
+
public boolean isSubclassEntityName(String entityName) {
return Custom.class.getName().equals(entityName);
}
@@ -616,4 +617,9 @@
public Comparator getVersionComparator() {
return null;
}
+
+ public EntityMetamodel getEntityMetamodel() {
+ return null;
+ }
+
}