Spring DB ์ ๊ทผ ๊ธฐ์
- JDBC ๋ SQL ๋ก ์๋ฒ๋ DB ์ฐ๊ฒฐํ๋ ๊ธฐ์ ์ด๋ค.
- JdbcTemplate ์ ํตํด SQL ๋ฅผ ํธ๋ฆฌํ๊ฒ ๋ ๋ฆด ์ ์๋ค.
- JPA ๊ธฐ์ ์ด ๋ฑ๋ก, ์์ , ์ญ์ ์ฟผ๋ฆฌ๋ฅผ ๋ง๋ค์ด์ ๋ ๋ ค์ค๋ค.
์์ JDBC
-
build.gradle
์์ jdbc, h2 DB ๊ด๋ จ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ถ๊ฐํ๋ค.implementation 'org.springframework.boot:spring-boot-starter-jdbc' runtimeOnly 'com.h2database:h2'
-
application.properties
์์ ์คํ๋ง ๋ถํธ DB ์ฐ๊ฒฐ ์ค์ ์ ์ถ๊ฐํ๋ค.spring.datasource.url=jdbc:h2:tcp://localhost/~/test spring.datasource.driver-class-name=org.h2.Driver spring.datasource.username=sa
Jdbc Repository ๊ตฌํ ํ ์คํ๋ง ์ค์ ๋ณ๊ฒฝ
@Configuration
public class SpringConfig {
private final DataSource dataSource;
public SpringConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
// return new MemoryMemberRepository();
return new JdbcMemberRepository(dataSource);
}
}
- ๊ธฐ์กด์ ์ฌ์ฉํ๋
MemoryMemberRepository
๋ฅผJdbcMemberRepository
๋ก ๋ณ๊ฒฝํ๋ค. DataSource
๋ DB Connection ์ ํ๋ํ ๋ ์ฌ์ฉํ๋ ๊ฐ์ฒด๋ค.- ์คํ๋ง ๋ถํธ๋ DB Connection ์ ๋ณด๋ฅผ ๋ฐํ์ผ๋ก
DataSource
๋ฅผ ์์ฑํ๊ณ ์คํ๋ง ๋น์ผ๋ก ๋ง๋ค์ด DI ๋ฅผ ๋ฐ์ ์ ์๋ค. MemberService
๋ ์ธํฐํ์ด์ค์ธMemberRepository
๋ฅผ ์์กดํ๊ณ ์๊ธฐ ๋๋ฌธ์MemberRepository
์ ๊ตฌํ์ฒด๋ฅผ ๋ชจ๋ ์ฐธ์กฐํ ์ ์๋ค.- ๋ณ๊ฒฝ๋
@Configuration
์ ๋ฐํ์ผ๋ก ์คํ๋ง ์ปจํ ์ด๋ ๋ด๋ถ์์ DI ๋ฅผmemberRepository
๋ก ๋ณ๊ฒฝํ ์ ์๋ค. - OCP(Open-Closed Principle, ๊ฐ๋ฐฉ-ํ์ ์์น)
- ํ์ฅ์๋ ์ด๋ ค์๊ณ , ์์ , ๋ณ๊ฒฝ์๋ ๋ซํ์๋ค.
- ๊ฐ์ฒด์งํฅ ํ๋ก๊ทธ๋๋ฐ์ ๋คํ์ฑ์ ํตํด OCP ์์น์ ์ง์ผ๋ผ ์ ์๋ค.
- ์คํ๋ง์ DI ๋ฅผ ์ฌ์ฉํ์ฌ ๊ธฐ์กด ์ฝ๋๋ฅผ ์ ํ ์๋์ง ์๊ณ , ์ค์ ๋ง์ผ๋ก ๊ตฌํ ํด๋์ค๋ฅผ ๋ณ๊ฒฝํ ์ ์๋ค.
์คํ๋ง ํตํฉ ํ ์คํธ
- ํ
์คํธ ํด๋์ค ๋จ์์
@SpringBootTest
๋ฅผ ์ถ๊ฐํ์ฌ ์คํ๋ง ์ปจํ ์ด๋์ ํ ์คํธ๋ฅผ ํจ๊ป ์คํํ๋ค. - ํ
์คํธ ํด๋์ค ๋จ์์
@Transactional
์ ์ถ๊ฐํ๋ฉด, ํ ์คํธ ์์ ์ ์ Transaction ์ ์์ํ๊ณ , ํ ์คํธ ์๋ฃ ํ์ ํญ์ ๋กค๋ฐฑํ๋ค.
์คํ๋ง JdbcTemplate
- ์์ JDBC ์์ ์ค๋ณต๋๋ ์ฝ๋๋ค์ ์ ๊ฑฐํ์ฌ ๊ฐ๋จํ๊ฒ DB ์ ์ ๊ทผํ ์ ์๋ ์ฝ๋๋ฅผ ์์ฑํ๋๋ฐ ๋์์ ์ฃผ๋ template ์ด๋ค.
public JdbcTemplateMemberRepository(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource);
}
- ์คํ๋ง ์ปจํ
์ด๋์ DI ๋ฅผ ํตํด
JdbcTemplate
์DataSource
๋ฅผ ์ฃผ์ ํ์ฌ ์ฌ์ฉํ๋ค.
SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate);
jdbcInsert.withTableName("member").usingGeneratedKeyColumns("id");
Map<String, Object> parameters = new HashMap<>();
parameters.put("name", member.getName());
Number key = jdbcInsert.executeAndReturnKey(new MapSqlParameterSource(parameters));
member.setId(key.longValue());
SimpleJdbcInsert
๋ฅผ ํตํดINSERT
์ฟผ๋ฆฌ๋ฌธ๊ณผ ํจ๊ป ์๋์์ฑ๋๋ id ๊ฐ์ ๋ฐํ ๋ฐ์ ์ ์๋ค.
JPA
JPA ๋ฅผ ํตํด ๊ธฐ๋ณธ์ ์ธ SQL ์ JPA ๊ฐ ๋ง๋ค์ด ์คํํ๊ณ , SQL ์ค์ฌ ์ค๊ณ์์ ๊ฐ์ฒด ์ค์ฌ ์ค๊ณ๋ก ๊ฐ๋ฐ์ ์งํํ ์ ์๋ค. ์ฆ, JPA ๋ฅผ ํตํด ๊ฐ๋ฐ ์์ฐ์ฑ์ ํฌ๊ฒ ๋์ผ ์ ์๋ค. JPA ๋ ์ธํฐํ์ด์ค์ด๋ฉฐ ๊ตฌํ์ฒด๋ก Hibernate ๋ฅผ ๋ง์ด ์ฌ์ฉํ๋ค. JPA ๋ ORM ๊ธฐ์ ๋ก์ Object Relational Mapping ์ ์ฝ์์ด๋ค. ์ฆ, ๊ฐ์ฒด์ RDB ๋ฅผ ๋งคํํด์ฃผ๋ ๊ธฐ์ ์ด๋ค.
build.gradle
์ dependencies ์ถ๊ฐ
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
// implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
spring-boot-starter-data-jpa
๋ด๋ถ์ jdbc ๊ด๋ จ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํฌํจํ๊ธฐ ๋๋ฌธ์ jdbc ๋ ์ ๊ฑฐํด๋ ๋๋ค.
application.properties
์ ์ค์ ์ถ๊ฐ
spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
show-sql
์ค์ ์ ํตํด JPA ๊ฐ ์์ฑํ๋ SQL ์ ์ถ๋ ฅํ๋ค.ddl-auto
JPA ๋ ํ ์ด๋ธ์ ์๋์ผ๋ก ์์ฑํ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋๋ฐ,none
์ ์ค์ ํด์ฃผ๋ฉด ํด๋น ๊ธฐ๋ฅ์ ์ฌ์ฉํ์ง ์๊ฒ๋๋ค.create
์ค์ ์ ์ํฐํฐ ์ ๋ณด๋ฅผ ๋ฐํ์ผ๋ก ํ ์ด๋ธ๋ ์ง์ ์์ฑํด์ค๋ค.
JPA ์ํฐํฐ ๋งคํ
package hello.hellospring.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
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;
}
}
@Entity
๋ฅผ ํตํด ๊ฐ์ฒด๊ฐ ์ํฐํฐ๋ผ๋ ๊ฒ์ ๋ช
์ํ๋ค.
@Id
๋ฅผ ํตํด ํด๋น ํ๋๊ฐ DB ์ Id ๊ฐ์ด๋ผ๊ณ ๋ช
์ํด์ค๋ค.
@GeneratedValue(strategy = GenerationType.IDENTITY)
๋ฅผ ํตํด AUTO_INCREMENT ๋๋ ๊ฐ์ด๋ผ๋ ๊ฒ์ ๋ช
์ํด์ค๋ค.
JPA ํ์ Repository
package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import javax.persistence.EntityManager;
import java.util.List;
import java.util.Optional;
public class JpaMemberRepository implements MemberRepository {
private final EntityManager em;
public JpaMemberRepository(EntityManager em) {
this.em = em;
}
public Member save(Member member) {
em.persist(member);
return member;
}
public Optional<Member> findById(Long id) {
Member member = em.find(Member.class, id);
return Optional.ofNullable(member);
}
public List<Member> findAll() {
return em.createQuery("select m from Member m", Member.class)
.getResultList();
}
public Optional<Member> findByName(String name) {
List<Member> result = em.createQuery("select m from Member m where m.name = :name", Member.class)
.setParameter("name", name)
.getResultList();
return result.stream().findAny();
}
}
Spring DI ๋ฅผ ํตํด EntityManager ๋ฅผ ์ฃผ์
ํด์ค๋ค. ์ฃผ์
๋ em ์ ํตํด ๋ชจ๋ ๊ธฐ๋ฅ์ ๊ตฌํํ ์ ์๋ค.
JPA ๋ ์กฐํ ๋ก์ง์ด Id ๊ฐ์ผ๋ก ์ฐพ๋ ๊ธฐ๋ฅ๋ง ์ ๊ณตํ๊ธฐ ๋๋ฌธ์, findByName(String name)
์ ๊ฐ์ ๋ก์ง์ ์ํํ๊ธฐ ์ํด์ JPQL ์ด๋ผ๋ ๊ฐ์ฒด ์งํฅ ์ฟผ๋ฆฌ๋ฅผ ํตํด JPA ๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
Spring Data ๋ฅผ ์ฌ์ฉํ๋ฉด JPQL ๋ ์ฌ์ฉํ์ง ์์ ์ ์๋ค.
Service ๊ณ์ธต์ Transaction ์ถ๊ฐ
import org.springframework.transaction.annotation.Transactional
@Transactional
public class MemberService {}
๋ชจ๋ ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ด Transaction ์์์ ์คํ๋์ด์ผ ํ๊ธฐ ๋๋ฌธ์ @Transactional
์ ๋ํ
์ด์
์ด ํ์ํ๋ค.
JPA ๋ฅผ ์ฌ์ฉํ๋๋ก ์คํ๋ง ์ค์ ๋ณ๊ฒฝ
package hello.hellospring;
import hello.hellospring.repository.*;
import hello.hellospring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.persistence.EntityManager;
import javax.sql.DataSource;
@Configuration
public class SpringConfig {
private final DataSource dataSource;
private final EntityManager em;
public SpringConfig(DataSource dataSource, EntityManager em) {
this.dataSource = dataSource;
this.em = em;
}
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
// return new MemoryMemberRepository();
// return new JdbcMemberRepository(dataSource);
// return new JdbcTemplateMemberRepository(dataSource);
return new JpaMemberRepository(em);
}
}
EntityManager ๋ฅผ DI ๋ฐ๊ธฐ ์ํด์ SpringConfig ํด๋์ค์์ ์ถ๊ฐ์ ์ธ ์ค์ ์ ํด์ฃผ์ด์ผ ํ๋ค.
Spring Data JPA
Spring Boot ์ JPA ๋ง ์ฌ์ฉํด๋ ๊ฐ๋ฐ ์์ฐ์ฑ์ด ๋ง์ด ์ฆ๊ฐํ๋ฉฐ, ๊ฐ๋ฐํด์ผ ํ ์ฝ๋๋ ํ์ฐํ ์ค์ด๋ ๋ค. Spring Data JPA ๋ฅผ ์ฌ์ฉํ๋ฉด, Repository ๊ตฌํ ํด๋์ค ์์ด ์ธํฐํ์ด์ค ๋ง์ผ๋ก ๊ฐ๋ฐ์ ์๋ฃํ ์ ์๋ค. Spring Data JPA ๋ JPA ๋ฅผ ํธ๋ฆฌํ๊ฒ ํด์ฃผ๋ ๋๊ตฌ์ผ ๋ฟ์ด๊ธฐ ๋๋ฌธ์ JPA ๋ฅผ ๋จผ์ ํ์ตํด์ผํ๋ค.
์คํ๋ง ๋ฐ์ดํฐ JPA ํ์ Repository
package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
public interface SpringDataJpaMemberRepository extends JpaRepository<Member, Long>, MemberRepository {
@Override
Optional<Member> findByName(String name);
}
JpaRepository
์ ์ฌ์ฉํ Repository ์ธ MemberRepository
๋ฅผ ์์๋ฐ์ ์๋ก์ด ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ํ ๊ธฐ์กด์ JPQL ๋ก ๊ตฌํํ ๋ฉ์๋๋ฅผ Override ํด์ฃผ๋ฉด ๋์ด๋ค.
Spring Data ๊ฐ ํด๋น ์ธํฐํ์ด์ค๋ฅผ ์ฐพ์ ๋ฉ์๋๋ฅผ ์์์ ๋ง๋ค์ด ์ค๋ค.
์คํ๋ง ๋ฐ์ดํฐ JPA ํ์ Repository ๋ฅผ ์ฌ์ฉํ๋๋ก ์ค์ ๋ณ๊ฒฝ
package hello.hellospring;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig {
private final MemberRepository memberRepository;
public SpringConfig(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Bean
public MemberService memberService() {
return new MemberService(memberRepository);
}
}
ํด๋น ์ค์ ์ ํตํด Spring ์ด ์์์ SpringDataJpaMemberRepository
๋ฅผ ์ฐพ์ DI ํด์ค๋ค.
์ฃผ์ํ ์ ์, SpringDataJpaMemberRepository
์ธ์ ๋ค๋ฅธ ๊ตฌํ์ฒด์ @Repository
์ ๋ํ
์ด์
์ด ์ถ๊ฐ๋์ด ์์ผ๋ฉด, Bean ์ด 2๊ฐ์ง๊ฐ ๋ฑ๋ก๋์ด ์๊ธฐ ๋๋ฌธ์ ์ปดํ์ผ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
์คํ๋ง ๋ฐ์ดํฐ JPA ์ ๊ณต ๊ธฐ๋ฅ
์คํ๋ง ๋ฐ์ดํฐ JPA ๋ ์ธํฐํ์ด์ค๋ฅผ ํตํด ๊ธฐ๋ณธ์ ์ธ CRUD ๋ฅผ ์ ๊ณตํ๋ค. ๋ํ, findByName()
findByEmail()
์ฒ๋ผ ๋ฉ์๋ ์ด๋ฆ ๋ง์ผ๋ก๋ JPQL ์ ์๋์ผ๋ก ์์ฑํ์ฌ ์กฐํ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
์ถ๊ฐ์ ์ผ๋ก, PagingAndSortingRepository
๋ฅผ ํตํด ํ์ด์ง ๊ธฐ๋ฅ๋ ์๋์ผ๋ก ์ ๊ณตํ๋ค.
์ค๋ฌด์์๋ JPA ์ ์คํ๋ง ๋ฐ์ดํฐ JPA ๋ฅผ ๊ธฐ๋ณธ์ผ๋ก ์ฌ์ฉํ๊ณ , ๋ณต์กํ ๋์ ์ฟผ๋ฆฌ๋ Querydsl ์ด๋ผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ค. Querydsl ์ ์ฌ์ฉํ๋ฉด ์๋ฐ ์ฝ๋๋ก ์ฟผ๋ฆฌ๋ ์์ ํ๊ฒ ์์ฑํ ์ ์๊ณ , ๋์ ์ฟผ๋ฆฌ๋ ํธ๋ฆฌํ๊ฒ ์์ฑํ ์ ์๋ค. ์ด ์กฐํฉ์ผ๋ก ํด๊ฒฐํ๊ธฐ ์ด๋ ค์ด ์ฟผ๋ฆฌ๋ JPA ๊ฐ ์ ๊ณตํ๋ ๋ค์ดํฐ๋ธ ์ฟผ๋ฆฌ๋ฅผ ์ฌ์ฉํ๊ฑฐ๋, JdbcTemplate ์ ์ฌ์ฉํ ์ ์๋ค.
SQL ์ค์ฌ์ ์ธ ๊ฐ๋ฐ์ ๋ฌธ์ ์
JPA ์ ๋ชจ๋ ์๋ฐ ๋ฐ์ดํฐ ์ ์ฅ ๊ธฐ์
ํ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ์ฒด ์งํฅ ์ธ์ด๋ฅผ ์ฃผ๋ก ์ฌ์ฉํ๊ณ , ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ๊ด๊ณํ DB ๋ฅผ ์ฌ์ฉํ๋ค. ์ฆ, ์ง๊ธ ์๋๋ ๊ฐ์ฒด๋ฅผ ๊ด๊ณํ DB ์ ์ ์ฅํด์ ์ฌ์ฉํ๊ณ ์๋ค.
SQL ์ค์ฌ์ ์ธ ๊ฐ๋ฐ์ ๋ฌธ์ ์
๋ฌดํ ๋ฐ๋ณต๋๋ ์ง๋ฃจํ CRUD ์ฝ๋๋ฅผ ์์ฑํด์ผ ํ๋ฉฐ, ๊ฐ์ฒด ์์ฑ์ด ์ถ๊ฐ๋ ๋ ๋ง๋ค ์๋์ ๊ฐ์ด SQL ์ฟผ๋ฆฌ๋ฌธ์ด ์์ฃผ ๋ณ๊ฒฝ๋๋ค.
public class Member {
private String memberId;
private String name;
private String tel;
}
INSERT INTO MEMBER(MEMBER_ID, NAME, TEL) VALUES
SELECT MEMBER_ID, NAME, TEL FROM MEMBER M
UPDATE MEMBER SET ... TEL = ?
์ ์ด์ ๊ฐ์ฒด ์งํฅ๊ณผ ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ฌ์์ ์์ ํ ๋ค๋ฅด๋ค. ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ๋ฐ์ดํฐ๋ฅผ ์ ๊ทํํด์ ๋ณด๊ดํ๋ ๊ฒ์ด ๋ชฉํ์ธ ๋ฐ๋ฉด, ๊ฐ์ฒด ์งํฅ ํ๋ก๊ทธ๋๋ฐ์ ์์ฑ๊ณผ ๊ธฐ๋ฅ์ ์ ๋ฌถ์ด์ ์บก์ํํ์ฌ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ชฉํ๋ค.
๊ทธ๋ผ ์ ๊ตณ์ด SQL?
๊ฐ์ฒด๋ฅผ ์๊ตฌ ๋ณด๊ดํ๋ ๋ค์ํ ์ ์ฅ์๊ฐ ์กด์ฌํ์ง๋ง, ๊ทธ ์ค์ ๊ฐ์ฅ ํ์ค์ ์ธ ๋์์ด ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๋ฟ์ด๋ค. ์ฆ, SQL ์ ์์กด์ ์ธ ๊ฐ๋ฐ์ ํผํ๊ธฐ ์ด๋ ต๋ค.
๊ฐ์ฒด์ ๊ด๊ณํ DB ์ ์ฐจ์ด
1. ์์
2. ์ฐ๊ด๊ด๊ณ
3. ๋ฐ์ดํฐ ํ์
4. ๋ฐ์ดํฐ ์๋ณ ๋ฐฉ๋ฒ
๊ฐ์ฒด์ ๊ด๊ณํ DB ์๋ ์์ ๊ฐ์ ์ฐจ์ด์ ๋ค์ด ์๋๋ฐ, ๊ฐ๋จํ ์์๊ณผ ์ฐ๊ด๊ด๊ณ๋ฅผ ์ดํด๋ณด์. ๊ฐ์ฒด ์งํฅ ํ๋ก๊ทธ๋๋ฐ์๋ ์์ ๊ด๊ณ๊ฐ ์กด์ฌํ์ง๋ง, ๊ด๊ณํ DB ์๋ ์๋ค๊ณ ๋ณผ ์ ์๋ค. ์ฐ๊ด๊ด๊ณ ๋ํ, ๊ฐ์ฒด ์งํฅ ํ๋ก๊ทธ๋๋ฐ์์๋ getter ๋ฅผ ํตํด ์ฐ๊ด๋ ๊ฐ์ฒด๋ฅผ ๊ฐ์ ธ์ค์ง๋ง, ๊ด๊ณํ DB ์์๋ PK, FK ๋ฑ์ผ๋ก ์ฐ๊ด๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์จ๋ค.
์์
๊ฐ์ฒด์ ์์ ๊ด๊ณ ์ค๊ณ๋ฅผ ์์ ๊ฐ์ด ๋ง๋ค์ด๋๋ค๊ณ ๊ฐ์ ํด๋ณด์.
์์ ๊ฐ์ด ์ค๊ณ๋ ๊ฐ์ฒด๋ฅผ DB ์ ์ ์ฅํ๊ธฐ ์ํด์ ์ด๋ป๊ฒ ํด์ผํ ๊น?
๊ฐ์ฒด์ ์์ ๊ด๊ณ์ ๊ทธ๋๋ง ์ ์ฌํ ์ํผํ์
, ์๋ธํ์
๋ชจ๋ธ๋ก DB ์ ์ ์ฅํ๋ค.
ํ
์ด๋ธ์ ์ค๊ณํ๊ณ ๊ฐ์ฒด๋ฅผ ์ ์ฅํ๊ณ ์กฐํํ๋ ๊ณผ์ ์ ์ดํด๋ณด์.
Album ๊ฐ์ฒด๋ฅผ ์ ์ฅํ๋ ค๋ฉด?
1. ๊ฐ์ฒด ๋ถํด
2. INSERT INTO ITEM โฆ
3. INSERT INTO ALBUM โฆ
์ฌ๊ธฐ๊น์ง๋ ๊ด์ฐฎ์ ๊ฒ ๊ฐ๋ค.
Album ๊ฐ์ฒด๋ฅผ ์กฐํํ๋ ค๋ฉด?
1. ๊ฐ๊ฐ์ ํ
์ด๋ธ์ ๋ฐ๋ฅธ JOIN SQL ์์ฑโฆ
2. ๊ฐ๊ฐ์ ๊ฐ์ฒด ์์ฑโฆ
์์๋ง ํด๋ ๋ณต์กํ๋ค. ๊ทธ๋์ DB ์ ์ ์ฅํ ๊ฐ์ฒด์๋ ์์ ๊ด๊ณ๋ฅผ ์ฌ์ฉํ์ง ์๋๋ค.
DB ๊ฐ ์๋๋ผ ์๋ฐ ์ปฌ๋ ์ ์ ์ ์ฅํ๋ค๋ฉด?
list.add(album);
Album album = list.get(albumId);
// ๋ถ๋ชจ ํ์
์ผ๋ก ์กฐํ ํ ๋คํ์ฑ ํ์ฉ๋ ๊ฐ๋ฅ
Item item = list.get(albumId);
๋ฌธ๋ ์๋ฐ ์ปฌ๋ ์ ์ ์ฌ์ฉํ๋ค๋ฉด ํจ์ฌ ๊ฐํธํ ๊ฒ ๊ฐ๋ค๋ ์๊ฐ์ด ์ด์ด ๋ ๋ค.
์ฐ๊ด๊ด๊ณ
์ด๋ฒ์ OOP ์ RDB ์ ์ฐ๊ด๊ด๊ณ์ ์ด๋ค ์ฐจ์ด์ ์ด ์๋์ง ์ดํด๋ณด์.
member.getTeam();
๊ฐ์ฒด๋ ์ฐธ์กฐ๋ฅผ ์ฌ์ฉํ๋ค. ๊ฐ์ฒด๋ ๋จ๋ฐฉํฅ ์ฐธ์กฐ๊ธฐ ๋๋ฌธ์ Team
์ผ๋ก Member
๋ฅผ ์ฐพ์ ์ ์๋ค.
JOIN ON M.TEAM_ID = T.TEAM_ID
ํ
์ด๋ธ์ ์ธ๋ ํค๋ฅผ ์ฌ์ฉํ๋ค. ๋๋ฌธ์ Team
์ผ๋ก Member
๋ฅผ ์ฐพ์ ์ ์๋ค.
์ฆ, ํ
์ด๋ธ์ ์๋ฐฉํฅ์ผ๋ก ์ฐพ์ ์ ์๋ค.
๊ฐ์ฒด๋ฅผ ํ ์ด๋ธ์ ๋ง์ถ์ด ๋ชจ๋ธ๋ง
์์ ์์๋ก ๋ค์๋ ๊ฐ์ฒด๋ฅผ DB ์ ์ ์ฅํ๋ ค๊ณ ํ ๋, ํ ์ด๋ธ ์ค๊ณ์ ๋ง์ถฐ ๊ฐ์ฒด๋ฅผ ๋ชจ๋ธ๋งํ์ฌ ์๋์ ๊ฐ์ด ๊ฐ์ฒด๋ฅผ ์ ์ฅํ ์ ์์ ๊ฒ์ด๋ค.
class Member {
String id;
Long teamId;
String username;
}
class Team {
Long id;
String name;
}
INSERT INTO MEMBER(MEMBER_ID, TEAM_ID, USERNAME) VALUES ...
ํ์ง๋ง, Member
๋ด๋ถ์ teamId
๊ฐ ์๋ ๊ฒ์ด ๋ญ๊ฐ ๊ฐ์ฒด ์งํฅ์ ์ด์ง ์์ ๊ฒ ๊ฐ๋ค.
๊ฐ์ฒด ์งํฅ์ ์ผ๋ก ๋ชจ๋ธ๋ง์ ๋ณ๊ฒฝํด๋ณด์.
๊ฐ์ฒด ์งํฅ์ ์ธ ๋ชจ๋ธ๋ง
class Member {
String id;
Team team;
String username;
Team getTeam() {
return team;
}
}
class Team {
Long id;
String name;
}
// member.getTeam().getId() ๋ก TEAM_ID ๋ฅผ ๊ฐ์ ธ์ด
INSERT INTO MEMBER(MEMBER_ID, TEAM_ID, USERNAME) VALUES ...
๋ชจ๋ธ๋ง์ ๊ฐ์ฒด์งํฅ์ ์ผ๋ก ๋ณ๊ฒฝํ์, SQL ๋ฌธ์์ TEAM_ID ๋ฅผ ์๊ตฌํ ๋, member.getTeam().getId()
์ ๊ฐ์ ์ฝ๋๊ฐ ํ์ํด์ง๋ค.
์ฌ๊ธฐ๊น์ง ๊ด์ฐฎ์ ๊ฒ ๊ฐ๋ค.
๊ฐ์ฒด ๋ชจ๋ธ๋ง ์กฐํ
ํ์ง๋ง ์กฐํ๋ฅผ ํ ๊ฒฝ์ฐ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
SELECT M.*, T.*
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
public Member find(String memberId) {
//SQL ์คํ ...
Member member = new Member();
//๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์กฐํํ ํ์ ๊ด๋ จ ์ ๋ณด๋ฅผ ๋ชจ๋ ์
๋ ฅ
Team team = new Team();
//๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์กฐํํ ํ ๊ด๋ จ ์ ๋ณด๋ฅผ ๋ชจ๋ ์
๋ ฅ
//ํ์๊ณผ ํ ๊ด๊ณ ์ค์
member.setTeam(team); //
return member;
}
JOIN ๊ตฌ๋ฌธ์ด ํฌํจ๋ SQL ์ฟผ๋ฆฌ๋ฅผ ํตํด ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์์ ๊ฐ๊ฐ ๊ฐ์ฒด๋ฅผ ์์ฑํ์ฌ ๊ด๊ณ๋ฅผ ์ค์ ํด์ฃผ์ด์ผํ๋ค. ์๋ฐ ์ปฌ๋ ์ API ๊ฐ ๊ทธ๋ฆฌ์์ง๋ ์๊ฐ์ด๋ค.
๊ฐ์ฒด ๋ชจ๋ธ๋ง, ์๋ฐ ์ปฌ๋ ์ ์ ๊ด๋ฆฌ
list.add(member);
Member member = list.get(memberId);
Team team = member.getTeam();
์ ์ฒ๋ผ ์๋ฐ ์ปฌ๋ ์ API ๋ฅผ ์ฌ์ฉํ๋ค๋ฉด ์ ๋ง ์ฌ์ธํ ๋ฐโฆ
๊ฐ์ฒด ๊ทธ๋ํ ํ์
๊ฐ์ฒด๋ ์์ ๋กญ๊ฒ ๊ฐ์ฒด ๊ทธ๋ํ๋ฅผ ํ์ํ ์ ์์ด์ผ ํ๋ค.
ํ์ง๋ง ์ฒ์ ์คํํ๋ SQL ์ ๋ฐ๋ผ ํ์ ๋ฒ์๊ฐ ๊ฒฐ์ ๋๋ค.
์๋ฅผ ๋ค์ด ์๋์ ๊ฐ์ SQL ์ ํตํด ๊ฐ์ฒด๋ฅผ ๊ฐ์ ธ์จ๋ค๊ณ ๊ฐ์ ํด๋ณด์.
SELECT M.*, T.*
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
๊ฐ์ฒด ์์ฑ ์์ ์ team
ํ๋๋ง ์ฑ์ ๋ฃ์๊ธฐ ๋๋ฌธ์ member.getOrder()
ํธ์ถ ์ null
๊ฐ์ด ๋ฐํ๋๋ค.
์ํฐํฐ ์ ๋ขฐ ๋ฌธ์
์์ ๊ฐ์ ์ํฉ์ ์ํฐํฐ์ ๋ํ ์ ๋ขฐ ๋ฌธ์ ๋ฅผ ๋ฐ์ํ ์ ์๋ค. ์๋ฅผ ๋ค์ด ์๋์ ๊ฐ์ ์ฝ๋๊ฐ ์๋ค๊ณ ๊ฐ์ ํด๋ณด์.
class MemberService {
...
public void process() {
Member member = memberDAO.find(memberId);
member.getTeam(); //???
member.getOrder().getDelivery(); // ???
}
}
๋ค๋ฅธ ํ์ ๋๊ตฐ๊ฐ๊ฐ ๊ฐ๋ฐํ memberDAO.find()
๋ฅผ ํตํด member
๋ฅผ ์กฐํํ ๊ฒฝ์ฐ, memberDAO ๋ด๋ถ์์ ์ด๋ค ์ฟผ๋ฆฌ๊ฐ ์คํ๋์๊ณ , ์ด๋ค ๋ฐ์ดํฐ๋ฅผ ์กฐ๋ฆฝํ์๋์ง ํ์ธํ์ง ์๋๋ค๋ฉด, ๋ฐํ๋ ์ํฐํฐ๋ฅผ ์ ๋ขฐํ ์ ์๋ค.
๋ชจ๋ ๊ฐ์ฒด๋ฅผ ๋ฏธ๋ฆฌ ๋ก๋ฉํ ์๋ ์์๊น?
//Member๋ง ์กฐํ
memberDAO.getMember();
//Member์ Team ์กฐํ
memberDAO.getMemberWithTeam();
//Member,Order,Delivery ์กฐํ
memberDAO.getMemberWithOrderWithDelivery();
์ํฉ์ ๋ฐ๋ผ ๋์ผํ ํ์ ์กฐํ ๋ฉ์๋๋ฅผ ์ฌ๋ฌ๋ฒ ์์ฑํ ์ ๋ฐ์ ์๋ค.
๊ณ์ธตํ ์ํคํ ์ฒ์์ ์ง์ ํ ์๋ฏธ์ ๊ณ์ธต ๋ถํ ์ด ์ด๋ ต๋ค.
Layered Architecture ์์๋ ๊ทธ ๋ค์ ๊ณ์ธต์์ ์ ๋ขฐํ๊ณ ์ฌ์ฉํด์ผ ํ๋ค.
๋ฌผ๋ฆฌ์ ์ผ๋ก๋ ๊ณ์ธต์ด service
์ dao
๋ก ๋๋์ด์ ธ ์์ง๋ง, ๋
ผ๋ฆฌ์ ์ผ๋ก ์ด๋์ ๋ ์ฐ๊ฒฐ๋์ด ์๋ค. ์ฆ, dependency ๊ฐ ์ข์ง ์๋ค.
DB ์กฐํ์ ์๋ฐ ์ปฌ๋ ์ ์กฐํ ๋น๊ตํ๊ธฐ
String memberId = "100";
Member member1 = memberDAO.getMember(memberId);
Member member2 = memberDAO.getMember(memberId);
member1 == member2; //๋ค๋ฅด๋ค.
class MemberDAO {
public Member getMember(String memberId) {
String sql = "SELECT * FROM MEMBER WHERE MEMBER_ID = ?";
...
//JDBC API, SQL ์คํ
return new Member(...);
}
}
String memberId = "100";
Member member1 = list.get(memberId);
Member member2 = list.get(memberId);
member1 == member2; //๊ฐ๋ค.
๊ฐ์ฒด๋ต๊ฒ ๋ชจ๋ธ๋ง ํ ์๋ก ๋งคํ ์์ ๋ง ๋์ด๋๋คโฆ
SQL ์ ๋ง์ถฐ์ ๊ฐ์ฒด๋ฅผ ๋ฐ์ดํฐ ์ ์ก์ฉ์ผ๋ก๋ง ์ค๊ณ๋ฅผ ํ ์ ๋ฐ์ ์๋ค. ๊ฐ์ฒด ์งํฅ์ ์ผ๋ก ์ค๊ณ๋ฅผ ํ ์ ์์ด๋, ROI ๊ฐ ์ ๋์จ๋ค.
๊ฐ์ฒด๋ฅผ ์๋ฐ ์ปฌ๋ ์ ์ ์ ์ฅ ํ๋ฏ์ด DB ์ ์ ์ฅํ ์ ์์๊น?
๊ทธ๋์ ๋ฑ์ฅํ ๊ฒ์ด ๋ฐ๋ก JPA(Java Persistence API) ์ด๋ค.
JPA ์๊ฐ
JPA ๋?
Java Persistence API ์ ์ฝ์์ด๋ฉฐ, Java ์ง์์ ORM ๊ธฐ์ ํ์ค์ด๋ค.
ORM ์ด ๋ญ์ผ?
Object-Relational Mapping ์ ์ฝ์๋ก, ๊ฐ์ฒด๋ ๊ฐ์ฒด๋๋ก ์ค๊ณํ๊ณ , ๊ด๊ณํ DB ๋ ๊ด๊ณํ DB ๋๋ก ์ค๊ณํ์ฌ ๊ฐ์ฒด์ ๊ด๊ณํ DB ์ค๊ฐ์์ ์๋ก๋ฅผ ๋งคํํด์ฃผ๋ ํ๋ ์์ํฌ์ด๋ค. ๋์ค์ ์ธ ์ธ์ด์๋ ๋๋ถ๋ถ ORM ๊ธฐ์ ์ด ์กด์ฌํ๋ค.
JPA ๋ ์ ํ๋ฆฌ์ผ์ด์ ๊ณผ JDBC ์ฌ์ด์์ ๋์
๊ฐ๋ฐ์๊ฐ ์๋ JPA ๊ฐ JDBC ๋ฅผ ํธ์ถํ์ฌ SQL ์ ์์ฑํ๊ณ DB ์ ์ํตํ๋ ๋ฐฉ์์ผ๋ก ๋์ํ๋ค.
JPA ๋์ - ์ ์ฅ
JPA ๋์ - ์กฐํ
JAP ๋ ํ์ค ๋ช ์ธ
JPA ๋ ์ธํฐํ์ด์ค์ ๋ชจ์์ด๋ค. JPA 2.1 ํ์ค ๋ช ์ธ๋ฅผ ๊ตฌํํ ์ฌ๋ฌ ๊ตฌํ์ฒด๊ฐ ์กด์ฌํ๋๋ฐ, ๊ทธ์ค ์ ์ผ ์ ๋ช ํ 3๊ฐ์ง ๊ตฌํ์ฒด๊ฐ ํ์ด๋ฒ๋ค์ดํธ, EclipseLink, DataNucleus ์ด๋ค. ์ด ์ค ํ์ด๋ฒ๋ค์ดํธ๊ฐ ๊ฐ์ฅ ๋ง์ด ์ฐ์ธ๋ค.
JPA ๋ฅผ ์ ์ฌ์ฉํด์ผ ํ ๊น?
- SQL ์ค์ฌ์ ์ธ ๊ฐ๋ฐ์์ ๊ฐ์ฒด ์ค์ฌ์ผ๋ก ๊ฐ๋ฐ
- ์์ฐ์ฑ
- ์ ์ง๋ณด์
- ํจ๋ฌ๋ค์์ ๋ถ์ผ์น ํด๊ฒฐ
- ์ฑ๋ฅ
- ๋ฐ์ดํฐ ์ ๊ทผ ์ถ์ํ์ ๋ฒค๋ ๋ ๋ฆฝ์ฑ
- ํ์ค ์์ ๊ฐ์ด ์ฌ๋ฌ ์ฅ์ ์ด ์๋๋ฐ ์ด ์ค ์ค์ํ ์์๋ค์ ๊ฐ๋จํ ์ดํด๋ณด์.
์์ฐ์ฑ - JPA ์ CRUD
- ์ ์ฅ -
jpa.persist(member)
- ์กฐํ -
Member member = jpa.find(memberId)
- ์์ -
member.setName(โ๋ณ๊ฒฝํ ์ด๋ฆ")
- ์ญ์ -
jpa.remove(member)
์์ฒ๋ผ SQL ๊ตฌ๋ฌธ ์์ด ์๋ฐ ์ปฌ๋ ์ ์ ์ฌ์ฉํ๋ ๊ฒ์ฒ๋ผ ์ฌ์ฉํ ์ ์๋ค.
์ ์ง๋ณด์ - JPA ํ๋๋ง ์ถ๊ฐํ๋ฉด ๋จ, SQL ์ JPA ๊ฐ ์ฒ๋ฆฌ
๊ธฐ์กด์ ๊ฐ์ฒด ์์ฑ์ด ์ถ๊ฐ๋ ๋ ์ฟผ๋ฆฌ๋ฌธ์ ๋ชจ๋ ์์ ํด์ฃผ์ด์ผํ์ง๋ง, JPA ๋ฅผ ์ฌ์ฉํ๋ค๋ฉด, ๊ฐ์ฒด ์์ฑ๊ณผ DB ์ปฌ๋ผ๋ง ์ถ๊ฐํ์ฌ ํด๊ฒฐํ ์ ์๋ค. ์ฆ, ์ฟผ๋ฆฌ๋ฅผ ์์ ํ ํ์๊ฐ ์์ด์ง๋ค.
JPA ์ ์์ - ์ ์ฅ
JPA ์ฌ์ฉ ์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ ๊ณผ์ ์์ ๊ฐ๋ฐ์๊ฐ ํ ์ผ์ด ์๋์ ๊ฐ์์ง๊ณ ,
jpa.persist(album);
๋๋จธ์ง SQL ์ฟผ๋ฆฌ ์์ฑ๊ณผ ๊ฐ์ ์ผ๋ค์ 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
JPA ์ ์ฐ๊ด๊ด๊ณ, ๊ฐ์ฒด ๊ทธ๋ํ ํ์
์๋์ ๊ฐ์ ์ฝ๋๋ก ์ฐ๊ด๊ด๊ณ ์ ์ฅ ์,
member.setTeam(team);
jpa.persist(member);
์ดํ ์กฐํํ์์ ๋ ๊ฐ์ฒด ๊ทธ๋ํ ํ์์ด ์ ์์ ์ผ๋ก ๋์ํ๋ค.
Member member = jpa.find(Member.class, memberId);
Team team = member.getTeam();
์ ๋ขฐํ ์ ์๋ ์ํฐํฐ, ๊ณ์ธต
class MemberService {
...
public void process() {
Member member = memberDAO.find(memberId);
member.getTeam(); //์์ ๋ก์ด ๊ฐ์ฒด ๊ทธ๋ํ ํ์
member.getOrder().getDelivery();
}
}
์์ฒ๋ผ SQL ์ฟผ๋ฆฌ๋ฅผ ์ง์ ์์ฑํ์ฌ ์กฐํํ ๋ ๋ณด๋ค ์์ ๋กญ๊ฒ ๊ฐ์ฒด ๊ทธ๋ํ๋ฅผ ํ์ํ ์ ์๊ธฐ์, ์ ํํ ์ ์๋ ์ํฐํฐ๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
JPA ์ ๋น๊ตํ๊ธฐ
๋ฟ๋ง ์๋๋ผ, ๋์ผํ ํธ๋์ญ์ ์์ ์กฐํํ ์ํฐํฐ๋ ๊ฐ์์ ๋ณด์ฅํ๋ค.
String memberId = "100";
Member member1 = jpa.find(Member.class, memberId);
Member member2 = jpa.find(Member.class, memberId);
member1 == member2; //๊ฐ๋ค
JPA์ ์ฑ๋ฅ ์ต์ ํ - 1์ฐจ ์บ์์ ๋์ผ์ฑ(identity) ๋ณด์ฅ
๊ฐ์ ํธ๋์ญ์ ์์์๋ ๊ฐ์ ์ํฐํฐ๋ฅผ ๋ฐํํ์ฌ ์ฝ๊ฐ์ ์กฐํ ์ฑ๋ฅ์ ํฅ์ํ๊ณ , DB Isolation Level ์ด Read Commit ์ด์ด๋ ์ ํ๋ฆฌ์ผ์ด์ ์์๋ Repeatable Read ๋ฅผ ๋ณด์ฅํ๋ค.
String memberId = "100";
Member member1 = jpa.find(Member.class, memberId); // SQL
Member member2 = jpa.find(Member.class, memberId); // ์บ์
member1 == member2; //๊ฐ๋ค
์์ฒ๋ผ ์บ์๋ฅผ ํตํด SQL ์ 1๋ฒ๋ง ์คํํด๋ ๋๋ค.
JPA์ ์ฑ๋ฅ ์ต์ ํ - ํธ๋์ญ์ ์ ์ง์ํ๋ INSERT ์ง์ฐ
transaction.begin(); // [ํธ๋์ญ์
] ์์
em.persist(memberA);
em.persist(memberB);
em.persist(memberC);
//์ฌ๊ธฐ๊น์ง INSERT SQL์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ณด๋ด์ง ์๋๋ค.
//์ปค๋ฐํ๋ ์๊ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ INSERT SQL์ ๋ชจ์์ ๋ณด๋ธ๋ค.
transaction.commit(); // [ํธ๋์ญ์
] ์ปค๋ฐ
์์ฒ๋ผ ํธ๋์ญ์ ์ ์ปค๋ฐํ ๋๊น์ง INSERT SQL ์ ๋ชจ์์ JDBC BATCH SQL ๊ธฐ๋ฅ์ ์ฌ์ฉํด ํ๋ฒ์ SQL ์ ์ ์กํ๋ค.
JPA์ ์ฑ๋ฅ ์ต์ ํ - ํธ๋์ญ์ ์ ์ง์ํ๋ UPDATE ์ง์ฐ
transaction.begin(); // [ํธ๋์ญ์
] ์์
changeMember(memberA);
deleteMember(memberB);
๋น์ฆ๋์ค_๋ก์ง_์ํ(); //๋น์ฆ๋์ค ๋ก์ง ์ํ ๋์ DB ๋ก์ฐ ๋ฝ์ด ๊ฑธ๋ฆฌ์ง ์๋๋ค.
//์ปค๋ฐํ๋ ์๊ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ UPDATE, DELETE SQL์ ๋ณด๋ธ๋ค.
transaction.commit(); // [ํธ๋์ญ์
] ์ปค๋ฐ
์์ฒ๋ผ UPDATE, DELETE๋ก ์ธํ ๋ก์ฐ(ROW)๋ฝ ์๊ฐ์ ์ต์ํํ๊ณ , ํธ๋์ญ์ ์ปค๋ฐ ์ UPDATE, DELETE SQL ์คํํ๊ณ , ๋ฐ๋ก ์ปค๋ฐํ๋ค.
JPA์ ์ฑ๋ฅ ์ต์ ํ - ์ง์ฐ ๋ก๋ฉ๊ณผ ์ฆ์ ๋ก๋ฉ
Member member = memberDAO.find(memberId);
// SELECT * FROM MEMBER ์คํ
Team team = member.getTeam();
String teamName = team.getName();
// SELECT * FROM TEAM ์คํ
์ง์ฐ ๋ก๋ฉ์ ๊ฐ์ฒด๊ฐ ์ค์ ์ฌ์ฉ๋ ๋ ๋ก๋ฉ๋์ด ์ฌ์ฉํ ์ ์๋๋ก ์ต์ ํ ๋๋ค.
Member member = memberDAO.find(memberId);
// SELECT M.*, T.* FROM MEMBER JOIN TEAM โฆ ์คํ
Team team = member.getTeam();
String teamName = team.getName();
์ฆ์ ๋ก๋ฉ์ JOIN SQL ๋ก ํ๋ฒ์ ์ฐ๊ด๋ ๊ฐ์ฒด๊น์ง ๋ฏธ๋ฆฌ ์กฐํํ์ฌ ์ฌ์ฉํ ์ ์๋๋ก ์ต์ ํ ๋๋ค.
ORM ์ ๊ฐ์ฒด์ RDB ๋ ๊ธฐ๋ฅ ์์ ์๋ ๊ธฐ์
ORM ์ ์ ์ฌ์ฉํ๊ธฐ ์ํด์ OOP ์ RDB ์ ๋ํ ์ง์ ๋ชจ๋ ์ค์ํ๋ค. OOP ์ธ์ด๋ RDB ์ ๋นํด ๋ณํ ๊ฐ๋ฅ์ฑ์ด ๋๊ธฐ ๋๋ฌธ์ RDB ์ ๋ํ ๊ณต๋ถ๋ฅผ ๊พธ์คํ ํ์!
Hello JPA - ํ๋ก์ ํธ ์์ฑ
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>2.1.214</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
</project>
persistence.xml
JPA ์ค์ ํ๊ธฐ
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2"
xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
<persistence-unit name="hello">
<properties>
<!-- ํ์ ์์ฑ -->
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
<property name="javax.persistence.jdbc.user" value="sa"/>
<property name="javax.persistence.jdbc.password" value=""/>
<property name="javax.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/test"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<!-- ์ต์
-->
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.use_sql_comments" value="true"/>
<!--<property name="hibernate.hbm2ddl.auto" value="create" />-->
</properties>
</persistence-unit>
</persistence>
๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฐฉ์ธ
JPA ๋ ํน์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ข
์์ ์ด์ง ์๋ค.
๊ฐ๊ฐ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ์ ๊ณตํ๋ SQL ๋ฌธ๋ฒ๊ณผ ํจ์๋ ์กฐ๊ธ์ฉ ๋ค๋ฅด๋ค.
์๋ฅผ ๋ค์ด, MySQL ์ VARCHAR, Oracle ์ VARCHAR2 ๋ฅผ ์ฌ์ฉํ๋ค.
์ฆ, ๋ฐฉ์ธ์ SQL ํ์ค์ ์งํค์ง ์๋ ํน์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ง์ ๊ณ ์ ํ ๊ธฐ๋ฅ์ ์๋ฏธํ๋ค.
JPA ์์๋
hibernate.dialect
์์ฑ์ ์ง์ ํ์ฌ ํน์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฐฉ์ธ์ ์ค์ ํ ์ ์๋ค.
- H2 : org.hibernate.dialect.H2Dialect
- Oracle 10g : org.hibernate.dialect.Oracle10gDialect
- MySQL : org.hibernate.dialect.MySQL5InnoDBDialect
Hello JPA - ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ
JPA ๊ตฌ๋ ๋ฐฉ์
Persistence
๋ผ๋ ํด๋์ค์์ ์ค์ ํ์ผ์ธ persistence.xml
์ ์ฝ์ด์ EntityManagerFactory
๋ฅผ ์์ฑํ๋ค. ์ดํ EntityManagerFactory
์์ EntityManager ๋ฅผ ์ฐ์ด๋ด์ ์ฌ์ฉํ๋ค.
๊ฐ์ฒด์ ํ ์ด๋ธ์ ์์ฑํ๊ณ ๋งคํํด๋ณด์
package hellojpa;
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity // JPA ๊ฐ ๊ด๋ฆฌํ ๊ฐ์ฒด๋ฅผ ์๋ฏธํ๋ค.
public class Member {
@Id // ๋ฐ์ดํฐ๋ฒ ์ด์ค์ PK ์ ๋งคํํ๋ค.
private Long id;
private String name;
// Getter, Setter โฆ
}
create table Member (
id bigint not null,
name varchar(255),
primary key (id)
);
ํ์ ๋ฑ๋กํ๊ธฐ
public static void main(String[] args) {
// persistence.xml ์ ์๋ persistence-unit name ๊ฐ์ ๋ฃ์ด์ค๋ค.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
// code ์์ฑ
try {
// ์์
Member member = em.find(Member.class, 150L);
member.setName("ZZZZZ");
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
์ฃผ์
- ์ํฐํฐ ๋งค๋์ ํฉํ ๋ฆฌ๋ ํ๋๋ง ์์ฑํด์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฒด์์ ๊ณต์ ํ๋ค.
- ์ํฐํฐ ๋งค๋์ ๋ ์ฐ๋ ๋๊ฐ ๊ณต์ ๋์ง ์๊ธฐ ๋๋ฌธ์ ์ฌ์ฉํ๊ณ ๋ฒ๋ ค์ผ ํ๋ค.
- JPA ์ ๋ชจ๋ ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ ํธ๋์ญ์ ์์์ ์คํ๋๋ค.
JPQL
JPA ๋ฅผ ์ฌ์ฉํ๋ฉด ์ํฐํฐ ๊ฐ์ฒด๋ฅผ ์ค์ฌ์ผ๋ก ๊ฐ๋ฐ์ ์งํํ๊ฒ ๋๋ค.
ํน์ ์ํฐํฐ ๊ฒ์ ์์ em.find()
๋ก ๋จ์ํ ์กฐํํ๊ฑฐ๋ a.getB().getC()
์ ๊ฐ์ด ๊ฐ์ฒด ๊ทธ๋ํ ํ์์ ํตํด ์กฐํ๋ฅผ ์ํํ ์ ์๋ค.
๋ฌธ์ ๋ ์กฐ๊ฑด์ด ํฌํจ๋ ๊ฒ์ ์ฟผ๋ฆฌ๋ฅผ ์ ์กํ ๋ ๋ฐ์ํ๋ค.
์กฐ๊ฑด์ด ํฌํจ๋ ๊ฒ์์ ํ ๋๋ ํ
์ด๋ธ์ด ์๋ ์ํฐํฐ ๊ฐ์ฒด๋ฅผ ๋์์ผ๋ก ๊ฒ์ํ๊ธฐ ๋๋ฌธ์ ๋ชจ๋ DB ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ฒด๋ก ๋ณํํด์ ๊ฒ์ํ๋ ๊ฒ์ ๋นํจ์จ์ ์ด๋ค.
์ ํ๋ฆฌ์ผ์ด์
์ด ํ์ํ ๋ฐ์ดํฐ๋ง DB ์์ ๋ถ๋ฌ์ค๋ ค๋ฉด ๊ฒฐ๊ตญ ๊ฒ์ ์กฐ๊ฑด์ด ํฌํจ๋ SQL ์ด ํ์ํ๋ค.
์ฆ, DB ์ ์ข
์์ ์ผ๋ก ์ค๊ณ๊ฐ ๋ ์ ๋ฐ์ ์๋ค๋ ์๋ฏธ๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด JPA ๋ SQL ์ ์ถ์ํํ JPQL ์ด๋ผ๋ ๊ฐ์ฒด ์งํฅ ์ฟผ๋ฆฌ ์ธ์ด ์ ๊ณตํ๋ค.
SQL ์ด DB ์ ํ
์ด๋ธ์ ๋์์ผ๋ก ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆฐ๋ค๋ฉด, JPQL ์ ์ํฐํฐ ๊ฐ์ฒด๋ฅผ ๋์์ผ๋ก ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆฐ๋ค.
JPQL ์ ํ
์ด๋ธ์ด ์๋ ๊ฐ์ฒด๋ฅผ ๋์์ผ๋ก ํ๋ ๊ฐ์ฒด ์งํฅ ์ฟผ๋ฆฌ์ด๊ธฐ ๋๋ฌธ์ ๋ฐฉ์ธ ์ค์ ์ ๋ง์ถฐ์ ๊ฐ DB ์ ๋ง๊ฒ ๋ฒ์ญํ์ฌ ์ฟผ๋ฆฌ๋ฅผ ์ ์กํ๋ค.
์ด๋ JPQL ์ด SQL ์ ์ถ์ํํ๊ธฐ ๋๋ฌธ์ ํน์ ๋ฐ์ดํฐ๋ฒ ์ด์ค SQL ์ ์์กดํ์ง ์๋๋ค๋ ์๋ฏธ์ด๊ธฐ๋ ํ๋ค.