-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 14 posts ] 
Author Message
 Post subject: Delete and relationships
PostPosted: Thu Sep 22, 2005 4:29 am 
Newbie

Joined: Wed Sep 21, 2005 7:16 am
Posts: 10
Hibernate version: 3.0.2 (in JBoss 4.0.2)

Hello,

i'm having some problems when deleting objects (inverse side) that are involved in relationships. Maybe someone could help me out or give me some hints?

Suppose i have a one-to-many relationship between Company and Employee. Company is declared as the inverse side and the Employee table contains a Foreign Key to the Company. When the Company is deleted (hSession.delete(company)) the foreign key will not be removed from the Employee table.
If this would be a many-to-many relationship, with a CompaniesEmployees table for this relationship, the rows containing the deleted Company's ID will not be removed from this table.

I understand that this is caused because of using the inverse="true" attribute in the Company's mapping file. Probably i should clear up the Company's relationships before deleting it:

Code:
public class Company
{
  ...
  public void clearAllYourInverseRelationships()
  {
    employees.clear();
    // This probably isn't sufficient ...
  }
}

company.clearAllYourInverseRelationships();
hSession.delete(company);


But what should i do when the Company is deleted because of a cascade delete? In this case i don't know when it will be deleted so i can't call the clearAllYourInverseRelationships() method.
Maybe i could write a PreDeleteEventListener that will be called when something is deleted by a cascade delete. This listener could call the clearAllYourInverseRelationships() method. But this would mean my POJO's would have to imlement an interface that defines the clearAllYourInverseRelationships() method. Furthermore i'm using JBoss 4.0.2 so it can't add event listeners using the Hibenate MBean in a har (yet?).
And isn't all of this going a bit to far?

Maybe i'm looking at this wrong and should i take a completely other approach??? I can of course always remove the inverse="true" from my mapping files but its use is recommended.
With one-to-many and many-to-many relationships i don't get exceptions because of this problem, there is just data in the database that shouldn't be there but maybe for certain applications i could live with that.

Unfortunately things are worse for one-to-one relationships with foreign keys. Suppose i have a bi-directional one-to-one relationship between Employee and Address, using the following mapping files:

Code:
<class name="myapp.Employee">
   ...
   <many-to-one name="address" column="address" unique="true"/>
</class>

<class name="myapp.Address">
   ...
   <one-to-one name="employee" property-ref="address"/>
</class>


Fist problem is that when i delete an Address the foreign key isn't remove from the Employee table although there is no inverse side here.
Secondly when i call getAddress() on an Employee of which the Address was deleted before i get an Exception since an Address with that key doesn't exist any more.
What am i doing wrong here?

Any help with this would be much appreciated!

Regards,
Erwin

PS: I was so stupid to post this message in the wrong forum (JBoss Application Server & JBoss Cache, http://forum.hibernate.org/viewtopic.php?t=947926), sorry about that. I don't know how to move it although the FAQ mentions that it should be possible to delete it (can't find the edit or delete buttons).


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 24, 2006 4:51 am 
Newbie

Joined: Wed Sep 21, 2005 7:16 am
Posts: 10
I'm still facing these problems.
Nobody answered last year, maybe i'll have better luck this time!


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 24, 2006 6:33 am 
Regular
Regular

Joined: Tue Nov 29, 2005 12:31 pm
Posts: 75
Hi

Post your mappings and the code you're using.

And post only relevant parts (that's one of the reasons nobody answered to ypur problem).


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 27, 2006 5:19 am 
Newbie

Joined: Wed Sep 21, 2005 7:16 am
Posts: 10
Sure, here you can find everything:

The Java Code for the POJO's:

Code:
package test.hibernate;

import java.util.*;

public class Company
{
   private long id;
   private String name;

   private Set employees;
   private Set customers;

   public Company()
   {
   }

   public long getId()
   {
      return id;
   }

   public void setId(long id)
   {
      this.id = id;
   }

   public String getName()
   {
      return name;
   }

   public void setName(String name)
   {
      this.name = name;
   }

   public Set getEmployees()
   {
      return employees;
   }

   public void setEmployees(Set employees)
   {
      this.employees = employees;
   }
   
   public void addEmployee(Employee employee)
   {
      employees.add(employee);
      employee.setCompany(this);
   }

   public void removeEmployee(Employee employee)
   {
      employee.setCompany(null);
      employees.remove(employee);
   }

   public Set getCustomers()
   {
      return customers;
   }

   public void setCustomers(Set customers)
   {
      this.customers = customers;
   }

   public void addCustomer(Customer customer)
   {
      customers.add(customer);
      customer.getCompanies().add(this);
   }

   public void removeCustomer(Customer customer)
   {
      customers.remove(customer);
      customer.getCompanies().remove(this);
   }
}

Code:
package test.hibernate;

public class Employee
{
   private long id;
   private String firstName;
   private String lastName;

   private Address address;
   private Company company;

   public Employee()
   {
   }

   public long getId()
   {
      return id;
   }

   public void setId(long id)
   {
      this.id = id;
   }

   public String getFirstName()
   {
      return firstName;
   }

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

   public String getLastName()
   {
      return lastName;
   }

   public void setLastName(String lastName)
   {
      this.lastName = lastName;
   }

   public Address getAddress()
   {
      return address;
   }

   public void setAddress(Address address)
   {
      this.address = address;
   }
   
   public void addAddress(Address address)
   {
      this.address = address;
      address.setEmployee(this);
   }

   public void removeAddress()
   {
      address.setEmployee(null);
      address = null;
   }

   public Company getCompany()
   {
      return company;
   }

   public void setCompany(Company company)
   {
      this.company = company;
   }

   public void addCompany(Company company)
   {
      this.company = company;
      company.getEmployees().add(this);
   }

   public void removeCompany()
   {
      company.getEmployees().remove(this);
      company = null;
   }
}

Code:
package test.hibernate;

public class Address
{
   private long id;
   private String street;

   private Employee employee;

   public Address()
   {
   }

   public long getId()
   {
      return id;
   }

   public void setId(long id)
   {
      this.id = id;
   }

   public String getStreet()
   {
      return street;
   }

   public void setStreet(String street)
   {
      this.street = street;
   }

   public Employee getEmployee()
   {
      return employee;
   }

   public void setEmployee(Employee employee)
   {
      this.employee = employee;
   }
   
   public void addEmployee(Employee employee)
   {
      this.employee = employee;
      this.employee.setAddress(this);
   }

   public void removeEmployee()
   {
      employee.setAddress(null);
      employee = null;
   }
}

Code:
package test.hibernate;

import java.util.*;

public class Customer
{
   private long id;
   private String name;

   private Set companies;

   public Customer()
   {
   }

   public long getId()
   {
      return id;
   }

   public void setId(long id)
   {
      this.id = id;
   }

   public String getName()
   {
      return name;
   }

   public void setName(String name)
   {
      this.name = name;
   }

   public Set getCompanies()
   {
      return companies;
   }

   public void setCompanies(Set companies)
   {
      this.companies = companies;
   }
   
   public void addCompany(Company company)
   {
      companies.add(company);
      company.getCustomers().add(this);
   }
   
   public void removeCompany(Company company)
   {
      companies.remove(company);
      company.getCustomers().remove(this);
   }
}

The Hibernate Mapping Files:

Code:
<hibernate-mapping>
   <class name="test.hibernate.Company">
      <id name="id"/>
      <property name="name"/>

      <set name="employees" inverse="true">
         <key column="company"/>
         <one-to-many class="test.hibernate.Employee"/>
      </set>

      <set name="customers" table="CompaniesCustomers" inverse="true">
         <key column="companies"/>
         <many-to-many column="customers" class="test.hibernate.Customer"/>
      </set>

   </class>
</hibernate-mapping>

Code:
<hibernate-mapping>
   <class name="test.hibernate.Employee">
      <id name="id"/>
      <property name="firstName"/>
      <property name="lastName"/>

      <many-to-one name="address" column="address" unique="true" cascade="delete"/>

      <many-to-one name="company" column="company"/>
   </class>
</hibernate-mapping>

Code:
<hibernate-mapping>
   <class name="test.hibernate.Address">
      <id name="id"/>
      <property name="street"/>

      <one-to-one name="employee" property-ref="address"/>
   </class>
</hibernate-mapping>

Code:
<hibernate-mapping>
   <class name="test.hibernate.Customer">
      <id name="id"/>
      <property name="name"/>

      <set name="companies" table="CompaniesCustomers">
         <key column="customers"/>
         <many-to-many column="companies" class="test.hibernate.Company"/>
      </set>
   </class>
</hibernate-mapping>

The Database Initialization Script:

Code:
DROP TABLE IF EXISTS Company;
CREATE TABLE Company(
   id BIGINT PRIMARY KEY,
   name VARCHAR(30) NOT NULL
) TYPE=INNODB;

DROP TABLE IF EXISTS Employee;
CREATE TABLE Employee(
   id BIGINT PRIMARY KEY,
   firstName VARCHAR(30) NOT NULL,
   lastName VARCHAR(30) NOT NULL,
   address BIGINT UNIQUE,
   company BIGINT
) TYPE=INNODB;

DROP TABLE IF EXISTS Address;
CREATE TABLE Address(
   id BIGINT PRIMARY KEY,
   street VARCHAR(30) NOT NULL
) TYPE=INNODB;

DROP TABLE IF EXISTS Customer;
CREATE TABLE Customer(
   id BIGINT PRIMARY KEY,
   name VARCHAR(30) NOT NULL
) TYPE=INNODB;

DROP TABLE IF EXISTS CompaniesCustomers;
CREATE TABLE CompaniesCustomers(
    companies BIGINT,
    customers BIGINT
) TYPE=INNODB;

One-to-one relationship:
There is a one-to-one relationship between Employee and Address. The FK is the Employee table. The following code deletes an Address:

Code:
Session session = getHibernateSession();
Address address = (Address)session.get(Address.class.getName(), new Long(id));
session.delete(address);

After executing this code the Address row is deleted from the database but the FK is still in the Employee table. When getAddress is called on that Employee an Exception occurs since the Address doesn't exist anymore.

One-to-many relationship:
There is a one-to-many relationship between Company and Employee. The FK is in the Employee table.The following code deletes a Company:

Code:
Session session = getHibernateSession();
Company company   = (Company)session.get(Company.class.getName(), new Long(id));
session.delete(company);

When deleting the Company it is removed from the DB but the FK isn't set to null in the Employee table.

Many-to-many relationship:
There is a many-to-many relationship between Company and Customer with a seperate table CompaniesCustomers.

When a Customer is deleted Hibernate executes two SQL-statements, one to remove the Customer from the Customer table and one to clear it from the CompaniesCustomers table. This is great.
Unfortunately only one statement is executed when deleting a Company, the Company doesn't get removed from the CompaniesCustomers table.

I could solve these problems by removing the relationships in my code, before deleting an entity but i do have some problems with this:
1. It's overkill (but i could live with that).
2. When the entity gets deleted because of a cascade-delete (e.g. because of a delete of a parent) i can't call the code to clean-up its relationships.

I think this is all very basic stuff when working with relationships but what is the best way to handle it???


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 27, 2006 6:40 am 
Regular
Regular

Joined: Sat May 29, 2004 2:16 pm
Posts: 81
Quote:
After executing this code the Address row is deleted from the database but the FK is still in the Employee table.

just a guess: have you tried removing employee first before deleting address?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 27, 2006 7:47 am 
Newbie

Joined: Wed Sep 21, 2005 7:16 am
Posts: 10
I don't want to remove the Employee from the database, only the Address. Problem is that after an Address is deleted the address column of the Employee table still contains the primary key of the deleted Address and that's no good.

Of course i could extend the deleteAddress method and set the Employee's Address to null:

Code:
Session session = getHibernateSession();
Address address = (Address)session.get(Address.class.getName(), new Long(id));

Employee employee = (Employee)address.getEmployee();
if(employee != null)
    employee.setAddress(null);

session.delete(address);

But that's the question: should something like this be done or is there a way to make it happen automatically?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 27, 2006 1:30 pm 
Regular
Regular

Joined: Sat May 29, 2004 2:16 pm
Posts: 81
what i meant was removing, not deleting from database; anyway, in my app i've a collection instead:
my parent's (actoMedico) hbm relevant config:
Code:
      <set
         name = "tecnicas"
         lazy = "true"
         inverse = "false"
         cascade = "none">
         <key
            column = "actoMedico_id" />
         <many-to-many
            class = "Tecnica" />
      </set>

and in my code:
Code:
        tecnicaCristina.setActoMedico(null);

        s.delete(am);

relevant config for child:
Code:
         <many-to-one
            name = "actoMedico"
            column = "actoMedico_id"
            class = "ActoMedico" />

where ActoMedico (am) is the parent and Tecnica (tecnicaCristina) is a child


Top
 Profile  
 
 Post subject:
PostPosted: Tue Mar 28, 2006 3:20 am 
Newbie

Joined: Wed Sep 21, 2005 7:16 am
Posts: 10
OK, that's what i'm saying; is it really needed to do this, to explicitely set the Technica's ActoMedico to null? It can be done this way but there are a few problems with it:

1. If the ActoMedico that should be deleted has 100 Technicas in the collection we should iterate over the collection and call nextTechnica.setActoMedico(null); 100 times. This will execute 100 SQL-statements on the database which of course is bad. Hibernate should be intelligent enough to do this with one SQL statement.

2. Suppose the ActoMedico class has a relationship with another class and it can be deleted because of a cascade-delete (that's probably not the case in your application, but suppose this is possible). When an ActoMedico is deleted because of that cascade-delete we don't know so we can never call tecnicaCristina.setActoMedico(null);.

Like is said before; this is very basic stuff when working with relationships. Anyone who ever creates an application with a relationship in it (probably every application) has to deal with this. The answers to my questions should be in the manual but unfortunately i can't find them there.

By the way, you have set inverse to false in your ActoMedico's hbm file. I think Hibernate will automatically set the relationship to null in this case.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Mar 28, 2006 5:11 pm 
Regular
Regular

Joined: Sat May 29, 2004 2:16 pm
Posts: 81
i see your point, but coud not find another way out

Quote:
By the way, you have set inverse to false in your ActoMedico's hbm file. I think Hibernate will automatically set the relationship to null in this case.

my app works fine and for sure does that relationship :)


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 30, 2006 1:53 pm 
Regular
Regular

Joined: Sat May 29, 2004 2:16 pm
Posts: 81
erwinp wrote:
1. If the ActoMedico that should be deleted has 100 Technicas in the collection we should iterate over the collection and call nextTechnica.setActoMedico(null); 100 times. This will execute 100 SQL-statements on the database which of course is bad. Hibernate should be intelligent enough to do this with one SQL statement

giving it a 2nd thought, i guess hibernate goes just once to database, because when you iterate the colletion, you'r operating on memory terms, and only when you persist (s.saveOrUpdate(myObject)) sql executes the whole update (once)


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 31, 2006 5:05 am 
Newbie

Joined: Wed Sep 21, 2005 7:16 am
Posts: 10
That's very well possible, i must admit i haven't tried it. Never the less there's still a problem with a cascade-delete.

For a one-to-many it's solved by setting inverse to false. For a one-to-one and (i think) for a many-to-many this can't be done (1-1) or doesn't help (1-*). It is obvious to me that Hibernate is missing something there and i'll give it a try at the JIRA.

In each case, thanks a lot for your responses!!!


Top
 Profile  
 
 Post subject:
PostPosted: Sat Apr 01, 2006 6:10 am 
Regular
Regular

Joined: Sat May 29, 2004 2:16 pm
Posts: 81
if i were you i'd take a look at this friendly http://saloon.javaranch.com/cgi-bin/ubb/ultimatebb.cgi?ubb=forum&f=78&SUBMIT=Go forum before making up my mind :)


Top
 Profile  
 
 Post subject:
PostPosted: Sat Apr 08, 2006 4:51 am 
Regular
Regular

Joined: Tue May 04, 2004 6:15 am
Posts: 51
erwinp, thats the same problem I'm facing. Which approach did you follow to tackle it?

I'm surprised noone has given any input on this.

_________________
eu:life
http://www.eulife.gr


Top
 Profile  
 
 Post subject:
PostPosted: Mon Apr 10, 2006 3:19 am 
Newbie

Joined: Wed Sep 21, 2005 7:16 am
Posts: 10
Well, i didn't tackle it yet...

Everybody who uses relationships has to deal with these problems so i also think it's very surprising there isn't more input on this post.

In each case, i posted it at the JIRA (http://opensource.atlassian.com/projects/hibernate/browse/HHH-1623). Only for one-to-one relationships. Setting inverse to false will solve the problems with one- and many-to-many (we don't want to set inverse to false but it's the only option).
Unfortunately there isn't any reaction there neither.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 14 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.