Object-relational mapping(๊ฐ์ฒด ๊ด๊ณ ๋งคํ)
ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์ ๊ฐ์ฒด์ ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ฐ์ดํฐ๋ฅผ ์๋์ผ๋ก ๋งคํ(์ฐ๊ฒฐ)ํด์ฃผ๋ ๋๊ตฌ์ด๋ค.
๊ฐ์ฒด๋ ๊ฐ์ฒด๋๋ก ์ค๊ณํ๊ณ ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋๋ก ์ค๊ณ.
Java ์ง์์์ ORM(Object-Relational Mapping) ๊ธฐ์ ํ์ค์ผ๋ก ์ฌ์ฉํ๋ ์ธํฐํ์ด์ค ๋ชจ์์ผ๋ก ์๋ฐ ์ดํ๋ฆฌ์ผ์ด์ ์์ ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ์์ ์ ์ํ ์ธํฐํ์ด์ค์ด๋ค.
1. ๋ฐ๋ณต์ ์ธ CRUD SQL์ ์ฒ๋ฆฌํด์ค๋ค. -> ์์ฐ์ฑ
์ ์ฅ : jpa.persist(member)
์กฐํ : Member member = jpa.find(memberId)
์์ : member.setName("๋ณ๊ฒฝํ ์ด๋ฆ")
์ญ์ : jpa.remove(member)
2. ์ ์ง๋ณด์ => ๊ธฐ์กด์๋ ํ๋ ๋ณ๊ฒฝ ์ ๋ชจ๋ SQL์ ์์ ํ์ง๋ง JPA๋ ํ๋๋ง ์ถ๊ฐํ๋ฉด ๋๋ค. ์ฆ SQL์ JPA๊ฐ ์ฒ๋ฆฌํด์ค๋ค.
3. ์์, ์ฐ๊ด๊ด๊ณ, ๊ฐ์ฒด ๊ทธ๋ํ ํ์, ๋น๊ตํ๊ธฐ ๊ฐ์ ํจ๋ฌ๋ค์ ๋ถ์ผ์น ํด๊ฒฐํด์ค๋ค.
JPA์ ์์ - ์ ์ฅ
๊ฐ๋ฐ์์ ์ผ -> jpa.persist(album);
JPA ์ฒ๋ฆฌ -> INSERT INTO ITEM, INSERT INTO ALBUM
JPA์ ์์ - ์กฐํ
๊ฐ๋ฐ์์ ์ผ -> Album album = jpa.find(Album.class, albumId);
JPA ์ฒ๋ฆฌ
-> SELECT I., A.
FROM ITEM I
JOIN ALBUM A ON I.ITEM_ID = A.ITEM_ID
์ ๋ขฐํ ์ ์๋ ์ํฐํฐ, ๊ณ์ธต
๋น๊ตํ๊ธฐ
String memberId = "100";
Member member1 = jpa.find(Member.class, memberId);
Member member2 = jpa.find(Member.class, memberId);
member1 == member2; //true
๋์ผํ ํธ๋์ญ์ ์์ ์กฐํํ ์ํฐํฐ๋ ๊ฐ์์ ๋ณด์ฅํ๋ค.
JPA์ ์ฑ๋ฅ ์ต์ ํ ๊ธฐ๋ฅ
1์ฐจ ์บ์์ ๋์ผ์ฑ ๋ณด๋น
๊ฐ์ ํธ๋์ญ์
์์์๋ ๊ฐ์ ์ํฐํฐ๋ฅผ ๋ฐํํ๋ค.
ํธ๋์ญ์
์ ์ง์ํ๋ ์ฐ๊ธฐ ์ง์ฐ
ํธ๋์ญ์
์ ์ปค๋ฐํ ๋๊น์ง INSERT SQL์ ๋ชจ์ธ ์ ์๋ค.
JDBC BATCH SQL ๊ธฐ๋ฅ๋ฅผ ์ฌ์ฉํด์ ํ ๋ฒ์ SQL ์ ์กํ๋ค.
์ง์ฐ ๋ก๋ฉ
์ง์ฐ๋ก๋ฉ์ ๊ฐ์ฒด๊ฐ ์ค์ ์ฌ์ฉ๋ ๋ ๋ก๋ฉํ๋ ๊ฒ์ด๊ณ ์ฆ์ ๋ก๋ฉ์ JOIN SQL๋ก ํ ๋ฒ์ ์ฐ๊ด๋ ๊ฐ์ฒด๊น์ง ๋ฏธ๋ฆฌ ์กฐํํ๋ค.
Hibernate๋ JPA๋ฅผ ๊ตฌํํ ๊ตฌํ์ฒด.
์์์ฑ ์ปจํ ์คํธ๋ฅผ ํตํด์ ์ํฐํฐ๋ฅผ ์์ํ ํ๋ค๋ ๋ป์ด๋ค. ๊ฐ์ฒด๋ฅผ ์ ์ฅํ ์ํ๊ฐ ๋จ.
JPA๊ฐ ๊ด๋ฆฌํ๋ ๊ฐ์ฒด๊ณ DB ํ
์ด๋ธ๊ณผ ๋งคํํ๋ค.
JPA๋ฅผ ์ฌ์ฉํด์ ํ
์ด๋ธ๊ณผ ๋งคํํ ํด๋์ค๋ ํ์๋ก ์จ์ผํ๋ค.
(๊ธฐ๋ณธ ์์ฑ์ ํ์)
๋ค๋ฅธ ํจํค์ง์ ๊ฐ์ ์ด๋ฆ์ ํด๋์ค๊ฐ ๋งคํ์ด ๋์ด์์ ๋ ์ฌ์ฉ
์ํฐํฐ์ ๋งคํํ ํ
์ด๋ธ ์ง์
(name = "MBR") ์ผ๊ฒฝ์ฐ "MBR" ์ด๋ผ๋ ํ
์ด๋ธ๊ณผ ๋งคํํ๋ค. <ํ
์ด๋ธ ๋ช
์ ์ ํด์ค>
ํ๋ผ๋ฏธํฐ๊ฐ ์๋ ๊ธฐ๋ณธ์์ฑ์๋ฅผ ๋ง๋ค์ด์ค๋ค.
๋ชจ๋ ํ๋ ๊ฐ์ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ๋ ์์ฑ์๋ฅผ ๋ง๋ค์ด์ค.
final์ด๋ @NonNull์ธ ํ๋ ๊ฐ๋ง ํ๋ผ๋ฏธํฐ๋ก ๋ฐ๋ ์์ฑ์ ๋ง๋ค์ด์ค.
๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ธฐ๋ณธํค(PK)์ ๋งคํ
์ปฌ๋ผ๋ช
์ผ๋ก name = "" ๋ผ๊ณ ์ค ์ ์๋ค.
unique๋ @Table ์ ์๋ ๊ฒ์ด ๋ ์ข์
ex)@Table(uniqueConstraints = "")
๋ฑ๋ก, ๋ณ๊ฒฝ ๊ฐ๋ฅ ์ฌ๋ถ (true, false)
null ๊ฐ์ ํ์ฉ ์ฌ๋ถ๋ฅผ ์ค์
(false๋ก ์ค์ ํ๋ฉด DDL ์์ฑ ์์ not null ์ ์ฝ์กฐ๊ฑด์ด ๋ถ๋๋ค)
DB ์ปฌ๋ผ ์ ๋ณด๋ฅผ ์ง์ ์ค ์ ์๋ค.
ex) columnDefinition = "varchar(100) default 'EMPTY'"
varchar(์ซ์)
BigDecimal ํ์
์์ ์ฌ์ฉํ๋ค. (BigInteger ๋ ์ฌ์ฉ ๊ฐ๋ฅ)
precision์ ์์์ ์ ํฌํจํ ์ ์ฒด ์๋ฆฟ์๋ฅผ, scale์ ์์ ์๋ฆฟ์ (double, float ํ์
์๋ ์ ์ฉ๋์ง ์๋๋ค.)
์ ๋ฐํ ์์๋ฅผ ๋ค๋ฃฐ ๋ ์ฌ์ฉ
์ฝ๊ธฐ ์ ์ฉ์ผ๋ก ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ด ๋์ง ์๊ฒ ํ๊ธฐ ์ํด ์ฌ์ฉ.
์กฐํ์์ ์์ฃผ ์ฌ์ฉ.
์ฐ๊ด๊ด๊ณ ๋งคํ.
ex) ํ์์ด ์ด๋ ํ์ ์์๋์ด ์๋์ง์ ๊ด๊ณ๋ฅผ ๋งคํ
IllegalArgumentException
์ ์ ํ์ง ์๊ฑฐ๋ ์ ํจํ์ง ์์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ ๋ ๋ฐ์ํ๋ ์์ธ ex) ์์๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ๊ธฐ๋ํ๊ณ ์์๋๋ฐ ์์๊ฐ ๋์ด์ด.
IllegalStateException
์ ์ ํ์ง ์๊ฑฐ๋ ์ ํจํ์ง ์์ ์๊ฐ๋์ ๋ฉ์๋๊ฐ ๋ถ๋ฌ์ก์ ๋ ๋ฐ์ํ๋ ์์ธ ex) ์ฃฝ์ ์ฐ๋ ๋๋ก๋ถํฐ ๋ถ๋ ค์ง๋ ๊ฒฝ์ฐ์ ๊ฐ์ด ๋ฉ์๋๊ฐ ๋ถ๋ ค์ ธ์ ์๋ ์๊ธฐ์ ๋ฉ์๋๊ฐ ํธ์ถ๋ ๋ ๋ฐ์
-> ๋ฉ์๋๊ฐ ์๊ตฌ๋ ์ฒ๋ฆฌ๋ฅผ ํ๊ธฐ์ ์ ํฉํ ์ํ์ ์์ง ์์ ๋ ๋ฐ์
Enum ํ์
์ ๋งคํํ ๋ ์ฌ์ฉ
์ค๊ฐ์ ๋ค๋ฅธ ์ํ๊ฐ ์ค๊ฐ์ ๋ค์ด๊ฐ๋ฉด ์ซ์๊ฐ ๋ฐ๋ฆฌ๊ฒ ๋์ด ๋งํ๊ฒ ๋๋ค.
์ ๋ @Enumerated(EnumType.ORDINARY) ์ฌ์ฉํ์ง ๋ง์. ๋ฌด์กฐ๊ฑด STRING์ผ๋ก
๋ ์ง ํ์
์ ๋งคํํ ๋ ์ฌ์ฉ
ex)TemporalType.DATE -> ๋ ์ง, DB date ํ์
๊ณผ ๋งคํ (2022-08-26)
TemporalType.TIMe -> ์๊ฐ, DB time ํ์ ๊ณผ ๋งคํ (11:23:12)
TemporalType.TIMESTAMP -> ๋ ์ง์ ์๊ฐ, DB timestamp ํ์ ๊ณผ ๋งคํ
๋ฐ์ดํฐ๋ฒ ์ด์ค BLOB, CLOB ํ์
๊ณผ ๋งคํ
@Lob์๋ ์ง์ ํ ์ ์๋ ์์ฑ์ด ์๋ค.
๋งคํํ๋ ํ๋ ํ์
์ด ๋ฌธ์๋ฉด CLOB๋งคํ, ๋๋จธ์ง๋ BLOB ๋งคํ
CLOB: String, char[], java.sql.CLOB
BLOB: byte[], java.sql.BLOB
ํ๋ ๋งคํํ๊ธฐ ์ซ์ ๋ ์ฌ์ฉ
DB์ ์ ์ฅ X, ์กฐํ X
์ฃผ๋ก ๋ฉ๋ชจ๋ฆฌ์์์๋ง ์์๋ก ์ด๋ค ๊ฐ์ ๋ณด๊ดํ๊ณ ์ถ์ ๋ ์ฌ์ฉ
@Transient
private Integer temp;
์์ ๊ฒ๋ค์ ํ๋์ ์ปฌ๋ผ ๋งคํ์ด์์.
๋ช ๊ฐ์ง ์๋์ ์ด๋ค ๊ธฐ์ค์ ๋ฐ๋ผ ๊ณต๊ฐ์ ๋๋ ์๊ฐ์ ์ผ๋ก ์์๋ฅผ ์ ํด ๋๋ ๊ฒ
์ฆ ์ค๋ผํด์์ ํ์ ๊ตฌ๋ถํ๊ธฐ ์ํด์ ๊ธฐ๋ณธํค๋ฅผ ๋๋๋ฐ ๊ธฐ ๊ธฐ๋ณธ ํค๋ ์ค๋ณต๋ ๊ฐ์ ๊ฐ์ง ์ ์์ผ๋ฏ๋ก ํญ์ ์ ์ผํ ๊ฐ์ ๊ฐ์ ธ์ผ ํ๋ค.
๊ทธ๋ฐ ๊ธฐ๋ณธ ํค๊ฐ ์ ์ผํ ๊ฐ์ ๊ฐ๋๋ก ์ฌ์ฉ์๊ฐ ์ง์ ์์ฑํด๋ด๋ ๊ฒ์ด ์๋๋ผ ์ซ์๋ฅผ ์๋์ผ๋ก ์์ฑํด์ฃผ๋ ๋ช
๋ น์ด๋ฅผ ์๋ฏธํ๋ค.
(์์ ๊ฐ์ด 1๋ถํฐ ํด์ ์ฆ๊ฐํ๋ ๊ฐ 1๋ก ์งํ๋๋ค.)
PK๋ฅผ ๋ํ๋ด๊ธฐ ์ํด @Id ์ด๋
ธํ
์ด์
์ ์ฌ์ฉํ๋ฉฐ, ์์ฑ ์ ๋ต์ ์ ์ํ๊ธฐ ์ํด @GeneratedValue ๋ฅผ ์ฌ์ฉ
๊ธฐ๋ณธํค๋ฅผ ์๋์ผ๋ก ์์ฑํ ๋์๋ @Id์ @GenerratedValue ์ด๋
ธํ
์ด์
์ด ํจ๊ป ์ฌ์ฉ๋์ด์ผ ํ๋ค.
strategy = GenerationType.Identity
๊ธฐ๋ณธ ํค ์์ฑ์ DB์ ์์
(DB๊ฐ ์์์ ํด์ฃผ๋๋ก)
IDENTITY ์ ๋ต์ em.persist() ์์ ์ ์ฆ์ INSERT SQL ์คํ ํ๊ณ DB์์ ์๋ณ์๋ฅผ ์กฐํ
์๋ํ๋ฉด ์์์ฑ์ ์ํฐํฐ์ ์๋ณ๊ฐ์ ๊ธฐ์ค์ผ๋ก ์ํฐํฐ๋ฅผ ๊ตฌ๋ณํ๋๋ฐ ํค๊ฐ์ด ํ์ํ๊ธฐ ๋๋ฌธ
strategy = GenerationType.SEQUENCE
๋ฐ์ดํฐ๋ฒ ์ด์ค ์ํ์ค๋ฅผ ์ฌ์ฉํ์ฌ ๊ธฐ๋ณธ ํค๋ฅผ ํ ๋นํ๋ค.
์ ์ผํ ๊ฐ์ ์์๋๋ก ์์ฑํ๋ ํน๋ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ค๋ธ์ ํธ ์ฌ์ฉ
Data๋ฅผ DB์ Insertํ ํ ๊ธฐ๋ณธ ํค ๊ฐ์ ์กฐํํ ์ ์๋ค
ํ
์ด๋ธ๋ง๋ค ๋ฐ๋ก ์ํ์ค๋ฅผ ๊ด๋ฆฌํ๊ณ ์ถ๋ค๋ฉด
@SequenceGenerator() ๋ก ๋งคํํ๋ค.
strategy = GenerationType.AUTO
DB ๋ฐฉ์ธ์ ๋ฐ๋ผ ์๋ ์ง์
strategy = GenerationType.TABLE
ํค ์์ฑ ์ ์ฉ ํ
์ด๋ธ์ ํ๋ ๋ง๋ค์ด์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ํ์ค๋ฅผ ํ๋ด๋ด๋ ์ ๋ต
๋ชจ๋ ์์
์ ์ ์์ ์ผ๋ก ์ฒ๋ฆฌํ๊ฒ ๋ค๊ณ ํ์ ํ๋ ๋ช
๋ น์ด
commitํ๋ฉด ํ๋์ ํธ๋์ญ์
๊ณผ์ ์ ์ข
๋ฃํ๊ฒ ๋๋ค.
Transaction ์์
๋ด์ฉ์ ์ค์ DB์ ์ ์ฅํ๊ณ ์ด์ ๋ฐ์ดํฐ๊ฐ ์์ ํ UPDATE ๋๋ค.
์์
์ค ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ ๋, ํธ๋์ญ์
์ ์ฒ๋ฆฌ ๊ณผ์ ์์ ๋ฐ์ํ ๋ณ๊ฒฝ ์ฌํญ์ ์ทจ์ํ๊ณ , ํธ๋์ญ์
๊ณผ์ ์ ์ข
๋ฃ์ํจ๋ค.
Transaction ์์
๋ด์ฉ์ ์ทจ์ํ๊ณ ์ด์ commitํ ๊ณณ๊น์ง๋ง ๋ณต๊ตฌํ๋ค.
ํ ๋ฒ์ ์๋ฐฉํฅ ๊ด๊ณ๋ฅผ ์ค์ ํ๋ ๋ฉ์๋ > ์๋ฐฉํฅ์ธ ๊ฒ์ ๊ฐ๊ฒฐํ๊ฒ ์์ฑํ ์ ์๋ค.
์ฐ๊ด๊ด๊ณ ํธ์ ๋ฉ์๋์ ์์น๋ ํต์ฌ์ ์ผ๋ก ์ปจํธ๋กค ํ๋ ์ชฝ์ด ์๋ ๊ฒ์ด ์ข๋ค.
ex) member์ order ๊ฐ ์๋ฐฉํฅ์ธ๋ฐ ์๋๋
public ~~(){
Member member = new Member();
Order order = new Order();
member.getOrders().add(order);
order.setMember(member);
}
์ด๋ฐ ์์ผ๋ก ํ ๊ฒ์ ๋ค์๊ณผ ๊ฐ์ด ๊ฐ๊ฒฐํ๊ฒ
public void setMember(Member member) {
this.member = member;
member.getOrders().add(this);
}
์คํ๋ง์ด ์์ฑํ EntityManager๋ฅผ ์ฃผ์ ํด์ค๋ค.
@PersistenceContext
private EntityManager em;
EntityManagerFactory๋ฅผ ์ฃผ์ ๋ฐ๊ณ ์ถ๋ค๋ฉด
@PersistenceUnit
private EntityManagerFactory emf;
๊ณต๋ถํ๋ ๋์ค localhost:8082 ์
๋ ฅ์ ํ๋๋ฐ h2database์ ์ฐ๊ฒฐํ์ง ๋ชปํ๋ ์ํฉ์ด ๋ฐ์.
์ด์ ์ ํ์ ๋๋ ์ ์คํ ๋๋๋ฐ....
๊ทธ๋์ intellij ์์ JPA Buddy ๋ฅผ ๋ค์ restart ํ๊ณ
<!-- H2 ๋ฐ์ดํฐ๋ฒ ์ด์ค -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.199</version>
</dependency>
์ ๋๋ก ์ ๋ ฅํ๋์ง ํ์ธ
๊ทธ๋ฆฌ๊ณ ์คํํด๋ดค๋๋ ์ฐ๊ฒฐ ์ ๋......
EntityManager : ์ํฐํฐ๋ฅผ ๊ด๋ฆฌํ๋ ์ญํ ์ ์ํ.
์ํฐํฐ ๋งค๋์ ๋ด๋ถ์ ์์์ฑ ์ปจํ
์คํธ(Persistence Context)**๋ผ๋ ๊ฑธ ๋์ด์ ์ํฐํฐ๋ค์ ๊ด๋ฆฌ
EntityManagerFactory : ์ํฐํฐ ๋งค๋์ (Entity Manager)๋ฅผ ๋ง๋๋ ๊ณต์ฅ
Persistence.xml์ JPA๊ฐ ์์ธ์คํ๋ ค๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ค์ ๋ํด ํ์ํ ์ค์ ์ ๋ณด ๋ค์ ๊ธฐ์ ํด๋ ํ์ผ์ด๋ค.
Member
@Entity //JPA๊ฐ ๊ด๋ฆฌํ ๊ฐ์ฒด
//@Table(name="user") //์ฟผ๋ฆฌ๊ฐ ๋๊ฐ ๋ user๋ผ๋ ํ
์ด๋ธ์ insertํ๋ผ๊ณ ํ๊ฒ ๋จ
public class Member {
//PK๊ฐ ๋ญ์ง ์๋ ค์ค
@Id
private Long id;
private String name;
//์์ฑ์๋ฅผ ์์ฑํ๋ฉด ์๋ฌ๊ฐ ๋๋ ์ด์ ๋ JPA๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ด๋ถ์ ์ผ๋ก reflection ์ด๋ฐ ๊ฒ๋ค์
//์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ๋์ ์ผ๋ก ๊ฐ์ฒด๋ฅผ ์์ฑํด์ผ ํ๋ค.
//๊ทธ๋์ ๊ธฐ๋ณธ์์ฑ์๋ฅผ ์์ฑ
public Member() {}
public Member(Long id, String name) {
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
JpaMain
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("hello"); //persistence.xml ์์ ์ ํด๋์ "hello"
//EntityManagerFactory๋ฅผ ๋ง๋๋ ์๊ฐ ๋ฐ์ดํฐ๋ฒ ์ด์์ ์ฐ๊ฒฐ๋ ๋ค ๋๊ณ ์ฌ๋งํ๊ฒ ๋ค ๋จ
EntityManager em = emf.createEntityManager();
//JPA ์์๋ ๊ผญ ํธ๋์ญ์
์ด๋ผ๋ ๋จ์๊ฐ ๋งค์ฐ ์ค์ํ๋ค.
//๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝํ๋ ๋ชจ๋ ์์
์ ๊ผญ ํธ๋์ญ์
์์์ ์์
์ ํด์ผํ๋ค.
EntityTransaction tx = em.getTransaction();
tx.begin();
try{
// ๋ฑ๋ก
// Member member = new Member();
// member.setId(2L);
// member.setName("HelloB");
// ์กฐํํ๊ณ ์ถ๋ค๋ฉด
Member findMember = em.find(Member.class, 1L); //๊ฐ์ฒด๋ฅผ ๋์ ์ ์ฅํด์ฃผ๋ ๊ฒ(์๋ฐ ์ปฌ๋ ์
์ฒ๋ผ ์๊ฐ)
System.out.println("findMember.getId() = " + findMember.getId());
System.out.println("findMember.getName() = " + findMember.getName());
// ์ญ์
// em.remove(findMember) // ์ฐพ์ ์๋ฅผ ๋ฃ์ด์ฃผ๋ฉด ๋๋ค.
// ์์
findMember.setName("HelloJPA");
// ์ฌ๊ธฐ์ em.persist()๋ก ์ ์ฅํ์ง ์์๋ ๋๋ค. -> JPA๋ฅผ ํตํด์ Entity๋ฅผ ๊ฐ์ ธ์ค๋ฉด JPA๊ฐ ๊ด๋ฆฌ๋ฅผ ํด์ ๋ณ๊ฒฝ์ด ๋๋์ง ์ ๋๋์ง ํธ๋์ญ์
์ ์ปค๋ฐํ๋ ์์ ์ ๋ค ์ฒดํฌ๋ฅผ ํ๋ค.
// ๊ทธ๋์ ๋ฐ๊ผ๋ค๋ฉด update ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆฐ๋ค.
tx.commit();
}catch(Exception e){
tx.rollback(); //๋ฌธ์ ๊ฐ ์๊ธฐ๋ฉด ๋กค๋ฐฑ
}finally{
em.close();
//entityManager๊ฐ ๊ฒฐ๊ตญ ๋ฐ์ดํฐ ์ปค๋ฅ์
์ ๋ฌผ๊ณ ๋์ํ๊ธฐ ๋๋ฌธ์ ๊ผญ ๋ซ๋๋ค.
}
emf.close();
}
}
์คํํ๋๋ Exception in thread "main" org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]
Caused by: org.hibernate.boot.registry.classloading.spi.ClassLoadingException Unable to load class [org.h2.Driver]
Caused by: java.lang.ClassNotFoundException: Could not load requested class : org.h2.Driver
์๋ฌ ๋ฐ์
ํด๊ฒฐ ->
pom.xml ์์
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>jpa-basic</groupId>
<artifactId>ex1-hello-jpa</artifactId>
<version>1.0.0</version>
<dependencies>
<!-- JPA ํ์ด๋ฒ๋ค์ดํธ -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.3.10.Final</version>
</dependency>
<!-- H2 ๋ฐ์ดํฐ๋ฒ ์ด์ค -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.199</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
</project>
๊ทธ๋ฆฌ๊ณ file -> project structure ์ ํํ๊ณ Project Settings -> Project์์ SDK ๋ฒ์ ํ์ธ (11๋ก ๋ณ๊ฒฝํจ)
Modules ์์ Language Level์ 11๋ก ๋ณ๊ฒฝ (5๋ก ๋์ด์์ด์ Error:java: error: release version 5 not supported ์๋ฌ๊ฐ ๋ฐ์ํจ)
๊ทธ๋๋ ์์ง ํด๊ฒฐ ์๋จ.
H2 Database ์ ์ฐ๊ฒฐ์ ํ์ง ์์์ ๋ฌธ์
๊ทธ๋์ ์จ๊ฒจ์ง ์์ด์ฝ ์ด์ด์ H2 DataBase ์ฐ๊ฒฐํ๋ฉด H2 ์ฝ์ ์ฐฝ์ด ์๊ธด๋ค.
๋ค ์ ๋ ฅํ๊ณ ์ฐ๊ฒฐ ์ํ ํ๋๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค "C:/Users/park/jpashop"์ ์ฐพ์ ์ ์์ต๋๋ค. ๋ฏธ๋ฆฌ ๋ง๋ค๊ฑฐ๋ ์๊ฒฉ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ฑ์ ํ์ฉ(๋ณด์ ํ๊ฒฝ์์๋ ๊ถ์ฅ๋์ง ์์) ์ด๋ฐ ์๋ฌ๊ฐ ๋ ๋ฐ์...... ๋ค์ ํด๊ฒฐ ๋ฐฉ์ ์ฐพ์
ํด๊ฒฐ ์๋ฃ
์๋ก์ด DataBase ๋ฅผ ์์ฑํ ๋๋ ์์ด์ฝ์ ์ค๋ฅธ์ชฝ ๋ฒํผ ํด๋ฆญํด์ create new DataBase๋ก ์์ฑํ๋ค. ๊ทธ๋ฌ๋ฉด
์๋ฐ ์ฐฝ์ด ๋จ๋๋ฐ Database Path๋ฅผ ์์๋ก ๋ฐ๊พธ๊ณ ๋น๋ฐ๋ฒํธ๋ ์๋ฌด๊ฑฐ๋ ์ณ๋ ์๊ด์์.
๊ทธ๋ฆฌ๊ณ ๋์ ๋ค์ H2 ์ฝ์๋ก ๊ฐ์ JDBC URL์ jdbc:h2:./jpashop ๋ฅผ ์ ๋ ฅํ๊ณ ๋น๋ฐ๋ฒํธ ์ ๋ ฅํ๋ฉด ์ฐ๊ฒฐ ์๋ฃ๋.
์ฌ๊ธฐ๋ ์์
H2 ์ฝ์์ ํด๊ฒฐ ๋๋๋ฐ ๋ Intellij์์ File corrupted while reading record ์๋ฌ ๋ฐ์
ํด๊ฒฐ -> Intellij ์์ jpashop.mv.db ์ jpashop.trace.db ์ญ์ ํ ๋ค์ ์คํ
์๊พธ ์คํ๋๋ค ์๋๋ค ํ๋๊ฒ ๋ถํธํ์๋ค...... ์ด์ ํด๊ฒฐ๋๋ค... (๋๋ฌด ์ค๋ ๊ฑธ๋ ธ๋ค....)
๋ฉค๋ฒ๋ฅผ ์ ์ฅํ๋ ๋ฐฉ๋ฒ
EntityManagerFactory๋ฅผ Application ๋ก๋ฉ ์์ ์ ๋ฑ ํ๋๋ง ๋ง๋ ๋ค.
์ค์ DB์ ์ ์ฅํ๋ ๊ฒ์ ํธ๋์ญ์
๋จ์๋ก DB ์ปค๋ฅ์
์ ์ป์ด์ ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆฌ๊ณ ์ข
๋ฃ๋๋ ์ผ๊ด์ ์ธ ๋จ์๋ฅผ ํ ๋๋ง๋ค EntityManager๋ผ๋ ๊ฒ์ ๋ง๋ค์ด ์ค์ผ ํ๋ค.
1) EntityManagerFactory๋ ํ๋๋ง ์์ฑํด์ ์ ํ๋ฆฌ์ผ์ด์
์ ์ฒด์์ ๊ณต์
2) EntityManager๋ ์ฐ๋ ๋๊ฐ์ ๊ณต์ X(์ฌ์ฉํ๊ณ ๋ฒ๋ ค์ผ ํ๋ค.)
3) JPA์ ๋ชจ๋ ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ ํธ๋์ญ์
์์์ ์คํํด์ผ ํ๋ค.
JPQL๋ก ์ ์ฒด ํ์ ๊ฒ์ํ๊ณ ์ถ๋ค๋ฉด try{} ์๋ค๊ฐ ์ฝ๋ ์์ฑ.
//JPQL๋ก ์ ์ฒด ํ์ ๊ฒ์ํ๊ณ ์ถ๋ค๋ฉด
List<Member> result = em.createQuery("select m from Member as m", Member.class)
.getResultList();
//JPA๋ ์ ๋ ํ
์ด๋ธ์ ๋์์ผ๋ก ์ฝ๋๋ฅผ ์ง์ง ์๋๋ค. Member ๊ฐ์ฒด๋ฅผ ๋์์ผ๋ก ํ๋ค.
for (Member member : result) {
System.out.println("member.getName() = " + member.getName());
}
JPA๋ฅผ ์ฌ์ฉํ๋ฉด ์ํฐํฐ ๊ฐ์ฒด๋ฅผ ์ค์ฌ์ผ๋ก ๊ฐ๋ฐํ๋๋ฐ ๋ฌธ์ ๋ ๊ฒ์ ์ฟผ๋ฆฌ์ด๋ค. ๊ฒ์์ ํ ๋๋ ํ ์ด๋ธ์ด ์๋ ๊ฐ์ฒด๋ฅผ ๋์์ผ๋ก ๊ฒ์์ ํด๋ฒ๋ฆฐ๋ค.
๋ชจ๋ DB ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ฒด๋ก ๋ณํํด์ ๊ฒ์ํ๋ ๊ฒ์ ๋ถ๊ฐ๋ฅ.
์ ํ๋ฆฌ์ผ์ด์ ์ด ํ์ํ ๋ฐ์ดํฐ๋ง DB์์ ๋ถ๋ฌ์ค๋ ค๋ฉด ๊ฒฐ๊ตญ ๊ฒ์ ์กฐ๊ฑด์ด ํฌํจ๋ SQL์ด ํ์.
JPQL
public static void main(String[] args) {
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("hello"); //persistence.xml ์์ ์ ํด๋์ "hello"
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try{
Member member = new Member();
member.setId(101L);
member.setName("HelloJPA");
//์ฌ๊ธฐ๊น์ง๋ ๋น์์
//em.persist(member); //์ฌ๊ธฐ๋ถํฐ ์์์ํ๊ฐ ๋จ, DB์ ์ ์ฅ์ด ๋์ง๋ ์์
// EntityManager ์์ ์๋ ์์์ฑ ์ปจํ
์คํธ๋ผ๋ ๋ฐ๋ฅผ ํตํด์ member๊ฐ ๊ด๋ฆฌ๋๋ค๋ ๋ป
System.out.println("BEFORE");
em.persist(member);
System.out.println("AFTER");
//em.detach(member);
//ํ์ ์ํฐํฐ๋ฅผ ์์์ฑ ์ปจํ
์คํธ์์ ๋ถ๋ฆฌ (์ค์์ ์ํ)
//์กฐํ
Member findMember1 = em.find(Member.class, 101L);
Member findMember2 = em.find(Member.class, 101L);
System.out.println("result = " + (findMember1 == findMember2));
// ๊ฒฐ๊ณผ๋ true๊ฐ ๋์จ๋ค.
//์์ ์ํฐํฐ์ ๋์ผ์ฑ์ ๋ณด์ฅํด์ฃผ๊ธฐ ๋๋ฌธ์ด๋ค.
tx.commit();
}
catch(Exception e){
tx.rollback();
}
finally{
em.close();
}
emf.close();
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("hello"); //persistence.xml ์์ ์ ํด๋์ "hello"
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try{
Member member1 = new Member(150L, "A");
Member member2 = new Member(151L, "B");
em.persist(member1);
em.persist(member2);
//์ด ์๊ฐ์ DB์ ์ ์ฅ๋๋ ๊ฒ์ด ์๋๋ผ ์์์ฑ ์ปจํ
์คํธ์ ์ฐจ๊ณก์ฐจ๊ณก ์ํฐํฐ์ ์ฟผ๋ฆฌ๊ฐ ์์ธ๋ค.
tx.commit(); //์ฌ๊ธฐ ์ปค๋ฐ์ ํ๋ ์์ ์ ์ง์ง DB์ ์ฟผ๋ฆฌ๊ฐ ๋ ๋ผ๊ฐ
System.out.println("===================");
//๋ฒํผ๋ง์ด๋ผ๋ ๊ธฐ๋ฅ์ด ์๋ค. ์์ฒ๋ผ ์ฟผ๋ฆฌ๋ฅผ DB์ ๋ ๋ฆฌ๋ฉด ์ต์ ํ์ ์ฌ์ง๊ฐ ์๋ค.
//member1,2 ๊ฐ ์์๋๋ฐ ์ด๊ฒ๋ค์ DB์ ํ ๋ฒ์ ๋ณด๋ผ ์ ์๋ค.
//hibernate์ batch_size๊ฐ ์๋๋ฐ ์ด๊ฒ์ size ๋งํผ ๋ชจ์์ DB์ ํ ๋ฐฉ์ ๋คํธ์ํฌ๋ก ์ฟผ๋ฆฌ 2๊ฐ๋ฅผ ๋ณด๋ด๊ณ DB๋ฅผ commitํ๋ค.
}catch(Exception e){
tx.rollback();
}finally{
em.close();
}
emf.close();
์ค๋ ์ท: ํฅํ ๋ณ๊ฒฝ ๊ฐ์ง๋ฅผ ์ํด์ ์๋ณธ์ ๋ณต์ฌํด์ ๋ง๋ค์ด๋๋ ๊ฐ์ฒด๊ฐ ๋ฐ๋ก ์ค๋ ์ท์ด๋ค. ๋ณ๊ฒฝ ๊ฐ์ง๊ฐ ์ผ์ด๋ฌ์ ๋ 1์ฐจ์บ์์ ์๋ ์๋ณธ ๊ฐ์ฒด๊ฐ ์ค๊ฐ์ ๋ณ๊ฒฝ๋์๋์ง ์ด ์ค๋ ์ท์ผ๋ก ํ์ธํ๊ฒ ๋๋ค.
try{
//์ํฐํฐ ์์
/*Member member = em.find(Member.class, 150);
member.setName("ZZZZZ");
em.persist(member); ์ฐ์ง ์๋๋ก ํ๋ค.
์ปฌ๋ ์
์ ๋ฃ์ ๊ฒ์ฒ๋ผ ๊ฐ์ ๊บผ๋ด๊ณ ๋ณ๊ฒฝํ๋๋ฐ ๋ค์ ์ปฌ๋ ์
์ ๋ฃ์ ํ์๊ฐ ์๋ค.*/
Member member = new Member(200L, "member200");
em.persist(member);
//๋ฉค๋ฒ๋ฅผ ์ ์ฅํ๋๋ฐ ์ปค๋ฐ๋๊ธฐ ์ ๊น์ง๋ ์ฟผ๋ฆฌ๋ฅผ ๋ณผ ์๊ฐ ์๋ค.
//๋ฏธ๋ฆฌ DB์ ๋ฐ์ํ๊ณ ์ถ๋ค๊ฑฐ๋ ์ฟผ๋ฆฌ๋ฅผ ๋ฏธ๋ฆฌ ๋ณด๊ณ ์ถ๋ค๋ฉด
em.flush(); //๋ฅผ ๊ฐ์ ๋ก ํธ์ถํ๋ค. flush ํด๋ 1์ฐจ ์บ์๋ ์ง์์ง์ง ์์
System.out.println("==============");
//๊ทธ๋ฌ๋ฉด update ์ปค๋ฆฌ๋ ๋์จ๋ค.
}
catch(Exception e){
tx.rollback();
}
finally{
em.close();
}
emf.close();
โ ์ํฐํฐ๋ฅผ ์๊ตฌ ์ ์ฅํ๋ ํ๊ฒฝ์ด๋ค.
โ ์ ํ๋ฆฌ์ผ์ด์
๊ณผ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฌ์ด์์ ๊ฐ์ฒด๋ฅผ ๋ณด๊ดํ๋ ๊ฐ์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ฐ์ ์ญํ ์ ํ๋ค.
โ ์ํฐํฐ ๋งค๋์ ๋ฅผ ํตํด ์ํฐํฐ๋ฅผ ์ ์ฅํ๊ฑฐ๋ ์กฐํํ๋ฉด ์ํฐํฐ ๋งค๋์ ๋ ์์์ฑ ์ปจํ
์คํธ์ ์ํฐํฐ๋ฅผ ๋ณด๊ดํ๊ณ ๊ด๋ฆฌํ๋ค
์ฅ์ : 1์ฐจ ์บ์, ๋์ผ์ฑ ๋ณด์ฅ, ํธ๋์ญ์ ์ ์ง์ํ๋ ์ฐ๊ธฐ ์ง์ฐ, ๋ณ๊ฒฝ ๊ฐ์ง, ์ง์ฐ ๋ก๋ฉ
1์ฐจ ์บ์
๋์ผํ ํธ๋์ญ์
๋ด์์ ํ๋ฒ ์กฐํํ๋๊ฑฐ ๋ ์กฐํํ๋ฉด DB์ ์ฟผ๋ฆฌ ๋ ๋ผ๊ฐ์ง ์์
flush() : ์์์ฑ ์ปจํ
์คํธ์ ๋ณ๊ฒฝ๋ด์ฉ์ DB์ ๋ฐ์
flush ๋ฐ์ํ๋ฉด ๋ณ๊ฒฝ์ ๊ฐ์งํ๊ณ ์์ ๋ ์ํฐํฐ ์ฐ๊ธฐ ์ง์ฐ SQL ์ ์ฅ์์ ๋ฑ๋กํ๊ณ ์ฐ๊ธฐ ์ง์ฐ SQL ์ ์ฅ์์ ์ฟผ๋ฆฌ๋ฅผ DB์ ์ ์กํ๋ค. (SQL ์ ์ฅ์์๋ ๋ฑ๋ก, ์์ , ์ญ์ ์ฟผ๋ฆฌ๊ฐ ๋ด๊ฒจ์๋ค.)
ํ๋ฌ์๋ ์์์ฑ ์ปจํ
์คํธ๋ฅผ ๋น์ฐ๋ ๊ฒ์ด ์๋๋ค.
์ปจํ
์คํธ์ ๋ณ๊ฒฝ ๋ด์ฉ์ DB์ ๋๊ธฐํํ๋ค๊ณ ์ดํด.
ํธ๋์ญ์
์ด๋ผ๋ ์์
๋จ์๊ฐ ์ค์ -> ์ปค๋ฐ ์ง์ ์๋ง ๋๊ธฐํ
์ด๋ป๊ฒ๋ ์ปค๋ฐ ์ง์ ์๋ง ๋ณ๊ฒฝ ๋ด์ฉ์ DB์ ๋ ๋ ค์ฃผ๋ฉด ๋๋ค.
em.persist(member)
์ด๊ฑฐ ํ ๋ ์ค์ ๋ก DB์ ์ฟผ๋ฆฌ๊ฐ ๋ ๋ผ๊ฐ์ง๋ ์๋๋ค.
์ฆ ์์์ฑ ์ํ๊ฐ ๋๋ค๊ณ ํด์ ๋ฐ๋ก DB์ ์ฟผ๋ฆฌ๊ฐ ๋ ๋ผ๊ฐ๋ ๊ฒ์ ์๋๋ค. ํธ๋์ญ์
์ ์ปค๋ฐํ๋ ์์ ์ ์์์ฑ ์ปจํ
์คํธ์ ์๋ ์ฟผ๋ฆฌ๊ฐ DB๋ก ๋ ๋ผ๊ฐ๋ค.
em.find() ๋ก member2๋ฅผ ์กฐํํ๋๋ฐ 1์ฐจ ์บ์์ ์๋ค๋ฉด DB์์ ๊ฐ์ ธ์์ 1์ฐจ ์บ์์ ์ฌ๋ฆฐ๋ค. ์ด ๋ ์ฌ๋ผ๊ฐ ์ํ๊ฐ ์์ ์ํ์ด๋ค.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello"); //persistence.xml ์์ ์ ํด๋์ "hello"
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try{
Member member = em.find(Member.class, 150L);
member.setName("AAAA");
//์กฐํํ๋๋ 150L์ด ์์ผ๋ฏ๋ก ์์์ฑ ์ปจํ
์คํธ์ 150L์ ์ฌ๋ฆฐ๋ค. ๊ทธ๋ฆฌ๊ณ ๊ฐ์ "AAAA"๋ก ๋ณ๊ฒฝ
//๋ ์ด์ ์์์ฑ ๊ด๋ฆฌ๋ฅผ ํ๊ธฐ ์ซ๋ค๋ฉด
em.detach(member); //๋ถ๋ฆฌ -> JPA์์ ๋ ์ด์ ๊ด๋ฆฌ๋ฅผ ํ์ง ์์
//๊ทธ๋ฌ๋ฉด ํธ๋์ญ์
commitํ ๋ ์๋ฌด ์ผ๋ ์ผ์ด๋์ง ์์
}
catch(Exception e){
tx.rollback();
}
finally{
em.close();
}
emf.close();
em.detach(entity)
ํน์ ์ํฐํฐ๋ง ์ค์์ ์ํ๋ก ์ ํ
em.clear()
์์์ฑ ์ปจํ
์คํธ๋ฅผ ํต์งธ๋ก ์ง์๋ฒ๋ฆผ (์ด๊ธฐํ)
em.close()
์์์ฑ ์ปจํ
์คํธ๋ฅผ ์ข
๋ฃ
FlushModeType.AUTO
์ปค๋ฐ์ด๋ ์ฟผ๋ฆฌ๋ฅผ ์คํํ ๋ ํ๋ฌ์ํ๋ค.
(AUTO ๋ง ์ฌ์ฉํ๋ค๊ณ ์ดํด)
๋ฐ์ดํฐ๋ฅผ ์์ ํด์ Transaction์์ ์ปค๋ฐํ๋ฉด JPA๊ฐ ๋ณ๊ฒฝ๋ถ์ ๋ํด์ ์ฐพ์์ ์๋์ผ๋ก update ์ฟผ๋ฆฌ๋ฅผ ์์ฑํด์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ฐ์ํ๋ค.
JPA์ ์ํฐํฐ๋ฅผ ๋ฐ๊ฟ ์ ์๋ค.
์์์ฑ ์ปจํ ์คํธ๊ฐ ๋๋ ๊ด๋ฆฌํ์ง ์๋ ์ํฐํฐ
@PostMapping("items/{itemId}/edit")
//ํ๋ผ๋ฏธํฐ๋ก ๋์ด์จ ์ค์์ ์ํ์ ์ํฐํฐ.
public String updateItem(@PathVariable Long itemId, @ModelAttribute("form") BookForm form) {
Book book = new Book();
book.setId(form.getId()); //form์ book์ผ๋ก ๋ฐ๊ฟ
book.setName(form.getName());
book.setPrice(form.getPrice());
book.setStockQuantity(form.getStockQuantity());
book.setAuthor(form.getAuthor());
book.setIsbn(form.getIsbn());
//์ด์คํ๊ฒ ์ํฐํฐ๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ์ ์ฐ๊ณ ์ ํํ๊ฒ ํ์ํ ๋ฐ์ดํฐ๋ง ๋ฐ์
itemService.saveItem(book);
return "redirect:items";
}
์ฌ๊ธฐ์ ๊ฐ์ฒด๋ ์๋ก์ด ๊ฐ์ฒด์ง๋ง Id๊ฐ ์ธํ ๋์ด์์ผ๋ฏ๋ก ๋ญ๊ฐ JPA์ ๋ค์ด๊ฐ๋ค ๋์จ ๊ฒ์ด๋ผ๋ ์๋ฏธ์ด๋ค. ๊ทธ๋์ DB์ ์ ํํ ๊ฐ๋ค ์จ ์ํ(ํ ๋ฒ ์ ์ฅ๋ ์ํ)๋ก ์๋ณ์๊ฐ DB์ ์์ผ๋ฉด ์ค์์ ์ํ์ ๊ฐ์ฒด๋ผ ํ๋ค.
์์์ฑ ์ํฐํฐ๋ JPA๊ฐ ๊ด๋ฆฌํด์ ๋ณ๊ฒฝ๊ฐ์ง๊ฐ ์ผ์ด๋๋ค. ์ค์์ ์ํ์ ์ํฐํฐ๋ JPA๊ฐ ๊ด๋ฆฌํ์ง ์๋๋ค.
๐ง ๊ทธ๋์ ์ค์์ ์ํ ์ํฐํฐ์ง๋ง ๋ณ๊ฒฝ ๊ฐ์ง ๊ธฐ๋ฅ์ ์ฌ์ฉํ๊ฑฐ๋ ๋ณํฉ(merge)์ ์ฌ์ฉํ ์ ์๋ค.
//์ค์์์ฑ ์ํ Book์ ๋ณ๊ฒฝ ๊ฐ์ง
@Transactional
public void updateItem(Long itemId, Book param){
//itemId๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ค์ DB์ ์๋ ์์์ฑ ์ํฐํฐ๋ฅผ ์ฐพ์์ด
Item findItem = itemRepository.findOne(itemId);
findItem.setPrice(param.getPrice());
findItem.setName(param.getName());
findItem.setStockQuantity(param.getStockQuantity());
//save ํธ์ถ๋ ํ์์์. persist ํธ์ถ๋ ํ์์์
// ์ด ๋ผ์ธ์ด ๋๋๋ฉด ์คํ๋ง์ ํธ๋์ญ์
์ ์ํด์ ํธ๋์ญ์
์ด ์ปค๋ฐ๋๋ค.
}
findItem์ ์์ ์ํ์ด๋ค.
๊ฐ์ ์ธํ
ํ ๋ค์์๋ ์คํ๋ง์ @Transactional์ ์ํด ์ปค๋ฐ๋๋ค.
์ปค๋ฐ๋๋ฉด JPA๋ flush๋ฅผ ํ๋ค. ์์์ฑ ์ปจํ
์คํธ์ ์๋ ์ํฐํฐ ์ค์์ ๋ณ๊ฒฝ๋ ๊ฒ๋ค์ ๋ค ์ฐพ๋๋ค.
๋ฐ๋์๋ค๋ฉด ๋ฐ๋ ๊ฒ์ ๋ํด์ update ์ฟผ๋ฆฌ๋ฅผ DB์ ๋ ๋ ค์ updateํ๋ค.
์ค์์ ์ํ์ ์ํฐํธ๋ฅผ ์์ ์ํ๋ก ๋ณ๊ฒฝํ ๋ ์ฌ์ฉํ๋ ๊ธฐ๋ฅ์ด๋ค.
findItemId
๋ก ์ฐพ์ Id๋ก DB๋ฅผ ๋ค์ ธ์ Item์ ์ฐพ๊ณ ํ๋ผ๋ฏธํฐitem
๊ฐ์ผ๋ก ์ฐพ์์จ ๊ฐ๋ค์ ์ ๋ถ ๋ฐ๊ฟ์น๊ธฐ ํ๋ค.public void save(Item item) {
if (item.getId() == null) { //item์ JPA์ ๋ฐ์ดํฐ ์ ์ฅ ์ ๊น์ง Id๊ฐ ์๋ค.
em.persist(item);
}//๊ทธ๋์ JPA๊ฐ ์ ๊ณตํ๋ persist๋ฅผ ์ฌ์ฉ
else{ //์ด๋ฏธ DB์ ๋ฑ๋ก๋ผ์ ๊ฐ์ ธ์จ ๊ฒ
em.merge(item);
}
}
// Merge
@Transactional
public Item updateItem(Long itemId, Book param){
//itemId๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ค์ DB์ ์๋ ์์์ฑ ์ํฐํฐ๋ฅผ ์ฐพ์์ด
Item findItem = itemRepository.findOne(itemId);
findItem.setPrice(param.getPrice());
findItem.setName(param.getName());
findItem.setStockQuantity(param.getStockQuantity());
return findItem;
}
๋ณ๊ฒฝ ๊ฐ์ง ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ฉด ์ํ๋ ์์ฑ๋ง ์ ํํด์ ๋ณ๊ฒฝํ ์ ์์ง๋ง, ๋ณํฉ์ ์ฌ์ฉํ๋ฉด ๋ชจ๋ ์์ฑ์ด ๋ณ๊ฒฝ๋๋ค . ๋ณํฉ ์ ๊ฐ์ด ์์ผ๋ฉด null๋ก ์ ๋ฐ์ดํธ ํ ์ํ๋ ์๋ค. (๋ณํฉ์ ๋ชจ๋ ํ๋๋ฅผ ๊ต์ฒดํ๊ธฐ ๋๋ฌธ.)
ex) ๊ฐ๊ฒฉ์ด ํ๋ฒ ์ฑ
์ ๋๋ฉด ๋ฐ๊ฟ ์ ์๋ค๊ณ ๊ฐ์ ํ๋ฉด book.setPrice(form.getPrice)
์์ด update๋ฅผ ํ๋๋ฐ ์ด๋๋ก Merge๋ฅผ ํธ์ถํด๋ฒ๋ฆฌ๋ฉด DB์ Price๋ NULL๋ก ์
๋ฐ์ดํธ ๋๋ฒ๋ฆฐ๋ค. (๋งค์ฐ ์ํ.)
โ ์ปจํธ๋กค๋ฌ์์ ์ด์คํ๊ฒ ์ํฐํฐ ์์ฑ ๊ธ์ง
โ ํธ๋์ญ์
์ด ์๋ ์๋น์ค ๊ณ์ธต์ ์๋ณ์์ ๋ณ๊ฒฝํ ๋ฐ์ดํฐ๋ฅผ ๋ช
ํํ๊ฒ ์ ๋ฌ. ํ๋ผ๋ฏธํฐ๋ Dto๋ก
โ ํธ๋์ญ์
์ด ์๋ ์๋น์ค ๊ณ์ธต์์ ์์ ์ํ์ ์ํฐํฐ๋ฅผ ์กฐํํ๊ณ , ์ํฐํฐ์ ๋ฐ์ดํฐ๋ฅผ ์ง์ ๋ณ๊ฒฝ
โ ํธ๋์ญ์
์ปค๋ฐ ์์ ์ ๋ณ๊ฒฝ ๊ฐ์ง๊ฐ ์คํ๋๋ค.
ํ๋ผ๋ฏธํฐ๋ก ๋ณ๊ฒฝํ ๋ฐ์ดํฐ ๋ช
ํํ๊ฒ ์ ๋ฌ
ItemService.class
@Transactional
public void updateItem(Long itemId, String name, int price, int stockQuantity){
//itemId๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ค์ DB์ ์๋ ์์์ฑ ์ํฐํฐ๋ฅผ ์ฐพ์์ด
Item findItem = itemRepository.findOne(itemId);
findItem.setName(name);
findItem.setPrice(price);
findItem.setStockQuantity(stockQuantity);
}
ItemController.class
@PostMapping("item/{itemId}/edit")
public String updateItem(@PathVariable Long itemId, @ModelAttribute("form") BookForm form) {
//itemService์์ ํ๋ผ๋ฏธํฐ๋ก ํ์ํ ๊ฐ๋ค๋ง์ ๊ฐ์ ธ์ด
itemService.updateItem(itemId, form.getName(), form.getPrice(), form.getStockQuantity());
//itemService.saveItem(book);
return "redirect:items";
}
Dto๋ก ๋ณ๊ฒฝํ ๋ฐ์ดํฐ ๋ช ํํ๊ฒ ์ ๋ฌ
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class UpdateItemDto {
private String Name;
private int price;
private int stockQuantity;
}
ItemService.class
@Transactional
public void updateItem2(Long itemId, UpdateItemDto itemDto){
//itemId๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ค์ DB์ ์๋ ์์์ฑ ์ํฐํฐ๋ฅผ ์ฐพ์์ด
Item findItem = itemRepository.findOne(itemId);
findItem.setName(itemDto.getName());
findItem.setPrice(itemDto.getPrice());
findItem.setStockQuantity(itemDto.getStockQuantity());
}
addStock์ด๋ change์ฒ๋ผ ์๋ฏธ์๋ ๋ฉ์๋๋ฅผ ๋ง๋ค์ด์ผ์ง set์ ๋ง ๊น๋ฉด ์ข์ง ์๋ค. ๋ณ๊ฒฝ ์ง์ ์ด ์ํฐํฐ๋ก ๋ค ๊ฐ๊ธฐ ๋๋ฌธ์ set์ผ๋ก ํ๋ฉด ์ถ์ ์ด ์ด๋ ต๋ค.
@Transactional
public void updateItem2(Long itemId, UpdateItemDto itemDto){
//itemId๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ค์ DB์ ์๋ ์์์ฑ ์ํฐํฐ๋ฅผ ์ฐพ์์ด
Item findItem = itemRepository.findOne(itemId);
//findItem.setName(itemDto.getName());
//findItem.setPrice(itemDto.getPrice());
// ์ด๋ฐ์์ผ๋ก ํด์ผ ์ด๋์ ๋ฐ๋๋์ง ์ถ์ ์ด ์ฌ์
findItem.change(price, name, stockQuantity);
}
addStock์ ๊ฒฝ์ฐ Item ์ํฐํฐ์์ ๊ด๋ฆฌํ๋ stockQuantity๋ฅผ ๋ณ๊ฒฝํ๋ ๋น์ฆ๋์ค๋ก์ง์ ์ํฐํฐ์์ ์ฒ๋ฆฌํ์.
์๋น์ค ๋จ์์ ์ฒ๋ฆฌํ ์ ์์ง๋ง, ์ํฐํฐ์์ ์ฒ๋ฆฌํ๋ ๊ฒ์ด ์์ง๋๊ฐ ๋์ ์ค๊ณ๋ก ๋ณผ ์ ์๋ค.
ItemController.class
//ํ๋ผ๋ฏธํฐ๊ฐ ์๋ Dto๋ก ๋ณ๊ฒฝํ ๋ฐ์ดํฐ๋ฅผ ๋ช
ํํ๊ฒ ์ ๋ฌ.
@PostMapping("item/{itemId}/edit")
public String updateItem2(@PathVariable Long itemId, UpdateItemDto itemDto) {
itemService.updateItem2(itemId, itemDto);
return "redirect:items";
}
create : ๊ธฐ์กด ํ ์ด๋ธ ์ญ์ ํ ๋ค์ ์์ฑ (DROP + CREATE)
create-drop : create์ ๊ฐ์ผ๋ ์ข ๋ฃ์์ ์ ํ ์ด๋ธ DROP
update : ๋ณ๊ฒฝ๋ถ๋ง ๋ฐ์ (์ด์ DB์๋ ์ฌ์ฉ ์๋) ๊ทธ๋ฆฌ๊ณ ์ง์ฐ๋ ๊ฑด ์๋๊ณ ์ถ๊ฐ๋ง ๊ฐ๋ฅ
validate : ์ํฐํฐ์ ํ ์ด๋ธ์ด ์ ์ ๋งคํ๋์๋์ง๋ง ํ์ธ
none : ์ฌ์ฉํ์ง ์์
์ฃผ์์
1) ์ด์ ์ฅ๋น์๋ ์ ๋ create, create-drop, update ์ฌ์ฉ ๋ถ๊ฐ
2) ๊ฐ๋ฐ ์ด๊ธฐ ๋จ๊ณ๋ create or update
3) ํ
์คํธ ์๋ฒ๋ update or validate
4) ์คํ
์ด์ง๊ณผ ์ด์ ์๋ฒ๋ validate or none
ํ ์คํธ ์๋ฒ, ์ด์ ์๋ฒ๋ ๊ฐ๊ธ์ ์ฐ์ง ๋ง๊ธฐ (์ฐ๊ฒ ๋๋ฉด ์กฐ์ฌํด์)
๊ธฐ๋ณธํค ๋งคํ ์ ์ดํด๊ฐ ๊ฐ์ง ์๋๋ค.
๋์ค์ ๋ค์ ์จ๋ค.
em.persist ํ๊ณ ๋๋ฉด ํญ์ id์ ๊ฐ์ด ๋ค์ด๊ฐ, ์์์ฑ ์ํ๊ฐ ๋๋ฉด ๋ฌด์กฐ๊ฑด PK๊ฐ ์ธํ ๋๊ณ ์์์ฑ์ด ๋๋ค.
public class Team {
@Id
@GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
//@Column(name = "TEAM_ID")
//private Long teamId;
@ManyToOne//์ผ๋๋ค ๋ผ๋ ๊ด๊ณ๋ก Member ์
์ฅ์์ Many, Team ์
์ฅ์์ One์ ์ค์ผ ํ๋ค.
@JoinColumn(name = "TEAM_ID") //team ์ด๋ Member ํ
์ด๋ธ์ FK์ ๋งคํ์ ํด์ผํ๋ค.
private Team team; //์๋ฌ ์๊ธฐ๋ ์ด์ ๋ JPA ์๊ฒ ๋์ ๊ด๊ณ๊ฐ ์ผ๋๋ค ์ธ์ง ๋ค๋๋ค์ธ์ง ์ด๋ฐ๊ฑธ ์๋ ค์ค์ผ ํ๋ค.
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Team getTeam() {
return team;
}
public void setTeam(Team team) {
this.team = team;
}
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello"); //persistence.xml ์์ ์ ํด๋์ "hello"
//EntityManagerFactory๋ฅผ ๋ง๋๋ ์๊ฐ ๋ฐ์ดํฐ๋ฒ ์ด์์ ์ฐ๊ฒฐ๋ ๋ค ๋๊ณ ์ฌ๋งํ๊ฒ ๋ค ๋จ
EntityManager em = emf.createEntityManager();
//JPA ์์๋ ๊ผญ ํธ๋์ญ์
์ด๋ผ๋ ๋จ์๊ฐ ๋งค์ฐ ์ค์ํ๋ค.
//๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝํ๋ ๋ชจ๋ ์์
์ ๊ผญ ํธ๋์ญ์
์์์ ์์
์ ํด์ผํ๋ค.
EntityTransaction tx = em.getTransaction();
tx.begin();
try{
//์ ์ฅํ๋ ์ฝ๋
Team team = new Team();
team.setName("Team A");
em.persist(team);
Member member = new Member();
member.setUsername("member1");
member.setTeam(team);
em.persist(member);
//์์์ฑ ์ปจํ
์คํธ๊ฐ ์๋ DB์์ ๊ฐ์ ธ์จ ์ฟผ๋ฆฌ๋ฅผ ๋ณด๊ณ ์ถ๋ค๋ฉด
em.flush();//flush ํด์ ์์์ฑ ์ปจํ
์คํธ์ ์๋ ๊ฑธ DB๋ก ์ฟผ๋ฆฌ ๋ ๋ ค๋ฒ๋ ค์ ์ฑํฌ ๋ง์ถ๊ณ
em.clear();//์ด๊ฑธ๋ก ์์์ฑ์ ์ด๊ธฐํ ์์ผ๋ฒ๋ฆฐ๋ค.
//์กฐํ์ผ ๋
Member findMember = em.find(Member.class, member.getId());
Team findTeam = findMember.getTeam();
System.out.println("findTeam = " + findTeam.getName());
tx.commit();
}
catch(Exception e){
tx.rollback(); //๋ฌธ์ ๊ฐ ์๊ธฐ๋ฉด ๋กค๋ฐฑ
}
finally{
em.close();
//entityManager๊ฐ ๊ฒฐ๊ตญ ๋ฐ์ดํฐ ์ปค๋ฅ์
์ ๋ฌผ๊ณ ๋์ํ๊ธฐ ๋๋ฌธ์ ๊ผญ ๋ซ๋๋ค.
}
emf.close();
getTeamId() ์ด๋ฐ ์์ผ๋ก ๊ฐ์ ธ์ค๋ ๊ฒ์ด ์๋๋ผ getTeam() ์ผ๋ก ๊ฐ์ ธ์์ ์ข ๋ ๊ฐ์ฒด ์งํฅ์ ์ผ๋ก ๊ฐ์ ธ์ฌ ์ ์๋ค.
์ข ๋ ์ดํด ํ์
ํ ์ด๋ฆ์ ๋ฐ๊พธ๊ณ ์ถ๋ค๋ฉด DB์ 100๋ฒ ์ด๋ผ๋ ํ์ด ์๋ค๋ ๊ฐ์ ํ์
Team newTeam = em.find(Team.class, 100L);
findMember.setTeam(newTeam);
์ด๋ ๊ฒ๋ง ํด์ฃผ๋ฉด ๋๋ค.
setTeam ํ๋ฉด DB์ ์ธ๋ํค ๊ฐ์ด ์ ๋ฐ์ดํธ ๋๋ค.
@OneToMany(mappedBy = "team") //mappedBy๋ ์ง๊ธ ์ผ๋๋ค ๋งคํ์์ team์ด๋ ์ฐ๊ฒฐ์ด ๋์ด์๋ค๊ณ ์๋ ค์ฃผ๋ ๊ฒ์ด๋ค.
private List<Member> members = new ArrayList<>();
try{
Team team = new Team();
team.setName("Team A");
em.persist(team);
Member member = new Member();
member.setUsername("member1");
member.setTeam(team);
em.persist(member);
//์์์ฑ ์ปจํ
์คํธ๊ฐ ์๋ DB์์ ๊ฐ์ ธ์จ ์ฟผ๋ฆฌ๋ฅผ ๋ณด๊ณ ์ถ๋ค๋ฉด
em.flush();//flush ํด์ ์์์ฑ ์ปจํ
์คํธ์ ์๋ ๊ฑธ DB๋ก ์ฟผ๋ฆฌ ๋ ๋ ค๋ฒ๋ ค์ ์ฑํฌ ๋ง์ถ๊ณ
em.clear();//์ด๊ฑธ๋ก ์์์ฑ์ ์ด๊ธฐํ ์์ผ๋ฒ๋ฆฐ๋ค.
//์กฐํ์ผ ๋
Member findMember = em.find(Member.class, member.getId());
List<Member> members = findMember.getTeam().getMembers();
for (Member m : members) {
System.out.println("m = " + m.getUsername());
}
tx.commit();
}
catch(Exception e){
tx.rollback(); //๋ฌธ์ ๊ฐ ์๊ธฐ๋ฉด ๋กค๋ฐฑ
}
finally{
em.close();
//entityManager๊ฐ ๊ฒฐ๊ตญ ๋ฐ์ดํฐ ์ปค๋ฅ์
์ ๋ฌผ๊ณ ๋์ํ๊ธฐ ๋๋ฌธ์ ๊ผญ ๋ซ๋๋ค.
}
emf.close();
mappedBy๋ ์ง๊ธ ์ผ๋๋ค ๋งคํ์์ team์ด๋ ์ฐ๊ฒฐ์ด ๋์ด์๋ค๊ณ ์๋ ค์ฃผ๋ ๊ฒ์ด๋ค.
์ฌ๊ธฐ์ JoinColumn() ํด์ ํ๋ ๋ฐฉ๋ฒ์ด๋ ๋ญ๊ฐ ์ฐจ์ด์ธ์ง ์๊ธฐ๊ฐ ์ด๋ ต๋ค.
์๋ฐฉํฅ ๊ด๊ณ๋ถํฐ ์์๋ณธ๋ค.
๊ฐ์ฒด ์ฐ๊ด๊ด๊ณ = 2๊ฐ๋ก ํ์ -> ํ ์ฐ๊ด๊ด๊ณ , ํ -> ํ์ ์ฐ๊ด๊ด๊ณ
ํ
์ด๋ธ ์ฐ๊ด๊ด๊ณ = 1๊ฐ๋ก ํ์ <-> ํ ์ฐ๊ด๊ด๊ณ
๊ทธ๋ฐ๋ฐ ์๋ฐฉํฅ ๊ด๊ณ๋ผ๋ ๊ฒ๋ณด๋ค ๋จ๋ฐฉํฅ ๊ด๊ณ 2๊ฐ๊ฐ ์๋ค๊ณ ์ดํดํ์.
๊ทธ๋์ ๊ฐ์ฒด๋ฅผ ์๋ฐฉํฅ์ผ๋ก ์ฐธ์กฐํ๋ ค๋ฉด ๋จ๋ฐฉํฅ ์ฐ๊ด๊ด๊ณ๋ฅผ 2๊ฐ ๋ง๋ค์ด์ผ ํ๋ค.
ํ ์ด๋ธ์ ์ธ๋ํค ํ๋๋ก ๋ ํ ์ด๋ธ์ ์ฐ๊ด๊ด๊ณ๋ฅผ ๊ด๋ฆฌํ๋ค.
Member์ team ์ ์
๋ฐ์ดํธ ํ ์ง Team์ List members๋ฅผ ์
๋ฐ์ดํธ ํ ์ง ๊ฒฐ์ ์ ํด์ ์ธ๋ํค๋ฅผ ์
๋ฐ์ดํธํด์ผ ํ๋ค. (๋ ์ค ํ๋๋ก ์ธ๋ํค๋ฅผ ๊ด๋ฆฌํด์ผ ํ๋ค.)
๊ฐ์ฒด์ ๋ ๊ด๊ณ ์ค ํ๋๋ฅผ ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ์ผ๋ก ์ง์ ํ๋ค.
์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ๋ง์ด ์ธ๋ํค๋ฅผ ๊ด๋ฆฌํ๋ค.(๋ฑ๋ก, ์์ )
์ฃผ์ธ์ด ์๋ ์ชฝ์ ์ฝ๊ธฐ๋ง ๊ฐ๋ฅํ๋ค.
์ฃผ์ธ์ mappedBy ์์ฑ ์ฌ์ฉ ๋ถ๊ฐ
์ฃผ์ธ์ด ์๋๋ฉด mappedBy ์์ฑ์ผ๋ก ์ฃผ์ธ ์ง์ .
@JoinColumn์ ์ฐ๋ ์ชฝ์ ํด๋์ค๊ฐ ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ์ด๋ค.
์ธ๋ํค๊ฐ ์๋ ๊ณณ์ ์ฃผ์ธ์ผ๋ก ์ ํด์ผ ํ๋ค.
์ธ๋ํค๊ฐ ์๋ ์ชฝ์ด ํญ์ n ์ด๊ณ ์๋ ์ชฝ์ด 1์ด๋ค. ๊ทธ๋์ n ์ชฝ์ด ์ฐ๊ด๊ด๊ณ ์ฃผ์ธ์ด๋ค.
(n ์ชฝ์ด @ManyToOne)
์ ์ฝ๋์์ ์ฐ๊ด๊ด๊ณ ์ฃผ์ธ์ Member ๊ฐ์ฒด์ team ์ด๋ค.
Team์ List<Member> members
๋ ๊ฐ์ฒด ๊ด์ ์์ ์๋ฐฉํฅ ์ฐ๊ด๊ด๊ณ๋ฅผ ๊ตฌํ ํ๊ธฐ ์ํด ์ถ๊ฐ๋ ๊ฒ์ด๋ค.
์ฃผ์ธ์๊ฒ ๋งคํ๋ members๋ ํน์ฑ์ ๋ฐ์ดํฐ๊ฐ ๋ฑ๋ก, ์์ , ์ญ์ ๋๋๋ผ๋ DB์ ๋ฐ์์ด ๋์ง๋ ์๋๋ค.
Member์ team์ ๋ณ๊ฒฝํ๋ฉด DB์ ๋ฐ์์ ๋๋ค.
๋น์ฐํ ํ์ด ๋ฐ๋์์ผ๋ฏ๋ก Team ๋ด members์์ ํน์ member๋ฅผ ์ง์์ฃผ์ด์ผ ํ์ง๋ง DB๊ด์ ์์๋ ๊ตณ์ด ์ง์์ฃผ์ง ์์๋ ๋๋ค.
์๋ํ๋ฉด ์ด๋ฏธ Member๊ฐ team์ ๋ณ๊ฒฝํ๊ธฐ๋ง ํด๋ Member ํ
์ด๋ธ์ team fk๊ฐ ๋ณ๊ฒฝ๋์๊ธฐ ๋๋ฌธ์ด๋ค.
Team์ members๋ ์ฐ๊ด๊ด๊ณ ์ฃผ์ธ์ด ์๋๊ธฐ ๋๋ฌธ์ members์์ ํน์ member๋ฅผ ์ง์๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์๋ ๋ฐ์๋์ง ์๋๋ค. ๊ฒฐ๋ก ์ ์ผ๋ก ๊ฐ์ฒด ๊ด์ ์์๋ ํ์ ๋ณ๊ฒฝํ ๋ฉค๋ฒ๋ฅผ ํ ๊ฐ์ฒด์ ๋ฉค๋ฒ ๋ชฉ๋ก์์ ์ง์์ฃผ๋ ๊ฒ ๋ง์ง๋ง ๋ณดํต ๋ณ๊ฒฝ๋ ๋ฐ์ดํฐ๋ฅผ ์ฆ์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๋ ๋๋ฌผ๊ธฐ์ ์ง์์ฃผ๋ ๋ก์ง์ ์ถ๊ฐํ์ง ์์๋ค๊ณ ๋ณด๋ฉด ๋๋ค.
์ฃผ์ -> ์๋ฐฉํฅ ์ฐ๊ด ์ธํ ์์๋ ์์ชฝ ๋ชจ๋ ๊ฐ์ ์ธํ ํด์ฃผ์ด์ผ ํ๋ค.
Team team = new Team();
team.setName("Team A");
//team.getMembers().add(member); ์ฃผ์ธ์ด ์๋ ๋ฑกํฅ (์ญ๋ฐฉํฅ)๋ง ์ฐ๊ด๊ด๊ณ ์ค์
em.persist(team);
Member member = new Member();
member.setUsername("member1");
member.setTeam(team);
em.persist(member);
team.getMembers().add(member); //์์ชฝ ๋ชจ๋ ๊ฐ์ ์ธํ
ํด์ค
Member findMember = em.find(Member.class, member.getId());
List<Member> members = findMember.getTeam().getMembers();
for (Member m : members) {
System.out.println("m = " + m.getUsername());
}
tx.commit();
Team team = new Team();
team.setName("Team A");
//team.getMembers().add(member); ์ฃผ์ธ์ด ์๋ ๋ฑกํฅ (์ญ๋ฐฉํฅ)๋ง ์ฐ๊ด๊ด๊ณ ์ค์
em.persist(team);
Member member = new Member();
member.setUsername("member1");
member.changeTeam(team);
em.persist(member);
Member findMember = em.find(Member.class, member.getId());
List<Member> members = findMember.getTeam().getMembers();
for (Member m : members) {
System.out.println("m = " + m.getUsername());
}
tx.commit();
Member
public void setTeam(Team team) {
this.team = team;
team.getMembers().add(this); //this๋ Member ๋ ์์
}
์ด๋ ๊ฒ๋ ๊ฐ๋ฅ
member.changeTeam(team) //์ ์ฃผ์์ฒ๋ฆฌํ๊ณ (Member.class ์์๋ changeTeam() ์ฃผ์์ฒ๋ฆฌ)
team.addMember(member); //member๋ฅผ ๊ธฐ์ค์ผ๋ก ํ์ ์งค์ง ํ์ ๊ธฐ์ค์ผ๋ก ๋ฉค๋ฒ๋ฅผ ๋ฃ์์ง ๊ฒฐ์
public void addMember(Member member) {
member.setTeam(this);
members.add(member);
}
//Team.class ์ ์ด๊ฑฐ ์ถ๊ฐ
์๋ฐฉํฅ ๋งคํ ์์๋ ๋ฌดํ ๋ฃจํ ์กฐ์ฌํ์.
ex) toString(), lombok, JSON ์์ฑ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
์๋ฐฉํฅ ๋งคํ์ ๋ฐ๋ ๋ฐฉํฅ์ผ๋ก ์กฐํ ๊ธฐ๋ฅ์ด ์ถ๊ฐ๋ ๊ฒ ๋ถ์ด๋ค.
๋ค์ค์ฑ, ๋จ๋ฐฉํฅ/์๋ฐฉํฅ, ์ฐ๊ด๊ด๊ณ ์ฃผ์ธ
์คํํด๋ณด๋ฉด Update ์ฟผ๋ฆฌ๊ฐ ์ถ๊ฐ๋ก ๋๊ฐ๋ค. ์ด์ ๋
setName("teamA") ๋ถ๋ถ์ TEAM ํ ์ด๋ธ์ ๊ทธ๋ฅ ๋ฃ์ผ๋ฉด ๋์ ๋ฐ๋ก ์์ ์ด ๋๋ค.
team.getMembers().add(member); ๋ถ๋ถ์์ team ์ํฐํฐ๋ฅผ ์ ์ฅํ๋๋ฐ MEMBER ํ ์ด๋ธ์ ์ธ๋ํค TEAM_ID๋ฅผ ์ด๋ป๊ฒ ํ ๋ฐฉ๋ฒ์ด ์์ด ์ ํ ์ด๋ธ์์ ๊ฐ์ UPDATE๋ฅผ ์น๋ ๊ฒ์ด๋ค. ๊ทธ๋์ ์ฟผ๋ฆฌ๊ฐ ํ๋ฒ ๋ ๋๊ฐ๋ค.
๊ทธ๋ฌ๋ฏ๋ก ์ผ๋๋ค ๊ด๊ณ๋ฅผ ๊ถ์ฅํ์ง ์๋๋ค. (๋ค๋์ผ ๊ถ์ฅ)
์ํฐํฐ๊ฐ ๊ด๋ฆฌํ๋ ์ธ๋ํค๊ฐ ๋ค๋ฅธ ํ ์ด๋ธ์ ์๊ณ ์ฐ๊ด๊ด๊ณ ๊ด๋ฆฌ๋ฅผ ์ํด ์ถ๊ฐ๋ก UPDATE SQL์ด ์คํ๋๋ค.
JPA์์ ์์์ด๋ผ๋ ๊ด๊ณ๋ฅผ ํด๊ฒฐํ ์ ์๋ค. (3๊ฐ์ง)
*** class Item์ ์ถ์ ํด๋์ค๋ก ๋ง๋ค์ด์ผ ํ๋ค.
JOINED ๋ฐฉ์์ ALBUM, MOVIE, BOOK ํ ์ด๋ธ์ด Item์ PK๋ฅผ ์ธ๋ํค๋ก ๊ฐ๋ ๋ฐฉ์์ด๋ค.
@DiscriminatorColumn
๋ถ๋ชจ ํด๋์ค์ ์ ์ธํ๊ณ ๋ถ๋ชจ ํด๋์ค์ ๊ตฌ๋ถ ์ปฌ๋ผ์ ์ง์ . ํ์ ํด๋์ค๋ฅผ ๊ตฌ๋ถํ๋ ์ฉ๋๋ก ์ฐ์ธ๋ค. DTYPE์ด ๋ฌด์์ธ์ง ์๋ ค์ค๋ค.
๋ช
ํํ๊ฒ ์ด๋ ํด๋์ค์ ์กฐ์ธํ๋์ง ์๊ธฐ ์ํด์ ๋ฃ์ด์ฃผ์.
@DiscriminatorValue("")
ํ์ ํด๋์ค์ ์ ์ธํ๋ค. ์ํฐํฐ ์ ์ฅ ์ ๊ตฌ๋ถ ์ปฌ๋ผ์ ์
๋ ฅํ ๊ฐ์ ์ง์ .
"A" ๋ผ ํ๋ฉด ๋ถ๋ชจ ํด๋์ค์ DTYPE์ด A ๋ก ์ ์ฅ๋๋ค.
๋ถ๋ชจ ํด๋์ค์ ์์ ํ
์ด๋ธ์ ์์ฑ๋ค์ ๋ชจ๋ ๋๋ ค๋ฐ์
ALBUM, MOVIE, BOOK ํ
์ด๋ธ์ ์์ฑ๋์ง ์์
๊ตณ์ด @DiscriminatorColumn ํ์ง ์์๋ DTYPE์ด ๋ค์ด๊ฐ์์
-> ํ๋์ ํ
์ด๋ธ์ ๋ค ๋ค์ด๊ฐ ์๊ธฐ ๋๋ฌธ์ DTYPE์ด ๋ญ์ง ์ ์๊ฐ ์๋ค. ๊ทธ๋์ ํ์๋ก, ์๋์ผ๋ก ์์ฑ์ด ๋๋ค.
JpaMain์์ Movie movie์ ์ํด์ ๋ฐ์ดํฐ ์์ฑํ๊ณ persist ํ๋ฉด ์ฟผ๋ฆฌ๊ฐ ํ ๋ฒ๋ง ๋๊ฐ๋ค.
์กฐํ๋ ์กฐ์ธํ ํ์์์ด ์ฟผ๋ฆฌ๊ฐ ์ฌํํ๊ฒ ๋๊ฐ๋ค.
Item ํ
์ด๋ธ์ด ์๋ค. -> ALBUM, MOVIE, BOOK ํ
์ด๋ธ๋ง ์๋ค.
๊ฑฐ์ ์ฐ์ง ์์....
์์๊ด๊ณ ๋งคํ
๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ์์ ๊ด๊ณX
์ํผํ์ ์๋ธํ์ ๊ด๊ณ๋ผ๋ ๋ชจ๋ธ๋ง ๊ธฐ๋ฒ์ด ๊ฐ์ฒด ์์๊ณผ ์ ์ฌ
์์๊ด๊ณ ๋งคํ : ๊ฐ์ฒด์ ์์๊ณผ ๊ตฌ์กฐ์ DB์ ์ํผํ์ ์๋ธํ์ ๊ด๊ณ๋ฅผ ๋งคํ
๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค
์ํผํ์ , ์๋ธํ์
@MappedSuperclass
๋ค๋ฅธ ํด๋์ค์ ์๋ ์์ฑ์ ๊ฐ์ด ์ฐ๊ณ ์ถ์ ๋ ์ฌ์ฉ.
์ฆ ๊ณตํต์ ์ผ๋ก ์ฌ์ฉํ ์์ฑ์ ์ฌ์ฉ.
์์๊ด๊ณ ๋งคํ์๋๊ณ ์ํฐํฐ๋ ์๋๊ณ ํ
์ด๋ธ๊ณผ ๋งคํํ์ง๋ ์๋๋ค.
์์๋ฐ๋ ์์ ํด๋์ค์ ๋งคํ ์ ๋ณด๋ง ์ ๊ณตํ๋ค.
์ง์ ์์ฑํด์ ์ฌ์ฉํ ์ผ์ด ์์ผ๋ฏ๋ก ์ถ์ ํด๋์ค ๊ถ์ฅ
Member findMember = em.getReference(Member.class, member.getId());
System.out.println("findMember = " + findMember.getClass());
๋ฐ์ดํฐ๋ฒ ์ด์ค ์กฐํ๋ฅผ ๋ฏธ๋ฃจ๋ ๊ฐ์ง(ํ๋ก์) ์ํฐํฐ ๊ฐ์ฒด๋ฅผ ์กฐํํ๋ค.
em.find()๋ ์ง์ง ๊ฐ์ฒด๋ฅผ ์กฐํ.
์ค์ ํด๋์ค๋ฅผ ์์ ๋ฐ์์ ๋ง๋ค์ด์ง๊ณ ์ค์ ํฌ๋์ค์ ๊ฒ๋ชจ์์ด ๊ฐ๋ค. (ํ
๋น์ด์์)
์ฌ์ฉํ๋ ์ ์ฅ์์๋ ์ง์ง ๊ฐ์ฒด์ธ์ง ํ๋ก์ ๊ฐ์ฒด์ธ์ง ๊ตฌ๋ถํ์ง ์๊ณ ์ฌ์ฉํ๋ฉด ๋๋ค.(์ด๋ก ์)
ํ๋ก์ ๊ฐ์ฒด๋ ์ค์ ๊ฐ์ฒด์ ์ฐธ์กฐ๋ฅผ ๋ณด๊ดํ๊ณ ์์ด ํ๋ก์ ๊ฐ์ฒด๋ฅผ ํธ์ถํ๋ฉด ์ค์ ๊ฐ์ฒด์ ๋ฉ์๋๋ฅผ ํธ์ถํ๊ฒ ๋๋ค.
member.getName()
ํธ์ถํ ๋ MemberProxy์ member target์ ๊ฐ์ด ์ฒ์์๋ ์๋ค. ๊ทธ๋ฌ๋ฉด JPA๊ฐ ์์์ฑ ์ปจํ
์คํธ์ ์ง์ง ๋ฉค๋ฒ ๊ฐ์ฒด๋ฅผ ๊ฐ์ ธ์ค๋ผ๋ ์์ฒญ์ ํ๋ค.
๊ทธ๋ฌ๋ฉด ์์์ฑ ์ปจํ
์คํธ๊ฐ DB๋ฅผ ์กฐํํด์ ์ค์ ์ํฐํฐ ๊ฐ์ฒด๋ฅผ ์์ฑํด์ Member์๊ฒ ์ค๋ค. ๊ทธ๋ฆฌ๊ณ MemberProxy์ target์ ์ฐ๊ฒฐ์์ผ์ค๋ค.
-> ๊ฒฐ๊ตญ ์์์ฑ ์ปจํ
์คํธ๋ฅผ ํตํด์ ์ด๊ธฐํ๋ฅผ ์์ฒญํ๋ ๊ฒ์ด๋ค. (DB๋ฅผ ํตํด์ ์ง์ง ๊ฐ์ ๊ฐ์ง๊ณ ์์ ์ง์ง ์ํฐํฐ๋ฅผ ๋ง๋ค์ด๋ด๋ ๊ณผ์ !! ์ด ์ด๊ธฐํ.)
ํ๋ก์ ๊ฐ์ฒด๋ ์ฒ์ ์ฌ์ฉํ ๋ ํ ๋ฒ๋ง ์ด๊ธฐํ๊ฐ ๋๋ค. -> ํ ๋ฒ ์ด๊ธฐํ๋๋ฉด ๊ทธ๊ฑธ ๊ณ์ ์ฌ์ฉํด์ผ ํจ.
ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์ด๊ธฐํํ ๋, ํ๋ก์ ๊ฐ์ฒด๊ฐ ์ค์ ์ํฐํฐ๋ก ๋ฐ๋๋ ๊ฒ์ ์๋๋ค. ์ด๊ธฐํ๋๋ฉด ํ๋ก์ ๊ฐ์ฒด๋ฅผ ํตํด์ ์ค์ ์ํฐํฐ์ ์ ๊ทผ ๊ฐ๋ฅํ ๊ฒ์ด๋ค. -> ๊ฐ์ฒด๊ฐ ๋ค๋ฅธ ๊ฒ์ผ๋ก ๊ต์ฒด๋๋ ๊ฒ์ด ์๋๊ณ Proxy ์ ์ง, ๋ด๋ถ์ target๋ง ๊ฐ์ด ์ฑ์์ง๋ ๊ฒ์ด๋ค.
ํ๋ก์ ๊ฐ์ฒด๋ ์๋ณธ ์ํฐํฐ๋ฅผ ์์๋ฐ๋๋ค. ๋ฐ๋ผ์ ํ์
์ฒดํฌ ์ ์ฃผ์ํด์ผ ํ๋ค. -> JPA ์์ ๋ด๊ฐ Proxy๋ฅผ ์ธ์ง ์ ์ธ์ง ๋ชจ๋ฅด๊ธฐ ๋๋ฌธ์, entity์ ๋ํด์ ๋น๊ตํ ๋๋ == ๋น๊ต ๋ง๊ณ instance of
๋ฅผ ์ฌ์ฉํ์.
Member member1 = new Member();
member1.setUsername("member1");
em.persist(member1);
em.flush();
em.clear();
Member m1 = em.find(Member.class, member1.getId());
System.out.println("m1.getClass() = " + m1.getClass()); //์ค์ ๊ฐ์ฒด
Member reference = em.getReference(Member.class, member1.getId());
System.out.println("reference = " + reference.getClass());
//์ถ๋ ฅ์ด ๋ ๊ฐ ๋ชจ๋ ๊ฐ๋ค๊ณ ๋์ด
//์ด๋ฏธ Member๋ฅผ ์์์ฑ ์ปจํ
์คํธ์ ์ฌ๋ ธ๋๋ฐ(1์ฐจ ์บ์์ ์์) ํ๋ก์๋ก ๊ฐ์ ธ์๋ด์ผ ์๋ฏธ๊ฐ ์์.
System.out.println("a == a : " + (m1 == reference));
์ด์ -> ํ ์์์ฑ ์ปจํ ์คํธ์์ ๊ฐ์ ธ์์ผ๋ฉด JPA๋ ํญ์ true๋ฅผ ๋ฐํํ๋ค.
Member refMember = em.getReference(Member.class, member1.getId());
System.out.println("ref = " + refMember.getClass()); //ํ๋ก์๋ก ๋์ด
Member findMember = em.find(Member.class, member1.getId());
System.out.println("find = " + findMember.getClass()); //์ด๋ฏธ ํ๋ก์๊ฐ ๋๋ฒ๋ ค์ findํด๋ ํ๋ก์๊ฐ ๋์จ๋ค.
System.out.println("ref == find : " + (refMember == findMember));
em.find() ํด๋ ํ๋ก์๊ฐ ๋์ฌ ์ ์๋ค.
์ค์ํ ๊ฒ์ ํ๋ก์๋ ์๋๋ ๋ฌธ์ ์๊ฒ ๊ฐ๋ฐํ๋ ๊ฒ
์ด๋ป๊ฒ๋ true๊ฐ ๋๋๋ก ๋ง์ถ๋ฉด ๋๋ค
์์์ฑ ์ปจํ ์คํธ์ ๋์์ ๋ฐ์ ์ ์๋ ์ค์์ ์ํ์ผ ๋, ํ๋ก์๋ฅผ ์ด๊ธฐํํ๋ฉด ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค. -> could not initialize proxy (No Session)
Member refMember = em.getReference(Member.class, member1.getId());
System.out.println("ref = " + refMember.getClass()); //ํ๋ก์๋ก ๋์ด
em.close(); //๊ทธ๋ฐ๋ฐ ์์์ฑ ์ปจํ
์คํธ๋ฅผ ๊บผ๋ฒ๋ฆฌ๋ฉด ๋๋ em.detach(refMember)๋ก ๋์ง์ด๋ผ ๊ฒฝ์ฐ
// em.clear() -> ์์์ฑ ์ปจํ
์คํธ ๊นจ๋ํ๊ฒ ์ง์.
refMember.getUsername(); //DB์ ์ฟผ๋ฆฌ ๋๊ฐ๋ฉด์ ํ๋ก์ ๊ฐ์ฒด๊ฐ ์ด๊ธฐํ ๋จ
//์ด๊ธฐํ ์์ฒญ์ ์์์ฑ ์ปจํ
์คํธ๋ฅผ ํตํด์ ์ผ์ด๋๋ค.
//close๋ก ์์์ฑ์ ๊ด๋ฆฌํ์ง ์๊ฒ ๋์ด could not initialize proxy ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
//์ฆ ์์์ฑ ์ปจํ
์คํธ๊ฐ ์๋ค๋ ์๋ฏธ(em.clear() ํด๋ ๊ฐ์)
๋์ด์ refMember๋ ์์์ฑ ์ปจํ
์คํธ์ ๋์์ ๋ชป ์ค.
ํธ๋์ญ์
์์ํ๊ณ ๋๋ ๋ ์์์ฑ ์ปจํ
์คํธ๋ ์์ํ๊ณ ๋๋๋๋ก ๋ง์ถ๋๋ฐ ํธ๋์ญ์
๋๋๊ณ ๋์ ํ๋ก์ ์กฐํํด ๋ณด๋ฉด no session ํ๊ณ ์๋ฌ๊ฐ ๋์จ๋ค. -> ์๊ฑฐ ๋ด์ฉ ๊ธฐ์ต!!!
ํ๋ก์ ์ธ์คํด์ค์ ์ด๊ธฐํ ์ฌ๋ถ ํ์ธ
getPersistenceUnitUtil().isLoaded(ObjectEntity)
Member refMemebr = em.getReference(Member.class, member1.getId());
System.out.println("refMmeber = " + refMember.getClass()); // Proxy
refMember.getUsername(); // ํ๋ก์ ์ด๊ธฐํ ํด์ค. -> ๊ฐ์ ์ด๊ธฐํ
System.out.println(emf.getPersistenceUnitUtil().isLoaded(ObjectEntity));
์ด๋ฌ๋ฉด true๊ฐ ์ถ๋ ฅ๋๋ค.
ํ๋ก์ ํด๋์ค ํ์ธ ๋ฐฉ๋ฒ
entity.getClass().getName()
์ถ๋ ฅ
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn
private Team team;
team์ ํ๋ก์ ๊ฐ์ฒด๋ก ์กฐํ -> member ํด๋์ค ๋ง DB์์ ์กฐํํ๋ค๋ ์๋ฏธ.
์ด ๊ฒฝ์ฐ Member๋ฅผ ๊ฑฐ์ ์ฐ๊ณ Team์ ๊ฑฐ์ ์ฐ์ง ์๋ ๊ฒฝ์ฐ์ ์ ์ฉํ๋ค.
๊ทธ๋ฐ๋ฐ Member์ Team์ ๊ฐ์ด ์ด๋ค๋ฉด LAZY ์ฒ๋ผ Member, Team ์ฟผ๋ฆฌ๊ฐ 2๋ฒ์ฉ ๋๊ฐ๋ ๊ฑด ์ฑ๋ฅ์ด ํจ์จ์ ์ด์ง ์๋ค.
๊ฐ๊ธ์ ์ง์ฐ ๋ก๋ฉ๋ง ์ฌ์ฉ(ํนํ ์ค๋ฌด์์)
์ฆ์ ๋ก๋ฉ์ ์ ์ฉํ๋ฉด ์์ํ์ง ๋ชปํ SQL์ด ๋ฐ์
์ฆ์ ๋ก๋ฉ์ JPQL์์ N+1 ๋ฌธ์ ๋ฅผ ์ผ์ผํจ๋ค.
@ManyToOne, @OneToOne ์ ๊ธฐ๋ณธ์ด ์ฆ์ ๋ก๋ฉ -> LAZY๋ก ์ค์
@OneToMany, @ManyToMany(์ ์ฐ์ง๋ง) ๋ ๊ธฐ๋ณธ์ด ์ง์ฐ ๋ก๋ฉ (LAZY) ์ค์ ํ์x
์์์ฑ ์ ์ด๋ ์์ ์ํ๋ก ๋ง๋ค ๋ ์ฐ๊ด๋ ์ํฐํฐ๋ ํจ๊ป ์์ ์ํ๋ก ๋ง๋ค๊ณ ์ถ์ ๋ ์ฌ์ฉํ๋ ๊ฒ์ด๋ค.
๋ถ๋ชจ๋ฅผ ์ ์ฅํ ๋ ์์๋ ๊ฐ์ด ์ ์ฅํ๊ณ ์ถ์ ๋ ์ฌ์ฉ, ์ฐ๊ด๊ด๊ณ๋ ์ง์ฐ/์ฆ์ ๋ก๋ฉ๊ณผ๋ ์๋ฌด ๊ด๊ณ๊ฐ ์๋ค.
-> ๋ถ๋ชจ๋ฅผ ์ ์ฅํ ๋ ์ฐ๊ด๋ ์์๋ ํจ๊ป ๋ค persist๋ก ํธ์ถํ๊ณ ์ถ์ ๋ ์ด๋ค. (๊ทธ์ ํธ๋ฆฌํจ๋ง ์ ๊ณตํ ๋ฟ)
์ ํํ ๊ฐ๋ ์ ํน์ ์ํฐํฐ๋ฅผ ์์ ์ํ๋ก ๋ง๋ค ๋, ์ฐ๊ด๋ ์ํฐํฐ๋ ํจ๊ป ์์ ์ํ๋ก ๋ง๋ค๊ณ ์ถ์ ๋ ์ฌ์ฉํ๋ค. child ๋ค์ parent ๊ฐ ํ๋์ผ ๋๋ง ์ฌ์ฉํ๋ค.
๊ฐ๋จํ๊ฒ persist๋ฅผ ํ ๋
em.persist(parent);
em.persist(child1);
em.persist(child2);
๋ฅผ ํ์ง ์๊ณ cascade = CascadeType.ALL ์ ์ฐ๋ฉด em.persist(parent); ๋ง ์จ๋ ์๋์ผ๋ก child1,2 ๊ฐ ๋ชจ๋ persist ๋๋ค.
cascade = CascadeType.ALL : ๋ชจ๋ ์ ์ฉ
cascade = CascadeType.PERSIST : ์์ (๋ฑ ์ ์ฅํ ๋๋ง ๋ผ์ดํ ์ฌ์ดํด ๋ง์ถ๊ณ ๋๋จธ์ง๋ ์ํํ๋ ๋ฐ๋ก ๋๊ณ ์ถ์ ๋)
cascade = CascadeType.REMOVE : ์ญ์
๊ฒ์ํ๊ณผ ํ์ผ ํ
์ด๋ธ์ด ์์ ๋ ์ฒจ๋ถํ์ผ ๊ฒฝ๋ก๊ฐ ์์ ๊ฒฝ์ฐ๋ ์๊ด์์ง๋ง ํ์ผ์ ์ฌ๋ฌ ๊ตฐ๋ฐ์์ ๊ด๋ฆฌํ๊ฑฐ๋ ๋ค๋ฅธ ์ํฐํฐ์์ ๊ด๋ฆฌํ๋ ๊ฒฝ์ฐ์๋ ์ฌ์ฉํ๋ฉด ์๋๋ค.
์๋ฅผ ๋ค์ด Parent๋ผ๋ ์ํฐํฐ๋ง child๋ฅผ ๊ด๋ฆฌํ๊ฑฐ๋ ์ฐ๊ดํ๊ฒ ์์ผ๋ฉด ์๊ด์์ง๋ง child๊ฐ ๋ค๋ฅธ ์ํฐํฐ์ ๊ด๊ณ๊ฐ ์๋ ๊ฒฝ์ฐ์๋ ์ฌ์ฉํ๋ฉด ์ ๋๋ค.
Parent๋ผ๋ ์ํฐํฐ๋ง child๋ฅผ ์์ ํ ๋ ์ฌ์ฉํ๊ณ ๋ค๋ฅธ ๋ฐ์ child๋ฅผ ์์ ํ๋ ๊ฒฝ์ฐ์๋ ์ฌ์ฉ X.
๊ณ ์ ๊ฐ์ฒด ์ ๊ฑฐ-> ๋ถ๋ชจ ์ํฐํฐ์ ์ฐ๊ด์ด ๋์ด์ง ์์ ์ํฐํฐ๋ฅผ ์๋์ผ๋ก ์ญ์ .
์ฐธ์กฐ๊ฐ ์ ๊ฑฐ๋ ์ํฐํฐ๋ ๋ค๋ฅธ ๊ณณ์์ ์ฐธ์กฐํ์ง ์๋ ๊ณ ์ ๊ฐ์ฒด๋ก ๋ณด๊ณ ์ญ์ ํ๋ ๊ธฐ๋ฅ.
์ฐธ์กฐํ๋ ๊ณณ์ด ํ๋์ด๊ณ ํน์ ์ํฐํฐ๊ฐ ๊ฐ์ธ ์์ ํ ๋(๋ถ๋ชจ๊ฐ ํ๋.) ์ฌ์ฉํ๋ค.
@OneToOne, @OneToMany ์์๋ง ์ฌ์ฉ ๊ฐ๋ฅ!!.
ํน์ ์ํฐํฐ๊ฐ ๊ฐ์ธ ์์ ํ ๋๋ง ์ฌ์ฉ.
CascadeType.ALL + orphanRemoval = true
๋ ์ต์
์ ๋ชจ๋ ํ์ฑํํ๋ฉด ๋ถ๋ชจ ์ํฐํฐ๋ฅผ ํตํด์ ์์์ ์๋ช
์ฃผ๊ธฐ๋ฅผ ๊ด๋ฆฌํ ์ ์๋ค.
๐ ์ฐธ๊ณ
๋ถ๋ชจ๋ฅผ ์ ๊ฑฐํ๋ฉด ์์์ ๊ณ ์๊ฐ ๋๋ค. ๋ฐ๋ผ์ ๊ฐ์ฒด ์ ๊ฑฐ ๊ธฐ๋ฅ์ ํ์ฑํํ๋ฉด ๋ถ๋ชจ๋ฅผ ์ ๊ฑฐํ ๋ ์์๋ ํจ๊ป ์ ๊ฑฐ๊ฐ ๋๋ค. CascadeType.REMOVE์ฒ๋ผ ๋์ํจ
๋ถ๋ชจ ์ํฐํฐ์์ ์์ ์ํฐํฐ ์ ๊ฑฐ
CascadeType.REMOVE๋ ์์ ์ํฐํฐ๊ฐ ๊ทธ๋๋ก ๋จ์์๋ ๋ฐ๋ฉด, orphanRemoval = true๋ ์์ ์ํฐํฐ๋ฅผ ์ ๊ฑฐํ๋ค.
โ ๊ธฐ๋ณธ๊ฐ ํ์
-> String, int, ...
์๋ช
์ฃผ๊ธฐ๋ฅผ ์ํฐํฐ์ ์์กด
ex) ํ์์ ์ญ์ ํ๋ฉด ์ด๋ฆ, ๋์ด ํ๋๋ ํจ๊ป ์ญ์ ๋จ
๊ฐ ํ์
์ ๊ณต์ ํ๋ฉด X
ex) ํ์ ์ด๋ฆ ๋ณ๊ฒฝ ์ ๋ค๋ฅธ ํ์์ ์ด๋ฆ๋ ํจ๊ป ๋ณ๊ฒฝ๋๋ฉด ์๋จ.
โ ์๋ฒ ๋๋ ํ์
(๋ณตํฉ ๊ฐ ํ์
)
์๋ก์ด ๊ฐ ํ์
์ ์ง์ ์ ์ํ ์ ์์
JPA๋ ์๋ฒ ๋๋ ํ์
์ด๋ผ ํจ
int, String๊ณผ ๊ฐ์ ๊ฐ ํ์
(์ํฐํฐ ํ์
์ด ์๋)
@Embeddable : ๊ฐ ํ์ ์ ์ ์ํ๋ ๊ณณ์ ํ์
@Embedded : ๊ฐ ํ์ ์ ์ฌ์ฉํ๋ ๊ณณ์ ํ์
์ฌ์ฉ ์ด์
private String city;
private String street;
private String zipcode;
โAddress ํด๋์ค์ 3๊ฐ์ ๋ฐ์ดํฐ๋ฅผ 1๊ฐ์ ์ฃผ์๋ผ๋ ์๋ฏธ์ ๊ฐ์ฒด๋ก ํํํ๋ค๋ฉด ํจ์ฌ ๋ ๊ฐ๋ ์ฑ์๊ณ ๊ฐ์ฒด ์งํฅ์ ์ธ ์ฝ๋๋ก ๋ง๋ค ์ ์๋ค.
์ปฌ๋ผ๋ช ์ด ์ค๋ณต๋จ
@Embedded
private Address homeAddress;
//์ฃผ์
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "city", column = @Column(name = "WORK_CITY")),
@AttributeOverride(name = "street",
column = @Column(name = "WORK_STREET")),
@AttributeOverride(name = "zipcode",
column = @Column(name = "WORK_ZIPCODE")),
})
private Address WorkAddress; //์ด๋ ๊ฒ ์ฐ๋ฉด ์ค๋ณต์ด ์ผ์ด๋ ์๋ฌ๊ฐ ๋ฐ์ -> AttributeOverride ์ฌ์ฉ
์๋ฒ ๋๋ ํ์ ๊ฐ์ด null์ด๋ฉด ๋งคํํ ์ปฌ๋ผ๊ฐ์ ๋ชจ๋ null
๊ฐ ํ์ ์ ๋ณต์กํ ๊ฐ์ฒด ์ธ์์ ์กฐ๊ธ์ด๋ผ๋ ๋จ์ํํ๋ ค๊ณ ๋ง๋ ๊ฐ๋ ์ด๋ค.
Address address = new Address("city", "street", "100012");
Member member = new Member();
member.setUsername("member1");
member.setHomeAddress(address);
em.persist(member);
Member member2 = new Member();
member.setUsername("member2");
member.setHomeAddress(address);
em.persist(member2);
member.getHomeAddress().setCity("newCity");
๊ฐ ํ์ ๊ณต์ ์ฐธ์กฐ๋ ์๋ฒ ๋๋ ํ์ ๊ฐ์ ๊ฐ ํ์ ์ ์ฌ๋ฌ ์ํฐํฐ์์ ๊ณต์ ํ๋ฉด ๋ถ์์ฉ์ด ๋ฐ์ํ๋ค. -> ๊ฐ์ด ๊ณต์ ํด์ ์ฌ์ฉํ๊ณ ์ถ๋ค๋ฉด ์ํฐํฐ ๊ฐ ํ์ ์ฌ์ฉ, ์๋๋ฉด ๋์ ๊ฐ(์ธ์คํด์ค)๋ฅผ ๋ณต์ฌํด์ ์ฌ์ฉ.
Address address = new Address("city", "street", "1000");
member1 ~~
member2 ~~
member.getHomeAddress().setCity("newCity");
์ด๋ ๊ฒ ์์ผ๋ฉด update ์ฟผ๋ฆฌ๊ฐ 2๋ฒ ๋๊ฐ์ member1, 2 ๋ ๋ค newcity๋ก ์์ ๋๋ค.
์ด๋ฐ ๊ฑธ ์ฌ์ด๋ ์ดํํธ๋ผ๊ณ ํ๋๋ฐ ์ด ๋ฒ๊ทธ๋ ์ง์ง ์ก๊ธฐ๊ฐ ์ด๋ ต๋ค.
๊ทธ๋์ address๊ฐ ์์ผ๋ฉด new address ๋ผ๋ ๊ฒ์ ๋ณต์ฌํด์ ๋ง๋ค์ด ์จ์ผ ํ๋ค.
Address address = new Address("city", "street", "100012");
Member member = new Member();
member.setUsername("member1");
member.setHomeAddress(address);
em.persist(member);
//์ด๋ ๊ฒ ๋ณต์ฌ
Address copyAddress = new Address(address.getCity(), address.getStreet(), address.getZipcode());
Member member2 = new Member();
member.setUsername("member2");
member.setHomeAddress(copyAddress);
em.persist(member2);
//์ฒซ ๋ฒ์งธ๋ง newCity๊ฐ ๋๊ณ ๋ ๋ฒ์งธ๋ city๊ฐ ๋๋ค.
member.getHomeAddress().setCity("newCity");
ํญ์ ๊ฐ์ ๋ณต์ฌํด์ ์ฌ์ฉํ๋ฉด ๊ณต์ ์ฐธ์กฐ๋ก ์ธํด ๋ฐ์ํ๋ ๋ถ์์ฉ์ ํผํ ์ ์์ง๋ง ์๋ฒ ๋๋ ํ์ ์ฒ๋ผ ์ง์ ์ ์ํ ๊ฐ ํ์ ์ ์๋ฐ์ ๊ธฐ๋ณธ ํ์ ์ด ์๋๋ผ ๊ฐ์ฒด ํ์ ์ด๋ค.
๊ฐ์ฒด ํ์
์ ์ฐธ์กฐ ๊ฐ์ ์ง์ ๋์
ํ๋ ๊ฒ์ ๋ง์ ๋ฐฉ๋ฒ์ด ์๋ค.
๊ฐ์ฒด์ ๊ณต์ ์ฐธ์กฐ๋ ํผํ ์ ์๋ค.
์ฆ ๊ธฐ๋ณธ ํ์ ์ ๊ฐ์ ๋ณต์ฌํ์ง๋ง ๊ฐ์ฒด ํ์ ์ ์ฐธ์กฐ(์ฃผ์ ๊ฐ)๋ฅผ ์ ๋ฌํ๋ค. ๊ทธ๋์ ๊ฐ์ฒด ๋ ๋ค ๊ฐ ๋ชจ๋ ๋ฐ๋๋ค.
์์ฑ ์์ ์ดํ ์ ๋ ๊ฐ์ ๋ณ๊ฒฝํ ์ ์๋ ๊ฐ์ฒด๋ก ๊ฐ ํ์
์ ๋ถ๋ณ ๊ฐ์ฒด๋ก ์ค๊ณํด์ผํ๋ค.
์์ฑ์๋ก๋ง ๊ฐ์ ์ค์ ํ๊ณ ์์ ์(Setter)๋ฅผ ๋ง๋ค์ง ์์ผ๋ฉด ๋๋ค. (์๋๋ฉด Setter๋ฅผ public์ด ์๋ private ์ผ๋ก ์์ฑํด๋ ๋จ)
// ์์ ํ๊ณ ์ถ๋ค๋ฉด
Address newAddress = new Address("New City", address.getStreet(), address.getZipcode());
member.setHomeAddress(newAddress);
๊ฐ ํ์ ์ ๊น๋ํ๊ฒ ๊ทธ๋ฅ ๋ชจ๋ ๋ถ๋ณ์ผ๋ก ๋ง๋ ๋ค๊ณ ์๊ฐํ์
๊ฐ์ฒด ์งํฅ ์ฟผ๋ฆฌ ์ธ์ด์ด๋ค. ํ
์ด๋ธ์ด ์๋ ์ํฐํฐ ๊ฐ์ฒด๋ฅผ ๋์์ผ๋ก ์ฟผ๋ฆฌํ๋ค.
SQL์ ์ถ์ํํด์ ํน์ ๋ฐ์ดํฐ๋ฒ ์ด์ค SQL์ ์์กดํ์ง ์๋๋ค.
๊ฒฐ๊ตญ SQL๋ก ๋ณํ๋๋ค.
JPQL ๋ฌธ๋ฒ ์ ์๋ฉด QueryDSL(์ค๋ฌด ์ฌ์ฉ ๊ถ์ฅ) ์ ์ฌ์ฉํ ์ ์๋ค.
JPA ์ฌ์ฉํ๋ฉด์ JDBC ์ปค๋ฅ์
์ ์ง์ ์ฌ์ฉํ๊ฑฐ๋ ์คํ๋ง JdbcTemplate, Mybatis ๋ฑ์ ํจ๊ป ์ฌ์ฉ ๊ฐ๋ฅ.
โ ๋จ ์์์ฑ ์ปคํ
์คํธ๋ฅผ ์ ์ ํ ์์ ์ ๊ฐ์ ๋ก flush() ํ์.
Member member = new Member();
member.setUsername("member1");
m.persist(member);
//flush๋ commit ๋ ๋์ query ๊ฐ ๋ ๋ผ๊ฐ ๋ flush ๋๋ค.
em.flush();
dbconn.executeQuery("select ~"); //flush ํ์ง ์์ผ๋ฉด DB์ ์๋ฌด๊ฒ๋ ์๋ ์ํ๋ผ ๊ฒฐ๊ณผ๊ฐ 0์ด ๋๋ค.
โ ์์์ฑ ์ปจํ
์คํธ์ ๋ณ๊ฒฝ ๋ด์ฉ์ DB์ ๋ฐ์ํ๋ ๊ฒ์ด๋ค.
โ Transaction commit์ด ์ผ์ด๋ ๋ flush๊ฐ ๋์ํ๋๋ฐ ์ด ๋ ์ฐ๊ธฐ ์ง์ฐ ์ ์ฅ์์ ์์ ๋จ๋ INSERT, UPDATE, DELETE SQL๋ค์ด DB์ ๋ ๋ผ๊ฐ๋ค.
์์์ฑ ์ปจํ
์คํธ๊ฐ ๋น์์ง๋ ๊ฒ์ด ์๋
em.persist(member)
ํ ๋๋ member ๊ฐ์ฒด๊ฐ ์์์ฑ ์ปจํ
์คํธ์ ์๊ณ DB์๋ ์์ง ๋ ๋ผ๊ฐ์ง ์์ ์ํ์ด๋ค. ๊ทธ๋์ ๊ฐ์ ๋ก em.flush()
๋ฅผ ํด์ค๋ค.
๊ฐ ํ์
์ ํ๋ ์ด์ ์ ์ฅํ ๋ ์ฌ์ฉ
@ElementCollection
, @CollectionTable
์ฌ์ฉ
๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ์ปฌ๋ ์
์ ๊ฐ์ ํ
์ด๋ธ์ ์ ์ฅํ ์ ์๋ค.
->์ปฌ๋ ์
๋ค์ ์ผ๋๋ค ๊ฐ๋
์ด๊ธฐ ๋๋ฌธ์ DB์์ ์ปฌ๋ ์
์ ํ ํ
์ด๋ธ์ ๋ฃ์ ๋ฐฉ๋ฒ์ด ์๋ค.
๊ทธ๋์ ์ผ๋๋ค์ ๋ณ๋์ ํ
์ด๋ธ์ ๋ง๋ค์ด ๋ด์ผ ํ๋ค.
์ปฌ๋ ์
์ ์ ์ฅํ๊ธฐ ์ํ ๋ณ๋์ ํ
์ด๋ธ์ด ํ์ํจ.
Set๊ณผ List์ฒ๋ผ ์ปฌ๋ ์
์ ๊ด๊ณํ DB์๋ ๊ธฐ๋ณธ์ ์ผ๋ก ํ
์ด๋ธ ์์ ์ปฌ๋ ์
์ ๋ด์ ์ ์๋ ๊ตฌ์กฐ๊ฐ ์๋ค. ๊ทธ๋ฅ Value๋ก ๊ฐ๋ง ๊ฐ๋ฅ.
๊ทธ๋์ ๋ณ๋์ ํ
์ด๋ธ์ ๋ฝ์์ผ ํ๋ค.
์๋ฅผ ๋ค๋ฉด addressHistory๋ก List๊ฐ ์๋ค๊ณ ํ๋ค๋ฉด ADDRESS ํ
์ด๋ธ์ ๋ฐ๋ก ๋ฝ์์ Member_id, CITY, STREET, ZIPCODE ์ปฌ๋ผ์ ๋ง๋ค์ด์ผ ํ๋ค.
favoriteFoods : Set<String>
addressHistory : List<Address>
@ElementCollection
@CollectionTable(name = "FAVORITE_FOOD", joinColumns = @JoinColumn(name = "MEMBER_ID"))
@Column(name = "FOOD_NAME") // ๊ฐ์ด String์ผ๋ก ํ๋๊ณ Address ์ฒ๋ผ ๋ด๊ฐ ์ ์ํ ๊ฒ์ด ์๋๊ธฐ ๋๋ฌธ์ ์์ธ์ ์ผ๋ก ์ด๋ฆ ์ค์ ๊ฐ๋ฅ.
private Set<String> favoriteFoods = new HashSet<>();
@ElementCollection
@CollectionTable(name = "ADDRESS", joinColumns = @JoinColumn(name = "MEMBER_ID"))
private Set<Adress> addressHistory = new ArrayList<>();
@ElementCollection
ํด๋น ํ๋๊ฐ ์ปฌ๋ ์
๊ฐ์ฒด์์ JPA์๊ฒ ์๋ ค์ฃผ๋ ์ด๋
ธํ
์ด์
๊ฐ ํ์
์ปฌ๋ ์
์ ๋งคํํ ๋ ์ฌ์ฉํ๋ค.
โ ์ฐ๊ด๋ ๋ถ๋ชจ Entity ํ๋์๋ง ์ฐ๊ด๋์ด ๊ด๋ฆฌ๋๋ค. (๋ถ๋ชจ Entity์ ๋
๋ฆฝ์ ์ผ๋ก ์ฌ์ฉ์ด ๋ถ๊ฐ๋ฅ)
โ ํญ์ ๋ถ๋ชจ์ ํจ๊ป ์ ์ฅ๋๊ณ ์ญ์ ๋๋ฏ๋ก cascade ์ต์
์ ์ ๊ณตX
โ ๋ถ๋ชจ Entity Id์ ์ถ๊ฐ ์ปฌ๋ผ(basic or ์๋ฒ ๋๋)์ผ๋ก ๊ตฌ์ฑ๋จ
โ ๊ธฐ๋ณธ์ ์ผ๋ก ์๋ณ์ ๊ฐ๋
์ด ์์ผ๋ฏ๋ก ์ปฌ๋ ์
๊ฐ ๋ณ๊ฒฝ ์, ์ ์ฒด ์ญ์ ํ ์๋ก ์ถ๊ฐํด์ผํ๋ค.
@CollectionTable
๊ฐ ํ์
์ปฌ๋ ์
์ ๋งคํํ ํ
์ด๋ธ์ ๋ํ ์ ๋ณด๋ฅผ ์ง์ ํ๋ ์ญํ ์ ์ํํฉ๋๋ค.
๊ฐ ํ์
๋ค์ ๋ณ๋๋ก persistํ๊ฑฐ๋ ์
๋ฐ์ดํธ ํ ๊ฒ ์๋ค. member์์ ๊ฐ์ ๋ฐ๊พธ๋ฉด ์๋์ผ๋ก ์
๋ฐ์ดํธ ๋๋ค.
๊ฐ ํ์
์ปฌ๋ ์
์ ์์์ฑ ์ ์ด(Cascade) + ๊ณ ์ ๊ฐ์ฒด ์ ๊ฑฐ ๊ธฐ๋ฅ์ ํ์๋ก ๊ฐ์ง๋ค๊ณ ๋ณผ ์ ์๋ค
์ปฌ๋ ์
๋ค์ ์ง์ฐ ๋ก๋ฉ์ด๋ค.
์ํฐํฐ์ ๋ค๋ฅด๊ฒ ์๋ณ์ ๊ฐ๋
์ด ์๊ณ ๊ฐ์ ๋ณ๊ฒฝํ๋ฉด ์ถ์ ์ด ์ด๋ ต๋ค.
๊ฐ ํ์
์ปฌ๋ ์
์ ๋ณ๊ฒฝ ์ฌํญ์ด ๋ฐ์ํ๋ฉด, ์ฃผ์ธ ์ํฐํฐ์ ์ฐ๊ด๋ ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ์ญ์ ํ๊ณ , ๊ฐ ํ์
์ปฌ๋ ์
์ ์๋ ํ์ฌ ๊ฐ์ ๋ชจ๋ ๋ค์ ์ ์ฅํ๋ค.
๊ฐ ํ์
์ปฌ๋ ์
์ ๋งคํํ๋ ํ
์ด๋ธ์ ๋ชจ๋ ์ปฌ๋ผ์ ๋ฌถ์ด์ ๊ธฐ๋ณธ ํค๋ฅผ ๊ตฌ์ฑํด์ผ ํ๋ค.
โ null ์
๋ ฅ๋ ๋ถ๊ฐ๋ฅํ๊ณ ์ฃผ์ฅ ์ ์ฅ๋ ๋ถ๊ฐ๋ฅํ๋ค.
String์ ์์ ํ ๋๋ ๋ณ ๋ค๋ฅธ ๋ฐฉ๋ฒ ์์ด ์ปฌ๋ ์ ์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ผ๋ก ์์ .
findMember.getFavoriteFoods().remove("์นํจ");
findMember.getFavoriteFoods().add("ํ์");
address์ ๊ฒฝ์ฐ๋ Address๋ก ์ ๋๋ฆญ์ด๊ธฐ ๋๋ฌธ์ ๋ถ๋ณ ๊ฐ์ฒด ํ ๋์ฒ๋ผ ํ๋ฉด ๋๋ค.
findMember.getFavoriteFoods().remove(new Address("old_1", "street", "1234"));
findMember.getFavoriteFoods().add(new Address("newCity1", "street", "1234"));
remove() ์์ฒด๊ฐ ๊ธฐ๋ณธ์ ์ผ๋ก equals๋ก ๋์ํ๊ธฐ ๋๋ฌธ์ ๊ธฐ์กด์ ์๋ ์์๋ฅผ ์ฐพ๊ธฐ ์ํด์๋ equals()๋ฅผ ๋์ํด ๊ฐ์ฒด์ ์ฌ๋ฐ๋ฅธ ๋น๊ต๊ฐ ํ์ํ๋ค.
(hashcode ํ์)
โ ์ค๋ฌด์์๋ ์ํฉ์ ๋ฐ๋ผ ๊ฐ ํ์ ์ปฌ๋ ์ ๋์ ์ ์ผ๋๋ค ๊ด๊ณ๋ฅผ ๊ณ ๋ ค
โ ์ผ๋๋ค ๊ด๊ณ๋ฅผ ์ํ ์ํฐํฐ๋ฅผ ๋ง๋ค๊ณ , ์ฌ๊ธฐ์์ ๊ฐ ํ์
์ ์ฌ์ฉ.
์ผ๋๋ค๋ก ์์ ์ update ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ๋ ๊ฑด ์ด์ฉ ์ ์์
->์ผ๋๋ค ๋จ๋ฐฉํฅ ๋งคํ์ ๊ฒฝ์ฐ ๋ค๋ฅธ ํ
์ด๋ธ์ ์ธ๋ํค๊ฐ ์์ด update ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ.
โ ์ง์ง ๋จ์ํ ๊ฑฐ ๋ ๊ฐ ํ์
์ฌ์ฉํ๊ณ ๋๋จธ์ง๋ ๊ทธ๋ฅ ์ผ๋๋ค ๊ด๊ณ๋ก ์ํฐํฐ ๋ง๋ ๋ค. (์ถ์ ํ ํ์๋ ์๊ณ update(๊ฐ์ด ๋ฐ๋) ๋ ํ์์์ ๋)
์๋ฅผ ๋ค๋ฉด ์
๋ ํธ ๋ฐ์ค์ฒ๋ผ.
โ ์์์ฑ ์ ์ด + ๊ณ ์ ๊ฐ์ฒด ์ ๊ฑฐ๋ฅผ ์ฌ์ฉํด์ ๊ฐ ํ์ ์ปฌ๋ ์ ์ฒ๋ผ ์ฌ์ฉ.
AddressEntity๋ฅผ ํ๋ ๋ ์์ฑ
@Entity
@Table(name = "ADDRESS")
public class AddressEntity{
@Id @GeneratedValue
private Long id;
private Address address; // ๊ฐ ํ์
์ปฌ๋ ์
public AddressEntity(){}
public AddressEntity(Address address){
this.address = address;
}
public AddressEntity(String city, String street, String zipcode){
this.address = new Address(city, street, zipcode);
}
}
Member.class
@OneToMany(casecade = CascadeType.AlLL, orphanRemoval = true)
@JoinColumn(name = "MEMBER_ID")
private List<AddressEntity> addressHistory = new ArrayList<>();
@OneToMany, @ManyToOne ์ผ๋ก ๊ธฐ๋ณธ ๋งคํํด์ ํ์ด๋ ๋๊ณ ์ผ๋๋ค ๋จ๋ฐฉํฅ ๋งคํ์ผ๋ก ํด๋ ๋๋ค.
select m from **Member** as m where m.**age** > 18
(์ํฐํฐ Member ์ ์์ฑ age๋ ๋์๋ฌธ์ ๊ตฌ๋ถ)
ํ
์ด๋ธ ์ด๋ฆ์ด ์๋ ์ํฐํฐ ์ด๋ฆ ์ฌ์ฉ
๋ณ์นญ(as m) ์ ํ์๋ค. as๋ ์๋ต ๊ฐ๋ฅ
Member member = new Member();
member.setUsername("member1");
member.setAge(10);
em.persist(member);
TypedQuery<Member> query = em.createQuery("select m from Member m", Member.class);
//๋ฐํ ํ์
์ด ๋ช
ํํ ๋๋ TypedQuery๋ฅผ ์ฌ์ฉํ๋ค.
/*List<Member> resultList = query.getResultList(); //์ปฌ๋ ์
์ด ๋ฐํ๋จ.
for (Member member1 : resultList) {
System.out.println("member1 = " + member1);
}*/
Member singleResult = query.getSingleResult();
System.out.println("singleResult = " + singleResult);
query.getResultList()
๊ฒฐ๊ณผ๊ฐ 2๊ฐ ์ด์์ผ ๋ ๋ฆฌ์คํธ ๋ฐํ , ๊ฒฐ๊ณผ๊ฐ ์์ผ๋ฉด ๋น ๋ฆฌ์คํธ ๋ฐํ(nullPointerException ๊ฑฑ์ ์์)
query.getSingleResult()
๊ฒฐ๊ณผ๊ฐ ์ ํํ๊ฒ ํ ๊ฐ ๋์์ผ ํ๋ค.
๊ฒฐ๊ณผ๊ฐ ์์ผ๋ฉด : javax.persistence.NoResultException
๊ฒฐ๊ณผ๊ฐ ๋ ์ด์์ด๋ฉด : javax.persistence.NonUniqueResultException
๋ฐ์
/*TypedQuery<Member> query = em.createQuery("select m from Member m where m.username = :username", Member.class);
query.setParameter("username", "member1");*/
//๋ฐํ ํ์
์ด ๋ช
ํํ ๋๋ TypedQuery๋ฅผ ์ฌ์ฉํ๋ค.
//Member singleResult = query.getSingleResult();
//System.out.println("singleResult = " + singleResult.getUsername());
//์ ์ฒ๋ผ ์ฐ์ง ์๊ณ ๋ฐ์ ์ฒ๋ผ ๊ฐ๋จํ๊ฒ ์ฌ์ฉํจ
Member result = em.createQuery("select m from Member m where m.username = :username", Member.class)
.setParameter("username", "member1")
.getSingleResult(); //์ด๋ฆ ๊ธฐ์ค ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉ
์์น ๊ธฐ์ค ๋ฐ์ธ๋ฉ์ ๋๋๋ก ์ฐ์ง ์๋ ๊ฒ์ด ์ข์ (์์ ๋ฐ๋ ์ ์์)
SELECT ์ ์ ์กฐํํ ๋์์ ์ง์ ํ๋ ๊ฒ
SELECT m FROM Member m -> ์ํฐํฐ ํ๋ก์ ์
SELECT m.team FROM Member m -> ์ํฐํฐ ํ๋ก์ ์
(๋ช
์์ ์ผ๋ก join ๋ฃ์ด์ฃผ๋ ๊ฒ์ด ์ข์)
SELECT m.address FROM Member m -> ์๋ฒ ๋๋ ํ์
ํ๋ก์ ์
SELECT m.username, m.age FROM Member m -> ์ค์นผ๋ผ ํ์
ํ๋ก์ ์
DISTINCT๋ก ์ค๋ณต ์ ๊ฑฐ
List<MemberDTO> result = em.createQuery("select new jpql.MemberDTO(m.username, m.age) from Member m", MemberDTO.class)
.getResultList
MemberDTO memberDTO = result.get(0);
System.out.println("memberDTO = " + memberDTO.getUsername());
System.out.println("memberDTO = " + memberDTO.getAge());
โํจํค์ง ๋ช
์ ํฌํจํ ์ ์ฒด ํด๋์ค๋ช
์
๋ ฅ
โ์์์ ํ์
์ด ์ผ์นํ๋ ์์ฑ์ ํ์
List<Member> result = em.createQuery("select m from Member m order by m.age desc", Member.class)
.setFirstResult(1)
.setMaxResults(10)
.getResultList();
.setFirstResult(1)
์กฐํ ์์ ์์น (0๋ถํฐ ์์)
.setMaxResults(10)
์กฐํํ ๋ฐ์ดํฐ ์
์ 2๊ฐ๋ง ์ฑ๊ฒจ์ ๋ฃ์ด์ฃผ๋ฉด ?์ ์ถ์์ ์ผ๋ก ์๊ฐํ๋ ๊ฐ์ ์์์ ๋ค ๋ฃ์ด์ค๋ค.
->์ฐ๋ฆฌ๋ ์ธ๋ฑ์ค, ๋ช ๊ฐ ๊ฐ์ ธ์ฌ์ง ์ด๋ฐ ๊ฒ๋ง ์ ๊ฒฝ์ฐ๋ฉด ๋จ.
๋ด๋ถ ์กฐ์ธ: SELECT ~~ [INNER] JOIN m.team t
์ธ๋ถ ์กฐ์ธ: SELECT ~~ LEFT [OUTER] JOIN m.team t
๋ด๋ถ ์กฐ์ธ๊ณผ ์ธ๋ถ ์กฐ์ธ ์ฐจ์ด
๋ด๋ถ๋ ํค ๊ฐ์ด ์๋ ํ ์ด๋ธ์ ์นผ๋ผ ๊ฐ์ ๋น๊ตํด์ ๋ง๋ ๊ฐ์ ๊ฐ์ ธ์ค๋ ๊ฒ์ด๋ค.
ํ ๋ง๋๋ก ์๋ก ๊ด๋ จ๋ ๋ด์ฉ์ ๊ฒ์ํด ๊ฐ์ ธ์ค๋ ๋ฐฉ๋ฒ์ด๋ผ ์๊ฐ
์ธ๋ถ๋ ์ฌ๋ฌ ํ ์ด๋ธ ์ค ํ ํ ์ด๋ธ์๋ง ๋ฐ์ดํฐ๊ฐ ์๊ณ ๋ค๋ฅธ ์ชฝ์๋ ์๋ ๊ฒฝ์ฐ, ๋ฐ์ดํฐ๊ฐ ์๋ ํ ์ด๋ธ์ ๋ด์ฉ์ ์ ๋ถ ์ถ๋ ฅํ๋ ๋ฐฉ๋ฒ์ด๋ค.
String query = "select m from Member m left join Team t on m.username = t.name";
WHERE, HAVING ์ ์์๋ง ์๋ธ ์ฟผ๋ฆฌ ์ฌ์ฉ ๊ฐ๋ฅ
SELECT ์ ๋ ๊ฐ๋ฅ
FROM ์ ์ ์๋ธ ์ฟผ๋ฆฌ๋ ํ์ฌ JPQL์์ ๋ถ๊ฐ๋ฅ
-> ์กฐ์ธ์ผ๋ก ํ ์ ์์ผ๋ฉด ํ์ด์ ํด๊ฒฐํ๋๋ก
//ํจํค์ง ๋ช
๊น์ง ๋ฃ์ด์ผ ํจ. -> ADMIN ํ์
์ ์ ์ ๋ง ์กฐํํจ
String query = "select m.username, 'HELLO', true From Member m " +
"where m.type = jpql.MemberType.USER";
List<Member> result = em.createQuery(query)
.getResultList();
String query = "select " +
"case when m.age <= 10 then 'ํ์์๊ธ' " +
"when m.age >= 60 then '๊ฒฝ๋ก์๊ธ' " +
"else '์ผ๋ฐ์๊ธ' " +
"end " +
"from Member m";
List<String> result = em.createQuery(query, String.class)
.getResultList();
coalesce : ํ๋์ฉ ์กฐํํด์ null์ด ์๋๋ฉด ๋ฐํ
String query = "select coalesce(m.username, '์ด๋ฆ ์๋ ํ์') as username " +
"from Member m ";
List<String> result = em.createQuery(query, String.class)
.getResultList();
// ๊ฒฐ๊ณผ๋ s = ์ด๋ฆ ์๋ ํ์
nullif: ๋ ๊ฐ์ด ๊ฐ์ผ๋ฉด null๋ฐํ, ๋ค๋ฅด๋ฉด ์ฒซ ๋ฒ์งธ ๊ฐ ๋ฐํ
String query = "select nullif(m.username, '๊ด๋ฆฌ์') as username " +
"from Member m ";
List<String> result = em.createQuery(query, String.class)
.getResultList();
//s = null
.(์ )์ ์ฐ์ด ๊ฐ์ฒด ๊ทธ๋ํ๋ฅผ ํ์ํ๋ ๊ฒ
select m.username
-> ์ํ ํ๋
from Member m join m.team t
-> ๋จ์ผ ๊ฐ ์ฐ๊ด ํ๋
m.orders o
-> ์ปฌ๋ ์
๊ฐ ์ฐ๊ด ํ๋
ํ๋ 3๊ฐ์ง (๊ถ์ฅX -> ๋ฌต์์ ์กฐ์ธ์ ์ํํ๋ค.)
์ํ ํ๋ : ๋จ์ํ ๊ฐ์ ์ ์ฅํ๊ธฐ ์ํ ํ๋, ๊ฒฝ๋ก ํ์์ ๋์ผ๋ก ํ์์ด ๋ถ๊ฐ๋ฅํ๋ค.
ex) m.username ์ด๋ผํ ๋ m.username.~~ ๋ก ๋ ํ์์ด ๋ถ๊ฐ๋ฅํ๋ค.
์ฐ๊ด ํ๋ : ์ฐ๊ด๊ด๊ณ๋ฅผ ์ํ ํ๋
โ ๋จ์ผ ๊ฐ : @ManyToOne, @OneToOne, ๋์์ด ์ํฐํฐ (ex m.team)
๋ฌต์์ ๋ด๋ถ ์กฐ์ธ ๋ฐ์, ํ์O
๋ฌต์์ ๋ด๋ถ ์กฐ์ธ ๋ฐ์ -> ๊ฐ์ฒด ์
์ฅ์์๋ m.team ์ด๋ฐ ์์ผ๋ก ํ๋ฉด ๋์ง๋ง DB์
์ฅ์์๋ ์กฐ์ธ์ด ์ผ์ด๋์ผ ํ๋ค.
์ฌ๋งํ๋ฉด ๋ฌต์์ ๋ด๋ถ ์กฐ์ธ์ ์ฌ์ฉX
โ ์ปฌ๋ ์
๊ฐ : @OneToMany, @ManyToMany, ๋์์ด ์ปฌ๋ ์
(ex m.orders)
๋ฌต์์ ๋ด๋ถ ์กฐ์ธ ๋ฐ์, ํ์X
๋ช
์์ ์กฐ์ธ join ํค์๋๋ฅผ ์ง์ ์ฌ์ฉํ๋ค. (์ด๊ฑธ ์ฌ์ฉ ๊ถ์ฅ)
ex) select m from Member m join m.team t
JPQL์์ ์ฑ๋ฅ ์ต์ ํ๋ฅผ ์ํด ์ ๊ณตํ๋ ๊ธฐ๋ฅ
์ฐ๊ด๋ ์ํฐํฐ๋ ์ปฌ๋ ์
์ SQL๋ก ํ ๋ฒ์ ํจ๊ป ์กฐํํ๋ ๊ธฐ๋ฅ
ex) ํ์์ ์กฐํํ๊ณ ์ถ์๋ฐ ํ์์ ์กฐํํ๋ฉด์ ์ฐ๊ด๋ ํ๋ ํจ๊ป ์กฐํ๋๋ค.(SQL ํ ๋ฒ์)
๊ทธ๋ฌ๋ฉด select m from Member m join fetch m.team
ํ๋ฉด ๋๋ค.
SQL ์์๋
SELECT M.*, T.* FROM MEMBER M INNER JOIN TEAM T ON M.TEAM_ID = T.ID
์ฟผ๋ฆฌ๋ก ๋ด๊ฐ ์ํ๋๋๋ก ์ด๋ค ๊ฐ์ฒด ๊ทธ๋ํ๋ฅผ ํ ๋ฒ์ ์กฐํํ ๊ฑฐ๋ผ๋ ๊ฒ์ ์ง์ ๋ช
์์ ์ผ๋ก, ๋์ ์ผ๋ก ๋ํ๋ธ๋ค.
Team teamA = new Team();
teamA.setName("ํA");
em.persist(teamA);
Team teamB = new Team();
teamB.setName("ํB");
em.persist(teamB);
Member member1 = new Member();
member1.setUsername("ํ์ 1");
member1.setTeam(teamA);
em.persist(member1);
Member member2 = new Member();
member2.setUsername("ํ์ 2");
member2.setTeam(teamA); //์์๋ ํ
em.persist(member2);
Member member3 = new Member();
member3.setUsername("ํ์ 3");
member3.setTeam(teamB); //์์๋ ํ
em.persist(member3);
String query = "select m From Member m ";
List<Member> result = em.createQuery(query, Member.class)
.getResultList();
for (Member member : result) {
System.out.println("member = " + member.getUsername() + ", " + member.getTeam().getName());
}
tx.commit();
์ฟผ๋ฆฌ ์คํํ๋ฉด ๋ชจ๋ Member ๊ฐ์ฒด๋ฅผ ์กฐํํจ.
tx.commit()
ํธ์ถ์ ํตํด ํธ๋์ญ์
์ด ์ปค๋ฐ๋๋ฉด, ์์์ฑ ์ปฉํ
์คํธ์ ์ํ๊ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ฐ์๋จ.
์ด๋ฌ๋ฉด ํ์ 100๋ช
์ด ๋ค ๋ค๋ฅธ ํ์ด๋ฉด ์ฟผ๋ฆฌ๊ฐ 100 + 1 (N+1) ๋ฒ ๋๊ฐ๋ค.
๊ทธ๋์ ์ด ๋ฌธ์ ๋ ํ์น ์กฐ์ธ์ผ๋ก ํผ๋ค.
String query = "select m From Member m join fetch m.team";
์ด๋ get.Team()์ ํ๋ก์๊ฐ ์๋๋ค.
์ด๋ฏธ member์ team์ ๋ฐ์ดํฐ๋ฅผ ์กฐ์ธํด์ ๋ค ๊ฐ์ ธ์๊ณ ์ฟผ๋ฆฌ๊ฐ ๋ ๋ผ๊ฐ์ result์ ๋ด๊ธฐ๋ ์์ ์ Team์ ํ๋ก์๊ฐ ์๋ ์ค์ ๋ฐ์ดํฐ๊ฐ ๋ด๊ธฐ๊ฒ ๋๋ค.
String query = "select t From Team t join fetch t.members";
List<Team> result = em.createQuery(query, Team.class)
.getResultList();
for (Team team : result) {
System.out.println("Team = " + team.getName() + "| members : " + team.getMembers().size());
}
tx.commit();
Team = ํA | members : 2
Team = ํA | members : 2
Team = ํB | members : 1
ํA์ ํ์์ด 2๋ช ์๊ธฐ ๋๋ฌธ์ 2๋ฒ ์ถ๋ ฅ์ด ๋ ๊ฒ์ด๋ค.
join fetch๋ฅผ ํ๊ฒ ๋๋ฉด ์ผ๋๋ค ๊ด๊ณ ๋๋ฌธ์ ๋ปฅํ๊ธฐ ๋์ result.size() ๋ฅผ ์ถ๋ ฅํ๋ฉด 3์ด ๋๋ค
-> (๋ค๋์ผ์ ๋ปฅํ๊ธฐ ์๋จ.)
ํด๊ฒฐ -> select distinct t
SQL์ distinct ์ถ๊ฐํด๋ ๋ฐ์ดํฐ๊ฐ ๋ค๋ฅด๋ฉด SQL ๊ฒฐ๊ณผ์์ ์ค๋ณต ์ ๊ฑฐ๊ฐ ์คํจํ๋ค.
๊ทธ๋์ JPA์์ distinct๊ฐ ์ถ๊ฐ๋ก ์ ํ๋ฆฌ์ผ์ด์
์์ ์ค๋ณต ์ ๊ฑฐ๋ฅผ ์๋ํ๋ค.
๊ทธ๋ฌ๋ฉด์ ๊ฐ์ ์๋ณ์๋ฅผ ๊ฐ์ง Team ์ํฐํฐ๋ฅผ ์ ๊ฑฐํ๋ค. -> ์ค๋ณต์ด ์ ๊ฑฐ๋ team ๊ฒฐ๊ณผ ๋ฆฌ์คํธ๋ฅผ ๋ฐํํ๋ค.
โ ์ผ๋์ผ, ๋ค๋์ผ ๊ฐ์ ๋จ์ผ ๊ฐ ์ฐ๊ด ํ๋๋ค์ ํ์น ์กฐ์ธํด๋ ํ์ด์ง ๊ฐ๋ฅํ๋ค.
์ผ๋๋ค ๊ด๊ณ์ธ ์ปฌ๋ ์ ์์๋ ๋ฐ์ดํฐ ๋ปฅํ๊ธฐ๊ฐ ๋๊ธฐ ๋๋ฌธ์ ์ฌ์ฉํ๋ฉด ์ํํ ๊ฒ์ด๋ค.
ex) TEAM์์ ํA๋ฅผ ์กฐํํ ๋ MEMBER๊ฐ 2๋ช ์กฐํ๊ฐ ๋๋ค๊ณ ํ์ ๋ ํ์ด์ง ์ฌ์ด์ฆ 1์ ๋ฐ์ดํฐ๋ฅผ ํ ๊ฑด๋ง ๊ฐ์ ธ์จ๋ค๊ณ ํ๋ค๋ฉด ํA๋ ํ์ ํ๋๋ง ๊ฐ์ง๊ณ ์๋ค๋ ์์ด ๋์ด ๋ฒ๋ฆฐ๋ค.
@BatchSize(size = 100)
@OneToMany(mappedBy = "team")
private List<Member> getMembers() {
return members;
}
์ด๋ฐ ์์ผ๋ก ์ธ ์ ์์ง๋ง ๊ธ๋ก๋ฒ ์ธํ ์ผ๋ก ๊ฐ์ ธ๊ฐ๋ ๋ฌด๊ด(์ค๋ฌด์์๋ ๊ธ๋ก๋ฒ ์ธํ ์ผ๋ก ๊น๊ณ ๊ฐ)
persistence.xml ์์ <property name="hibernate.default_batch_fetch_size" value="100" />
๊ทธ๋ฌ๋ฉด ์ฟผ๋ฆฌ๊ฐ N+1์ด ์๋ ํ ์ด๋ธ ์๋งํผ ๋ฑ ๋ง์ถฐ์ ๊ฐ์ ธ๊ฐ๊ฒ ๋จ
- ์ํฐํฐ์ ์ง์ ์ ์ฉํ๋ ๊ธ๋ก๋ฒ ๋ก๋ฉ ์ ๋ต๋ณด๋ค ์ฐ์ ์ ๋๋ค.
@OneToMany(fetch = FetchType.LAZY)
=> ๊ธ๋ก๋ฒ ์ ๋ต- ์ค๋ฌด์์ ๊ธ๋ก๋ฒ ๋ก๋ฉ ์ ๋ต์ ๋ชจ๋ ์ง์ฐ ๋ก๋ฉ์ผ๋ก ํ๋ค.
- ์ต์ ํ๊ฐ ํ์ํ ๊ณณ์๋ง ํ์น ์กฐ์ธ์ ์ ์ฉํ๋ค.
์ฌ๋ฌ ํ ์ด๋ธ์ ์กฐ์ธํด์ ์ํฐํฐ๊ฐ ๊ฐ์ง ๋ชจ์์ด ์๋ ์ ํ ๋ค๋ฅธ ๊ฒฐ๊ณผ๋ฅผ ๋ด์ผํ๋ค๋ฉด ํ์น ์กฐ์ธ ๋ณด๋ค๋ ์ผ๋ฐ ์กฐ์ธ์ ์ฌ์ฉํ๊ณ ํ์ํ ๋ฐ์ดํฐ๋ค๋ง ์กฐํํด์ DTO๋ก ๋ฐํํ๋ ๊ฒ์ด ํจ๊ณผ์ ์ด๋ค.
โ 3๊ฐ์ง ๋ฐฉ๋ฒ์ด ์๋ค.
1. ์ํฐํฐ๋ค์ ํ์น ์กฐ์ธ ์จ์ ์ํฐํฐ๋ค์ ์กฐํํด ์จ๋ค.
2. ํ์น ์กฐ์ธ ํด์ ์ ํ๋ฆฌ์ผ์ด์
์์ DTO๋ก ๋ฐ๊ฟ์ ํ๋ฉด์ ๋ฐํํ๋ค.
3. ์ฒ์ JPQL ์งค ๋๋ถํฐ ์์ new operation์ผ๋ก DTO๋ก ์ค์์นญํด์ ๊ฐ์ ธ์จ๋ค.
JPQL์์ ์ํฐํฐ๋ฅผ ์ง์ ์ฌ์ฉํ๋ฉด SQL์์ ํด๋น ์ํฐํฐ์ ๊ธฐ๋ณธ ํค ๊ฐ์ ์ฌ์ฉ
์ฆ select count(m.id) ๊ฐ ์๋ count(m) ์ ์ฌ์ฉํจ (๋์ด ๊ฒฐ๊ณผ๊ฐ ๊ฐ์)
ํด๋น ์ํฐํฐ์ ๊ธฐ๋ณธ ํค ๊ฐ์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์
์ํฐํฐ ์ง์ ์ฌ์ฉ -๊ธฐ๋ณธํค
String query = "select m From Member m where m.id = :memberId";
Member findMember = em.createQuery(query, Member.class)
.setParameter("memberId", member1.getId())
.getSingleResult();
System.out.println("findMember = " + findMember);
๊ฒฐ๊ณผ
where ember0_. id=?
findMember = Member{id=3, username='ํ์1', age=0}
์ํฐํฐ ์ง์ ์ฌ์ฉ -์ธ๋ํค
String query = "select m From Member m where m.team = :team";
List<Member> members = em.createQuery(query, Member.class)
.setParameter("team", teamA)
.getResultList();
for (Member member : members) {
System.out.println("member = " + member);
}
๊ฒฐ๊ณผ
where member0_.TEAM_ID=?
member = Member{id=3, username='ํ์ 1', age=0}
member = Member{id=4, username='ํ์ 2', age=0}
Named ์ฟผ๋ฆฌ(@Entity ๋ฐ์ ์)
์ ์ ์ฟผ๋ฆฌ๋ง ๊ฐ๋ฅ
๋งค์ฐ ํฐ ์ฅ์ 2๊ฐ์ง
โ ์ ํ๋ฆฌ์ผ์ด์
๋ก๋ฉ ์์ ์ ์ด๊ธฐํ ํ ์ฌ์ฌ์ฉ -> ๋ณํ์ง ์๋ ์ ์ ์ฟผ๋ฆฌ๋ฅผ ์ ํ๋ฆฌ์ผ์ด์
๋ก๋ฉ ์์ ์ SQL๋ก ํ์ฑํ๋ค.
โ ์ ํ๋ฆฌ์ผ์ด์ ๋ก๋ฉ ์์ ์ ์ฟผ๋ฆฌ๋ฅผ ๊ฒ์ฆ -> ์ฆ query ๋ฌธ๋ฒ์ด๋ ๋ฌธ์๊ฐ ๋ง๊ฒ ์ ๋ ฅ์ด ๋์๋์ง ๊ฒ์ฆ์ ํ๋ค.
SQL์ UPDATE, DELETE์ ๊ฐ๋ค๊ณ ๋ณด๋ฉด ๋จ
์์์ฑ ์ปจํ
์คํธ๋ฅผ ๋ฌด์ํ๊ณ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ง์ ์ฟผ๋ฆฌ
2 ๊ฐ์ง ๋ฐฉ๋ฒ ์ค ์ ํ
โ ๋ฒํฌ ์ฐ์ฐ์ ๋จผ์ ์ํ
โ ๋ฒํฌ ์ฐ์ฐ ์ํ ํ ์์์ฑ ์ปจํ
์คํธ ์ด๊ธฐํ(๊ถ์ฅ)
int resultCount = em.createQuery("update Member m set m.age = 20")
.executeUpdate();
Member findMember = em.find(Member.class, member1.getId());
System.out.println("findMember = " + findMember);
๋ฒํฌ ์ฐ์ฐ ์ DB์๋ ์ํฅ์ ์ฃผ์ง๋ง ์์์ฑ ์ปจํ
์คํธ์๋ ๋ฐ์์ด ๋์ง ์๋๋ค.
๊ทธ๋์ ์ด๊ธฐํ๋ก em.clear() ๋ฅผ ์ฌ์ฉํ๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก JPA๊ฐ ์ธ์ DB ์ปค๋ฅ์ ์ ๊ฐ์ง๊ณ ์ค๊ณ ์ธ์ DB ์ปค๋ฅ์ ์ DB์ ๋ฐํํ ๊น??
์ผ๋จ ์์์ฑ ์ปจํ ์คํธ๋ ๋์์ ํ๋ ค๋ฉด DB ์ปค๋ฅ์ ์ ๋ด๋ถ์ ์ผ๋ก ์ฌ์ฉ์ ํด์ผ LAZY ๋ก๋ฉ์ ํ๊ธฐ ๋๋ฌธ์ ์์์ฑ ์ปจํ ์คํธ์ DB ์ปค๋ฅ์ ์ ๊ต์ฅํ ๋ฐ์ ํ๊ฒ ๋งค์นญ์ด ๋์ด ์๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก DB ํธ๋์ญ์ ์ ์์ํ ๋ ์ฐ์์ฑ ์ปจํ ์คํธ๊ฐ DB ์ปค๋ฅ์ ์ ๊ฐ์ ธ์จ๋ค.
๊ทธ๋ฌ๋ฉด ์๋น์ค ๊ณ์ธต์์ ํธ๋์ญ์
์ ์์ํ๋ ์์ ์ ์ปค๋ฅ์
์ ๊ฐ์ ธ์ค๊ณ ์ด์ DB์๋ค ๋๋ ค์ค์ผ ํ๋๋ฐ ์ผ๋จ spring.jpa.open-in-view
๋ก default๋ก ์ผ์ ธ์๋ค.
์ด ๋ ๋ฐํ์ @Transaction
ํธ๋์ญ์
์ ๋๋ด๊ณ ๋ฐ์ ๋๊ฐ ๋๊น์ง ๋ฐํ์ ํ์ง ์๋๋ค.
์ด์ : LAZY ๋ก๋ฉ ์ผ์ด๋ ๋ ํ๋ก์ ๊ฐ์ฒด๋ฅผ DB์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์์ ์ฑ์ฐ๋ ํ๋ก์๋ฅผ ์ด๊ธฐํํ๋ ํด์ผ ํ๊ธฐ ๋๋ฌธ์ ์์์ฑ ์ปจํ
์คํธ๊ฐ DB ์ปค๋ฅ์
์ ๋ฌผ๊ณ ์ด์ ์์ด์ผ ํ๋ค.
'๋ฐ'์ ์๋ฏธ
๋ฐ์ด๋ผ๋ ๊ฒ์ ํธ๋์ญ์ ์ ๋ฒ์๋ฅผ ๋ฒ์ด๋ ์ง์ ์ผ๋ก @Transactional ๋ฉ์๋๊ฐ ๋๋๊ณ ํธ๋์ญ์ ์ด ์ข ๋ฃ๋๋ ์์ ์ ์๋ฏธํฉ๋๋ค. ์คํ๋ง์์๋ @Transactional ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ๋ฉด ํด๋น ๋ฉ์๋๊ฐ ์คํ๋๋ ๋์ ํธ๋์ญ์ ์ด ์ ์ง๋ฉ๋๋ค. ํธ๋์ญ์ ์ด ์๋ฃ๋๋ฉด(๋ฉ์๋ ์ข ๋ฃ) ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ปค๋ฅ์ ์ด ๋ฐํ๋ฉ๋๋ค.
OSIV๋ ์ด ํธ๋์ญ์ ์ด ๋๋๋ ์ด ์์์ฑ ์ปจํ ์คํธ๋ฅผ ๋๊น์ง ์ด๋ ค๋๋ค. (API๋ผ๋ฉด ์ ์ ํํ ๋ฐํํ ๋๊น์ง)
ํ๋ฉด์ธ ๊ฒฝ์ฐ๋ ๋ทฐ ํ
ํ๋ฆฟ ๊ฐ์ง๊ณ ๋ ๋๋ง ํ ๋๊น์ง ์ ์ ์๊ฒ response๊ฐ ๋๊ฐ ๋๊น์ง DB ์ปค๋ฅ์
์ ๋ฌผ๊ณ ์๋ค.
๊ฒฐ๋ก -> ์์์ฑ ์ปจํ
์คํธ๋ DB ์ปค๋ฅ์
์ด ๋๊น์ง ์ด์์๋ค.
๊ทธ๋ฌ๋ฉด spring.jpa.open-in-view: false
๋ก ํด์ OSIV ์ข
๋ฃํ๋ฉด ํธ๋์ญ์
์ ์์ํ๊ณ ๋๋ ๋๊น์ง๋ง DB ์ปค๋ฅ์
์ ์ ์งํ๋ค.
๊ทธ๋์ ์ด๋ค ๋ก์ง, ์ธ๋ถ API ํธ์ถํด๋ DB ์ปค๋ฅ์
์ ๊ฐ์ง๊ณ ์๊ธฐ ๋๋ฌธ์ ๋ฑ ๋ฐํํด์ ํธ๋ํฝ์ด ๋ง๊ฑฐ๋ ์ฌ์ฉ์ ์์ฒญ์ด ๋ง์ ๊ฒฝ์ฐ์๋ ํจ์ฌ ๋ ์ปค๋ฅ์
์ ์ ์ฐํ๊ฒ ์ธ ์ ์๋ค.
์ง์ฐ ๋ก๋ฉ์ ํ๋ ค๋ฉด ์์์ฑ ์ปจํ
์คํธ๊ฐ ์ด์ ์์ด์ผ ํ๋ค.
ํธ๋์ญ์
๋๋ฌ๋ค๊ณ ์์์ฑ ์ปจํ
์คํธ๊ฐ ์์ด์ ธ๋ฒ๋ฆฌ๋ฉด ๋ชจ๋ ์ง์ฐ ๋ก๋ฉ์ ํธ๋์ญ์
์์์ ์ฒ๋ฆฌํด์ผ ํ๋ค.
(์ ๊ทธ๋ฆผ์์ ์์์ฑ ์ปจํ
์คํธ ์์กด ๋ฒ์์์ ๋ชจ๋ ๋๋ด์ผ ํจ.)
๊ฒฐ๋ก -> ํธ๋์ญ์ ์ด ๋๋๊ธฐ ์ ์ ์ง์ฐ๋ก๋ฉ์ ๊ฐ์ ๋ก ํธ์ถํด์ผ ํ๋ค.
open-in-view: false
์ด๊ฒ์ ํด์ฃผ๋ฉด LazyInitializationException ์ด ๋ฐ์ํ๋ค.
Member๊ฐ proxy์ธ๋ฐ ์ด๊ธฐํ๋ฅผ ๋ชปํ๋ค.
๊ทธ๋ฆผ์์ ๋ณด๋ฉด Controller์์ ์์์ฑ ์ปจํ
์คํธ๊ฐ ์๊ธฐ ๋๋ฌธ์ ์๋ฌ๊ฐ ๋ฐ์ํ ๊ฒ์ด๋ค.
ํธ๋์ญ์
์์์ ๋ค ๋ก๋ฉํด ๋๊ฑฐ๋ fetchJoin์ ์ฌ์ฉํด์ ํด๊ฒฐํ๋ฉด ๋๋ค.
-> ํธ๋์ญ์
์์ผ๋ก ๊ฐ์ ธ๊ฐ๋ ๊ฒ!!
์ปค๋งจ๋์ ์ฟผ๋ฆฌ๋ฅผ ๋ถ๋ฆฌํ๋ฉด ๋๋ค.
https://en.wikipedia.org/wiki/Command-query_separation
๊ฐ๋ฐํ ๋ ํ ๋ ํฌ์งํ ๋ฆฌ๋ ํ ์๋น์ค์ ์ฟผ๋ฆฌ์ฉ์ด๋ ํต์ฌ ๋น์ฆ๋์ค์ฉ์ ๊ฐ์ด ๋ น์ฌ๋์ผ๋ฉด ๋ณต์กํ ์กฐํ์ฉ์ด 20~30๊ฐ ์ผ ๋ ์ ์ง๋ณด์๊ฐ ์ด๋ ค์์ง๋ค.
Service ์์ ~~Service(ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง)์ ~~QueryService(ํ๋ฉด์ด๋ API์ ๋ง์ถ ์๋น์ค๋ก ์ฃผ๋ก ์ฝ๊ธฐ ์ ์ฉ ํธ๋์ญ์ ์ฌ์ฉ)๋ก ๋๋์ด์ ๊ฐ๋ฐํ๋ ๊ฒ์ด ๋ฐ๋์งํ๋ค.
๊ฒฐ์
๊ณ ๊ฐ ์๋น์ค ๊ธฐ๋ฐ์ ํธ๋ํฝ์ด ๋ง์ ์ค์๊ฐ API๋ค์ ๋ง์ด ์ ๊ณตํด์ผ ํ๋ API ์๋ฒ๋ off-session-inview ๋๊ธฐ
์๋น์ค API ํธ๋ํฝ์ด ์ ๋ง ๋ง๋ค๋ฉด ๋๊ธฐ
ADMIN ์ฒ๋ผ ์ปค๋ฅ์ ์ ๋ง์ด ์ฌ์ฉํ์ง ์๋ ๊ณณ์์๋ OSIV ์ผ๊ธฐ.
์ปค๋ฅ์ ์ ๋ช ๊ฐ ์ ์ฐ๊ธฐ ๋๋ฌธ์.
๊ธฐ๋ณธ์ ์ผ๋ก๋ ํค๋ ๊ฒ์ด ์ข๊ธด ํ๋ฐ ์ฑ๋ฅ์ด ์ด์๊ฐ ๋๋ฉด ๋๋ ๊ฒ์ด ์ข๋ค.