Reputation: 879
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?
Upvotes: 1
Views: 4654
Reputation: 3288
I wasn't able to get these mapping to work as written by Arthur Ronald F D Garcia. This code didn't work and resulted in a MappingException.
Invalid Attempt
<set name="StoreDepartments" table="`ou_store_org_unit`">
<key column="`ou_id`" />
<composite-element class="NHibernate.Map.OrganizationUnit+StoreDepartment+Relation, CentralDataLayer">
<property name="Id" column="`ou_store_id`" type="Int32" />
<many-to-one name="OrganizationUnit" column="`ou_id`" class="NHibernate.Map.OrganizationUnit, CentralDataLayer" />
<many-to-one name="StoreDepartment" column="`store_ou_id`" class="NHibernate.Map.OrganizationUnit+StoreDepartment, CentralDataLayer" />
</composite-element>
</set>
Resulting Error: Repeated column in mapping for collection:
NHibernate.Map.OrganizationUnit.StoreDepartments column: ou_id
I was able to get it working with the following.
Valid Attempt
<set name="StoreDepartments" table="`ou_store_org_unit`">
<key column="`ou_id`" />
<composite-element class="NHibernate.Map.OrganizationUnit+StoreDepartment+Relation">
<parent name="OrganizationUnit" />
<property name="Id" column="`ou_store_id`" type="Int32" />
<many-to-one name="StoreDepartment" column="`store_ou_id`" class="NHibernate.Map.OrganizationUnit+StoreDepartment" />
</composite-element>
</set>
Additionally:
If nHibernate tries to update your set even if you don't modify it, try overriding the GetHasCode
and Equals
methods. More information here.
Upvotes: 0
Reputation: 33783
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>
Upvotes: 3