There is a structure. I want to link the three entities in this way: the Company should contain id, name of company and the list of Departments, each Department has a list of Workers, id and name of department. Each worker has name, id.
+Company
-int companyId
-String companyName
-Set<Department> listOfDepartments = new HashSet<Department>();
+Department
-int departmentId
-String departmentName
-Set<Worker> listOfWorkers = new HashSet<Worker>();
+Worker
-int workerId
-String workerName
I tried to make a connection with the one-to-many and many-to-one, but is not successful.
Сompany
#Entity
#Table(name="Company")
public class Company {
#Id #GeneratedValue(strategy = GenerationType.AUTO)
private int idCompany;
private String companyName;
#OneToMany(mappedBy = "company")
private Set<Department> listOfDepartments = new HashSet<Department>();
Department
#Entity
public class Department {
#Id #GeneratedValue(strategy = GenerationType.AUTO)
private int idDepartment;
private String departmentName;
#ManyToOne
#JoinColumn(name="idCompany")
private Company company;
#OneToMany(mappedBy = "department")
private Set<Worker> listOfWorkers = new HashSet<Worker>();
Worker
#Entity
public class Worker {
#Id #GeneratedValue(strategy = GenerationType.AUTO)
private int idWorker;
private String workerName;
#ManyToOne
#JoinColumn(name="idDepartment")
I start with:
Session session = sessionFactory.openSession();
session.beginTransaction();
Worker worker1 = new Worker("WorkerName1");
Worker worker2 = new Worker("WorkerName2");
Worker worker3 = new Worker("WorkerName3");
Worker worker4 = new Worker("WorkerName4");
Department department = new Department("Technical");
department.getListOfWorkers().add(worker1);
department.getListOfWorkers().add(worker2);
department.getListOfWorkers().add(worker3);
department.getListOfWorkers().add(worker4);
company = new Company("MyCompanyName");
company.getListOfDepartments().add(department);
session.save(company);
session.getTransaction().commit();
session.close();
It fills company, but not fills other tables and also it's not creating any joins(maps)
Error:
Hibernate: alter table Department drop foreign key FK_l7sg67atqhnn0soqynpvxrtpk
Hibernate: alter table Worker drop foreign key FK_s53hyohtyjy93srd2wkksairk
Hibernate: drop table if exists Company
Hibernate: drop table if exists Department
Hibernate: drop table if exists Worker
Hibernate: create table Company (idCompany integer not null auto_increment, companyName varchar(255), primary key (idCompany))
Hibernate: create table Department (idDepartment integer not null auto_increment, departmentName varchar(255), idCompany integer, primary key (idDepartment))
Hibernate: create table Worker (idWorker integer not null auto_increment, workerName varchar(255), idDepartment integer, primary key (idWorker))
Hibernate: alter table Department add index FK_l7sg67atqhnn0soqynpvxrtpk (idCompany), add constraint FK_l7sg67atqhnn0soqynpvxrtpk foreign key (idCompany) references Company (idCompany)
Hibernate: alter table Worker add index FK_s53hyohtyjy93srd2wkksairk (idDepartment), add constraint FK_s53hyohtyjy93srd2wkksairk foreign key (idDepartment) references Department (idDepartment)
ноя 11, 2013 3:10:31 AM org.hibernate.tool.hbm2ddl.SchemaExport execute
INFO: HHH000230: Schema export complete
Hibernate: insert into Company (companyName) values (?)
In addition to the cascade mentioned in Glenn Lane's answer, you also need to understand how bidirectional associations work.
They have an owner side, and an inverse side. JPA only cares about the owner side to decide which association exists between entities. The owner side is the one which doesn't have the mappedBy attribute.
Your code add depertments to the company, and workers to the departments, but it only initializes the inverse sides. You forgot to initialize the owner side:
worker1.setDepartment(department);
worker2.setDepartment(department);
...
department.setCompany(company);
If you want JPA to automatically persist the children, you need to further decorate your #OneToMany:
#OneToMany(mappedBy = "department", cascade={CascadeType.PERSIST})
If you want cascading behaviour for other operations, like remove, merge, refresh, you'll need to add those to the cascade list as well.
You can use another approach:
Unidirectional OneToMany:
http://en.wikibooks.org/wiki/Java_Persistence/OneToMany#Unidirectional_OneToMany.2C_No_Inverse_ManyToOne.2C_No_Join_Table_.28JPA_2.0_ONLY.29
You still need to persist the Object and then add it into your Collection.
Related
table: emps
(
id int primary key,
name varchar(50)
);
table: emps_sal
(
emps_sal_id int primary key auto_increment,
ids int,
salary int,
foreign key(ids) references emps(id)
);
Entity Classes:
#Entity
public class Emps {
#Id
#Column(name="id")
int id;
String name;
#OneToOne(mappedBy = "e")
Emps_sal esal;
--
}
#Entity
public class Emps_sal {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
int emps_sal_id;
int ids ;
int salary;
#OneToOne(cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn(name="ids")
Emps e;
--
}
Now when I am doing simple select query its working fine. But when I am trying to add entry its working weirdly:
Emps e=new Emps();
e.setId(100);
e.setName("Johnson");
Emps_sal es=new Emps_sal();
es.setIds(100);
es.setSalary(5000);
es.setE(e);
e.setEsal(es);
Firstly in "emps" table it added a extra column "esal_emps_sal_id".
Though it added "100,Johnson" properly. But in Emps_sal nothing is
added. I was expecting "7, 100, 5000".
Change CascadeType to persist in Emps_sal entity class
#OneToOne(cascade = CascadeType.PERSIST)
#PrimaryKeyJoinColumn(name="ids")
Emps e;
The persist operation makes a transient instance persistent. CascadeType PERSIST propagates the persist operation from a parent to a child entity. When we save the Emps_sal entity, the Emp entity will also get saved.
Here is how you need to set the data
Emps e=new Emps();
e.setId(100);
e.setName("Johnson");
Emps_sal es=new Emps_sal();
es.setIds(100);
es.setSalary(5000);
es.setE(e);
entityManager.persist(es);
I want to persist an entity which both doesn't have a generated value for the identifier and also cascade persists another entity. However this combination doesn't seem to be possible. I'm using Spring Data JPA for saving the entities.
Let's say you have two entities, for e.g. Student and Address. For students you want to keep their registration date (let's assume it's always unique), name and address. From the address you want to only save the street for simplicity sake. When saving the Student entity you want to cascade persist the Address entity. So you create the following entities.
#Entity
public class Student {
#Id
private LocalDateTime registrationDateTime;
#Column(nullable = false)
private String name;
#ManyToOne(targetEntity = Address.class, cascade=CascadeType.PERSIST)
private Address address;
public Student(LocalDateTime registrationDateTime, String name, Address address) {
setRegistrationDateTime(registrationDateTime);
setName(name);
setAddress(address);
}
public Student() {
}
// ...getters and setters omitted...
}
and:
#Entity
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column
private String street;
public Address(String street) {
setStreet(street);
}
public Address() {
}
// ...getters and setters omitted...
}
When you execute the following code both entity are persisted but street is NULL.
Student student = new Student(LocalDateTime.now(), "John Doe");
student.setAddress(new Address("Mainstreet"));
studentRepository.save(student);
When I remove the #Id annotation from the LocalDateTime property and add the following code then the address is saved correctly.
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
These are the queries which Hibernate executes (it seems odd by the way that it's inserting the student first since that entity needs the id of the address).
Hibernate:
alter table Student
drop constraint FKf12myy73nsf6soln9xli8th80
Hibernate:
drop table Address
Hibernate:
drop table Student
Hibernate:
drop sequence hibernate_sequence restrict
Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate:
create table Address (
id integer not null,
street varchar(255),
primary key (id)
)
Hibernate:
create table Student (
dateTime timestamp not null,
name varchar(255) not null,
address_id integer,
primary key (dateTime)
)
Hibernate:
alter table Student
add constraint FKf12myy73nsf6soln9xli8th80
foreign key (address_id)
references Address
Hibernate:
select
student0_.dateTime as dateTime1_3_0_,
student0_.address_id as address_3_3_0_,
student0_.name as name2_3_0_
from
Student student0_
where
student0_.dateTime=?
Hibernate:
values
next value for hibernate_sequence
Hibernate:
insert
into
Student
(address_id, name, dateTime)
values
(?, ?, ?)
Hibernate:
insert
into
Address
(street, id)
values
(?, ?)
Hibernate:
update
Student
set
address_id=?,
name=?
where
dateTime=?
I have following entities:
#Entity
#Table(name = "chat",
uniqueConstraints = {
#UniqueConstraint(columnNames = {"user_1", "user_2"})
})
public class Chat {
#ManyToOne
#JoinColumn(name = "user_1")
#OnDelete(action = OnDeleteAction.CASCADE)
private User user1;
#ManyToOne
#JoinColumn(name = "user_2")
#OnDelete(action = OnDeleteAction.CASCADE)
private User user2;
}
User class:
#Entity
#Table(name="users",
uniqueConstraints={
#UniqueConstraint(columnNames={"company_id", "username"})
}
)
public class User {
#Id
#GenericGenerator(name = "uuid-gen", strategy = "uuid2")
#GeneratedValue(generator = "uuid-gen",strategy=GenerationType.IDENTITY)
private String id;
// there is no field/reference to Chat entity
}
And User entity without any references to Chat entity. I need to remove user with it's chats. Problem is that user id (that I want to remove) could be either in user1 or user2 field. For example, I have user A and user B. They have chat C. And if I try to remove, for example, user A, it should remove user A and chat C. But with provided configuration, I have following error:
Cannot delete or update a parent row: a foreign key constraint fails
(`mydb`.`chat`, CONSTRAINT `FKqslncg7pcc89gvjjpp9jypbha`
FOREIGN KEY (`user_2`) REFERENCES `users` (`id`))
As possible solution I used this answer. But using of
entityManager.remove(user);
entityManager.clear();
does not help. Also, I checked ddl code and there is no any mention of Cascade actions.
How to fix this?
I found a solution. The first thing that you need is to add the human-readable ForeignKey constraint and remove #OnDelete. Now my code looks like this:
#ManyToOne
#JoinColumn(name = "user_1", foreignKey = #ForeignKey(name = "user_1_fk"))
private User user1;
#ManyToOne
#JoinColumn(name = "user_2", foreignKey = #ForeignKey(name = "user_2_fk"))
private User user2;
Then I dropped this table, launched an application to allow hibernate re-create this table with proper FK names provided in annotations. Then I opened MySQL workbench and modified foreign keys for this table using following SQL:
ALTER TABLE chat DROP FOREIGN KEY `user_1_fk`;
ALTER TABLE chat DROP FOREIGN KEY `user_2_fk`;
ALTER TABLE chat
ADD CONSTRAINT `user_1_fk`
FOREIGN KEY (`user_1` )
REFERENCES `users` (`id` )
ON DELETE CASCADE;
ALTER TABLE chat
ADD CONSTRAINT `user_2_fk`
FOREIGN KEY (`user_2` )
REFERENCES `users` (`id` )
ON DELETE CASCADE;
That's all.
I have two simple tables Customers and Orders with relation oneToMany from customer to Orders table.
This is my Customers.java
#Entity
public class Customers implements Serializable {
#Id
#GeneratedValue
private int cID;
private String name;
private String email;
// getter and setters
}
And this is Orders.java:
#Entity
public class Orders implements Serializable {
#Id
#GeneratedValue
private int orderID;
private int cId;
#Column(nullable = false)
#Temporal(TemporalType.DATE)
private Date date;
#ManyToOne(cascade = CascadeType.ALL)
private Customers customers;
// getter and setters
}
Now, i am going to insert two record in Orders table:
public static void main(String[] args) {
SessionFactory sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
Orders orders1 = new Orders();
Orders orders2 = new Orders();
Customers customer = new Customers();
customer.setName("c1");
customer.setEmail("abc#gmail.com");
orders1.setDate(new Date());
orders2.setDate(new Date());
orders1.setCustomers(customer);
orders2.setCustomers(customer);
session.save(orders1);
session.save(orders2);
session.getTransaction().commit();
session.close();
sessionFactory.close();
}
This is the result in console:
Hibernate: alter table Orders drop foreign key FK_hmbx2rg9tsgqikb3kodqp90c4
Hibernate: drop table if exists Customers
Hibernate: drop table if exists Orders
Hibernate: create table Customers (cID integer not null auto_increment, email varchar(255), name varchar(255), primary key (cID))
Hibernate: create table Orders (orderID integer not null auto_increment, cId integer not null, date date not null, customers_cID integer, primary key (orderID))
Hibernate: alter table Orders add constraint FK_hmbx2rg9tsgqikb3kodqp90c4 foreign key (customers_cID) references Customers (cID)
Feb 24, 2015 1:58:52 PM org.hibernate.tool.hbm2ddl.SchemaExport execute
INFO: HHH000230: Schema export complete
Hibernate: insert into Customers (email, name) values (?, ?)
Hibernate: insert into Orders (cId, customers_cID, date) values (?, ?, ?)
Hibernate: insert into Orders (cId, customers_cID, date) values (?, ?, ?)
And this is the result tables:
Why the cID in Orders table (which is a foreign key references to customers) is 0?
It should be 1.
It think in your orders table customers_cId is the actual foreign key reference column to the customers table. As you haven't gave any column name explicitly, it internally took column name as customers_cId by joining the variables from both the entities. customers from the orders and cId from the customers entity.
Just to verify you can try giving some other name using #JoinColumn annotation.
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="order_cId")
private Customers customers;
And cId in orders table is just one more independent column, as you have not set any value to it, its taking the default value as 0. Try setting some random value to it.
I have two entities having parent child relationship - Person and Address.
They have a one-to-one relationship.
I am able to cascade save both of them by one save.
But the below code does not add a foreign_key address_id to Person table.
SQL:
create table PERSON (
id number(20) NOT NULL ,
PRIMARY KEY (id)
);
alter table Person add address_id number(20);
alter table Person add CONSTRAINT FK_Address FOREIGN KEY (address_id) REFERENCES address (id);
create table Address (
id number(20) NOT NULL ,
PRIMARY KEY (id)
);
Hibernate code:
#Entity
#Table(name="Person")
#javax.persistence.SequenceGenerator(name="SOME_SEQ", sequenceName="pk_sequence")
public class Person
{
#Id #GeneratedValue(strategy = GenerationType.SEQUENCE, generator="SOME_SEQ")
private int id;
#OneToOne
#PrimaryKeyJoinColumn
#Fetch(FetchMode.JOIN)
#Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE})
private Address address;
}
#Entity
#javax.persistence.SequenceGenerator(name="SOME_SEQ", sequenceName="pk_sequence")
public class Address
{
#Id #GeneratedValue(strategy = GenerationType.SEQUENCE, generator="SOME_SEQ")
private int id;
#Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE})
#OneToOne(mappedBy="address")
#Fetch(FetchMode.JOIN)
private Person person;
}
Driving code:
Session session = factory.openSession();
Transaction tx = session.beginTransaction();
Person p = new Person();
Address add = new Address ();
p.setAddress(add);
add.setPerson(p);
id = (Integer) session.save(p);
tx.commit();
Your mapping is wrong. You have a column in Person that is a foreign key to the address table. But you map the association with #PrimaryKeyJoinColumn, which means: The ID of this entity is also a foreign key to the address table. Use #JoinColumn. Not #PrimaryKeyJoinColumn.