Hibernate parent/child association. Update parent make child lost all associated objects - java

I have following classes.
public class Employee {
private int id;
private String firstName;
private String lastName;
private Address address;
private Employer employer;
}
public class Employer {
private int id;
private String name;
private Set<Employee> employees;
}
public class Address {
private int id;
}
public class Project{
private int id;
}
<hibernate-mapping>
<class name="me.hibernate.basic.xml.Employee" table="EMPLOYEE">
<id name="id" type="int" column="id">
<generator class="native" />
</id>
<property name="firstName" column="first_name" type="string" />
<property name="lastName" column="last_name" type="string" />
<many-to-one name="address" column="address" unique="true" class="me.hibernate.basic.xml.Address"/>
<set name="projects" cascade="save-update, delete-orphan" sort="natural">
<key column="employee_proj_id" />
<one-to-many class="me.hibernate.basic.xml.Project" />
</set>
<many-to-one name="employer" column="employer" class="me.hibernate.basic.xml.Employer"/>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="me.hibernate.basic.xml.Employer" table="EMPLOYER">
<id name="id" type="int" column="id">
<generator class="native" />
</id>
<property name="name" column="name" type="string" />
<set name="employees" cascade="save-update, delete-orphan" table="EMPLOYEE">
<key column="employer"/>
<one-to-many class="me.hibernate.basic.xml.Employee" />
</set>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="me.hibernate.basic.xml.Address" table="ADDRESS">
<meta attribute="class-description"> This class contains the address detail. </meta>
<id name="id" type="int" column="id">
<generator class="native" />
</id>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="me.hibernate.basic.xml.Project" table="PROJECT">
<meta attribute="class-description"> This class contains the project detail. </meta>
<id name="id" type="int" column="id">
<generator class="native" />
</id>
</class>
</hibernate-mapping>
In my application I create few Employees and assign an address to them. And add some projects. Then I create an employer and add all the employees to employer. Once I add employees and update the employer, all the employees lose their addresses and projects. How can I do this with keeping lazy loading feature. I don't need to set lazy="false".
Employee emp1 = ME.addEmployee("Zara", "Ali");
Employee emp2 = ME.addEmployee("Daisy", "Das");
Address addr1 = ME.addAddress(35, "xxxxxx Street", "XXXXX", "XY7 0ZZ");
Address addr2 = ME.addAddress(42, "xxxxxx Street", "XXXXX", "XY7 7ZZ");
ME.setAddress(emp1.getId(), addr1);
ME.setAddress(emp2.getId(), addr2);
Set<Project> proj = new HashSet<Project>();
proj.add(new Project("NOVA"));
proj.add(new Project("GTA Simplify"));
proj.add(new Project("Jazz"));
ME.addProjects(emp.getId(), proj);
ME.addProjects(emp.getId(), proj);
All working up to this point.
Set<Employee> emps = new HashSet<Employee>();
emps.add(emp1); emps.add(emp2);
//Add existing employees to employer - Many-to-one bidirectional
Employer employer = ME.addEmployer("XYZ");
ME.addEmployees(employer.getId(), emps);
public Integer addEmployees(Integer employerID, Set<Employee> employees) {
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Employer employer = (Employer) session.get(Employer.class,employerID);
employer.getEmployees().clear();
employer.getEmployees().addAll(employees);
session.update(employer);
tx.commit();
} catch (HibernateException e) {
if (tx != null)
tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
return employerID;
}
Once I added employees, all the foreign key references are lost in the PROJECT.employee_proj_id and EMPLOYEE.address.
Hibernate log:
->Hibernate: update EMPLOYEE set first_name=?, last_name=?, basic=?, address=?, employer=? where id=?
->Hibernate: update EMPLOYEE set first_name=?, last_name=?, basic=?, address=?, employer=? where id=?
->Hibernate: update PROJECT set employee_proj_id=null where employee_proj_id=?
->Hibernate: update PROJECT set employee_proj_id=null where employee_proj_id=?
->Hibernate: update EMPLOYEE set employer=? where id=? ->Hibernate: update EMPLOYEE set employer=? where id=?

I see that you are clearing the employees before you add more employees, which is the root cause of everything. I'm not sure what you are doing when adding addresses, Project, employer etc., You need not clear the employees. If you have cascadetype.all set on the employees then saving the employer will update the newly added employees.
employer.getEmployees().clear();//remove this line.
employer.getEmployees().addAll(employees);

Related

Hibrernate: Attribute "type" must be declared for element type "column"

I am trying to save to two tables in SQL server using Hibernate:
ORDER and ORDER_ITEM
I get an error:
Attribute "type" must be declared for element type "column".
Initial SessionFactory creation failed.org.hibernate.InvalidMappingException: Unable to read XML.
This produces a NullPointerException
If I understand correctly, this means that when I try to save to the order_item table, the getter for the foreign key is empty, but how would I set it if is designed to 'Autoincrement', I thought that hibernate would handle this in it's transaction.
Below are my POJO's and Hibernate mappings. I have omitted the getters and setters from this copy/paste, but they are present in my actual code
I am also successful in saving just to the ORDER table if I remove the set
Order.java:
public class Order {
public Order(){
super();
}
private int orderId;
private Set<LineItem> items;
private String strPhone;
private String strEmail;
private String strFirstName;
private String strLastName;
private String strParentFirstName;
private String strParentLastName;
private String strOrganizationName;
private String strOrganizationType;
private String strComment;
}
order.hbm.xml:
<hibernate-mapping>
<class name="dbobjects.Order" table="orders">
<id name="orderId" type="integer" column="order_id">
<generator class="increment"/>
</id>
<property name="strPhone" type="string" column="phone_number"/>
<property name="strFirstName" type="string" column="first_name"/>
<property name="strLastName" type="string" column="last_name"/>
<property name="strParentFirstName" type="string" column="parent_first_name"/>
<property name="strParentLastName" type="string" column="parent_last_name"/>
<property name="strOrganizationName" type="string" column="organization_name"/>
<property name="strOrganizationType" type="string" column="organization_type"/>
<property name="strComment" type="string" column="comments"/>
<set name="items" table="order_item" fetch="select" cascade="all">
<key>
<column name="orderId" type="java.lang.Integer"/>
</key>
<one-to-many class="dbobjects.LineItem"/>
</set>
</class>
LineItem.java:
public class LineItem {
public LineItem(){
super();
}
private int orderItemId;//this will be the primary key
private int orderId;//this is the foreign key to the order
private String age;
private String gender;
private String type;
private String itemSize;
private int itemQuantity;
}
lineItem.hbm.xml:
<id column="order_item_id" name="orderItemId" type="integer">
<generator class="increment"/>
</id>
<property column="age" name="age" type="string"/>
<property column="gender" name="gender" type="string"/>
<property column="quantity" name="itemQuantity" type="integer"/>
<property column="size" name="itemSize" type="string"/>
<property column="clothing_type" name="clothingType" type="string"/>
<many-to-one name="orderId" class="dbobjects.Order" fetch="select" column="order_id" type="java.lang.Integer"/>
This is where the error is thrown when I Instanciate the session:
**session = HibernateUtil.getSessionFactory().openSession();**←
try{
session.beginTransaction();
session.save(order);
session.getTransaction().commit();
So the question is: How do I handle this situation with 'autoincrement' primary key that is not accessible to another table as a foreign key
So, There were a few problems:
In order to submit a 'child' to the database, I needed to show who is the parent.
This means that my LineItem Class needed a
Order order;
property instead of a simple
private String orderID;
this was accomplished after the Order object was ready to be submitted to the DB but before the actual call:
for (LineItem item : order.getItems()) {
item.setOrder(order);
}
I also changed my DTD's from !DOCTYPE hibernate-configuration SYSTEM classpath://org/hibernate/hibernate-mapping-3.0.dtd"
to
!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"
Just add "type" attribute like this :
<column name="orderId" type="java.lang.Integer"/>
Try to replace :
<many-to-one name="orderId" column="order_id" class="dbobjects.Order" cascade="all">
<column name="orderId" type="java.lang.Integer"/>
</many-to-one>
by
<many-to-one name="orderId" column="order_id" class="dbobjects.Order" cascade="all"/>

Hibernate - insert data with one to many relationship

I am trying to insert data into a Patient table, which has a many-to-one relationship with Site. Site has a one-to-many relationship with Patient.
However, I get a org.postgresql.util.PSQLException: ERROR: insert or update on table "patients" violates foreign key constraint "fk_427e3ubwhw8n7a4id3mmrmjgj"
Detail: Key (patient_id)=(31) is not present in table "sites".
I have tried to create a set of patients, add my patient to this set, create a site object, then use this object to set the patients. I am not sure which part of this is going wrong.
Session session = this.getFactory().openSession();
Transaction transaction = null;
try{
transaction = session.beginTransaction();
Date parsedDob = Date.valueOf(dob);
Date parsedDateReg = Date.valueOf(dateReg);
Site site = new Site();
site.setSiteId(1);
IPatient p = new Patient();
p.setFirstName(firstName);
p.setLastName(lastName);
p.setDob(parsedDob);
p.setDateRegistered(parsedDateReg);
p.setSite(site);
Set<IPatient> patientSet = new HashSet<IPatient>();
patientSet.add(p);
site.setPatients(patientSet);
session.save(site);
session.save(p);
transaction.commit();
}catch(Exception e){
e.printStackTrace();
}
My Patient.hbm.xml file is:
<?xml version="1.0"?><!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.example.model">
<class name="Patient" table="patients">
<id name="patientId" column="patient_id">
<generator class="sequence" />
</id>
<version name="version" column="version" />
<property name="firstName" column="first_name" />
<property name="lastName" column="last_name" />
<property name="dob" column="dob" />
<property name="gender" column="gender" />
<property name="dateRegistered" column="date_registered" />
<many-to-one name="site" class="com.example.model.Site" not-null="true" />
<set name="visits" cascade="all">
<key column="visit_id" />
<one-to-many class="Visit" />
</set>
</class>
My Site.hmx.xml is:
<?xml version="1.0"?><!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.example.model">
<class name="Site" table="sites">
<id name="siteId" column="site_id">
<generator class="sequence" />
</id>
<version name="version" column="version" />
<property name="name" column="name" />
<set name="patients" cascade="all">
<key column="patient_id" />
<one-to-many class="Patient" />
</set>
</class>
My Patient.java
public class Patient implements IPatient{
private Integer version;
private Integer patientId;
private Set<IVisit> visits;
private Site site;
private String firstName;
private String lastName;
private Date dob;
private Gender gender;
private Date dateRegistered;
public Patient(){
}
}
My Site.java
public class Site {
private Integer siteId;
private Integer version;
private Set<IPatient> patients;
private String name;
public Site(){
}
}
After the line below you should save your site object to db;
site.setSiteId(1);
session.save(site);
After that you can set site object to patient;
p.setSite(site);

How to structure SQL Statement to avoid so many joins

I am trying to build a select statement which will allow me to build a local in app cache when my application starts up. My table structure looks as follows;
I know to get some help I must demonstrate what I have already attempted but its extremely
I am using hibernate which should make things easier but I am really hitting a road block, the only approach I can think of is to select all the fields and then do a left outer join on the table ids matching, however the issue with this approach is that one user can have 0-1 business cards/ 0-1 social cards, 0-1 personal cards which means when i left outer join some of the data potentially might be missing for a user. The personalInfo/Logindetails and Device Data is all 1:1 mapping
Is there a more efficient way to do the select in hibernate which will allow me to easily construct my cache?
Ideally I would like to construct an object such that;
public class User {
private BusinessCard businessCard;
private SocialCard socialCard;
private PersonalCard personalCard;
private PersonalInformation personalInformation;
private LoginDetails loginDetails;
private DeviceData deviceData;
public BusinessCard getBusinessCard() {
return businessCard;
}
public void setBusinessCard(BusinessCard businessCard) {
this.businessCard = businessCard;
}
public SocialCard getSocialCard() {
return socialCard;
}
public void setSocialCard(SocialCard socialCard) {
this.socialCard = socialCard;
}
public PersonalCard getPersonalCard() {
return personalCard;
}
public void setPersonalCard(PersonalCard personalCard) {
this.personalCard = personalCard;
}
public PersonalInformation getPersonalInformation() {
return personalInformation;
}
public void setPersonalInformation(PersonalInformation personalInformation) {
this.personalInformation = personalInformation;
}
public LoginDetails getLoginDetails() {
return loginDetails;
}
public void setLoginDetails(LoginDetails loginDetails) {
this.loginDetails = loginDetails;
}
public DeviceData getDeviceData() {
return deviceData;
}
public void setDeviceData(DeviceData deviceData) {
this.deviceData = deviceData;
}
}
Thanks
From Hibernate Community Documentation - Charpter 7 - Association Mappings:
7.2.1. Many-to-one
A unidirectional many-to-one association is the most common kind of unidirectional association.
<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>
</class>
create table Person (
personId bigint not null primary key
, addressId bigint not null
)
create table Address (
addressId bigint not null primary key
)
7.2.2. One-to-one
A unidirectional one-to-one association on a foreign key is almost identical. The only difference is the column unique constraint.
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address"
column="addressId"
unique="true"
not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>
create table Person (
personId bigint not null primary key
, addressId bigint not null unique
)
create table Address (
addressId bigint not null primary key
)
This is how you may map one-to-one relationship. Notice this is the same sa many-to-one mapping, except for the unique constraint.
Now having mapped your classes, your Person class shall look like the following.
public class Person {
public Address getAddress() { return address; }
public void setAddress(Address address) { this.address = address; }
public int getId() { return id; }
protected void setId(int id) { this.id = id; }
private Address address;
private int id;
}
To answer your question
It all depends how you want the data to be persisted. As expressed, I would go with the identities of BusinessCard, SocialCard and PersonalCard into the User table so that one and only one card may exist per user.
<class name "User" table="Users">
<id name="id" column="userId">
<generator class="native" />
</id>
<many-to-one name="businessCard"
column="businessCardId"
unique="true"
not-null="true" />
<many-to-one name="socialCard"
column="socialCardId"
unique="true"
not-null="true" />
<many-to-one name="personalCard"
column="personalCardId"
unqiue="true"
not-null="true" />
</class>
<class name="BusinessCard" table="BusinessCards">
<id name="id" column="businessCardId">
<generator class="native" />
</id>
</class>
<class name="SocialCard" table="SocialCards">
<id name="id" column="socialCardId">
<generator class="native" />
</id>
</class>
<class name="PersonalCard" table="PersonalCards">
<id name="id" column="personalCardId">
<generator class="native" />
</id>
</class>

hibernate change the column name in a select

I have a table (node) with 3 data
- id(pk)
- question
- result
and 2 foreign key (many-to-one)
- LEFT_ID
- RIGHT_ID
here my hbm
<?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.beans.Nodes" table="node">
<id name="id" type="int" access="field">
<column name="id" />
<generator class="assigned" />
</id>
<property name="question" type="java.lang.String">
<column name="question" />
</property>
<property name="result" type="java.lang.String">
<column name="result" />
</property>
<many-to-one column="LEFT_ID" name="left" class="com.beans.Nodes" insert="false" update="false"></many-to-one>
<many-to-one column="RIGHT_ID" name="right" class="com.beans.Nodes" insert="false" update="false"></many-to-one>
</class>
</hibernate-mapping>
and my bean with getter/setter on LEFT/RIGHT_ID
#Entity
#Table (name = "node")
public class Nodes
{
#Id
#GeneratedValue (strategy = GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column (name = "question")
private String question;
#Column ( name = "result")
private String result;
private Nodes LEFT_ID;
private Nodes RIGHT_ID;
public Nodes getLeftNodes()
{
return LEFT_ID;
}
public void setLeftNodes(Nodes LEFT_ID)
{
this.LEFT_ID=LEFT_ID;
}
public Nodes getRightNodes()
{
return RIGHT_ID;
}
public void setRifhtNodes(Nodes right)
{
this.RIGHT_ID=right;
}
}
But when i deploy my project, i have this error
**Query: ReadAllQuery(referenceClass=Nodes sql="SELECT id, question, result, LEFT_ID_id, RIGHT_ID_id FROM node")**
When hibernate is a select, he change the name of the column. LEFT_ID becomes LEFT_ID_id and inevitably he finds nothing !
Why hibernate change the name of the LEFT_ID column ?
thanks

I can't insert foreign key

I have a node db with five pieces of data:
id(int, PK)
question(varchar)
result(varchar)
left_id(int, FK)
right_id(int, FK)
and two foreign keys in the same table
left_id -> id
right_id -> id
here is my bean
#Id
#GeneratedValue (strategy = GenerationType.IDENTITY)
#Column(name="id")
#NotNull
private int id;
#Column (name = "question")
private String question;
#Column ( name = "result")
private String result;
#OneToOne(fetch= FetchType.LAZY)
#JoinColumn(name="left_id", referencedColumnName="id", insertable=true, updatable=true)
private Nodes left;
#OneToOne(fetch= FetchType.LAZY)
#JoinColumn(name="right_id", referencedColumnName="id", table="node", insertable=true, updatable=true)
private Nodes right;
and here is my dao
public void insert_question(String question, int left) throws HibernateException
{
try
{
Session session = getSession();
Transaction tx = session.beginTransaction();
Nodes node = new Nodes();
Nodes leftNode = new Nodes();
//insert question
node.setQuestion(question);
// insert left_id with fk
leftNode.setId_node(left);
node.setLeftNodes(leftNode);
session.saveOrUpdate(node);
tx.commit();
session.close();
}
catch (HibernateException e)
{
e.printStackTrace();
}
}
I have a same insert method for result but without foreign key.
My new question and new result are insert my db but not my left_id.
In Eclipse, hibernate tell me
Infos: Hibernate: select nodes_.id, nodes_.question as question0_, nodes_.result as result0_ from node nodes_ where nodes_.id=?
Infos: Hibernate: insert into node (question, result, id) values (?, ?, ?)
Infos: Hibernate: insert into node (question, result, id) values (?, ?, ?)
When hibernate insert a new data, why there are no left/right_id ?
thanks
EDIT:
I changed my hbm. Before it was like that
<hibernate-mapping>
<class name="com.beans.Nodes" table="node">
<id name="id" type="int" access="field">
<column name="id" />
<generator class="assigned" />
</id>
<property name="question" type="java.lang.String">
<column name="question" />
</property>
<property name="result" type="java.lang.String">
<column name="result" />
</property>
<one-to-one name="left" class="com.beans.Nodes" access="field"></one-to-one>
<one-to-one name="right" class="com.beans.Nodes" access="field"></one-to-one>
</class>
</hibernate-mapping>
and now it's like that
<hibernate-mapping>
<class name="com.beans.Nodes" table="node">
<id name="id" type="int" access="field">
<column name="id" />
<generator class="assigned" />
</id>
<property name="question" type="java.lang.String">
<column name="question" not-null="false"/>
</property>
<property name="result" type="java.lang.String">
<column name="result" not-null="false"/>
</property>
<many-to-one name="Left"
column="left_id"
unique="true"
not-null="false"
/>
<many-to-one name="Right"
column="right_id"
unique="true"
not-null="false"
/>
</class>
</hibernate-mapping>
and it's works !

Categories

Resources