Discriminator column for collections?

Is there something similar to the discriminator column that can be used for sets in Hibernate 4.3.7?

We have a base class AbstractMaterial, and two subclasses RawMaterial and CarbonMaterial. Three sets are contained in a Product class, one for each material type. Each collection is stored in the same table, and I recently noticed that this results in only the last set actually getting saved. This seems to be because when updating the collection, Hibernate will first delete everything in the table with a Product ID matching the object we are saving and then insert all elements in the set.

Mapping excerpt:

<set lazy="true" name="rawMaterials" sort="natural" table="R053_TPG_RMALLOWED">
    <key column="TP_GROUP_ID"/>
    <many-to-many class="AbstractMaterial" column="RAWMATERIAL_ID" not-found="ignore" unique="false"/>
</set>
<!-- RawMaterial has extra data in table R023 -->
<set lazy="true" name="rawMaterialWithConstraints" table="R053_TPG_RMALLOWED">
    <key column="TP_GROUP_ID"/>
    <composite-element class="RawMaterial">
        <property column="RAWMATERIAL_ID" name="id"/>
        <many-to-one cascade="save-update,delete" class="MassConstraint" column="MASS_CONSTRAINT_ID" lazy="false" name="massConstraint" not-found="ignore"/>
    </composite-element>
</set>
<!-- CarbonMaterial has extra data in table R025 -->
<set lazy="true" name="carbonMaterialWithConstraints" table="R053_TPG_RMALLOWED">
    <key column="TP_GROUP_ID"/>
    <composite-element class="CarbonMaterial">
        <property column="RAWMATERIAL_ID" name="id"/>
        <many-to-one cascade="save-update,delete" class="MassConstraint" column="MASS_CONSTRAINT_ID" lazy="false" name="massConstraint" not-found="ignore"/>
    </composite-element>
</set>

I found something called subselect in the DTD, but could not find any documentation on how to use it. So I would like to to something like

<set lazy="true" name="rawMaterials" sort="natural" table="R053_TPG_RMALLOWED">
    <key column="TP_GROUP_ID"/>
    <discriminator column="MATERIAL_CATEGORY" value="2">
    <many-to-many class="AbstractMaterial" column="RAWMATERIAL_ID" not-found="ignore" unique="false"/>
</set>
<set lazy="true" name="rawMaterialWithConstraints" table="R053_TPG_RMALLOWED">
    <key column="TP_GROUP_ID"/>
    <discriminator column="MATERIAL_CATEGORY" value="5">
    <composite-element class="RawMaterial">
        <property column="RAWMATERIAL_ID" name="id"/>
        <many-to-one cascade="save-update,delete" class="MassConstraint" column="MASS_CONSTRAINT_ID" lazy="false" name="massConstraint" not-found="ignore"/>
    </composite-element>
</set>
<set lazy="true" name="carbonMaterialWithConstraints" table="R053_TPG_RMALLOWED">
    <key column="TP_GROUP_ID"/>
    <discriminator column="MATERIAL_CATEGORY" value="7">
    <composite-element class="CarbonMaterial">
        <property column="RAWMATERIAL_ID" name="id"/>
        <many-to-one cascade="save-update,delete" class="MassConstraint" column="MASS_CONSTRAINT_ID" lazy="false" name="massConstraint" not-found="ignore"/>
    </composite-element>
</set>

The materialCategory value is part of the AbstractMaterial class.

EDIT: We have a whole bunch of Material classes, around half of which are subclasses of RawMaterial, but then we also have some others. We want this list to contain RawMaterials (and its subclasses) or CarbonMaterials, but not the other kinds. In all other mappings we can use AbstractMaterial as the class, but for composite-element it tries to instantiate the (abstract) AbstractMaterial rather than the actual class that ID represents.

With SQL debugging turned on, it prints something like this:

delete from R053_TPG_RMALLOWED where TP_GROUP_ID=? // this is the first set
insert into R053_TPG_RMALLOWED values (?, ?, ?, ?)
insert into R053_TPG_RMALLOWED values (?, ?, ?, ?)
insert into R053_TPG_RMALLOWED values (?, ?, ?, ?)
delete from R053_TPG_RMALLOWED where TP_GROUP_ID=? // this is the second set
insert into R053_TPG_RMALLOWED values (?, ?, ?, ?)
delete from R053_TPG_RMALLOWED where TP_GROUP_ID=? // this is the third set
insert into R053_TPG_RMALLOWED values (?, ?, ?, ?)
insert into R053_TPG_RMALLOWED values (?, ?, ?, ?)
insert into R053_TPG_RMALLOWED values (?, ?, ?, ?)

I would like to modify the delete statements to be something like

delete from R053_TPG_RMALLOWED where TP_GROUP_ID=? and MASS_CONSTRAINT_ID is null
...
delete from R053_TPG_RMALLOWED where TP_GROUP_ID=? and MASS_CONSTRAINT_ID is not null
...
delete from R053_TPG_RMALLOWED where TP_GROUP_ID=? and RAWMATERIAL_ID in (
  select RAWMATERIAL_ID from R025_CARBON_MATERIAL)

Ah, there’s a tag named <sql-delete-all> that solves the problem for me. Nice.

Don’t map the same table as collection multiple times. This will lead to issues. Instead, map the table as collection just once, and split up the collection into 3 sub-collections in Java code, based on your own logic.

We tried that, but since AbstractMaterial is an abstract class and Hibernate fails to instatiate each material as a RawMaterial or CarbonMaterial (when it is trying to extract the MassConstraint from the Material), we had to keep the multiple mappings.

Another workaround was to give each collection its own table, but that needs quite a lot of copying of existing data.

There is no inheritance for composite types, only for entities. So if you want to model this, you have to make AbstractMaterial an abstract entity, i.e. @Entity abstract class AbstractMaterial {...} and the subclasses define a discriminator.

The entity is abstract

<class abstract="true" name="AbstractMaterial" table="R022_RM_ABSTRACT">

and when getting a material it works

        case 
            when rawmateria0_2_.RAWMATERIAL_ID is not null then 2 
            when rawmateria0_3_.RAWMATERIAL_ID is not null then 3 
            when rawmateria0_.RAWMATERIAL_ID is not null then 1 
        end as clazz_0_ 
    from
        R023_RM_SCRAP rawmateria0_ 
    inner join
        R022_RM_ABSTRACT rawmateria0_1_ 
            on rawmateria0_.RAWMATERIAL_ID=rawmateria0_1_.RAWMATERIAL_ID 
    left outer join
        R030_RM_ORE rawmateria0_2_ 
            on rawmateria0_.RAWMATERIAL_ID=rawmateria0_2_.RAWMATERIAL_ID 
    left outer join
        R026_RM_DRI rawmateria0_3_ 
            on rawmateria0_.RAWMATERIAL_ID=rawmateria0_3_.RAWMATERIAL_ID 
    where
        rawmateria0_.RAWMATERIAL_ID=?

but for the composite it does not join with any of the Material tables

    from
        R053_TPG_RMALLOWED rawmateria0_ 
    left outer join
        R169_MASSCONSTRAINT massconstr1_ 
            on rawmateria0_.MASS_CONSTRAINT_ID=massconstr1_.MASSCONSTRAINT_ID 
    where
        rawmateria0_.TP_GROUP_ID=?

but for the composite it does not join with any of the Material tables

Exactly, because there is no inheritance support for composite types. Either way, I would suggest you to use the annotation model instead as hbm.xml is deprecated since 6.0 and a lot more people are capable to help you with questions about the annotation model.

1 Like