Entity Relationships
relationship multiplicities 4가지 종류
- @OneToOne
- @OneToMany
- @ManyToOne
- ManyToMany
relationship의 방향
- bidirectional(양방향): owning side와 inverse side
- unidirectional(단방향): owning side only
※ owning side란?
레퍼런스를 가지고 있는 부분(side) -> 다른 쪽을 참조하고 있는 Entity
Entity Relation Attributes
- updates/deletes cascadng 지원
- CascadeType
- ALL, PERSIST, MERGE, REMOVE, REFRESH
- 관련 행 패치에 performance strategy를 선언 가능
- FetchType
- LAZY, EAGER
- DB에 Entity를 패치 했을 때, Entity에 속하는 다른 Entity를 같이 읽을 것인가 아닌가를 결정
EAGER이라고 설정을 하면 같이 읽는다.
LAZY는 같이 읽지 않는다. - Performace관점(메모리관점)에서는 LAZY가 더 좋다. 필요한 것만 읽어들이기 때문, 대신 속도가 늦을 수 있다.
ex)
@ManyToMany( cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.EAGER ) |
관계 코드에서의 상식!
- OneToMany관계에서 One side 부분을 parent라고 하고 Many side를 child라고 한다.
- Class에 @Entity 어노테이션을 달아주어야 hibernate가 자동으로 이 객체를 table로 mapping 해준다.
1. OneToMany Unidirectional
- ★OneToMany 관계에서 일반적으로는 Many(child side)에서 reference(forein key)를 가진다.
( child가 parent를 참조한다.) - 객체에서는 reference는 메모리 주소지만 table에서는 forein key다.
-> annotation(@ManyToOne(),@JoinColumn())을 달아주어 table에서 forein key로써 처리 될 수 있도록 한다.
예시 관계 ( Category, Product )
- Category에는 많은 Product가 있을 수 있다.
- 특정 Product는 하나의 Category에만 속한다.
코드
public class Category {
@Id
@GeneratedValue
private int id;
private String name;
}
public class Product {
@Id
@GeneratedValue
private int id;
private String name;
private int price;
private String description;
@ManyToOne(cascade=CascadeType.ALL)
@JoinColumn(name="category_id")
private Category category;
}
- @Id로 PK라는 것을 명시
- @GeneratedValue로 값을 넣어주지 않아도 자동으로 값이 들어감 (1, 2, 3...)
- @ManyToOne으로 cascade를 ALL로 설정하여 모든 경우에 cascade가 일어나게 한다.
- @ManyToOne으로 어떤 객체가 자식 객체인지 Hibernate에 알릴 수있다.
- @OneToMany으로 어떤 객체가 부모 객체인지 Hibernate에 알릴 수있다.
- @JoinColum으로 조인으로 사용할 열을 지정할 수 있으며 열 이름을 지정할 수도 있다.
sessionFactory = new Configuration().configure()
.buildSessionFactory();
Category category1 = new Category("가전제품");
Product product1 = new Product("노트북",200, "노트북 좋아",category1);
Product product2 = new Product("TV", 150, "TV 좋아", category1);
Category category2 = new Category("책");
Product product3 = new Product("신세계에서", 2000, "내 최애", category2);
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
session.save(product1);
session.save(product2);
session.save(product3);
tx.commit();
session.close();
- session.save를 하는 경우, product에서 Cascade를 All로 설정했기 때문에 Category까지 같이 저장이 된다.
-> 그렇다면 hibernate는 product를 먼저 만들까 category를 먼저 만들까?
-> 당연히 Category, 그래야지 foreign키를 채우기 위해서는 참조하려는 것이 있어야지만 가능하기 때문
2. OneToMany Bidirectional
- UniDirectional과 테이블상에서는 변화가 없기 때문에 객체에서의 관계만 조금 달라진다.
- One입장에서는 Many를 가리키는 어러개의 reference가 필요하다.
ex) One side인 Category에서는 Set을 사용하여 여러개의 product를 참조하고 있다. - 자신의 입장에서 oneToMany 인지 ManyToOne지를 쓴다.
- OneToMany - Category입장
- ManyToOne - product입장
예시 코드
public class Category {
@Id
@GeneratedValue
private int id;
private String name;
@OneToMany(mappedBy="category", cascade=CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Product> products;
}
public class Product {
@Id
@GeneratedValue
private int id;
…
@ManyToOne
@JoinColumn(name="category_id")
private Category category;
}
- mappedBy = "여기에 들어가는 것은 Product의 category 필드명 "
-> Hibernate에게 자식 클래스에있는 부모 클래스를 표현하기 위해 어떤 변수를 사용해야 하는지를 알려주는 것
-> child에서 parent를 reference하고 있는 부분이 무엇인지를 써주어야지 hibernate가 parent에서 child 방향을 연결해준다.
->Table입장에서는 양방향이라고 해도 따로 테이블이 만들어 지는 것이 없다. 따라서 객체 입장에서 설정을 해주어야 한다. - unidirectional에서는 child side를 저장을 했지만 Bidirectional에서는 parent side를 저장한다.
-> 어차피 child가 저장되어야 하기 때문
-> cacade설정이 parent side로 옮겨갔다. - cascade=CascadeType.ALL
- Category가 save되면 모든 Product들도 save된다.
- Category가 delete되면 관련된 Product 인스턴스들도 삭제가 된다.
- fetch=FetchType.LAZY
- Category가 로드될 때마다 모든 product가 로드되는 것을 원하지 않음
- 필요에 의해서 접근할 때만 로드된다.
sessionFactory = new Configuration().configure().buildSessionFactory();
Category category1 = new Category("가전제품");
Product product1 = new Product("노트북",200, "노트북 좋아",category1);
Product product2 = new Product("TV", 150, "TV 좋아", category1);
category1.getProducts().add(product1);
category1.getProducts().add(product2);
Category category2 = new Category("책");
Product product3 = new Product("신세계에서", 2000, "내 최애", category2);
category2.getProducts().add(product3);
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
session.save(category1);
session.save(category2);
tx.commit();
session.close();
- 과정
- Instantiate/Load parent object
- Instantiate/Load child objects
- Set the parent object in the child objects
- Set the collection of child objects on the parent
- Save the parent
- category에서 set을 사용하였기 때문에 add이라는 메서드를 사용하여 product객체를 넣어준다.
3. OneToOne Unidirectional
예시 관계 ( Person, License )
- Person에는 하나의 License가 존재할 수 있다.
- 하나의 License 한 사람에게 속한다.
코드
public class Person {
@Id
@GeneratedValue
@Column(name="person_id")
private long id;
private String firstName;
private String lastName;
}
public class License {
@Id
@GeneratedValue
@Column(name="license_id")
private long id;
private String license_number;
private Date issue_date;
@OneToOne(optional=false, cascade=CascadeType.ALL)
@JoinColumn(unique=true, name="person_id")
private Person person;
}
- 1대1에선 참조할 값이 유니크 해야되기 때문에 -> @JoinColumn(unique = true, name = ~~);
sessionFactory =
new Configuration().configure().buildSessionFactory();
Person person = new Person();
person.setFirstName("대환");
person.setLastName("권");
License license = new License();
license.setLicense_number("DT123548");
license.setIssue_date(new Date());
license.setPerson(person);
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
session.save(license);
tx.commit();
session.close();
- @JoinColumn이 License에 있으므로 session.save(license)로 License를 save해준다.
4. One-To-One bidirectional
예시 코드
public class Person {
@Id
@GeneratedValue
@Column(name="person_id")
private long id;
private String firstName;
private String lastName;
@OneToOne(mappedBy="person", cascade=CascadeType.ALL)
private License license;
}
- @OneToOne이 Clind(License)에서 Parent(Person)으로 넘어갔다.
- mappedBy 속성을 사용하여 License안에서 Person entity 참조에 사용되는 변수명(person)을 명시
sessionFactory =
new Configuration().configure().buildSessionFactory();
Person person = new Person();
person.setFirstName("대환");
person.setLastName("권");
License license = new License();
license.setLicense_number("DT516841");
license.setIssue_date(new Date());
license.setPerson(person);
person.setLicense(license);
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
session.save(person);
tx.commit();
session.close();
- 양방향이기 때문에 license도 person을 넣고 person도 license를 넣는다.
- person을 저장하면 cascade 설정으로 license도 저장이 된다.
5. ManyToMany Unidirectional
- 조인테이블을 하나 만들어서 다대다 관계를 관리한다.
- 조인테이블은 두 테이블의 primary key만을 참조한다.
코드
public class Author {
@Id
@GeneratedValue
@Column(name="author_id")
private long id;
@Column(name="author_name")
private String name;
}
public class Book {
@Id
@GeneratedValue
@Column(name = "book_id")
private long id;
@Column(name = "book_name")
private String title;
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "author_book",
joinColumns = { @JoinColumn(name = "book_id") },
inverseJoinColumns = { @JoinColumn(name = "author_id") })
private Set<Author> authors ;
}
- Set을 사용하여 Author를 참조하였다.
- @JoinTable을 사용하여 조인테이블의 이름과 joinColumns에 @JoinColumn으로 한쪽의 primary key를 설정하고 inverseJoinColums 에다가 또 JoinColumn을 사용하여 다른 쪽 primary key를 설정한다.
Book book1 = new Book();
book1.setTitle("과학책");
Book book2 = new Book();
book2.setTitle("수학책");
Book book3 = new Book();
book3.setTitle("영어책");
Author author1 = new Author();
author1.setName("권대환");
Author author2 = new Author();
author2.setName("건대한");
Author author3 = new Author();
author3.setName("곤대훈");
Set<Author> scienceAuthors = new HashSet<Author>();
Set<Author> mathAuthors = new HashSet<Author>();
Set<Author> englishAuthors = new HashSet<Author>();
scienceAuthors.add(author1);
scienceAuthors.add(author2);
scienceAuthors.add(author3);
mathAuthors.add(author1);
mathAuthors.add(author2);
englishAuthors.add(author2);
englishAuthors.add(author3);
book1.setAuthors(scienceAuthors);
book2.setAuthors(mathAuthors);
book3.setAuthors(englishAuthors);
sessionFactory = new Configuration().configure().
buildSessionFactory();
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
session.save(book1);
session.save(book2);
session.save(book3);
tx.commit();
session.close();
- HashSet으로 여러 Author들을 묶고 book에 넣어주면
book은 여러 명의 author가 있는 것이고
author가 여러 방법으로 묶였으므로(author가 여러번 사용됨) 한 author가 여러 book의 author로 들어갔다.
따라서 하나의 책하나에 여러명의 저자, 한명의 저자에 여러 개의 책 즉 다대다 관계이다. - @JoinColumn이 Book에 있으므로 save를 book 객체로 한다.
Reference
https://howtoprogramwithjava.com/hibernate-manytomany-unidirectional-bidirectional/
'Spring > 이론' 카테고리의 다른 글
Restful Web Service (0) | 2020.04.19 |
---|---|
Hibernate with Spring (0) | 2020.04.19 |
Hibernate (0) | 2020.04.10 |
Apache Tiles (0) | 2020.04.08 |
Logging (SLF4J and Logback) (0) | 2020.04.01 |
댓글