Relacja @OneToOne jest to połączenie dwóch tabel za pomocą klucza głównego pierwszej tabeli i klucza obcego drugiej tabeli. Relacje dwukierunkową w Hibernejcie realizujemy na encjach, dzięki temu struktura tabel w bazie danych pozostaje niezmieniona.
✅ Stworzenie tabel w bazie danych
CREATE DATABASE IF NOT EXISTS `bazatestowa2`;
USE `bazatestowa2`;
CREATE TABLE IF NOT EXISTS company_detail (
company_detail_id INT AUTO_INCREMENT,
created DATE,
country VARCHAR(40),
PRIMARY KEY (company_detail_id)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS company (
company_id INT AUTO_INCREMENT,
company_name VARCHAR(40) DEFAULT NULL,
PRIMARY KEY (company_id),
company_detail_id INT,
CONSTRAINT FK_COMPANY_COMPANYDETAIL FOREIGN KEY (company_detail_id) REFERENCES company_detail (company_detail_id)
) ENGINE=InnoDB;
✅ Stworzenie klas Entity w relacji jednokierunkowej
👉 Entity Company
package myhibernate.onedirection.entity;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;
@Entity
@Table(name = "company")
public class Company {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="company_id")
private Integer companyId;
@Column(name="company_name")
private String companyName;
@OneToOne(cascade= CascadeType.ALL)
@JoinColumn(name="company_detail_id")
private CompanyDetail companyDetail;
public Integer getCompanyId() {
return companyId;
}
public void setCompanyId(Integer companyId) {
this.companyId = companyId;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
public CompanyDetail getCompanyDetail() {
return companyDetail;
}
public void setCompanyDetail(CompanyDetail companyDetail) {
this.companyDetail = companyDetail;
}
@Override
public String toString() {
return "Company [companyId=" + companyId + ", companyName=" + companyName + "]";
}
}
👉 Entity CompanyDetail
package myhibernate.onedirection.entity;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
@Entity
@Table(name = "company_detail")
public class CompanyDetail {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="company_detail_id")
private Integer companyDetailId;
@Temporal(value=TemporalType.TIMESTAMP)
@Column(name="created")
private Date created;
@Column(name="country")
private String country;
public Integer getCompanyDetailId() {
return companyDetailId;
}
public void setCompanyDetailId(Integer companyDetailId) {
this.companyDetailId = companyDetailId;
}
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
@Override
public String toString() {
return "CompanyDetail [companyDetailId=" + companyDetailId + ", created=" + created + ", country=" + country
+ "]";
}
}
✅ Stworzenie klas Entity w relacji dwukierunkowej
❗ Relacje dwukierunkową w hibernejcie realizujemy na encjach, dzięki temu struktura tabel w bazie danych pozostaje niezmieniona.
👉 Entity Company
package myhibernate.twodirection.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.CascadeType;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
@Entity
@Table(name = "company")
public class Company {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="company_id")
private Integer companyId;
@Column(name="company_name")
private String companyName;
@OneToOne(cascade= CascadeType.ALL)
@JoinColumn(name="company_detail_id")
private CompanyDetail companyDetail;
public Integer getCompanyId() {
return companyId;
}
public void setCompanyId(Integer companyId) {
this.companyId = companyId;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
public CompanyDetail getCompanyDetail() {
return companyDetail;
}
public void setCompanyDetail(CompanyDetail companyDetail) {
this.companyDetail = companyDetail;
}
@Override
public String toString() {
return "Company [companyId=" + companyId + ", companyName=" + companyName + "]";
}
}
👉 Entity CompanyDetail
package myhibernate.twodirection.entity;
import java.util.Date;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
@Entity
@Table(name = "company_detail")
public class CompanyDetail {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="company_detail_id")
private Integer companyDetailId;
@Temporal(value=TemporalType.TIMESTAMP)
@Column(name="created")
private Date created;
@Column(name="country")
private String country;
@OneToOne(mappedBy="companyDetail", cascade= CascadeType.ALL)
private Company company;
public Integer getCompanyDetailId() {
return companyDetailId;
}
public void setCompanyDetailId(Integer companyDetailId) {
this.companyDetailId = companyDetailId;
}
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
@Override
public String toString() {
return "CompanyDetail [companyDetailId=" + companyDetailId + ", created=" + created + ", country=" + country
+ "]";
}
}
w polu mappedBy podajemy nazwę pola z klasy, która na bazie ma w sobie klucz [Foreign Key]. W tym przypadku jest to pole companyDetail z klasy Company.
✅ Klasa testująca dodawanie obiektu w relacji jednokierunkowej
package myhibernate.onedirection;
import java.util.Date;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import myhibernate.onedirection.entity.Company;
import myhibernate.onedirection.entity.CompanyDetail;
public class RelationOneToOneApp {
public static void main(String[] args) {
Configuration conf = new Configuration();
conf.configure("hibernate.cfg.xml");
conf.addAnnotatedClass(Company.class);
conf.addAnnotatedClass(CompanyDetail.class);
SessionFactory factory = conf.buildSessionFactory();
Session session = factory.getCurrentSession();
session.beginTransaction();
//create company
Company company = new Company();
company.setCompanyName("BIG Company");
//create company detail
CompanyDetail companyDetail = new CompanyDetail();
companyDetail.setCountry("USA");
Date dateCreatedCompany = new Date();
companyDetail.setCreated(dateCreatedCompany);
//add ref
company.setCompanyDetail(companyDetail);
session.save(company);
session.getTransaction().commit();
factory.close();
}
}
❗ Wynik działania w konsoli:
❗Wynik w bazie danych:
1. tabela company
2. tabela company_detail
✅ Klasa testująca usuwanie obiektu w relacji jednokierunkowej
❗ Aktualy stan w bazie danych:
1. tabela company
2. tabela
company_detail
package myhibernate.onedirection;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.query.Query;
import myhibernate.onedirection.entity.Company;
import myhibernate.onedirection.entity.CompanyDetail;
public class DeleteOneToOneApp {
public static void main(String[] args) {
Configuration conf = new Configuration();
conf.configure("hibernate.cfg.xml");
conf.addAnnotatedClass(Company.class);
conf.addAnnotatedClass(CompanyDetail.class);
SessionFactory factory = conf.buildSessionFactory();
Session session = factory.getCurrentSession();
session.beginTransaction();
String sql = "from Company c where c.companyName = :companyName ";
Query<Company> query = session.createQuery(sql);
query.setParameter("companyName", "BIG Company 1");
Company company = query.getSingleResult();
System.out.println("obiekt istnieje w baze:");
System.out.println(company);
session.delete(company);
System.out.println("obiekt skasowany.");
session.getTransaction().commit();
factory.close();
}
}
❗ Wynik działania w konsoli:
❗ Wynik w bazie danych:
1. tabela company
2. tabela company_detail
✅ Klasa testująca dodawanie obiektu w relacji dwukierunkowej
👉 Klasa testująca
package myhibernate.twodirection;
import java.util.Date;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import myhibernate.twodirection.entity.Company;
import myhibernate.twodirection.entity.CompanyDetail;
public class RelationOneToOneApp {
public static void main(String[] args) {
Configuration conf = new Configuration();
conf.configure("hibernate.cfg.xml");
conf.addAnnotatedClass(Company.class);
conf.addAnnotatedClass(CompanyDetail.class);
SessionFactory factory = conf.buildSessionFactory();
Session session = factory.getCurrentSession();
session.beginTransaction();
//create company
Company company = new Company();
company.setCompanyName("COMPANY 123");
//create company detail
CompanyDetail companyDetail = new CompanyDetail();
companyDetail.setCountry("POL");
Date dateCreatedCompany = new Date();
companyDetail.setCreated(dateCreatedCompany);
//add ref
company.setCompanyDetail(companyDetail);
session.save(company);
session.getTransaction().commit();
factory.close();
}
}
❗ Wynik działania w konsoli:
❗Wynik w bazie danych:
1. tabela company
2. tabela company_detail
✅ Klasa testująca usuwanie obiektu w relacji dwukierunkowej
Dzięki relacji dwukierunkowej, możemy w łatwy sposób dostać się z obiektu Company mając obiekt CompanyDetail
❗ Aktualy stan w bazie danych:
2. tabela company_detail
👉 Klasa testująca
package myhibernate.twodirection;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.query.Query;
import myhibernate.twodirection.entity.Company;
import myhibernate.twodirection.entity.CompanyDetail;
public class DeleteOneToOneApp {
public static void main(String[] args) {
Configuration conf = new Configuration();
conf.configure("hibernate.cfg.xml");
conf.addAnnotatedClass(Company.class);
conf.addAnnotatedClass(CompanyDetail.class);
SessionFactory factory = conf.buildSessionFactory();
Session session = factory.getCurrentSession();
session.beginTransaction();
String sql = "from CompanyDetail c where c.companyDetailId = :companyId ";
Query<CompanyDetail> query = session.createQuery(sql);
query.setParameter("companyId", 2);
CompanyDetail companyDetail = query.getSingleResult();
System.out.println("obiekt istnieje w baze:");
System.out.println(companyDetail);
session.delete(companyDetail);
System.out.println("obiekt skasowany.");
session.getTransaction().commit();
factory.close();
}
}
❗ Wynik działania w konsoli:
Jak widać w konsoli powstał tutaj problem N + 1 zapytań. Najpierw w linii getSinleResult() wykonało się pierwsze zapytanie do wyciągnięcia informacji o obiekcie CompanyDetail
SELECT companydet0_.company_detail_id AS company_1_1_,
companydet0_.country AS country2_1_,
companydet0_.created AS created3_1_
FROM company_detail companydet0_
WHERE companydet0_.company_detail_id = ?
a chwile później wykonało się drugie zapytanie doczytujące informacje o obiekcie Company
SELECT company0_.company_id AS company_1_0_1_,
company0_.company_detail_id AS company_3_0_1_,
company0_.company_name AS company_2_0_1_,
companydet1_.company_detail_id AS company_1_1_0_,
companydet1_.country AS country2_1_0_,
companydet1_.created AS created3_1_0_
FROM company company0_
LEFT OUTER JOIN company_detail companydet1_
ON company0_.company_detail_id =
companydet1_.company_detail_id
WHERE company0_.company_detail_id = ?
w celu ominięcia tego problemu należy w zapytaniu podać join fetch c.company czyli
String sql = "from CompanyDetail c join fetch c.company where c.companyDetailId = :companyId ";
w tej sytuacji Hibernate buduje jedno złączone zapytanie dla
Company i CompanyDetail
SELECT companydet0_.company_detail_id AS company_1_1_0_,
company1_.company_id AS company_1_0_1_,
companydet0_.country AS country2_1_0_,
companydet0_.created AS created3_1_0_,
company1_.company_detail_id AS company_3_0_1_,
company1_.company_name AS company_2_0_1_
FROM company_detail companydet0_
INNER JOIN company company1_
ON companydet0_.company_detail_id = company1_.company_detail_id
WHERE companydet0_.company_detail_id = ?
❗Wynik w bazie danych:
1. tabela company
2. tabela company_detail
Brak komentarzy:
Prześlij komentarz