[JPA] JPA 기초
자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의
이 글은 인프런에서 제공하는 자바 ORM 표준 JPA 프로그래밍 - 기본편 강의를 참고했고
강의 내용을 다시 복습하면서 정리하려는 목적으로 작성합니다.
과거에는 객체를 DB에 저장하려면 👨🏼💻개발자는 JDBC API
를 이용하여 SQL
을 직접 작성해야 했습니다.
public class MemberDAOV1 {
// 회원 저장
public Long save(Connection connection, Member member) throws SQLException {
PreparedStatement pstmt = null;
String sql = "INSERT INTO MEMBER(USERNAME, AGE) VALUES (?, ?)";
try {
pstmt = connection.prepareStatement(sql);
pstmt.setString(1, member.getUsername());
pstmt.setInt(2, member.getAge());
pstmt.executeUpdate();
ResultSet generatedKeys = pstmt.getGeneratedKeys();
if(generatedKeys.next()) {
return generatedKeys.getLong(1);
}
return null;
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
pstmt.close();
}
}
// 회원 조회
public Member findMember(Connection connection, Long id) throws SQLException {
PreparedStatement pstmt = null;
ResultSet rs = null;
String sql = "SELECT * FROM MEMBER WHERE ID = ?";
try {
pstmt = connection.prepareStatement(sql);
pstmt.setLong(1, id);
rs = pstmt.executeQuery();
if(rs.next()) {
return Member.builder()
.id(rs.getLong("MEMBER_ID"))
.username(rs.getString("USERNAME"))
.age(rs.getInt("AGE"))
.build();
}
return null;
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
pstmt.close();
}
}
}
그런데 JDBC Template
나 Mybatis
같은 SQL Mapper
가 등장합니다…!!
public class MemberDAOV2 {
private JdbcTemplate jdbcTemplate;
private SimpleJdbcInsert insertActor;
// 회원 저장
public Long save(final Member member) {
SqlParameterSource parameterSource = new MapSqlParameterSource()
.addValue("USERNAME", member.getUsername())
.addValue("AGE", member.getAge());
Number newId = insertActor.executeAndReturnKey(parameterSource);
return newId.longValue();
}
// 회원 조회
public Member findMember(Long id) {
String sql = "SELECT MEMBER_ID, USERNAME, AGE FROM MEMBER WHERE ID = ?";
return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(), id);
}
}
코드는 많이 줄었지만 개발자가 여전히 직접 SQL
을 작성해야 했습니다. 😿
그러다가 JPA
가 등장합니다.
public class MemberDAOV3 {
@PersistenceContext
EntityManager em;
// 회원 저장
public void save(Member member) {
em.persist(member);
}
// 회원 조회
public Member findMember(Long id) {
return em.find(Member.class, id);
}
}
JPA
가 엄청나다는 것을 바로 느낄 수 있습니다…… ㄷ ㄷJPA
를 도입하면 개발 속도가 엄청나게 증가할것 같습니다.
이러한 이점으로 인해 개발자는 핵심적인 비지니스 로직에 대해서 더 고민할 수 있는 시간이 생기겠죠? 🤗
그런데 JPA
를 막상 사용하려고 하면 개발자들은 난관에 부딪힙니다.
그 이유는 다음과 같습니다.
- 객체와 테이블을 올바르게 매핑하는 방법을 모른다. 🤷♀️
- 실무는 수십 개 이상의 복잡한 객체와 테이블을 사용합니다.
- JPA 내부 동작 방식이 어떻게 돌아가는지 모른다. 🤷🏻♂️
- JPA가 어떤 SQL을 만들어 내는지 정확하게 이해하고 있어야 합니다.
- 제대로 알지 못하면 장애 상황에 대처하지 못합니다.
그래서 이 포스팅 시리즈에서는 객체와 테이블 설계 매핑에 관해서 집중적으로 말씀드릴려고 합니다.
목표
- 객체와 테이블을 제대로 설계하고 매핑하는 방법
- 기본 키(
PK
)와 외래 키(FK
) 매핑 - 일대다(
1:N
), 다대일(N:1
), 일대일(1:1
), 다대다(M:N
) 매핑
- 기본 키(
- JPA 내부 동작 방식
JPA
내부 동작 방식 이해JPA
가 어떤SQL
을 만들어 내는지 이해JPA
가 언제SQL
을 실행하는지 이해
JPA
JPA
는 Java Persistence API
의 약자 이고, JAVA
진영의 ORM
기술 표준 입니다.
그리고 인터페이스의 모음 입니다.
그렇다면 ORM
이란 무엇일까요?Object-Relational Mapping
의 약자 입니다.
한국어로 객체 관계 매핑 정도로 해석할 수 있습니다.
JPA 장점
JAVA
진영의 ORM
표준인 JPA
를 사용하면 다음과 같은 장점이 있습니다.
객체는 객체대로 설계, 관계형 데이터베이스는 관계형 데이터베이스대로 설계
- 객체 중심으로 애플리케이션 개발
생산성
- 저장:
jpa.persist(member);
- 조회:
Member member = jpa.find(memberId);
- 수정:
member.setName(“변경할 이름”);
- 삭제:
jpa.remove(member);
유지보수
Entity
에 필드를 추가하거나 삭제하면SQL
은JPA
가 처리
패러다임 불일치 해결
JPA
와 상속(ITEM
은 하나 이하의ALBUM
으로 구성)jpa.persist(album);
INSERT INTO ITEM …
INSERT INTO ALBUM …
JPA
와 연관관계member.setTeam(team);
jpa.persist(member);
JPA
와 객체 그래프 탐색Member member = jpa.find(Member.class, memberId);
Team team = member.getTeam();
JPA
와 비교하기Member member1 = jpa.find(Member.class, memberId);
Member member2 = jpa.find(Member.class, memberId);
member1 == member2; // 같다.
- 동일한 트랜잭션에서 조회한 엔티티는 같음을 보장함
성능
- 1차 캐시와 동일성(
identity
)보장- 같은 트랜잭션 안에서는 같은 엔티티 반환(약간의 조회 성능 향상)
DB Isolation Level
이Read Commited
이어도 애플리케이션에서Repeatable Read
보장- 트랜잭션을 지원하는 쓰기 지연(
transactional write-behind
)
INSERT
transaction.begin(); // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);
em.persist(memberC);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
//커밋하는 순간 데이터베이스에 INSERT SQL을 모아서 보낸다.
transaction.commit(); // [트랜잭션] 커밋
* 트랜잭션을 커밋할 때까지 `INSERT SQL`을 모음
* `JDBC BATCH SQL` 기능을 사용해서 한번 `SQL` 전송
UPDATE
transaction.begin(); // [트랜잭션] 시작
changeMember(memberA);
deleteMember(memberB);
비즈니스_로직_수행(); //비즈니스 로직 수행 동안 DB 로우 락이 걸리지 않는다.
//커밋하는 순간 데이터베이스에 UPDATE, DELETE SQL을 보낸다.
transaction.commit(); // [트랜잭션] 커밋
* `UPDATE`, `DELETE`로 인한 로우(`ROW`)락 시간 최소화
* 트랜잭션 커밋 시 `UPDATE`, `DELETE` `SQL` 실행하고, 바로 커밋
* 지연 로딩(`Lazy Loading`)
* 지연 로딩: **객체가 실제 사용될 때 로딩**
* 즉시 로딩: `JOIN` `SQL`로 한번에 연관된 객체까지 미리 조회
Lazy Loading
Member member = memberDAO.find(memberId); // SELECT * FROM MEMBER
Team team = member.getTeam();
String teamName = team.getName(); // 객체가 실제로 사용됨. SELECT * FROM TEAM
Eager Loading
Member member = memberDAO.find(memberId); // SELECT M.*, T.* FROM MEMBER JOIN TEAM ...
Team team = member.getTeam();
String teamName = team.getName();
- 데이터 접근 추상화와 벤더(DB회사(
MySQL, ORACLE …
)) 독립성JPA
의 구현체인Hibernate
는 여러 데이터베이스에 대해 각각의 기능을 구현
- 표준
JPA
는JAVA
진영의ORM
표준
JPA 구성도
JPA
는 애플리케이션과 JDBC
사이에서 동작합니다.
JPA 동작 -삽입
JPA
가 INSERT
쿼리를 생성하고 전달하는 과정을 그림으로 나타내면 다음과 같습니다.Object
를 JPA
에게 전달하면 저장됩니다.!
JPA 동작 -조회
조회하는 과정도 살펴볼까요?find()
메소드에 id
를 파라미터로 입력하면 Object
를 반환해 줍니다.
'0+ 스프링 > 0 + 스프링 ORM(JPA)' 카테고리의 다른 글
[JPA] 연관관계 매핑(@ManyToOne, @OneToMany, @OneToOne, @ManyToMany) (0) | 2023.02.20 |
---|---|
[JPA] 연관관계 매핑 개념(패러다임 불일치 해결) (0) | 2023.02.18 |
[JPA] 엔티티 매핑(@Entity, @Table) (0) | 2023.02.16 |
[JPA] JPA 영속성 컨텍스트(+ flush, 준영속 상태) (0) | 2023.02.09 |
[JPA] JPA 구동 방식과 간단 실습(CRUD) (0) | 2023.02.09 |