โ๏ธ ํ์ค ์ธ๊ณ์ฒ๋ผ, ๊ฐ์ฒด๋ค๋ ๊ด๊ณ๋ฅผ ๋งบ๋๋ค
ํ์ค์์ "ํ์์ ํ๊ต์ ๋ค๋๋ค", "๊ฐ๊ฒ๋ ์ฌ๋ฌ ์ํ์ ํ๋ค"์ฒ๋ผ ๊ด๊ณ๊ฐ ์๋ฏ์ด, ๊ฐ์ฒด ์ฌ์ด์๋ ๊ด๊ณ๊ฐ ์๋ค!
JPA๋ ์ด ๊ด๊ณ๋ฅผ ํ
์ด๋ธ์ด ์๋๋ผ ๊ฐ์ฒด ์ค์ฌ์ผ๋ก ํํํ ์ ์๊ฒ ํด์ฃผ๋ ๋๊ตฌ์ด๋ค (ORM)
ํ๋์ ๋ถ๋ชจ๊ฐ ์ฌ๋ฌ ์์์ ๊ฐ์ง ์ ์์ง๋ง, ์์์ ๋ถ๋ชจ๊ฐ ํ๋๋ฟ!
ex) ํ๋์ ๋์๊ด์ ์ฌ๋ฌ ๊ถ์ ์ฑ
! โ ๋์๊ด์ ์์ฒญ ๋ง์ ์ฑ
์ ๊ฐ์ง ์ ์์ง๋ง, ์ฑ
์ ๋์๊ด๋ฟ
:ํ ๊ฐ์ฒด(๋ถ๋ชจ)๊ฐ ๋ค๋ฅธ ์ฌ๋ฌ ๊ฐ์ฒด(์์)๋ฅผ ์ฐธ์กฐํ์ง๋ง, ์์์ ๋ถ๋ชจ๋ฅผ ๋ชจ๋ฅด๋ ๊ตฌ์กฐ
๋์๊ด์ ์ฑ
์ ์๊ณ ์์ง๋ง, ์ฑ
์ ์์ ์ด ์ด๋ ๋์๊ด์ ์ํ๋์ง ๋ชจ๋ฅธ๋ค
//๋์๊ด ๊ฐ์ฒด
@Entity
public class Library {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany
@JoinColumn(name = "library_id") // ์ฑ
ํ
์ด๋ธ์ ์ธ๋ ํค ์ถ๊ฐ -> ์ฑ
์ ์๋ฅผ ์ฐธ์กฐ
private List<Book> books = new ArrayList<>();
}
//์ฑ
๊ฐ์ฒด
@Entity
public class Book {
@Id @GeneratedValue
private Long id;
private String title;
}
์ธ๋ ํค(library_id)๋ Book ํ
์ด๋ธ(์์ ๊ฐ์ฒด)์ ์์ฑ!
ํ์ง๋ง Library ์ํฐํฐ์์ ๊ด๋ฆฌํ๊ธฐ ๋๋ฌธ์, Book์ ์ ์ฅํ ๋ค Library์ ์ถ๊ฐํด๋ ์ค์ SQL์ด 2๋ฒ ์ด์ ๋๊ฐ ์ ์์ผ๋ฎคใ
โ ์ฑ๋ฅ ์ธก๋ฉด์์ ๋นํจ์จ์
๐ก 1:N ๋จ๋ฐฉํฅ ๋งคํ์ Entity๊ฐ ๊ด๋ฆฌํ๋ ์ธ๋ ํค๊ฐ ๋ค๋ฅธ ํ ์ด๋ธ์ ์๊ณ ์ถ๊ฐ์ ์ธ Update SQL์ด ์คํ๋์ด์ผ ํ๊ธฐ ๋๋ฌธ์ ์ค๊ณ๊ฐ ๋ณต์กํด์ ธ๋ N:1 ์๋ฐฉํฅ ๋งคํ์ ์ฌ์ฉํ๋ฉด ๊ด๋ฆฌํ๊ธฐ ์ฝ๋ค!
์์ชฝ์ด ์๋ก๋ฅผ ์ฐธ์กฐํ๋ ๊ตฌ์กฐ!
๋์๊ด๋ ์ฑ
์ ์๊ณ , ์ฑ
๋ ์์ ์ด ์ด๋ ๋์๊ด์ ์ํ๋์ง ์๊ฒ๋จ!
์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ์ด ๋์ง ์๋๋ก insertable = false, updatable = false ์ค์
@Entity
public class Library {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "library") <-- ์ฌ๊ธฐ!
private List<Book> books = new ArrayList<>();
public void addBook(Book book) {
books.add(book);
book.setLibrary(this);
}
}
@Entity
public class Book {
@Id @GeneratedValue
private Long id;
private String title;
@ManyToOne
@JoinColumn(name = "library_id")
private Library library;
public void setLibrary(Library library) {
this.library = library;
}
}
books.add(book); ์ฌ๊ธฐ์
๋์๊ด ์
์ฅ์์๋ "๋ด๊ฐ ๊ฐ์ง ์ฑ
๋ชฉ๋ก์ ์ด๊ฑฐ ์ถ๊ฐํ ๊ฒ!" ์ด๊ณ
์ฑ
์
์ฅ์์๋ "๋ด ๋์๊ด์ ์ด ๋์๊ด์ด์ผ!"๋ผ๊ณ ๋งํ๋ ๊ฒ๊ณผ ๊ฐ๋ค
public void setLibrary(Library library) {
this.library = library;
}
โก๏ธ insertable = false, updatable = false๋ก ํ๋ฉด ์ฝ๊ธฐ ์ ์ฉ์ผ๋ก ๋ง๋ค ์ ์๋ค
Book์ด์ธ๋ ํค(library_id)๋ฅผ ์ค์ ๋ก ๊ฐ์ง๊ณ ์์ผ๋ฏ๋ก, ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ์Book
mappedBy = "library"๋ก Library๋ ๋จ์ํ ๋ณด์ฌ์ฃผ๊ธฐ์ฉ(์ฝ๊ธฐ์ฉ) ๊ด๊ณ๊ฐ ๋จ
addBook()์ฒ๋ผ ์์ชฝ ๊ด๊ณ๋ฅผ ๋ง์ถฐ์ฃผ๋ ํธ์ ๋ฉ์๋๋ฅผ ๊ผญ ๋ง๋ค์ด์ฃผ๋ ๊ฒ ์ข๋ค!
โ ๊ฐ์ฒด ๊ฐ ๊ด๊ณ๊ฐ ๋ช ํํจ but, ๋ณต์กํด์ง ์ ์๊ณ ์์ชฝ์ ์ฐ๊ด๊ด๊ณ ๋๊ธฐํ๊ฐ ํ์ํ๋ค
ํํฐ๋ ์์ ์ ์ฃผ์(Address)๋ฅผ ์๊ณ ์์ด์
@OneToOne
@JoinColumn(name = "address_id", unique = true)
private Address address;
์ฃผ์๋ ๋๊ฐ ์ฌ๋์ง ์์์.
@OneToOne(mappedBy = "address")
private Tutor tutor;
๐ ์ธ๋ ํค๋ ์ด๋ ํ ์ด๋ธ์ ๋ฌ๋ ๋์ง๋ง, ์ฑ๋ฅ, ๋ฌด๊ฒฐ์ฑ ๋ฑ์ ๊ณ ๋ คํด์ ๊ฒฐ์ ํด์ผ ํด์.
ํํฐ๋ ์ฌ๋ฌ ์ธ์ด๋ฅผ ์ฐ๊ณ , ์ธ์ด๋ ์ฌ๋ฌ ํํฐ์๊ฒ ์ฌ์ฉ๋ผ์.
@ManyToMany
@JoinTable(
name = "tutor_language",
joinColumns = @JoinColumn(name = "tutor_id"),
inverseJoinColumns = @JoinColumn(name = "language_id")
)
private List<Language> languages;
๐ ๋ฌธ์ ์ :
์ถ๊ฐ ์ ๋ณด(level, license ๋ฑ)๋ฅผ ๋ชป ๋ด์
์ค๊ฐ ํ
์ด๋ธ์ด ์จ๊ฒจ์ ธ ์์ด ๊ด๋ฆฌ ์ด๋ ค์
๐ ํด๊ฒฐ: ์ค๊ฐ ํ ์ด๋ธ์ ์ํฐํฐ๋ก ์ง์ ์์ฑํด์ @OneToMany + @ManyToOne์ผ๋ก ๋๋ ์.
JPA๋ ์๋ฐ์ ์์ ๊ตฌ์กฐ๋ฅผ ํ ์ด๋ธ๋ก ํํํ ์ ์๋๋ก 3๊ฐ์ง ์ ๋ต์ ์ ๊ณตํ๋ค
| ์ ๋ต | ํน์ง |
|---|---|
| SINGLE_TABLE | ํ ํ ์ด๋ธ์ ๋ชจ๋ ์ ์ฅ. ์ฑ๋ฅ ๋น ๋ฆ. |
| JOINED | ํ ์ด๋ธ์ ๋๋๊ณ JOIN. ์ ๊ทํ์ ์ข์. |
| TABLE_PER_CLASS | ๊ฐ๊ฐ ํ ์ด๋ธ. ๊ฑฐ์ ์ฌ์ฉ โ |
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "dtype")
์ํฉ์ ๋ฐ๋ผ ๋ง๊ฒ ์ ํํด์ผํ๋ค
๐ em.find vs em.getReference
em.find() โ ์ค์ ๊ฐ์ฒด๋ฅผ ๋ฐ๋ก ์กฐํ
em.getReference() โ Proxy ๊ฐ์ฒด(๊ฐ์ง ๊ฐ์ฒด)๋ฅผ ๋ฐํ โ ์ค์ ์ฌ์ฉ ์ ์กฐํ
Tutor proxy = em.getReference(Tutor.class, id);
proxy.getName(); // ์ด๋ DB ์กฐํ ๋ฐ์!
๐ ์ฃผ์: ํ๋ก์๋ ์์์ฑ ์ปจํ ์คํธ ๋ฐ์์๋ ์ฌ์ฉํ ์ ์๋ค โ ์์ธ ๋ฐ์โ
โ ์ค์ ์ฌ์ฉํ ๋ DB์์ ์กฐํ
โก๏ธ์ฐ๊ด๋ ๊ฐ์ฒด๊ฐ ํ์ ์์ผ๋ฉด SQL ์ ๋ ์๊ฐ โ ํจ์จ์ !
@ManyToOne(fetch = FetchType.LAZY)
: ๊ฐ์ฒด๋ฅผ ๋ถ๋ฌ์ฌ ๋ ์ฐ๊ด ๊ฐ์ฒด๋ ํจ๊ป ์กฐํ
โก๏ธ ๋ฌด์กฐ๊ฑด JOIN โ ์๊ธฐ์น ์์ ์ฑ๋ฅ ์ด์ ๋ฐ์ ๊ฐ๋ฅ
@ManyToOne(fetch = FetchType.EAGER)
๋๋ถ๋ถ์ LAZY๋ฅผ ์ฐ๊ณ , ๊ผญ ํ์ํ ๊ฒฝ์ฐ๋ง EAGER!
โ๏ธ ๋ถ๋ชจ ์ํฐํฐ ์ ์ฅ ์ ์์๋ ์๋ ์ ์ฅ!
@OneToMany(cascade = CascadeType.ALL)
ALL, PERSIST, REMOVE ๋ฑ ํ์์ ๋ฐ๋ผ์ ์ ํํด ์ฌ์ฉํ๊ธฐ
โก๏ธ ์๋ช
์ฃผ๊ธฐ๊ฐ ์์ ํ ๊ฐ์ ๊ฒฝ์ฐ์๋ง ์ฌ์ฉํ ์ ์๋ค!!
๋ถ๋ชจ์ ๊ด๊ณ๊ฐ ๋๊ฒผ์๋,๊ทธ ๋ถ๋ชจ์ ์์์ ๊ณ ์๊ฐ์ฒด๊ฐ ๋๋ค
๊ทธ๋ฌ๋ฉด ~ ์์ ๊ฐ์ฒด๋ ์๋์ผ๋ก ์ญ์ ๋๋ ๊ฒ
โ๏ธ ๋ถ๋ชจ์ ๊ด๊ณ ๋๊ธฐ๋ฉด ์์ ์๋ ์ญ์ !
@OneToMany(orphanRemoval = true)
: ๊ธฐ๋ณธ์ ์ผ๋ก ํ๋์ ํธ๋์ญ์
์์์ ์ฒ๋ฆฌ
โ ์ด ์ด๋
ธํ
์ด์
์ด ๋ถ์ ์ฝ๋๋, ๋ฐ๋ก๋ฐ๋ก DB์ ๋ฐ์๋์ง ์๊ณ ์ค๊ฐ์ ํ๋๋ผ๋ ์ค๋ฅ๊ฐ ์๊ธฐ๋ฉด ๋ค ์ทจ์ํ ์ ์๋๋ก
ํธ๋์ญ์
์ผ๋ก ๊ด๋ฆฌ!
โก๏ธ ๋ฌธ์ ๋ฐ์ ์ ์ ์ฒด ROLLBACK
๐ REQUIRES_NEW๋ฅผ ์ฐ๋ฉด ๋ค ๋กค๋ฐฑ๋์ง ์๊ฒ ํ ์ ์์
์๋ฅผ ๋ค์ด, ํ์๊ฐ์
์ ๋์ง๋ง, ํฌ์ธํธ๋ ์ง๊ธ๋์ง ์๋๋ก
@Transactional(propagation = Propagation.REQUIRES_NEW)
โก๏ธ ๋ ์์ ์ ๋ ๋ฆฝ์ ํธ๋์ญ์ ์ผ๋ก ์ฒ๋ฆฌ!!!