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=?
Related
Student entity:
#Entity
#Table(
name = "student"
)
public class Student implements Comparable<Student> {
#Id
#Column(
name = "id"
)
private Integer id;
#Column(
name = "name"
)
private String name;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(
name = "id",
referencedColumnName = "student_id"
)
private StudentCard studentCard;
.................................getter and setter below.........................
StudentCard entity:
#Entity
#Table(
name = "student_card"
)
public class StudentCard {
#Id
#GeneratedValue(
strategy = GenerationType.IDENTITY
)
private Integer id;
#Column(
name = "balance"
)
private Integer balance;
....................................getter and setter below.........................
I have two tables created, student and student card. Both tables have a column named id as their primary key, but I am not trying to map them together.
I created a foreign key in the student card table where column student_id is tied with column id from student table. The owner is student card. However, after I created key and annotation, the hibernate still map to the wrong key when I tried to pull out the card information for each student.
For student table, the id is the primary key, whereas the student_id in card table is not. When I call for the student information in the controller side, I get a message of :
Hibernate:
select
studentcar0_.id as id1_1_0_,
studentcar0_.balance as balance2_1_0_,
studentcar0_.student_id as student_3_1_0_
from
student_card studentcar0_
where
studentcar0_.id=?
and this is the wrong map, because I am not mapping student.id=studentcard.id
I tried to switch column/referencecolumn name and it gave me an error of Unknown column 'student_id' not in field list. so I am not sure what should I do at this point.
Industry_Codes Table
Industry_Code(Primary key) | Industry_name
1| Reliance
2| TaTa
Technology_codes Table
Technology_code(Primary Key) | technology name
81| java
81|cpp
carrier_codes Table
Industry_Code(Primary key)(Foreign Key to Industry_Codes table) | technology_code(pk)(Foreign key to Technology_Code table) | other fields
1 | 81 |
2| 81|
1| 82
Register
Mobile Number(Pk)|Industry_code(Fk to carrier_codes) |Technology_Code(Fk to carrier_codes)
12345|1|83
78913|1|88
for the given table structure the entity class are written as-
#Entity
#Table(name = "Industry_Codes")
public class IndustryCodes implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="TELECOM_INDUSTRY_CODE")
private String telecomIndustryCode;
#Column(name="INdustry_Name")
private String Industry_Name;
}
Table 2
#Entity
#Table(name="Technology_Codes")
public class TechnologyCodes {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "Technology_code")
private int targetTechnologyCode;
#Column(name="Technology_Name")
private String TechnologyName;
}
and
#Entity
#Table(name = "CARRIER_CODES")
public class CarrierCodes {
private static final long serialVersionUID = 1L;
#Id
#ManyToOne
#JoinColumn(name ="CARRIER_CODE",referencedColumnName="INDUSTRY_CODE")
private IndustryCodes carrierCode;
#Id
#ManyToOne
#JoinColumn(name ="TECHNOLOGY_CODE",referencedColumnName="TECHNOLOGY_CODE")
private TechnologyCodes TechnologyCode;
}
Now I am all lost on how to write entity for Register table which has column like carrier_code and technology_code both of these are foreign key to carrier_codes table adn both of these together form composite key for carrier_codes table also there are many occurance of both field in both table thus a many to many association between both columns of both table.
Any help on writing the register entity is appreciated. I am completely new to associations in JPA.
First, be sure you understand the relationships. UML and images are your friend.
Notice that IndustryCode and TechCode is a ManyToMany relationship. In DDL a many to many relationship is done with join table and a composite key. In JPA this is normally just mapped with a ManyToMany annotation but since you also want to use it as a foreign key for the Registry table you must define the entity yourself. This is done with CarrierCodes. In JPA an EmbeddableId is generally the easiest way to make a composite key for an entity that will be used in this manner. So the JPA can be done like this:
#Entity
public class IndustryCode {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String industryName;
#OneToMany(mappedBy="industryCode")
private Set<CarrierCodes> industryCodes;
#Entity
public class TechCode {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String techName;
#OneToMany(mappedBy="techCode")
private Set<CarrierCodes> techCodes;
#Entity
public class CarrierCodes {
#EmbeddedId
private CarrierCodesId id = new CarrierCodesId();
#ManyToOne
#MapsId("techCodeId")
private TechCode techCode;
#ManyToOne
#MapsId("industryCodeId")
private IndustryCode industryCode;
#SuppressWarnings("serial")
#Embeddable
public class CarrierCodesId implements Serializable {
private Long industryCodeId;
private Long techCodeId;
#Entity
public class Register {
#Id
private Long mobileNumber;
#ManyToOne
// optional but nice to have consistent names
#JoinColumns({
#JoinColumn(name="industryCode_id", referencedColumnName="industryCode_id"),
#JoinColumn(name="techCode_id", referencedColumnName="techCode_id")
})
private CarrierCodes carrierCodes;
And to use it similar to your example is like this.
tx.begin();
IndustryCode ic1 = new IndustryCode("Reliance");
IndustryCode ic2 = new IndustryCode("TaTa");
TechCode tc1 = new TechCode("java");
TechCode tc2 = new TechCode("cpp");
CarrierCodes cc1 = new CarrierCodes(tc1, ic1);
CarrierCodes cc2 = new CarrierCodes(tc1, ic2);
CarrierCodes cc3 = new CarrierCodes(tc2, ic1);
Register r1 = new Register(12345L, cc1);
Register r2 = new Register(78913L, cc2);
em.persist(ic1);
em.persist(ic2);
em.persist(tc1);
em.persist(tc2);
em.persist(cc1);
em.persist(cc2);
em.persist(cc3);
em.persist(r1);
em.persist(r2);
tx.commit();
em.clear();
List<Register> rs = em.createQuery("select r from Register r left outer join fetch r.carrierCodes cc where cc.techCode.techName = 'java'", Register.class).getResultList();
rs.stream().forEach(r->System.out.println(r.getMobileNumber() + " " + r.getCarrierCodes().getTechCode().getTechName()));
List<Register> rs2 = em.createQuery("select r from Register r left outer join fetch r.carrierCodes cc where cc.industryCode.industryName = 'TaTa'", Register.class).getResultList();
rs2.stream().forEach(r->System.out.println(r.getMobileNumber() + " " + r.getCarrierCodes().getIndustryCode().getIndustryName()));
That gives me the following output.
Hibernate: create table CarrierCodes (industryCode_id bigint not null, techCode_id bigint not null, primary key (industryCode_id, techCode_id))
Hibernate: create table IndustryCode (id bigint generated by default as identity (start with 1), industryName varchar(255), primary key (id))
Hibernate: create table Register (mobileNumber bigint not null, industryCode_id bigint, techCode_id bigint, primary key (mobileNumber))
Hibernate: create table TechCode (id bigint generated by default as identity (start with 1), techName varchar(255), primary key (id))
Hibernate: alter table CarrierCodes add constraint FKfq42ix66txvd15crq2pey3dcp foreign key (industryCode_id) references IndustryCode
Hibernate: alter table CarrierCodes add constraint FK9os97pd53ijerp2mibllknovn foreign key (techCode_id) references TechCode
Hibernate: alter table Register add constraint FK2k626ouo1ajsccqlpb5y3xa8u foreign key (industryCode_id, techCode_id) references CarrierCodes
Hibernate: insert into IndustryCode (id, industryName) values (default, ?)
Hibernate: insert into IndustryCode (id, industryName) values (default, ?)
Hibernate: insert into TechCode (id, techName) values (default, ?)
Hibernate: insert into TechCode (id, techName) values (default, ?)
Hibernate: insert into CarrierCodes (industryCode_id, techCode_id) values (?, ?)
Hibernate: insert into CarrierCodes (industryCode_id, techCode_id) values (?, ?)
Hibernate: insert into CarrierCodes (industryCode_id, techCode_id) values (?, ?)
Hibernate: insert into Register (industryCode_id, techCode_id, mobileNumber) values (?, ?, ?)
Hibernate: insert into Register (industryCode_id, techCode_id, mobileNumber) values (?, ?, ?)
Hibernate: select register0_.mobileNumber as mobileNu1_2_0_, carriercod1_.industryCode_id as industry1_0_1_, carriercod1_.techCode_id as techCode2_0_1_, register0_.industryCode_id as industry2_2_0_, register0_.techCode_id as techCode3_2_0_ from Register register0_ left outer join CarrierCodes carriercod1_ on register0_.industryCode_id=carriercod1_.industryCode_id and register0_.techCode_id=carriercod1_.techCode_id cross join TechCode techcode2_ where carriercod1_.techCode_id=techcode2_.id and techcode2_.techName='java'
Hibernate: select industryco0_.id as id1_1_0_, industryco0_.industryName as industry2_1_0_ from IndustryCode industryco0_ where industryco0_.id=?
Hibernate: select techcode0_.id as id1_3_0_, techcode0_.techName as techName2_3_0_ from TechCode techcode0_ where techcode0_.id=?
Hibernate: select industryco0_.id as id1_1_0_, industryco0_.industryName as industry2_1_0_ from IndustryCode industryco0_ where industryco0_.id=?
12345 java
78913 java
Hibernate: select register0_.mobileNumber as mobileNu1_2_0_, carriercod1_.industryCode_id as industry1_0_1_, carriercod1_.techCode_id as techCode2_0_1_, register0_.industryCode_id as industry2_2_0_, register0_.techCode_id as techCode3_2_0_ from Register register0_ left outer join CarrierCodes carriercod1_ on register0_.industryCode_id=carriercod1_.industryCode_id and register0_.techCode_id=carriercod1_.techCode_id cross join IndustryCode industryco2_ where carriercod1_.industryCode_id=industryco2_.id and industryco2_.industryName='TaTa'
78913 TaTa
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.
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.
I have defined a collection in Hibernate like this:
...
public class Item {
...
#ElementCollection
List<Object> relatedObjects;
}
It creates a mapping table with colums item_id and object_id.
The problem is that object_id seems to be unique. In other words I can not have two different items being related to the same object. But that is what I want.
I would like the combination of item_id and object_id to be unique. How do I do that?
That's not what I'm experiencing. For the following entity:
#Entity
public class Person implements Serializable {
#Id
#GeneratedValue
private Integer id;
private String firstName;
private String lastName;
#Enumerated(EnumType.STRING)
private Gender gender;
#ElementCollection
private Set<String> nicknames = new HashSet<String>();
private String dept;
// getters, setters
}
The following tables get created:
create table Person (id integer generated by default as identity, dept varchar(255), firstName varchar(255), gender varchar(255), lastName varchar(255), primary key (id))
create table Person_nicknames (Person_id integer not null, nicknames varchar(255))
alter table Person_nicknames add constraint FK24F0D97B19ACB65E foreign key (Person_id) references Person
There is no unique constraint. But I can't say more without seeing your "Object" class (it's an embeddable class, right?).
PS: ElementCollection can't be a ManyToMany, this is more a OneToMany.