Investigating Hibernate Associations – Cascading Styles

In today’s post we will try to understand how Hibernate’s associations work with the various cascading styles. We will use the many-to-many relationship defined in my previous post as reference.

The sample program of the many-to-many post uses a Many-To-Many association between Person and Phone objects. The program creates two persons and two phone objects. Each of the phones have an association with both the persons.

To determine how the cascade styles function, we will create a sample phone delete program that would clean up one of the Person –> Phone associations. Note as per current configuration the cascade attribute is set to “all“.

	Configuration cfg = new Configuration();
	cfg.configure();
	SessionFactory sessFactory = cfg.buildSessionFactory();
	try {
		Session session = sessFactory.openSession();

		Transaction tx = session.beginTransaction();

		Person per = (Person)session.load(Person.class,
			"12456");
		per.setPhones(null);
		tx.commit();

	} catch(Exception e) {
		e.printStackTrace();
	}

Here’s the Hibernate SQL output:

Hibernate: select person0_.id as id0_0_, person0_.firstName as firstName0_0_,
	   person0_.lastName as lastName0_0_, person0_.dob as dob0_0_
	   from PERSON person0_ where person0_.id=?
Hibernate: delete from PERSON_PHONE where PERSONID=?

Let’s query the database and check out the behavior.

SELECT * from PERSON
ID      FIRSTNAME LASTNAME DOB
-----   --------- -------- -------
1245	Clark	  Kent	   2007-09-25
12456	Bruce	  Willis   2007-09-25
SELECT * FROM PHONE

ID      CONTACTNUMBER PHONETYPE
----    ------------- ---------
451	1234567	      BUS
452	7654321	      PER

SELECT * FROM PERSON_PHONE

PERSONID PHONEID
-------- -------
1245	 452
1245	 451

As required only the entries in PERSON_PHONE get deleted. The rest remain. Let’s add a Phone number to only Person2 and leave the Person1 untouched. Here’s the new code:

	Configuration cfg = new Configuration();
	cfg.configure();
	SessionFactory sessFactory = cfg.buildSessionFactory();
	try {
		Session session = sessFactory.openSession();

		Person p1 = new Person();
		p1.setId("1245");
		p1.setFirstName("Clark");
		p1.setLastName("Kent");
		p1.setDob(new java.util.Date());

		Person p2 = new Person();
		p2.setId("12456");
		p2.setFirstName("Bruce");
		p2.setLastName("Willis");
		p2.setDob(new java.util.Date());

		Phone phone1 = new Phone();
		phone1.setId("451");
		phone1.setNumber("1234567");
		phone1.setType("BUS");
		p1.addPhone(phone1);
		p2.addPhone(phone1);

		Phone phone2 = new Phone();
		phone2.setId("452");
		phone2.setNumber("7654321");
		phone2.setType("PER");
		Phone phone3 = new Phone();
		phone3.setId("4521");
		phone3.setNumber("1236547");
		phone3.setType("PER");

		p1.addPhone(phone2);
		p2.addPhone(phone2);
		p2.addPhone(phone3);

		Transaction tx = session.beginTransaction();
		session.save(p1);
		session.save(p2);
		tx.commit();

		Transaction tx1 = session.beginTransaction();
		Person per = (Person)session.load(Person.class, "12456");
		System.out.println(per.getPhones().size());
		tx1.commit();

	} catch(Exception e) {
		e.printStackTrace();
	}

The Hibernate SQL output remains unchanged. The association table PERSON_PHONE removes the phone associations with Person2. However, the entry in PHONE table for phone3 remains. We have an orphaned object here.

Let’s now change the value of the cascade attribute to “all-delete-orphan” and run the sample program again.

Do trying the deletion sample program, we get the following exception:

Hibernate: select person0_.id as id0_0_, person0_.firstName as firstName0_0_, person0_.lastName as lastName0_0_, 	   person0_.dob as dob0_0_
	   from PERSON person0_ where person0_.id=?
org.hibernate.HibernateException: A collection with cascade="all-delete-orphan"
	was no longer referenced by the owning entity instance:
	com.tutorial.hibernate.simple.Person.phones
	at org.hibernate.engine.Collections.processDereferencedCollection(Collections.java:96)
	at org.hibernate.engine.Collections.processUnreachableCollection(Collections.java:39)
	at org.hibernate.event.def.AbstractFlushingEventListener.flushCollections(AbstractFlushingEventListener.java:218)
	at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:77)
	at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:26)
	at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
	at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
	at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
	at com.tutorial.hibernate.simple.PersonPersister8.main(PersonPersister8.java:24)

To get around this problem I tried the following:

	Configuration cfg = new Configuration();
	cfg.configure();
	SessionFactory sessFactory = cfg.buildSessionFactory();
	try {
		Session session = sessFactory.openSession();

		Transaction tx = session.beginTransaction();

		Person per = (Person)session.load(Person.class, "12456");
		Set phones = per.getPhones();
		phones.clear();
		tx.commit();

	} catch(Exception e) {
		e.printStackTrace();
	}

Now I got a new exception:

Hibernate: select person0_.id as id0_0_, person0_.firstName as firstName0_0_,
	   person0_.lastName as lastName0_0_, person0_.dob as dob0_0_
	   from PERSON person0_ where person0_.id=?
Hibernate: select phones0_.PERSONID as PERSONID1_, phones0_.PHONEID as PHONEID1_,
	   phone1_.id as id2_0_, phone1_.contactnumber as contactn2_2_0_,
	   phone1_.phonetype as phonetype2_0_
	   from PERSON_PHONE phones0_ left outer join PHONE phone1_ on phones0_.PHONEID=phone1_.id
           where phones0_.PERSONID=?
Hibernate: delete from PERSON_PHONE where PERSONID=?
Hibernate: delete from PHONE where id=?
Hibernate: delete from PHONE where id=?
Hibernate: delete from PHONE where id=?
org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
	at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:71)
	at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
	at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:253)
	at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:237)
	at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:146)
	at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
	at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
	at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
	at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
	at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
	at com.tutorial.hibernate.simple.PersonPersister8.main(PersonPersister8.java:28)
Caused by: java.sql.BatchUpdateException: Cannot delete or update a parent row: a foreign key constraint fails (`hibernate/person_phone`, CONSTRAINT `FKAE26A3E487A0D95A` FOREIGN KEY (`PHONEID`) REFERENCES `phone` (`id`))
	at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:1237)
	at com.mysql.jdbc.PreparedStatement.executeBatch(PreparedStatement.java:936)
	at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
	at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:246)
	... 8 more

I managed to read somewhere that the cascade attribute does not have much impact on many-to-many relationships. So I decided to move on to one-to-many relationships. We will use the same hibernate configuration setting for Person and Phone as mentioned in my post related to one-to-many relationship.

Here’s the sample code to insert records into the database.

	Configuration cfg = new Configuration();
	cfg.configure();
	SessionFactory sessFactory = cfg.buildSessionFactory();
	try {
		Session session = sessFactory.openSession();

		Person p = new Person();
		p.setId("21823");
		p.setFirstName("T1om");
		p.setLastName("Jones");
		p.setDob(new java.util.Date());

		Phone ph1 = new Phone();
		ph1.setId("123");
		ph1.setNumber("3033838");
		ph1.setType("BUS");
		p.addPhone(ph1);

		Phone ph2 = new Phone();
		ph2.setId("1234");
		ph2.setNumber("3033939");
		ph2.setType("BUS");
		p.addPhone(ph2);

		Transaction tx = session.beginTransaction();
		session.save(p);
		tx.commit();

		session = sessFactory.openSession();
		Query q = session.createQuery("FROM Person as p WHERE p.id = 21823");
		List l = q.list();
		System.out.println("Result Size: " + l.size());
		session.close();

	} catch(Exception e) {
		e.printStackTrace();
	}

The cascade attribute is set to “all”. Database is queried and the following results are displayed

SELECT * FROM Person
id       FirstName   LastName            DOB
-------  ----------- ------------------- --------------
21823	 T1om	     Jones	         2007-10-12
SELECT * FROM Phone
ID      CONTACTNUMBER PHONETYPE PERSONID
------  ------------- --------- --------
123	3033838	      BUS	21823
1234	3033939	      BUS	21823

Here’s the sample program to delete the phones.

	Configuration cfg = new Configuration();
	cfg.configure();
	SessionFactory sessFactory = cfg.buildSessionFactory();
	try {
		Session session = sessFactory.openSession();

		Transaction tx = session.beginTransaction();

		Person per = (Person)session.load(Person.class, "21823");
		Set phones = per.getPhones();
		System.out.println(phones.size());

		for (Iterator iterator = phones.iterator(); iterator.hasNext();) {
			Phone ph = (Phone) iterator.next();
			iterator.remove();
		}
		System.out.println(phones.size());
		session.save(per);
		tx.commit();

	} catch(Exception e) {
		e.printStackTrace();
	}

Initially we set the cascade attribute to “all”. Unfortunately the phones did not get deleted. Instead we changed the attribute value to “all-delete-orphan”. Here’s the hibernate output generated.

Hibernate: select person0_.id as id0_0_, person0_.firstName as firstName0_0_,
           person0_.lastName as lastName0_0_, person0_.dob as dob0_0_
	   from PERSON person0_ where person0_.id=?
Hibernate: select phones0_.personid as personid1_, phones0_.id as id1_,
	   phones0_.id as id1_0_, phones0_.contactnumber as contactn2_1_0_,
           phones0_.phonetype as phonetype1_0_ from PHONE phones0_
	   where phones0_.personid=?
2
0
Hibernate: delete from PHONE where id=?
Hibernate: delete from PHONE where id=?

Probably I will need to look at how the cascade styles work for one-to-many associations. But that I will do as some later point in time.




		
Advertisements

16 thoughts on “Investigating Hibernate Associations – Cascading Styles

  1. Hello Mr. President

    This is the greatest work you have done at least for me. I have read books on hibernate but I am still confused. If you can help me I have a problem. I am using struts-hibernate combination. I am using struts1.3.10 Now I have a these four tables. Person with fields id, name, genderId, phoneId. and the other table is Phone with fields phoneId, personId and the other table is PhoneMap with fields phoneId, phoneNo and the other table is genders with fields genderId, gender which all classes with setter getter methods should be created and which all .hbm.xml files should be created.

    Regards
    Soniya

  2. First of all, I think you need to get your naming conventions and table structures correct. Here’s how your tables should be structured.

    PERSON –> personid, name, genderid
    PHONE –> phoneid, phoneno
    PERSON_PHONE –> personid, phoneid
    GENDER –> genderid, gender

    HBM files and classes that need to be created for Person, Phone and Gender. In Person define one-to-one relationships between Person and Gender and define many-to-many relationship between Person and Phone. How to achieve one-to-one and many-to-many relationships is explained in my other posts.

  3. Hello President

    Thanks.
    I have tried doing as you have said. I am pasting my code. Please correct me if I am wrong.

    ****************************
    gender.hbm.xml
    ****************************

    *****************************
    person.hbm.xml
    *****************************

    ******************************
    Person Class
    *******************************
    public class Person {

    private int personId;
    private String firstName;
    private String lastName;
    private Gender gender;
    private int genderId;

    public int getPersonId() {
    return personId;
    }

    public void setPersonIdd(int personId) {
    this.personId= personId;
    }

    public String getFirstName() {
    return firstName;
    }

    public void setFirstName(String firstName) {
    System.out.println(firstName);
    this.firstName = firstName;
    }

    public String getLastName() {
    return lastName;
    }

    public void setLastName(String lastName) {
    this.lastName = lastName;
    System.out.println(lastName);
    }
    public int getGenderId() {
    return genderId;
    }

    public void setGenderId(int genderId) {
    System.out.println(genderId);
    this.genderId = genderId;
    }

    public void setGender(Gender gender) {
    this.gender = gender;
    }

    public Gender getGender() {
    return gender;
    }
    }

    ***********************************
    Gender class
    ***********************************
    public class Gender {

    private int genderId;
    private String genderName;
    private int enc_genderId;

    public int getGenderId() {
    return genderId;
    }

    public String getGenderName(){
    return genderName;
    }

    public int getEnc_genderId(){
    return enc_genderId;
    }

    public int setGenderId() {
    return genderId;
    }

    public String setGenderName(){
    return genderName;
    }

    public int setEnc_genderId(){
    return enc_genderId;
    }
    }

    I am getting the following exception
    org.hibernate.MappingException: property-ref not found: genderId in class: Gender

  4. Hello President

    Iam sending it again as the cose for the configuration files were not copied

    ****************************
    genders.hbm.xml
    ****************************

    *****************************
    person.hbm.xml
    *****************************

    I am getting the following exception
    org.hibernate.MappingException: property-ref not found: genderId in class: Gender

    Regards
    Soniya

  5. As you are unable to send me the *.hbm.xml files I am not sure how I can assist you. However if you refer to my examples in my other blogs, I have not used the property-ref attribute. May be you could start with that.

  6. one-to-one name=”gender” class=”Gender” property-ref=”genderId”

    This is the line I have written in person.hbm.xml along with the property and column tags as you have given in the example. I also have written gender.hbm.xml
    Hope you will be able to help me out.
    wit this much of the info.

    Regards
    Soniya

  7. Hello Mr President

    I am stuck again. I have written a query in which I have retrieved total three fields 2 fields “firstName”, “lastName” from “Person” table and “genderName” field from “Gender” table
    comparing genderId in the where clause. Now I want to create a List of the result and display it from .jsp page. I have retrieved the data but not able to display it.

    Regards
    Soniya

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s