views:

389

answers:

2

I have a table Users


CREATE TABLE "USERS" (
    "ID" NUMBER NOT NULL ,
    "LOGINNAME" VARCHAR2 (150) NOT NULL )

and I have a second table SpecialUsers. No UserId can occur twice in the SpecialUsers table, and only a small subset of the ids of users in the Users table are contained in the SpecialUsers table.


CREATE TABLE "SPECIALUSERS" (
    "USERID" NUMBER NOT NULL,
 CONSTRAINT "PK_SPECIALUSERS" PRIMARY KEY ("USERID") )

ALTER TABLE "SPECIALUSERS" ADD CONSTRAINT "FK_SPECIALUSERS_USERID" FOREIGN KEY ("USERID") 
REFERENCES "USERS" ("ID") 
/

Mapping the Users table in Hibernate works ok


<hibernate-mapping package="com.initech.domain">
    <class name="com.initech.User" table="USERS">

        <id name="id" column="ID" type="java.lang.Long">
            <meta attribute="use-in-tostring">true</meta>
            <generator class="sequence">
                <param name="sequence">SEQ_USERS_ID</param>
            </generator>
        </id>

        <property name="loginName" column="LOGINNAME" type="java.lang.String" not-null="true">
            <meta attribute="use-in-tostring">true</meta>
        </property>

    </class>
</hibernate-mapping>

But I'm struggling in creating the mapping for the SpecialUsers table. All the examples (e.g. in Hibernate documentation) in Internet I found don't have this type of self-reference. I tried a mapping like this:


<hibernate-mapping package="com.initech.domain">
    <class name="com.initech.User" table="SPECIALUSERS">        

        <id name="id" column="USERID">
            <meta attribute="use-in-tostring">true</meta>
            <generator class="foreign">
                <param name="property">user</param>
            </generator>
        </id>

        <one-to-one name="user" class="User"/>

    </class>
</hibernate-mapping>

but got the error


Invocation of init method failed; nested exception is org.hibernate.DuplicateMappingException: 
Duplicate class/entity mapping com.initech.User 

How should I map the SpecialUsers table? What I need on the application level is a list of the User objects contained in the SpecialUsers table.

+1  A: 

If you really have to use a second table, you must map it to a new class. Hibernate can always only map one table to one class.

Alternatively, add a column to the user table which says "this user is special". If that is not an option, create a view which joins the two tables and thus simulates this column. Then, you can select users by this column.

If you go the two class way, you must load a list of the special users and do the mapping in your application.

[EDIT] Maybe you should look into parent-child relations. In your case, the user is the parent and its roles are the children. So every user has 0..n roles which are mapped in a different table. You can then use this HQL

from User u where :role in elements(u.roles)

to find all users with a certain role. But most of the time, you'll have a specific user and just need to query whether she has a certain role.

Aaron Digulla
Thanks, the point about roles is insightful.
simon
Aaroon, while the initial title of the question was pretty confusing, I don't think that your edit reflects the initial intend (it matches your answer but this is not what the OP was asking for IMHO). And the physical model doesn't represent a 0..n relation BTW, it's a pure inheritance relation.
Pascal Thivent
+1  A: 

How should I map the SpecialUsers table?

According to the structure of your USERS and SPECIALUSERS tables, you have a Table per subclass hierarchy and it is perfectly possible to map this kind of hierarchy with Hibernate.

First, make sure that SpecialUser inherits from User at the object model level (this is an inheritance relationship, not a self-reference).

public class SpecialUser extends User { 
}

Then, declare a <joined-subclass> element in the User's mapping:

<hibernate-mapping package="com.initech.domain">
    <class name="com.initech.User" table="USERS">

        <id name="id" column="ID" type="java.lang.Long">
            <meta attribute="use-in-tostring">true</meta>
            <generator class="sequence">
                <param name="sequence">SEQ_USERS_ID</param>
            </generator>
        </id>

        <property name="loginName" column="LOGINNAME" type="java.lang.String" not-null="true">
            <meta attribute="use-in-tostring">true</meta>
        </property>

        <joined-subclass name="com.initech.SpecialUser" table="SPECIALUSERS">
            <key column="USERID"/>
        </joined-subclass>

    </class>
</hibernate-mapping>

What I need on the application level is a list of the User objects contained in the SpecialUsers table.

The following HQL query would return all the SpecialUser (which are User too):

from SpecialUser

If later you need to add columns to the SPECIALUSERS table, add the corresponding attributes to the SpecialUser class and map them inside the <joined-subclass> element. Refer to the provided link (to the documentation) for the details.

Pascal Thivent