hibernate many-to-many cascade doesn't work - java

I'm a hibernate newbie and I'm not entirely sure how to get the cascade behavior I'm looking for.
I have two classes Student and Class with unidirectional many-to-many mapping. When I delete a Student, I've this exception
Cannot delete or update a parent row: a foreign key constraint fails (projet.T_CLASS_STUDENT, CONSTRAINT FK5DBF3D8967BCDD8B FOREIGN KEY (PERSON_ID) REFERENCES T_STUDENT (PERSON_ID))
I don't understand why, I set cascade to "delete" but it's doesn't work !
In fact, when I delete a student I want to delete all Student which are in the association table.
My mapping files are:
<class name="persistenceClass.Class" table="T_CLASS">
<id name="Id" column="CLASS_ID">
<generator class="native" />
</id>
<many-to-one name="Formation" column="CLASS_FORMATION" class="persistenceClass.Formation" />
<many-to-one name="Year" column="CLASS_YEAR" class="persistenceClass.Year" />
<set name="Students" table="T_CLASS_STUDENT" cascade="delete" >
<key column="CLASS_ID" />
<many-to-many class="persistenceClass.Student" column="PERSON_ID" />
</set>
</class>
and:
<class name="persistenceClass.Person" table="T_PERSON" >
<id name="Id" column="PERSON_ID" >
<generator class="native" />
</id>
<property name="FirstName" column="PERSON_FIRST_NAME" not-null="true" />
<property name="LastName" column="PERSON_LAST_NAME" not-null="true" />
<property name="Type" column="PERSON_TYPE" not-null="true" />
<property name="BirthDate" column="PERSON_BIRTH_DATE" />
<property name="BirthCity" column="PERSON_BIRTH_CITY" />
<property name="PhoneNumber" column="PERSON_PHONE_NUMBER" />
<property name="MobileNumber" column="PERSON_MOBILE_NUMBER" />
<property name="Mail" column="PERSON_MAIL" />
<property name="Address" column="PERSON_ADDRESS_ADDRESS" />
<property name="ZipCode" column="PERSON_ADDRESS_ZIPCODE" />
<property name="City" column="PERSON_ADDRESS_CITY" />
<property name="Image" column="PERSON_IMAGE" type="image" />
<many-to-one name="Country" column="PERSON_ADDRESS_COUNTRY" class="persistenceClass.Country" />
<many-to-one name="BirthCountry" column="PERSON_BIRTH_COUNTRY" class="persistenceClass.Country" />
<many-to-one name="Civility" column="PERSON_CIVILITY" class="persistenceClass.Civility" />
<many-to-one name="Sex" column="PERSON_SEX" class="persistenceClass.Sex" />
<joined-subclass name="persistenceClass.Student" table="T_STUDENT">
<key column="PERSON_ID" />
</joined-subclass>
<joined-subclass name="persistenceClass.Teacher" table="T_TEACHER">
<key column="PERSON_ID" />
</joined-subclass>
</class>

As you described, you have an unidirectional many-to-many relationship between class and student with a cascade on delete. That means cascade on delete only works if you delete a class and not a student. If you want to delete a student, you have to first remove it from the collection in the corresponding class object. Otherwise you get the exception described in your question.
If you delete a class all students in that class should be deleted as well - unless these students participate in other associations.

I dont know, if I fully understand your code (I am using annotations in my project). But you have an unidirected association class => student with implicit join table. When you delete student, there is no way for the person entity to delete itself from the association.
This way of making many to many isnt recomended in documentation. Recomended is to explicitly specify the joining entity, bacause its more flexible. You can then easily query or delete the association, and you cas assign association parameters.

Related

Where clause is not working in Hibernate 5 XML Mapping

I used Hibernate 4 with XML Mapping and the code is:
<map name="scormData" table="scorm_data_attempt" where="current">
<key>
<column name="userid" />
<column name="course_id" />
</key>
<composite-map-key class="ScormDataKey">
<key-property name="scormId" column="scorm_id"
type="integer" />
<key-property name="variableName" column="variable_name"
type="string" />
<key-property name="attempt" />
</composite-map-key>
<element type="string" column="variable_value" />
</map>
However, now when I change to Hibernate 5, the where field is not working anymore and I cannot retrieve data from the column current. Does anyone know why and how to fix it ?

How can I fix "object references an unsaved transient instance - save the transient instance before flushing" on a large project?

I downloaded and installed the source code for the Perseus Project (Open Source Code) on to Ubuntu 14.04, and Tomcat 6. The project hasn't been modified in years, so it must be some new feature in hibernate that is doing this.
I see the solutions here on very many threads, but they address the issue of debugging code as it is being written as opposed to migrating a project that quite certainly worked "at one time". The code that I am working with hasn't been modified since 2008-2011. (I've determined that they are running it now on "Mandriva Linux 2010.2") Therefore, I need to be able to fix this issue on a large scale - either a search/replace method or a configuration file change. I have never used hibernate myself, so I don't understand the full meaning of the posted answers.
Thanks.
Also, the files don't use the # annotated syntax, but the mappings seem to be done via XML files. At this point, I would not mind just to update all these files if I knew what to change.
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="perseus.document.dao">
<class name="HibernateChunkDAO$ChunkRow" table="hib_chunks" batch-size="10">
<id name="id" type="int" column="id" unsaved-value="null">
<generator class="native" />
</id>
<property name="documentID" column="document_id" type="string"
length="50" index="doc_idx doc_type_idx doc_type_value_idx
doc_cid_idx doc_so_eo_idx doc_so_eo_type_idx doc_dq_idx
doc_so_eo_type_val_idx" />
<property name="elementName" column="element" type="string"
length="20" />
<property name="type" column="type" type="string" length="30"
index="doc_type_idx doc_type_value_idx doc_so_eo_type_idx
doc_so_eo_type_val_idx" />
<property name="value" column="value" type="string" length="250"
index="doc_type_value_idx doc_so_eo_type_val_idx" />
<property name="position" column="position" type="int" />
<property name="absolutePosition" column="abs_position" type="int" />
<property name="chunkID" column="chunk_id" type="string"
length="255" index="doc_cid_idx" />
<property name="openTags" column="open_tags" type="text" />
<property name="closeTags" column="close_tags" type="text" />
<property name="startOffset" column="start_offset" type="int"
index="doc_so_eo_idx doc_so_eo_type_idx doc_so_eo_type_val_idx" />
<property name="endOffset" column="end_offset" type="int"
index="doc_so_eo_idx doc_so_eo_type_idx doc_so_eo_type_val_idx" />
<property name="displayQuery" column="display_query" type="string"
length="100" index="doc_dq_idx" />
<property name="head" column="head" type="text" />
<property name="headLanguage" column="head_lang" type="string"
length="10" />
<property name="hasCustomHead" column="custom_head" type="boolean" />
<set name="frequencies" inverse="true" cascade="all-delete-orphan"
lazy="true" batch-size="30">
<key column="chunk_id" on-delete="cascade" />
<one-to-many class="perseus.ie.freq.Frequency" />
</set>
<many-to-one name="lemma" column="lemma_id" cascade="all" lazy="false" />
<!--
<list name="senses" inverse="false" cascade="all-delete-orphan"
lazy="false" batch-size="30">
<key column="chunk_id" />
<list-index column="position" />
<one-to-many class="perseus.voting.Sense" />
</list>
-->
</class>
</hibernate-mapping>
This is happening due to below reason:
You have defined a association in your entity which is not yet persisted in the database and before persisting this association you are trying to flush the Session.For example you have a collection association which has collection of transient instances, so you have to persist this collection first before flushing.
For resolving this issue, you have 2 options:
Either manually persist the collection in your code by calling Session.save() method.
If you want to use tags in Hibernate XML file or want to use annotations make use of cascade="all" (for xml) or cascade=CascadeType.ALL (for annotations) on your collection association.

Strange behavior on one-to-one and many-to-one (unique=true) fetching in hibernate

I'm trying to achieve lazy load on the following.
User.java
public class User {
private int id;
private String userName;
private String password;
private Employee employee;
//getter and setters
}
User.hbm.xml
<hibernate-mapping>
<class name="com.site.dto.User" table="user">
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="identity" />
</id>
<property name="userName" type="string" update="false">
<column name="user_name" length="50" not-null="true" unique="true" />
</property>
<property name="password" type="string">
<column name="password" length="50" not-null="true" unique="true" />
</property>
<one-to-one name="employee" class="com.site.dto.Employee" fetch="select" cascade="save-update" />
</class>
</hibernate-mapping>
Employee.java
public class Employee implements Serializable{
private int id;
private String name;
private String email;
private User user;
// getter and setters
}
Employee.hbm.xml
<hibernate-mapping>
<class name="com.site.dto.Employee" table="employee">
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="identity" />
</id>
<property name="name" type="string">
<column name="name" length="50" not-null="true" unique="true" />
</property>
<property name="email" type="string" update="false">
<column name="email" length="50" not-null="true" unique="true" />
</property>
// enforcing many-to-one to one-to-one by putting unique="true"
<many-to-one name="user" column="user_id" class="com.site.dto.User" unique="true" not-null="true" fetch="select" cascade="save-update" />
</class>
</hibernate-mapping>
First I'm getting the User Object based on username. Now I'm trying to load the employee object which gives me null pointer exception. So after digging on some debug, it seems to be using a select statement with wrong where clause. Here is the hibernate debug
select employee0_.id as id1_1_0_, employee0_.name as name2_1_0_, employee0_.email as email3_1_0_,employee0_.user_id as user_id25_1_0_, from employee employee0_ where employee0_.id=?
Why is the where clause is based on employee.id and not employee.user.id ? I think this is due to the reason on how one-to-one mapping works in hbm.xml configuration where one-to-one will be linked to child table's primary key id but not user_id. I'm forcing the many-to-one to one-to-one in employee by using unique="true". I can fetch the employee in Hibernate annotation's one-to-one by defining #Join-column but I can't figure out how to map the one-to-one in hbm.xml which should refer the child's user_id.
Figured out the solution a while back, but forget to post it.
The above problem is coz, by default one-to-one mapping will be implemented for a child table which have the parent's primary key as the Child's primary key. So if we're going to eliminate that default property and use one-to-one with many-to-one (unique=true), we should define property-ref
I've added property-ref in one-to-one mapping in User.hbm.xml and now it works fine.
<one-to-one name="employee" property-ref="user" class="com.site.dto.Employee" fetch="select" cascade="save-update" />

Why hibernate selects entities before persisting entities mapped by joined_subclass inheritance type?

I have superclass Person and two subclasses - Parent and Child. They are mapped with JOINED_TABLE inheritance type.
Parent and Child have bidirectional one-to-many relationship.
Person.hbm.xml contains all configuration:
<hibernate-mapping>
<class name="com.masterhibernate.SimpleHibernateDemo.Person"
table="Person">
<cache usage="read-write" />
<id name="id" column="id">
<generator class="assigned" />
</id>
<property name="name">
<column name="name" length="16" not-null="true" />
</property>
<property name="surname">
<column name="surname" length="36"></column>
</property>
<property name="address">
<column name="address" length="22"></column>
</property>
<joined-subclass name="com.masterhibernate.SimpleHibernateDemo.Parent"
table="Parent">
<key column="person_id" foreign-key="parent_person" />
<property name="job" column="WorkPlace" length="22" type="string" />
<set name="children" inverse="true" cascade="save-update" lazy="true">
<cache usage="read-write" />
<!-- specifies foreign key column of child table -->
<key column="ParentPK" />
<one-to-many class="com.masterhibernate.SimpleHibernateDemo.Child" />
</set>
</joined-subclass>
<joined-subclass name="com.masterhibernate.SimpleHibernateDemo.Child"
table="Child">
<key column="person_id" foreign-key="child_person" />
<property name="toy" column="toy" length="55" type="string" />
<many-to-one name="parent"
class="com.masterhibernate.SimpleHibernateDemo.Parent" column="ParentPK"
foreign-key="child_parent" />
</joined-subclass>
</class>
</hibernate-mapping>
When I persist Parent and associated Child entities hibernate issues select statements before inserting. These select statements query for data of Child entities from child and person tables.
Database is recreated on each run and there is nothing to select.
Here is extract from main method that persists Parent and associated Child entities:
Parent parent = new Parent("Volodia", "Levytskyi", "Mykolaiv");
parent.setId((long) 1);
Set<Child> children = new HashSet<>();
children.add(new Child((long) 2, "Ivan", "McGregor", "Odessa"));
children.add(new Child((long) 3, "Borys", "McRobin", "Donetsk"));
children.add(new Child((long) 4, "Nazar", "McCrespo", "Teernopil"));
parent.setChildren(children);
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
session.save(parent);
// Criteria criteria = session.createCriteria(Person.class);
// List<Person> list = criteria.list();
// System.out.println("list1=" + list);
transaction.commit();
session.close();
Why are those select statements before insert statements?
The problem was in id generator strategy of Parent and Child entities:
<generator class="assigned" />
Hibernate cannot determine if entity with assigned id exists in database or not. That's why it issues select statements to ensure that no duplicate ids are inserted.
After I moved to id generator strategy native it stopped selecting before inserting:
<generator class="native" />

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?

Categories