Does cascade="all-delete-orphan" inverse="true" work together? - java

This is my OrderSet.hbm file. It has OrderSetMembers as it's child (one-to-many) relationship.
<list name="orderSetMembers" lazy="true" cascade="all-delete-orphan" inverse="true">
<key column="order_set_id" not-null="true"/>
<list-index column="sequence_number"/>
<one-to-many class="OrderSetMember" />
</list>
This is my OrderSetMember.hbm file. OrderSetMember has a many-to-one relationship with its parent. I wanted a bi-directional mapping.
<many-to-one name="orderSet" class="OrderSet">
<column name="order_set_id"/>
</many-to-one>
Can the parent and the child both be saved with one session-save command?
Or do I need to have another session save to save the child as well?
Session session = sessionFactory.getCurrentSession();
session.saveOrUpdate(orderSet);
These are my data-models:
public class OrderSet {
private List<OrderSetMember> orderSetMembers;
}
public class OrderSetMember {
private OrderSet orderSet;
}

cascade and inverse have two completely different purposes.
cascade tells which entity lifecycle operation should be automatically applied on child(ren) when applied on parent.
inverse means that child is the owner of the association. That further means that if you don't associate child with the parent on the child side, the relationship information is not going to be synchronized with the database.
For example, if you add an OrderSetMember to the orderSetMembers collection in the parent entity instance, but you leave orderSet field null in the OrderSetMember instance, and then invoke session.saveOrUpdate(orderSet), the outcome will be:
Both OrderSet and OrderSetMember instances are saved to the database (save is cascaded to the children).
order_set_id (foreign key in the table to which OrderSetMember is mapped) is set to null. This is because OrderSetMember is the owner of the association, and there was no associated OrderSet entity when Hibernate inspected the owner of the association at dirty-check time.
When you read the above OrderSet instance in a new session, you'll notice that the OrderSetMember is not present in the orderSetMembers collection either.

Related

Hibernate: Update parent in Many to One relationship

Trying to update Parent while updating the child in a many-one relationship. Here is the hbm.xml
<class name="childProduct" table="CHILD_PRODUCT" lazy="true">
<many-to-one name="parent" class="Parent" column="Parent_ID" lazy="false" update="true"/>
</class>
Parent.java
String att1;
String att2;
String att3;
//Getter and Setters are there
Here what I do in the Dao class:
//Read the entity
entity = dao.read(request.getId());
//Set the entity from request
entity.setSomeAttribute(request.getSomeAttribute());
entity.setParent(request.getParent());
In this way if I am not sending any of the attribute value in request, it gets set as null for Parent. But it does not happen for childProduct.
How would I make sure that only the values sent from request, gets updated?

Hibernate - setting null on the associated collection

I am trying to delete all the referenced entity in a collection of a parent entity by setting null on the collection.
For eg :
A is the parent class having one-to-many relation with class B.
Class A {
private Set<B> setB = new HashSet<B>();
}
The mapping is as follows :
<set name="setB " table="B" cascade="save-update" inverse="true">
<key column="FKey"></key>
<one-to-many class="B" />
</set>
a.setB(null);//a is persistent instance of A
The above call to set the collection as null is not deleting the entries in B. Has this anything to do with inverse = true.
Is it illegal to delete the child entities this way?
What about a.getB().clear();?

Multiple flush in same EJB transaction causing the changes of the first flush disappear

I have a container managed transactions and in one method which has an entity manager associated to that transaction . Tables that i have is a parent table and an association for an another table which contains the children of the parent table.
In same session i do the following items in the sequence mentioned below :
Initially in the session we will not have the parent object or the child object .But parent object will be there in the DB and children will be added in the method that we are interested in .
I get the parent object from the DB in the session so this parent
object will be in the session.
I create a new child record and associate this child to the parent table, since the parent is part of the session hibernate must take care of adding the child record in the child table correct ??
Then i will perform a flush at this point.
Again i will add a new child to the collection of the parent
As said before hibernate must take care of adding it rite ?
I am performing the flush again now .
At the end of the EJB transaction the hibernate will generate all the other sql statements.
After performing these operations i see that in the DB only the child that is added second time is getting associated to the parent table.But for the first child entry is present but the parent association is not getting set .
If i remove the flush in between adding the children both the children are getting correctly associated to the parent .Flush is causing an issue here , but i have to use flush here at any cost because take into consideration i am associating 100 children once shot then perform a flush and later i will associate another 10 children and then do a flush only the last 10 children are associated to the parent all others are in orphan stage .This is causing a issue for me !:(
Please comment back on how to solve this issue .Should i perform some other operations like update or saveOrUpdate before the flush ?
THE algorithm is here please find it :
Since the code is big and long i will be posting the algorithm of how the operation is performed
Assume i have 2 tables one for the parent named PARENT and the other for the child named CHILD \
And the child entity is extended from the parent entity
Assume that initially i have only one parent record in the DB and there is no CHILD records in the child table
There is a bidirectional link between the child and the parent one-to-many and vice verse.
At the start of the transaction i ll get the parent data frm the DB so that the parent gets associated to the Persistence Context .
Now assume i am creating a new POGO object of the child and fill its data. Assume that i will create 20 such objects and then associate both bidirectional the child to parent and parent to child.
Since the parent is in hibernate context when i associate it to child hibernate will take care of creating and making the link .
Now assume i do a flush on the session context .
Next i create 10 more POGO objects of the child and do the same association to parent , and carry out a flush again.
after the end of transaction when i check the DB i see that the first 20 entries are there in the DB but the association for them to parent is absent. But for the last added 10 Child objects both the entries and the association for the parent is there.
When i remove the flush in between adding the child between 20- and 10 i see that all the 30 are correctly getting associated. I want to know why the flush is causing the problem.
You need to take special care if you use a bi-directional relation as described in the hibernate reference:
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address" column="addressId" not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<set name="people" inverse="true">
<key column="addressId"/>
<one-to-many class="Person"/>
</set>
</class>
In this case, if you add a person to address.people, but person.address is null, hibernate will not save the person, because the address.people relation is inverse="true".
You need to set both directions of the relation for hibernate to save it correctly:
public void addPerson(Person person) {
person.setAddress(this);
this.people.add(person);
}
See 1.2.6 Working bi-directional links in the hibernate tutorial.

Hibernate mapping with one-to-many polymorphic relationship

I have the following class diagram and I want to map it to a database (note that Person has a list with objects of class Vehicle).
Also my database looks like:
All tables in the database that represent a subclass of the Vehicle class have all the fields of the superclass Vehicle. Also, all the relations show a one-to-many relationship from Person to Vehicle, Car and Motorcycle.
My hibernate mapping files are the following:
Person.hbm.xml
<hibernate-mapping package="....">
<class name="Person" table="Persons">
<id name="key" column="Person_ID">
<generator class="native"/>
</id>
<list name="ownedVehicles" inverse="false" cascade="all">
<key column="Person_ID" not-null="true" />
<list-index column="idx"/>
<one-to-many class="Vehicle"/>
</list>
</class>
</hibernate-mapping>
Vehicle.hbm.xml
<hibernate-mapping package="...">
<class name="Vehicle" table="Vehicles" polymorphism="implicit">
<id name="id" type="int" column="Vehicle_ID">
<generator class="increment"/>
</id>
<property name="numOfSeats"/>
<union-subclass name="Car" table="Cars"></union-subclass>
<union-subclass name="Motorcycle" table="Motorcycles"></union-subclass>
</class>
</hibernate-mapping>
The problem (error I get) is the following:
Hibernate: insert into Persons (Person_ID) values (default)
2013-06-26 15:41:52 WARN JdbcCoordinatorImpl:424 - HHH000386: ResultSet had no statement associated with it, but was not yet registered
Hibernate: update Car set numOfSeats=? where Vehicle_ID=?
org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
I get this error when I run:
Car car = new Car();
car.setNumOfSeats(5);
Person person = new Person();
person.getOwnedVehicles().add(car);
ManagePerson managePerson = new ManagePerson();
Integer personID = managePerson.store(person);
The store() function of ManagePerson actually creates a session and a transaction and then uses the save() method provided by Hibernate to persist the objects into the database.
As far as I understand Hibernate usually will do insert into Persons, then insert into Cars and finally update Cars (the update is done to save the foreign keys on Cars table that will reference the Person that owns the cars). However, here this is not the case and the insert into Cars seems to be getting skipped. I understood how Hibernate works here by trying person.getOwnedVehicles().add(vehicle); instead of person.getOwnedVehicles().add(car); on the code given above.
As you might understand, I am trying to see if Hibernate actually understands in which "subclass" table a record should go, depending on the class of the object contained in the ownedVehicle list of the Person class. For example, if the ownedVehicles has an object of class Car and one of class Motorcycle, then each of these should go to Cars and Motorcycle tables respectively.
Note: I am using Hibernate 4.2.2 and HSQLDB 2.2.9.
I would appreciate any help with this.
Thanks.
I think it is just a matter of incorrect use of the implicit polymorphism of Hibernate.
Implicit polymorphism for your case can only work by changing your list to have
inverse="true". This can be done of course if your Vehicle class also 'knows' about the relationship with the Person class (e.g. by adding an 'Owner' property and the corresponding mapping).
(Have a look at this table and the case of "table per concrete-class (union-subclass)" and one-to-many associations.
If you enable logging and raise the log level to DEBUG you would see that currently Hibernate tries to update the Vehicles table with the Person_ID instead of the Car table like you meant it to. This is because of the inverse="true" and the limitations of the combination of the Table-per-concrete-class mapping strategy and implicit polymorphism (have a look at the documentation).
So, by having the Vehicle class know about its Owner and using inverse="true" you should be able to succeed in what you are trying to do. Either this or try one of the other inheritance mapping strategies (again have a look at the documentation).
If the managePerson.store(...) method doesn't have a recursive call to the objects in "getOwnedVehicles()" such that it can then call their "store" methods then you shouldn't expect that the created "car" object would be inserted into the table.
You are in fact calling "managePerson.store" not "manageCar.store", I'd have to see the code in the .store(...) method to be sure though but I would expect that it is not doing an iteration of the Vehicles and is not doing an insert for any discovered ones (why should it unless you built it explicitly to do that?).

Inserting data using Hibernate Template

Consider the following:
User.java
class User{
...
private Set<Community> communities = new HashSet<Community>();
...
}
Community.java
class Community{
...
private Set<User> users = new HashSet<User>();
...
}
User.hbm.xml
<set name="communities" cascade="save-update" lazy="false" table="tbl_user_community">
<key column="user_id" />
<many-to-many class="Community" column="community_id"/>
</set>
Community.hbm.xml
<set name="users" lazy="false" cascade="save-update" table="tbl_user_community" inverse="true">
<key column="community_id" />
<many-to-many class="User" column="user_id"/>
</set>
both has many-to-many relationship.
Code for adding community to user:
HibernateTemplate template = null;
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
template = new HibernateTemplate(sessionFactory);
User user = template.get(User.class, "100");
Community community = template.get(Community.class,1);
user.getCommunities().add(community);
template.saveOrUpdate(user);
Scenario:
community1 is assigned to user1 (inserted into database)
community2 is assigned to user1 (inserted into database)
community1 is assigned to user2 (inserted into database)
community2 is assigned to user2 (not inserted into database, throws NonUniqueObjectException)
if i use template.merge(user) instead of template.saveOrUpdate(user).. it works .. why???
saveOrUpdate() and update() take a detached object and attach this object instance to the session. So if an entity with the same type and ID is already attached to the session, Hibernate can't attach another instance: it would break the guarantee that only one entity with a given ID exists in a session. So it throws this exception.
merge() is different. It takes an detached entity, loads the entity with the same ID in the session if not already loaded, and copies the state (i.e. the fields) of the detached entity to the attached one. The detached entity stays deteached and unmodified.
So, in general, merge() should be preferred. BTW, the other methods don't exist in the JPA API. If you use saveOrUpdate(), you should generally call it as the first instruction of the transaction, to avoid being in the situation causing the NonUniqueException.

Categories