tags:

views:

2318

answers:

4

I have a table with one field that can point to a foreign key in one of 3 other tables based on what the descriminator value is (Project, TimeKeep, or CostCenter. Usually this is implemented with subclasses, and I am wondering if what I have below will work. Note the subclass name is the same as the parent class and the noteObject property is mapped to an instance variable of type java.lang.Object so it should accept either a Project, TimeKeep or CostCenter object as long as we cast to the correct type. Will hibernate allow this? Thanks.

<hibernate-mapping package="com.tlr.finance.mappings">

 <class name="AdminNotes" table="admin_notes">
    <id name="adminNoteId" column="admin_note_id" type="integer">
      <generator class="identity" />
    </id>

<discriminator column="note_type" type="string" />

<!-- make this property an enumerated type.  It is the discriminator -->
<property name="adminNoteType" column="note_type" type="string" not-null="true" />
<property name="adminNote" column="note" type="string" not-null="true" />
<property name="adminNoteAdded" column="note_date" type="timestamp"
  not-null="true" /> 

<subclass name="AdminNotes" discriminator-value="project" >
  <many-to-one name="noteObject" column="object_id" class="PsData" /><!-- Project -->
</subclass>

<subclass name="AdminNotes" discriminator-value="user" >
  <!-- rename timekeep to user -->
  <many-to-one name="noteObject" column="object_id" class="Timekeep" /><!-- user -->
</subclass>

<subclass name="AdminNotes" discriminator-value="costCenter" >
  <!-- rename timekeep to user -->
  <many-to-one name="noteObject" column="object_id" class="CostCenter" /><!-- cost center -->
</subclass>

  </class>

</hibernate-mapping>
+1  A: 

Discriminators are used for for storing class hierarchies in a single table. What you have there is a single class with multiple meanings.

http://www.hibernate.org/hib_docs/v3/reference/en-US/html/mapping.html#mapping-declaration-discriminator

The element is required for polymorphic persistence using the table-per-class-hierarchy mapping strategy and declares a discriminator column of the table. The discriminator column contains marker values that tell the persistence layer what subclass to instantiate for a particular row.

I don't think you'll be able to use a single AdminNote class for each of those different meanings. The discriminator is used at the database level to help distinguish one subclass from another - it's not actually part of the java object model.

You'll need to define multiple subclasses of AdminNote, one for each discriminator value.

skaffman
You are correct. I just had the chance to run my mapping and this is the error it gave meorg.hibernate.MappingException: Could not read mappings from resource: com/tlr/finance/mappings/AdminNotes.hbm.xmlSo it thinks the class is being mapped multiple times and Hibernate will not allow that.
Mike Pone
A: 

AFAIK you can reuse java class names for subclass mappings if you use enity-names.

Try the mapping below. Here the super class mapping itself is abstract. The subclasses use the same java class and an entity-name for each subclass. You might need to put entity-name to the super class mapping. However, I would personally use separate java subclasses for each subclass mapping, as with entity-names you would have to provide entity-name when you need to persist an object via Session api, or implement entity-names resolution in Tuplizer (hibernate 3.3.2) based on adminNoteType field.

<hibernate-mapping package="com.tlr.finance.mappings">

 <class name="AdminNotes" table="admin_notes" abstract="true">
    <id name="adminNoteId" column="admin_note_id" type="integer">
      <generator class="identity" />
    </id>

<discriminator column="note_type" type="string" />

<!-- make this property an enumerated type.  It is the discriminator -->
<property name="adminNoteType" column="note_type" type="string" not-null="true" />
<property name="adminNote" column="note" type="string" not-null="true" />
<property name="adminNoteAdded" column="note_date" type="timestamp"
  not-null="true" /> 

<subclass name="AdminNotes" discriminator-value="project" entity-name="project">
  <many-to-one name="noteObject" column="object_id" class="PsData" /><!-- Project -->
</subclass>

<subclass name="AdminNotes" discriminator-value="user" entity-name="user">
  <!-- rename timekeep to user -->
  <many-to-one name="noteObject" column="object_id" class="Timekeep" /><!-- user -->
</subclass>

<subclass name="AdminNotes" discriminator-value="costCenter" entity-name="costCenter">
  <!-- rename timekeep to user -->
  <many-to-one name="noteObject" column="object_id" class="CostCenter" /><!-- cost center -->
</subclass>

  </class>

</hibernate-mapping>

Hope it hepls.

Anton

Anton
A: 

You've already accepted an answer to the asked question (i.e. mapping using the same class as subclasses using a discriminator is not allowed), but I think it's worth adding the following alternative answer to the situation you've described:

Instead of trying to use the inheritance capabilities to get the reference to the right class, you should instead consider using the <any/> mapping type where you use the note_type to determine the type that the object_id is referencing and thus setting your noteObject value to the proper object reference.

For details on <any/>, see 'Any type mappings' in the NHibernate docs, and this blog post.

iammichael
whoops. Gave NHibernate references instead of Hiberate, but I believe the same information should hold for both Hibernate platforms.
iammichael
A: 

Discriminator as integer

Normally if You specify the discriminator-value as integer in subclass you get the error

"Could not format discriminator value 'TYPE' to sql string using the (...)"

If You wish to use the discriminator is a integer value. You need to first of all specify it for the base class as integer by setting the discriminator-value attribute in class element.

<class name="AdminNotes" table="admin_notes" abstract="true" discriminator-value= "-1">

This replace the default behavior where the discriminator is a class name when not value found.

<hibernate-mapping package="com.tlr.finance.mappings">

 <class name="AdminNotes" table="admin_notes" abstract="true" discriminator-value= "-1">
    <id name="adminNoteId" column="admin_note_id" type="integer">
      <generator class="identity" />
    </id>

<discriminator column="note_type" type="integer" />

<!-- make this property an enumerated type.  It is the discriminator -->
<property name="adminNoteType" column="note_type" type="string" not-null="true" />
<property name="adminNote" column="note" type="string" not-null="true" />
<property name="adminNoteAdded" column="note_date" type="timestamp"
  not-null="true" /> 

<subclass name="AdminNotes" discriminator-value="0" entity-name="project">
  <many-to-one name="noteObject" column="object_id" class="PsData" /><!-- Project -->
</subclass>

<subclass name="AdminNotes" discriminator-value="1" entity-name="user">
  <!-- rename timekeep to user -->
  <many-to-one name="noteObject" column="object_id" class="Timekeep" /><!-- user -->
</subclass>

<subclass name="AdminNotes" discriminator-value="2" entity-name="costCenter">
  <!-- rename timekeep to user -->
  <many-to-one name="noteObject" column="object_id" class="CostCenter" /><!-- cost center -->
</subclass>

  </class>

</hibernate-mapping>

Hope this help to someone

Vash