Search code examples
hibernatenhibernate-mapping

Hibernate bag with multiple elements


I have a bag whose structure looks like this:

<bag name="foo" fetch="select" table="table_of_foos">
    <key column="foo_ids"/>
    <many-to-many class="Bar" column="bar_ids"/>
</bag>

My "table_of_foos" table has two columns: "foo_ids" and "bar_ids", neither of which is unique. I want to add another column to this table that holds some information about the relationship of foos to bars, so that my table would hold three columns. Then, I want to be able to access this information in my webapp.

Unfortunately, it doesn't seem as if I can add a property or an element to the bag. What I want to do is something like this:

<bag name="foo" fetch="select" table="table_of_foos">
    <key column="foo_ids"/>
    <many-to-many class="Bar" column="bar_ids"/>
    <element column="still_valid" type="integer"/>
</bag>

What is the best way to accomplish this?


Solution

  • If your joined Table needs an additional column other than its (composite) primary-key, you must implement an additional joined class.

    public class Foo {
    
        Collection<FooBar> bar = new ArrayList<FooBar>();
    
    }
    
    public class Bar {
    
        Collection<FooBar> foo = new ArrayList<FooBar>();
    
    }
    

    The joined class (which needs a composite primary key - implemented as a static inner class) is described as follows

    public class FooBar {
    
        private FooBarId fooBarId;
    
        private String additionalProperty;
    
        public static class FooBarId implements Serializable {
    
            private Integer fooId;
            private Integer barId;
    
            private Foo foo;
            private Bar bar;
    
            // getter's and setter's
    
            public FooBarId() {}
            public FooBarId(Integer fooId, Integer barId) {
                this.fooId = fooId;
                this.barId = barId;
            }
    
            public boolean equals(Object o) {
                if(!(o instanceof FooBarId))
                    return false;
    
                FooBarId other = (FooBarId) o;
                return new EqualsBuilder()
                           .append(getFooId(), other.getFooId())
                           .append(getBarId(), other.getBarId())
                           .isEquals();
            }
    
            public int hashCode() {
                return new HashCodeBuilder()
                           .append(getFooId())
                           .append(getBarId())
                           .hashCode();
            }
    
        }
    
    }
    

    re-write your mapping as

    /* Foo.class */ 
    
    <bag name="bar" table="table_of_foos">
        <key column="BAR_ID" insert="false" update="false"/>
        <one-to-many class="FooBar" table="table_of_foos"/>
    </bag>
    
    /* Bar.class */ 
    
    <bag name="foo" table="table_of_foos">
        <key column="FOO_ID" insert="false" update="false"/>
        <one-to-many class="FooBar" table="table_of_foos"/>
    </bag>
    

    And FooBar mapping

    <class name="FooBar">
        <composite-id name="fooBarId" class="FooBar$FooBarId">
            <key-property name="fooId" column="FOO_ID"/>
            <key-property name="barId" column="BAR_ID"/>
        </composite-id>
        <property name="additionalProperty" type="string"/>
        <many-to-one name="foo" column="FOO_ID" class="Foo" insert="false" update="false"/>
        <many-to-one name="bar" column="BAR_ID" class="Bar" insert="false" update="false"/>
    </class>
    

    If you want, you can also map a composite element instead of a joined class (A component element does not need a (composite) primary key and has its has its lifecycle bound to that of its owning Entity instance. Keep this in mind)

    Create you composite element (now without identifier)

    public class FooBar {
    
        private String additionalProperty;
    
        private Foo foo;
        private Bar bar;
    
    }
    

    And define the following mapping

    /* Foo.class */ 
    
    <bag name="bar" table="table_of_foos">
        <key column="BAR_ID"/>
        <composite-element class="FooBar">
            <property name="additionalProperty" type="string"/>
            <many-to-one name="foo" column="FOO_ID" class="Foo" insert="false" update="false"/>
            <many-to-one name="bar" column="BAR_ID" class="Bar"/>
        </composite-element>
    </bag>
    
    /* Bar.class */ 
    
    <bag name="foo" table="table_of_foos">
        <key column="FOO_ID"/>
        <composite-element class="FooBar">
            <property name="additionalProperty" type="string"/>
            <many-to-one name="foo" column="FOO_ID" class="Foo" insert="false" update="false"/>
            <many-to-one name="bar" column="BAR_ID" class="Bar"/>
        </composite-element>
    </bag>