hibernate

Hibernate Cascade example

In this tutorial we are going to see the use of  the cascade feature of relational databases and how it is applied in Hibernate. This tutorial is based on the previous Hibernate One-to-Many Relationship Example (XML Mapping and Annotation). You can download the Eclipse project from there.

So these are the tools we are going to use on a Windows 7 platform:

  • JDK 1.7
  • Maven 3.0.5
  • Hibernate 3.6.3.Final
  • MySQL JDBC driver 5.1.9
  • Eclipse 4.2 Juno

Cascade feature

The cascade keyword is frequently applied in relational databases. It is mostly used in in databases that contain tables with one-to-one, one-to-may and many-to-many relationships. I think its name, cascade, is very revealing about its usage. When the cascade keyword is specified in a property of an entity, it means that the change applied on the entity will also be applied in the property as well. It is a fast way to manage the other side of the relationship automatically, without writing extra code.

1. Cascade save-update

Let’s say we have a one-to-many relationship like this one :

diagram

These tables are mapped to Student and Project entities:

Student.java:

public class Student implements java.io.Serializable {

	private static final long serialVersionUID = 1L;

	private Integer studentId;
	private String  studentName;
	private String  studentAge;
	private Set<Project> studentProjects = new HashSet(0);

        ...

Projects.java:

public class Project implements java.io.Serializable{

	private Integer projectId;

	private String title;
	private String semester;

	private Student student;

        ...

And these are the Hibernate Mapping files:

Student.hbm.xml:

<?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>
    <class name="com.javacodegeeks.enterprise.hibernate.Student" table="student" catalog="tutorials">
        <id name="studentId" type="java.lang.Integer">
            <column name="STUDENT_ID" />
            <generator class="identity" />
        </id>
        <property name="studentName" type="string">
            <column name="STUDENT_NAME" length="10" not-null="true" unique="true" />
        </property>
        <property name="studentAge" type="string">
            <column name="STUDENT_Age" length="20" not-null="true" unique="true" />
        </property>

        <set name="studentProjects" inverse="true" table="projects" lazy="true" fetch="select">

            <key>
                <column name="STUDENT_ID" not-null="true" />
            </key>

            <one-to-many class="com.javacodegeeks.enterprise.hibernate.Project" />

        </set>

    </class>
</hibernate-mapping>

Project.hbm.xml:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 25 April 2011 7:52:33 PM by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping>
	<class name="com.javacodegeeks.enterprise.hibernate.Project" table="projects" 	catalog="tutorials">

		<id name="projectId" type="java.lang.Integer">
			<column name="PROJECT_ID" />
			 <generator class="identity" />
		</id>

		<property name="title" type="string">
			<column name="TITLE" length="100" not-null="true" unique = "true" />
		</property>

		<property name="semester" type="string">
			<column name="SEMESTER" length="100"  />
		</property>

		<many-to-one name="student" class="com.javacodegeeks.enterprise.hibernate.Student" fetch="select">
                    <column name="STUDENT_ID" not-null="true" />
                </many-to-one>
	</class>
</hibernate-mapping>

Now let’s take a close look to App.java:

...
        Student student = new Student();

        student.setStudentName("Sarah");
        student.setStudentAge("21");

        Project project1 = new Project("L","Spring");
        project1.setStudent(student);

        student.getStudentProjects().add(project1);

        Project project2 = new Project("M","Spring");
        project2.setStudent(student);

        student.getStudentProjects().add(project2);

        session.save(student);
        session.save(project1);
        session.save(project2);

        session.getTransaction().commit();
...

As you can see when we don’t use the cascade keyword on the relationship mapping we have to save both Student and Project instances to the hibernate Session.

The output of the above program will be :

Hibernate: insert into tutorials.student (STUDENT_NAME, STUDENT_Age) values (?, ?)
Hibernate: insert into tutorials.projects (TITLE, SEMESTER, STUDENT_ID) values (?, ?, ?)
Hibernate: insert into tutorials.projects (TITLE, SEMESTER, STUDENT_ID) values (?, ?, ?)
Great! Student was saved

Now let’s add cascade="save-update" to Student.hbm.xml.

Student.hbm.xml:

<?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>
    <class name="com.javacodegeeks.enterprise.hibernate.Student" table="student" catalog="tutorials">
        <id name="studentId" type="java.lang.Integer">
            <column name="STUDENT_ID" />
            <generator class="identity" />
        </id>
        <property name="studentName" type="string">
            <column name="STUDENT_NAME" length="10" not-null="true" unique="true" />
        </property>
        <property name="studentAge" type="string">
            <column name="STUDENT_Age" length="20" not-null="true" unique="true" />
        </property>

        <set name="studentProjects" inverse="true" cascade="save-update" table="projects" lazy="true" fetch="select">

            <key>
                <column name="STUDENT_ID" not-null="true" />
            </key>

            <one-to-many class="com.javacodegeeks.enterprise.hibernate.Project" />

        </set>

    </class>
</hibernate-mapping>

You can chage App.java to this:

...
        Student student = new Student();

        student.setStudentName("Sarah");
        student.setStudentAge("21");

        Project project1 = new Project("R","Spring");
        project1.setStudent(student);

        student.getStudentProjects().add(project1);

        Project project2 = new Project("P","Spring");
        project2.setStudent(student);

        student.getStudentProjects().add(project2);

        session.save(student);

        session.getTransaction().commit();
...

This is the output:

Hibernate: insert into tutorials.student (STUDENT_NAME, STUDENT_Age) values (?, ?)
Hibernate: insert into tutorials.projects (TITLE, SEMESTER, STUDENT_ID) values (?, ?, ?)
Hibernate: insert into tutorials.projects (TITLE, SEMESTER, STUDENT_ID) values (?, ?, ?)
Great! Student was saved

No need to save Project instances to the Session. Because of the cascade keyword with save-update value in the studentProjects property of Student, whenever a new student tuple is saved, all projects in studentProjects set will be also persisted in projects table. The same thing applies when you update a Student. All projects in the aforementioned set will also be updated.

If we leave App.java as it is (saving just the Student instance) and remove the cascade keyword from Student.hbm.xml this is the the output:

Hibernate: insert into tutorials.student (STUDENT_NAME, STUDENT_Age) values (?, ?)
Great! Student was saved

It’s also possible that you get an Exception, if you’re not using inverse keyword which we explained in Hibernate One-to-Many Relationship Example (XML Mapping and Annotation).

2. Cascade delete

If you don’t use cascade with delete value, when deleting a Student you have to manually delete all of the projects is it’s studentProject in its set.

So in App.java you have to write something like this:

...
       session.beginTransaction();
       Query q = session.createQuery("from Student where studentId = :studentId ");

       q.setParameter("studentId", 50);

       Student student = (Student)q.list().get(0);

       for (Project project : student.getStudentProjects()){
                session.delete(project);
       }
       session.delete(student);

       session.getTransaction().commit();
...

This means that you have to fetch all the projects that this student is working on from the database and then delete them one by one. This is the result:

Hibernate: select student0_.STUDENT_ID as STUDENT1_0_, student0_.STUDENT_NAME as STUDENT2_0_, student0_.STUDENT_Age as STUDENT3_0_ from tutorials.student student0_ where student0_.STUDENT_ID=?
Hibernate: select studentpro0_.STUDENT_ID as STUDENT4_0_1_, studentpro0_.PROJECT_ID as PROJECT1_1_, studentpro0_.PROJECT_ID as PROJECT1_1_0_, studentpro0_.TITLE as TITLE1_0_, studentpro0_.SEMESTER as SEMESTER1_0_, studentpro0_.STUDENT_ID as STUDENT4_1_0_ from tutorials.projects studentpro0_ where studentpro0_.STUDENT_ID=?
Hibernate: delete from tutorials.projects where PROJECT_ID=?
Hibernate: delete from tutorials.projects where PROJECT_ID=?
Hibernate: delete from tutorials.student where STUDENT_ID=?

But if you change Student.hbm.xml to this:

Student.hbm.xml:

<?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>
    <class name="com.javacodegeeks.enterprise.hibernate.Student" table="student" catalog="tutorials">
        <id name="studentId" type="java.lang.Integer">
            <column name="STUDENT_ID" />
            <generator class="identity" />
        </id>
        <property name="studentName" type="string">
            <column name="STUDENT_NAME" length="10" not-null="true" unique="true" />
        </property>
        <property name="studentAge" type="string">
            <column name="STUDENT_Age" length="20" not-null="true" unique="true" />
        </property>

        <set name="studentProjects" inverse="true" cascade="delete" table="projects" lazy="true" fetch="select">

            <key>
                <column name="STUDENT_ID" not-null="true" />
            </key>

            <one-to-many class="com.javacodegeeks.enterprise.hibernate.Project" />

        </set>

    </class>
</hibernate-mapping>

You can change App.java to this:

...
       session.beginTransaction();
       Query q = session.createQuery("from Student where studentId = :studentId ");

       q.setParameter("studentId", 46);

       Student student = (Student)q.list().get(0);

       session.delete(student);

       session.getTransaction().commit();
...

This is the result:

Hibernate: select student0_.STUDENT_ID as STUDENT1_0_, student0_.STUDENT_NAME as STUDENT2_0_, student0_.STUDENT_Age as STUDENT3_0_ from tutorials.student student0_ where student0_.STUDENT_ID=?
Hibernate: select studentpro0_.STUDENT_ID as STUDENT4_0_1_, studentpro0_.PROJECT_ID as PROJECT1_1_, studentpro0_.PROJECT_ID as PROJECT1_1_0_, studentpro0_.TITLE as TITLE1_0_, studentpro0_.SEMESTER as SEMESTER1_0_, studentpro0_.STUDENT_ID as STUDENT4_1_0_ from tutorials.projects studentpro0_ where studentpro0_.STUDENT_ID=?
Hibernate: delete from tutorials.projects where PROJECT_ID=?
Hibernate: delete from tutorials.projects where PROJECT_ID=?
Hibernate: delete from tutorials.student where STUDENT_ID=?

3. Cascade delete-orphan

Now imagine that you don’t want to delete a student, but you do want to remove it’s assigned projects. This means that you want to remove some “couples” of the relationship. Without using cascade with value delete-orphan, you have to explicitly delete the corresponding tuples from the project table.
Like so:

...
       session.beginTransaction();
       project project1 = (Project) session.get(Project.class, new Integer(52));
       Project project2 = (Project) session.get(Project.class, new Integer(53));
       session.delete(project1);
       session.delete(project2);

       session.getTransaction().commit();
...

And the result will be:

Hibernate: select project0_.PROJECT_ID as PROJECT1_1_0_, project0_.TITLE as TITLE1_0_, project0_.SEMESTER as SEMESTER1_0_, project0_.STUDENT_ID as STUDENT4_1_0_ from tutorials.projects project0_ where project0_.PROJECT_ID=?
Hibernate: select project0_.PROJECT_ID as PROJECT1_1_0_, project0_.TITLE as TITLE1_0_, project0_.SEMESTER as SEMESTER1_0_, project0_.STUDENT_ID as STUDENT4_1_0_ from tutorials.projects project0_ where project0_.PROJECT_ID=?
Hibernate: delete from tutorials.projects where PROJECT_ID=?
Hibernate: delete from tutorials.projects where PROJECT_ID=?

But if you change Student.hbm.xml to this:

Student.hbm.xml:

<?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>
    <class name="com.javacodegeeks.enterprise.hibernate.Student" table="student" catalog="tutorials">
        <id name="studentId" type="java.lang.Integer">
            <column name="STUDENT_ID" />
            <generator class="identity" />
        </id>
        <property name="studentName" type="string">
            <column name="STUDENT_NAME" length="10" not-null="true" unique="true" />
        </property>
        <property name="studentAge" type="string">
            <column name="STUDENT_Age" length="20" not-null="true" unique="true" />
        </property>

        <set name="studentProjects" inverse="true" cascade="delete-orphan" table="projects" lazy="true" fetch="select">

            <key>
                <column name="STUDENT_ID" not-null="true" />
            </key>

            <one-to-many class="com.javacodegeeks.enterprise.hibernate.Project" />

        </set>

    </class>
</hibernate-mapping>

You can change App.java to this:

...
      session.beginTransaction();
      project project1 = (Project) session.get(Project.class, new Integer(48));
      Project project2 = (Project) session.get(Project.class, new Integer(49));

      Student student = (Student)session.get(Student.class, new Integer(42));

      student.getStudentProjects().remove(project1);
      student.getStudentProjects().remove(project2);

      session.saveOrUpdate(student);

      session.getTransaction().commit();
...

This means that when you want to delete a “couple” of the relationship you just have to to remove the projects from the student’s studentProjects set. Now when the Student is updated, the coresponding project tuples will be deleted. This is the result:

Hibernate: select project0_.PROJECT_ID as PROJECT1_1_0_, project0_.TITLE as TITLE1_0_, project0_.SEMESTER as SEMESTER1_0_, project0_.STUDENT_ID as STUDENT4_1_0_ from tutorials.projects project0_ where project0_.PROJECT_ID=?
Hibernate: select project0_.PROJECT_ID as PROJECT1_1_0_, project0_.TITLE as TITLE1_0_, project0_.SEMESTER as SEMESTER1_0_, project0_.STUDENT_ID as STUDENT4_1_0_ from tutorials.projects project0_ where project0_.PROJECT_ID=?
Hibernate: select student0_.STUDENT_ID as STUDENT1_0_0_, student0_.STUDENT_NAME as STUDENT2_0_0_, student0_.STUDENT_Age as STUDENT3_0_0_ from tutorials.student student0_ where student0_.STUDENT_ID=?
Hibernate: select studentpro0_.STUDENT_ID as STUDENT4_0_1_, studentpro0_.PROJECT_ID as PROJECT1_1_, studentpro0_.PROJECT_ID as PROJECT1_1_0_, studentpro0_.TITLE as TITLE1_0_, studentpro0_.SEMESTER as SEMESTER1_0_, studentpro0_.STUDENT_ID as STUDENT4_1_0_ from tutorials.projects studentpro0_ where studentpro0_.STUDENT_ID=?
Hibernate: delete from tutorials.projects where PROJECT_ID=?
Hibernate: delete from tutorials.projects where PROJECT_ID=?

Note that when using annotations you just have to use the @Cascade annotation, like so:
Student.java:

...
@OneToMany(fetch = FetchType.LAZY, mappedBy = "student")
@Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE})
public Set<Project> getStudentProjects() {
	return studentProjects;
}
...

This was an example on Hibernate Cascade.

Nikos Maravitsas

Nikos has graduated from the Department of Informatics and Telecommunications of The National and Kapodistrian University of Athens. During his studies he discovered his interests about software development and he has successfully completed numerous assignments in a variety of fields. Currently, his main interests are system’s security, parallel systems, artificial intelligence, operating systems, system programming, telecommunications, web applications, human – machine interaction and mobile development.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button