JPA(Java Persistence API)๋ Java ์ง์์์ ์ฌ์ฉํ๋ ORM ๊ธฐ์ ์ ํ์ค ์ฌ์์ด๋ค. JPA ํ์ค ์ฌ์์ ๊ตฌํํ ๊ตฌํ์ฒด๋ก๋ ๋ํ์ ์ผ๋ก Hibernate ORM์ด ์๋ค.
๋ฐ์ดํฐ ์์ธ์ค ๊ณ์ธต์์ JPA๋ ์๋จ์ ์์นํ๊ณ ์๊ณ JPA์ ๊ตฌํ์ฒด์ธ Hibernate ORM์ ํตํด์ ๋ฐ์ดํฐ์ ์ ์ฅ, ์กฐํ ๋ฑ์ ์์ ์ด ์งํ๋๋ค. ์ด๋ Hibernate ORM์ ๋ด๋ถ์ ์ผ๋ก JDBC API๋ฅผ ์ด์ฉํด์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ๊ทผํ๊ฒ ๋๋ค.
JPA์์ P๋ Persistence๋ก "์ค๋ ์ง์๋๊ฒ ํ๋ค"๋ผ๋ ๋ชฉ์ ์ ๊ฐ์ง๊ณ ์๋ค. ORM์ ๊ฐ์ฒด์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ
์ด๋ธ์ ๋งคํ์ ํตํด ์ํฐํฐ ํด๋์ค ๊ฐ์ฒด ์์ ํฌํจ๋ ์ ๋ณด๋ฅผ ํ
์ด๋ธ์ ์ ์ฅํ๋ ๊ธฐ์ ์ด๋ค.JPA์์๋ ํ
์ด๋ธ๊ณผ ๋งคํ๋๋ ์ํฐํฐ ๊ฐ์ฒด ์ ๋ณด๋ฅผ ์์์ฑ ์ปจํ
์คํธ(Persistence Context)์ ๋ณด๊ดํด์ ์ ํ๋ฆฌ์ผ์ด์
๋ด์์ ์ค๋ ์ง์๋๋๋ก ํ๋ค.
์์์ฑ ์ปจํ ์คํธ๋ 1์ฐจ ์บ์์ ์ฐ๊ธฐ ์ง์ฐ SQL ์ ์ฅ์๋ผ๋ ์์ญ์ด ์๋ค. ์ด์ ๋ํด์๋ JPA API๋ก ํ์ธํด๋ณด๋ ค ํ๋ค.
dependencies {
...
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
...
}
spring:
h2:
console:
enabled: true
path: /h2
datasource:
url: jdbc:h2:mem:test
jpa:
hibernate:
ddl-auto: create
show-sql: true
ddl-auto๋ฅผ create๋ก ์ค์ ํ์ฌ ๊ธฐ์กด ํ ์ด๋ธ ์ญ์ ํ ๋ค์ ์์ฑํ๋๋ก ์ค์ ํ๊ณ show-sql์ true๋ก ์ค์ ํ์ฌ DB์ ์ฟผ๋ฆฌ๋ฌธ์ ๋ ๋ ธ์ ๋ ๋ณด์ด๋๋ก ์ค์ ํ๋ค.
ddl-auto ์ต์ ์ข ๋ฅ
create : ๊ธฐ์กดํ ์ด๋ธ ์ญ์ ํ ๋ค์ ์์ฑ (DROP + CREATE)
create-drop : create์ ๊ฐ์ผ๋ ์ข ๋ฃ์์ ์ ํ ์ด๋ธ DROP
update : ๋ณ๊ฒฝ๋ถ๋ง ๋ฐ์(์ด์DB์์๋ ์ฌ์ฉํ๋ฉด ์๋จ)
validate : ์ํฐํฐ์ ํ ์ด๋ธ์ด ์ ์ ๋งคํ๋์๋์ง๋ง ํ์ธ
none : ์ฌ์ฉํ์ง ์์(์ฌ์ค์ ์๋ ๊ฐ์ด์ง๋ง ๊ด๋ก์ none์ด๋ผ๊ณ ํ๋ค.)
์ฃผ์ํ ์
์ด์ ์ฅ๋น์์๋ ์ ๋ create, create-drop, update ์ฌ์ฉํ๋ฉด ์๋๋ค.
๊ฐ๋ฐ ์ด๊ธฐ ๋จ๊ณ๋ create ๋๋ update
ํ ์คํธ ์๋ฒ๋ update ๋๋ validate
์คํ ์ด์ง๊ณผ ์ด์ ์๋ฒ๋ validate ๋๋ none
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
@Configuration
public class JpaBasicConfig {
private EntityManager em;
private EntityTransaction tx;
@Bean
public CommandLineRunner testJpaBasicRunner(EntityManagerFactory emFactory) {
this.em = emFactory.createEntityManager();
this.tx = em.getTransaction();
return args -> {
// ์ฌ๊ธฐ์ ํ
์คํธ ์คํ
};
}
}
JPA์ ์์์ฑ ์ปจํ ์คํธ๋ EntityManger์ ์ํด ๊ด๋ฆฌ๋๋๋ฐ EntityMangerFactory๋ฅผ DI ๋ฐ์ EntityManger๋ฅผ ์์ฑํ ์ ์๋ค.
@Getter
@Setter
@NoArgsConstructor
@Entity
public class Member {
@Id
@GeneratedValue
private Long memberId;
private String email;
public Member(String email) {
this.email = email;
}
}
@Configuration
public class JpaBasicConfig {
private EntityManager em;
private EntityTransaction tx;
@Bean
public CommandLineRunner testJpaBasicRunner(EntityManagerFactory emFactory) {
this.em = emFactory.createEntityManager();
this.tx = em.getTransaction();
return args -> {
Member member = new Member("hgd@gmail.com");
em.persist(member);
Member resultMember = em.find(Member.class, 1L);
System.out.println("Id: " + resultMember.getMemberId() + ", email: " +
resultMember.getEmail());
};
}
}
persist() ๋ฉ์๋๋ ์์์ฑ ์ปจํ
์คํธ์ ๊ฐ์ฒด๋ฅผ ์ ์ฅํ๋ ๋ฉ์๋์ด๋ค. ํด๋น ๋ฉ์๋๋ฅผ ์คํ์์ผฐ์๋ ๋ก๊ทธ์๋ insert ์ฟผ๋ฆฌ๊ฐ ๋ณด์ด์ง ์๋๋ค. ์ด๋ ์์์ฑ ์ปจํ
์คํธ์ 1์ฐจ ์บ์์ member ๊ฐ์ฒด๊ฐ ์ ์ฅ๋๊ณ ์ฐ๊ธฐ ์ง์ฐ SQL ์ ์ฅ์์ INSERT ์ฟผ๋ฆฌ๋ฌธ์ด ๋ค์ด๊ฐ์ง๋ง ์์ง DB์ ์ฟผ๋ฆฌ๋ฌธ์ ๋ ๋ฆฌ๊ณ ์์ง ์๊ธฐ ๋๋ฌธ์ด๋ค.
@Configuration
public class JpaBasicConfig {
private EntityManager em;
private EntityTransaction tx;
@Bean
public CommandLineRunner testJpaBasicRunner(EntityManagerFactory emFactory) {
this.em = emFactory.createEntityManager();
this.tx = em.getTransaction();
return args -> {
tx.begin(); // ์ถ๊ฐ
Member member = new Member("hgd@gmail.com");
em.persist(member); // insert ์ฟผ๋ฆฌ๋ฌธ ์คํ
Member resultMember = em.find(Member.class, 1L); // 1์ฐจ ์บ์์์ ๋ฐ์ดํฐ ๊ฐ์ ธ์ด
System.out.println("Id: " + resultMember.getMemberId() + ", email: " +
resultMember.getEmail());
tx.commit(); // ์ถ๊ฐ
};
}
}
์์ ์ฝ๋์ ๋น๊ตํ๋ค๋ฉด tx.begin()
, tx.commit()
์ด ์ถ๊ฐ๋์๋ค. ํธ๋์ญ์
์ ์์ํ๊ณ ์ปค๋ฐํ๋ ์์ ์ ์์์ฑ ์ปจํ
์คํธ์ SQL ์ฐ๊ธฐ ์ง์ฐ ์ ์ฅ์์ ์ ์ฅ๋์ด ์๋ ์ฟผ๋ฆฌ๋ฌธ์ ์คํํ๊ฒ ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋์ SQL ์ฐ๊ธฐ ์ง์ฐ ์ ์ฅ์๋ ๋น์์ง๊ฒ ๋๋ค. commit()
๋ฉ์๋๋ฅผ ํตํด DB์ ๋ฐ์์ด ๋์์ง๋ง ์ฌ์ค ๋ด๋ถ์ ์ผ๋ก flush()
๋ฉ์๋๊ฐ ํธ์ถ๋์ด ์์์ฑ ์ปจํ
์คํธ์ ๋ณ๊ฒฝ์ฌํญ์ด DB์ ๋ฐ์๋๊ฒ ๋๋ค. ํธ๋์ญ์
์ด ์คํ๋๋ ์ค๊ฐ์ em.find()
๋ฉ์๋๋ฅผ ์คํ์ํค๋ฉด DB์์ ์กฐํํด์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ๊ฒ ๊ฐ์ง๋ง 1์ฐจ ์บ์์ ์ ์ฅ๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ง๊ณ ์ค๊ฒ ๋๋ค.
@Configuration
public class JpaBasicConfig {
private EntityManager em;
private EntityTransaction tx;
@Bean
public CommandLineRunner testJpaBasicRunner(EntityManagerFactory emFactory) {
this.em = emFactory.createEntityManager();
this.tx = em.getTransaction();
return args -> {
tx.begin();
Member member = new Member("hgd@gmail.com");
em.persist(member);
member.setEmail("iss@gmail.com"); // ์ด๋ฉ์ผ ๋ณ๊ฒฝ
Member resultMember = em.find(Member.class, 1L);
System.out.println("Id: " + resultMember.getMemberId() + ", email: " +
resultMember.getEmail());
tx.commit();
};
}
}
persist()
๋ฉ์๋๋ฅผ ๋จผ์ ์คํํ๊ณ member์ ์ด๋ฉ์ผ์ ๋ณ๊ฒฝํ๋ค๋ฉด 1์ฐจ ์บ์์ ๋ค์ด์๋ ๋ฐ์ดํฐ์ ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ insert ์ฟผ๋ฆฌ๋ฌธ์ด ์คํ๋๊ณ ๋์ update ์ฟผ๋ฆฌ๋ฌธ์ด ์คํ๋๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก @Entity
, @Id
์ด๋
ธํ
์ด์
์ ๋ถ์ฌ์ฃผ๊ณ ์ํฐํฐ๋ฅผ ์ค์ ํ๋ค.
@Entity
public class Member {
@Id
private long memberId;
private String email;
}
@Entity
, @Id
์ด๋
ธํ
์ด์
์ ํ์์ด๋ค. ์ถ๊ฐ์ ์ธ JPA ์ด๋
ธํ
์ด์
๋ฆฌ์คํธ๋ ํ์ํ ๋ ์ฐพ์์ ์ฌ์ฉํ์.
@Id
์ด๋
ธํ
์ด์
์ ๋ถ์ธ ํ๋๊ฐ ํ
์ด๋ธ์ ๊ธฐ๋ณธํค๊ฐ ๋๋๋ฐ ๊ธฐ๋ณธํค์ ์์ฑ์ ๋ต ๋ค์ด ์๋ค.
@Id
์ด๋
ธํ
์ด์
๋ง ์ฌ์ฉ ํ DB์ ๋ฃ์ ๋ ์ง์ ๋ฃ๊ธฐ) @GeneratedValue
์ด๋
ธํ
์ด์
์ด์ฉ)@SequenceGenerator
ํ์@TableGenerator
ํ์
์ถ์ฒ : ๊ธฐ๋ณธ ํค ์์ฑ ์ ๋ต
IDENTITY ์ ๋ต ์ฌ์ฉ
member ํ
์ด๋ธ์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ฉด์ ์๋์ผ๋ก ๊ธฐ๋ณธํค ๊ฐ์ด ์ค์ ๋๋ค.
SEQUENCE ์ ๋ต ์ฌ์ฉ
DB์ SEQUENCE๋ฅผ ์์ฑํ๊ณ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ธฐ ์ ์ SEQUENCE๋ฅผ ์กฐํํด ๊ธฐ๋ณธํค ๊ฐ์ ๊ฐ์ ธ์ ์ ์ฅํ๋ค.
๋ณดํต ์ธ๋ํค๊ฐ ์๋ ์ํฐํฐ์์ @ManyToOne
์ด๋
ธํ
์ด์
์ ๋ถ์ฌ ๋จ๋ฐฉํฅ์ผ๋ก ์ฐ๊ด๊ด๊ณ๋ฅผ ๊ตฌ์ฑํ๊ณ ํ์ํ๋ค๋ฉด ์๋ฐฉํฅ์ผ๋ก ๊ตฌํ์ ํ๋ค.
์ธ๋ํค๊ฐ ์๋ ์ํฐํฐ์ @JoinColumn
์ด๋
ธํ
์ด์
์ ์ด์ฉํ์ฌ ์ฐ๊ด๊ด๊ณ๋ฅผ ๋งคํํ๋ค. ๋ง์ฝ ์๋ฐฉํฅ ๊ด๊ณ๋ฅผ ๊ตฌ์ฑํ๊ณ ์ ํ๋ค๋ฉด ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ์ @OneToMany
์ด๋
ธํ
์ด์
์ mappedBy๋ฅผ ์ด์ฉํ์ฌ ์ธ๋ํค๊ฐ ์๋ ํ
์ด๋ธ์ ์ ์ธํ ์ฐ๊ด๊ด๊ณ ์ฃผ์ธ์ ๋ณ์๋ช
์ ์ ์ด์ค๋ค. ๊ทธ๋ฆฌ๊ณ ๋ค๋๋ค ๊ด๊ณ์ผ ๊ฒฝ์ฐ 1:N, N:1๋ก ๋ถํ ํ์ฌ ์ฐ๊ด๊ด๊ณ๋ฅผ ๋งคํํ๋ค.
์ข์ ๋ด์ฉ ๊ฐ์ฌํฉ๋๋ค~