Hibernate many-to-many with subclasses using the same pivot table - java

Having this model schema:
Person
|__ Student
|__ SchoolBoy
|__ CollegeStudent
I'm using Hibernate 3.6 and I use the tperson table for all the classes, using a discriminator column. My mapping is done like that:
<class name="Person" table="tperson" discriminator-value="PERSON">
<id name="Id" column="id" type="integer">
<generator class="increment" />
</id>
<discriminator column="person_type" />
<subclass name="Student" discriminator-value="STUDENT">
<key column="id_person" />
<subclass name="SchoolBoy" discriminator-value="SCHOOL_BOY">
<join table="tstudent">
<key column="id_person" />
</join>
</subclass>
<subclass name="CollegeStudent" discriminator-value="COLLEGE_STUDENT">
<join table="tstudent">
<key column="id_person" />
</join>
</subclass>
</subclass>
</class>
Now I want to introduce the Course entity, implementing a relation between courses and students. Of course, this is a many-to-many relation. Let's suppose I use a pivot table named tstudent_course, which contains students of both types SchoolBoy and CollegeStudent. This table contains a reference to the person itself and the course he's studying.
Now I want to differ between college and school students when I load the Course entity. I do it like that:
<set name="CollegeStudents" table="tstudent_course"
inverse="true">
<key>
<column name="id_course" not-null="true" />
</key>
<many-to-many entity-name="CollegeStudent">
<column name="id_person" not-null="true" />
</many-to-many>
</set>
<set name="SchoolStudents" table="tstudent_course"
inverse="true">
<key>
<column name="id_course" not-null="true" />
</key>
<many-to-many entity-name="SchoolBoy">
<column name="id_person" not-null="true" />
</many-to-many>
</set>
However, being the pivot table a table which contains references to every type of students, it tries to load every single student in my collections and I receive the next Exception:
Object with id: 2 was not of the specified subclass:
CollegeStudent (loaded object was of wrong class class SchoolBoy)
It seems Hibernate is doing the join whithout evaluating the concrete type of student I have and tries to inject an SchoolBoy in my collection of College Students.
What can I do to avoid that? Is it possible to stablish a kind of discrimination in the pivot table? Or do I have to create an specific pivot table for each kind of subclass?

In your set you can add a filter:
<set name="CollegeStudents" table="tstudent_course"
inverse="true">
<key>
<column name="id_course" not-null="true" />
</key>
<many-to-many entity-name="CollegeStudent" where="person_type='COLLEGE_STUDENT'">
<column name="id_person" not-null="true" />
</many-to-many>
</set>
IMHO the mapping would be better without that filter (just a set of all students).

Related

Initial SessionFactory creation failed.org.hibernate.MappingException: Repeated column

I am getting following exception while calling a main method.
I am posting mapping file here.
I think this is due to
<list-index>
<column name="bill_no" />
</list-index>
What should be value for this
<hibernate-mapping>
<class name="iland.hbm.BillDetails" table="bill_details" catalog="retail_shop">
<id name="billNo" type="java.lang.Long">
<column name="bill_no" />
<generator class="identity"></generator>
</id>
<many-to-one name="customerDetails" class="iland.hbm.CustomerDetails" fetch="join">
<column name="customer_id" not-null="true" />
</many-to-one>
<property name="subTotal" type="java.lang.Float">
<column name="sub_total" precision="10" />
</property>
<list name="billProductDetailses" table="bill_product_details" inverse="true" lazy="false" fetch="join">
<key>
<column name="bill_no" not-null="true" />
</key>
<list-index>
<column name="bill_no" />
</list-index>
<one-to-many class="iland.hbm.BillProductDetails" />
</list>
</class>
</hibernate-mapping>
Error:
Initial SessionFactory creation failed.org.hibernate.MappingException: Repeated column in mapping for collection: iland.hbm.BillDetails.billProductDetailses column: bill_no
Exception in thread "main" java.lang.ExceptionInInitializerError
at iland.database.HibernateUtil.<clinit>(HibernateUtil.java:32)
at iland.bill.BillDAO.fetchAll(BillDAO.java:94)
Caused by: org.hibernate.MappingException: Repeated column in mapping for collection: iland.hbm.BillDetails.billProductDetailses column: bill_no
at org.hibernate.mapping.Collection.checkColumnDuplication(Collection.java:341)
at org.hibernate.mapping.Collection.checkColumnDuplication(Collection.java:354)
How to resolve above error.
Indeed you are mapping twice the same column in your entity.
<list name="billProductDetailses" table="bill_product_details" inverse="true" lazy="false" fetch="join">
<key>
<column name="bill_no" not-null="true" />
</key>
<list-index>
<column name="bill_no" />
</list-index>
<one-to-many class="iland.hbm.BillProductDetails" />
</list>
When you map twice the same column (bill_product_details.bill_no) in an entity (suppose the properties A and B of your object reference the same column), Hibernate produces that exception because it doesn't know when to update/insert the datum on the db (when A is changed or when B is changed? And what if A and B are different?), so you have to tell it which one is the "master", let's say.
In addition, let me point out that I don't understand your mapping. The "key" should map to the column with the foreign key constraint, while the index should map the column indicating what will be the object's position in your list/array, so I really don't get why they both reference the same column. This way the position of the object in the array will be equal to the id of the referenced entry. Won't it?

How to set a null in the database for the integer field using hibernate?

I am trying to update a field in the database to null for an integer field. I am trying to do that using hibernate. I can set object field like String and any other object to null but no integer.
<?xml version="1.0" encoding="UTF-8"?>
<class name="App_Users" table="app_users" schema="bidtool">
<id name="userId" type="int" column="user_id">
<generator class="assigned"/>
</id>
<property name="username" type="string">
<column name="username" length="20" not-null="true" />
</property>
<property name="password" type="string">
<column name="password" length="20" not-null="true" />
</property>
<property name="firstname" type="string">
<column name="firstname" length="20" />
</property>
<property name="lastname" type="string">
<column name="lastname" length="20" />
</property>
<property name="userType" type="int">
<column name="user_type" />
</property>
<many-to-one class="MasterOrg" fetch="select" name="masterOrg">
<column name="master_org_id" />
</many-to-one>
<many-to-one class="CarrierScac" fetch="select" name="carrierScac">
<column name="scac" />
</many-to-one>
<one-to-one class="AppUserDetails" fetch="select" name="details" constrained="true"/>
<set name="profiles" inverse="true">
<key>
<column name="user_id" />
</key>
<one-to-many class="Profiles" />
</set>
<set name="boilerPlates" inverse="true">
<key>
<column name="user_id" />
</key>
<one-to-many class="BoilerPlate" />
</set>
<set name="rates" inverse="true" >
<key>
<column name="user_id" />
</key>
<one-to-many class="BidToolRates" />
</set>
</class>
In the above hibernate mapping code, I want to set MasterOrg field as null.
It is best to use the object wrappers for the primitive types i.e. Integer for int, Double for double, ... etc because primitive types don't allow for the possibility of null which is always possible in a database design.
Even if a value is declared not null in the database an Object type is still useful. Consider the follow scenario as an example.
#Entity
public class ExampleEntity {
#Column(name="some_column") // assume this column is defined not null in the database
private int someProperty;
getttes settters other fields go here
}
Assume you write the following code
ExampleEntity t = new ExampleEntity();
entityManager.persist(t);
In this example t.someProperty has a value of 0 because that is the default value for an int therefore entityManager.persist works but maybe 0 is not a valid value for that column. If you have database constraints on that column then you get an error otherwise you have bad data in the database.
If someProperty was declared with wrapper type of Integer and the developer forgets to set the somePorpety value then you will get a not null exception.
A second reason to always use wrappers is simplicity as a developer I want consistent structure across entities because code is read more often that it is written so universally using wrapper types on entities makes things predictable for some one maintaining the code 5 years from now.

How do I join on two columns in the hibernate mapping file using the <join> tag?

I need to map a single class to two tables (both with multiple columns primary key). Let's say TABLE1 has id1, id2, id3 and TABLE2 has id1, id2 as primary keys. Now when writing the mapping file I would do something like the following:
<hibernate-mapping package="beans">
<class name="TABLE1Class" table="TABLE1">
<composite-id name="table1PK" class="TABLE1PKClass">
<key-many-to-one name="id1" class="ID1Class" column="id1"/>
<key-many-to-one name="id2" class="ID2Class" column="id2"/>
<key-many-to-one name="id3" class="ID3Class" column="id3"/>
</composite-id>
<property name="someProperty" type="integer" not-null="true" column="x"/>
<join table="TABLE2">
<key column="id1" />
<!-- <key column="id2"/> The join tag accepts only one key tag!!!
How do I map the second key??? -->
<property name="propertyFromTable2" type="float" not-null="true"/>
</join>
</class>
</hibernate-mapping>
As you can see the join tag accepts only one key tag! How do I map the second Id?
<key> may contain multiple <column> elements:
<key>
<column name = "id1" />
<column name = "id2" />
<column name = "id3" />
</key>

Hibernate One To Many Unidirectional Mapping List

I have one-to-many relationship between parent and child Java objects. The parent object uses java.util.List that stores multiple child objects. The problem I am experiencing is when updating the parent object after I have added one or more child object(s) to the List in the parent. I am using the saveOrUpdate method to save or update the parent. It works fine if I am saving a new instance of the parent, but after saving it, I try to add child object(s) into the parent List and then attempt to call saveOrUpdate on the parent object, but no entries of child object(s) get persisted into the database. I just would like some pointers. Note: I am not using annotations. A snippet of the Parent.hbm.xml, that defines the one-to-many unidirectional relationship:
<list name="children" cascade="all">
<key column="parent_id"/>
<index column="idx"/>
<one-to-many class="Child"/>
</list>
I just tried to reproduce this example and it worked OK for me.
Here are my mappings:
<hibernate-mapping package="com.example.domain">
<class name="com.example.domain.Parent" table="PARENT">
<id name="id" column="parent_id" access="field">
<generator class="increment" />
</id>
<property name="name" column="parent_name" access="field" />
<list name="children" access="field" cascade="all">
<key column="parent_id" not-null="true" />
<index column="idx" />
<one-to-many class="Child" />
</list>
</class>
</hibernate-mapping>
<hibernate-mapping package="com.example.domain">
<class name="com.example.domain.Child" table="CHILD">
<id name="id" column="child_id" access="field">
<generator class="increment" />
</id>
<property name="name" column="child_name" access="field" />
</class>
</hibernate-mapping>
I added not-null="true" to the parent mapping.
Did you try to set show_sql in your hibernate config to see generated SQL?

Delete element from Set

I have 2 classes Tema(Homework) and Disciplina (course), where a Course has a Set of homeworks.
In Hibernate i have mapped this to a one-to-many associations like this:
<class name="model.Disciplina" table="devgar_scoala.discipline" >
<id name="id" >
<generator class="increment"/>
</id>
<set name="listaTeme" table="devgar_scoala.teme">
<key column="Discipline_id" not-null="true" ></key>
<one-to-many class="model.Tema" ></one-to-many>
</set>
</class>
<class name="model.Tema" table="devgar_scoala.teme" >
<id name="id">
<generator class="increment" />
</id>
<property name="titlu" type="string" />
<property name="cerinta" type="binary">
<column name="cerinta" sql-type="blob" />
</property>
</class>
The problem is that it will add (insert rows in the table 'Teme') but it won't delete any rows and i get no exceptions thrown.
Im using the merge() method.
Although your question is unclear (how do you save and delete?), I'd suggest you need to set cascade:
<set cascade="all-delete-orphan">
As a sidenote - avoid names in your native language.
According to your description, I understand that a Tema cannot exist without its Disciplina: if you remove a Tema from the collection, you want it to be deleted. To tell Hibernate to do this, you must use cascade="all-delete-orphan".
<set name="listaTeme" table="devgar_scoala.teme" cascade="all-delete-orphan">
<key column="Discipline_id" not-null="true" ></key>
<one-to-many class="model.Tema" ></one-to-many>
</set>
Refer to the online documentation.

Categories