JPA 소개
SQL 중심적인 개발의 문제점
- 관계형 DB (Oracle, MySQL)에서는 SQL만 사용할 수 있으므로 SQL 의존적인 개발을 할 수 밖에 없다.
- 관계형 DB의 목적과 객체지향 프로그래밍의 목적이 일치하지 않는다. 그러나, 객체를 저장할 수 있는 가장 현실적인 방안은 관계형 DB이다.
객체와 관계형 데이터베이스의 차이
- 상속
DB에서는…
Album 객체를 저장할 경우 : 객체를 분해하여 artist는 album에 저장, name, price, dtype은 item에 저장 Album 객체를 조회할 경우 : 각 테이블을 조회하는 Join SQL 작성, 각각의 객체 생성… 복잡 그래서 DB에 저장할 객체에는 상속 관계를 쓰지 않는다.
자바에서는…
Album 객체를 저장할 경우 : list.add(album);
Album 객체를 조회할 경우 :Album album = list.get(albumId);
Item item = list.get(albumId);
부모 타입으로 조회 후 다형성을 활용할 수도 있다.
- 연관관계
- 객체는 참조를 사용 :
member.getTeam()
- 테이블은 외래 키를 사용 :
JOIN ON M.TEAM_ID = T.TEAM_ID
객체를 테이블에 맞추어 모델링
class Member {
String id; // MEMBER_ID
Long teamId; // TEAM_ID FK
String username;// USERNAME
}
class Team {
Long id; // TEAM_ID PK
String name; // NAME
}
객체다운 모델링
class Member {
String id; // MEMBER_ID
Team team; // 참조로 연관관계를 맺는다
String username;// USERNAME
Team getTeam() {
return team;
}
}
class Team {
Long id; // TEAM_ID PK
String name; // NAME
}
객체 모델링 조회
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;
}
이는 굉장히 번거로운 작업이다.
객체 그래프 탐색
객체는 자유롭게 객체 그래프를 탐색할 수 있어야 한다. 하지만 실행하는 SQL에 따라 탐색 범위가 결정되기 때문에 SQL 중심적인 개발에서는 그것이 불가능하다.
엔티티 신뢰 문제
class MemberService {
public void process() {
Member member = memberDAO.find(memberId);
member.getTeam(); // 사용 가능한가?
member.getOrder().getDelivery(); // 사용 가능한가?
}
}
SQL에서 탐색된 객체 이외에는 사용할 수 없으므로 엔티티를 신뢰할 수 없다. 계층 아키텍쳐에서는 이전 계층에서 넘어온 내용을 신뢰할 수 있어야 하는데 SQL 중심적인 개발에서는 그것이 불가능하므로 객체가 SQL에 의존한다.그렇다고 모든 객체를 미리 로딩할 수 없으므로 상황에 따라 동일한 회원 조회 메서드를 여러 벌 생성해야 한다.
- 비교하기
String memberId = "100";
Member member1 = memberDAO.getMember(memberId);
Member member2 = memberDAO.getMember(memberId);
member1 == member2; // 다르다.
String memberId = "100";
Member member1 = list.get(memberId);
Member member2 = list.get(memberId);
member1 == member2; // 같다.
SQL 중심적인 개발에서는 getMember()
을 호출할 때 New Member()
로 객체를 생성하기 때문에 member1
과 member2
가 다르다. 그러나 자바 컬렉션에서 조회할 경우 member1
과 member2
의 참조 값이 같기 때문에 두 객체는 같다.
이렇게 객체를 객체답게 모델링할수록 매핑 작업만 늘어난다.
JPA 소개
JPA?
- Java Persistence API
- 자바 진영의 ORM 기술 표준
ORM?
- Object-relational mapping(객체 관계 매핑)
- 객체는 객체대로 설계
- 관계형 데이터베이스는 관계형 데이터베이스대로 설계
- ORM 프레임워크가 중간에서 매핑
- 대중적인 언어에는 대부분 ORM 기술이 존재
JPA 동작
저장
조회
JPA는 표준 명세
- JPA는 인터페이스의 모음
- JPA 2.1 표준 명세를 구현한 3가지 구현체
- 하이버네이트, EclipseLink, DataNucleus
JPA를 왜 사용해야 하는가?
SQL 중심적인 개발에서 객체 중심으로 개발
-
생산성
- 저장: jpa.persist(member)
- 조회: Member member = jpa.find(memberId)
- 수정: member.setName(“변경할 이름”)
- 삭제: jpa.remove(member)
-
유지보수
- 기존에는 필드 변경시 모든 SQL 문을 수정해야 했지만, JPA에서는 필드만 추가하면 SQL은 JPA가 처리한다.
-
패러다임의 불일치 해결
JPA와 상속 : 특정 객체를 저장할 경우 상속 관계를 JPA가 분석하여 필요한 쿼리를 JPA가 생성한다.
JPA와 연관관계, JPA와 객체 그래프 탐색 : 지연 로딩을 사용하여 신뢰할 수 있는 엔티티, 계층을 제공한다.
JPA와 비교하기 : 동일한 트랜잭션에서 조회한 엔티티는 같음을 보장한다.
-
성능 최적화 기능
-
1차 캐시와 동일성(identity) 보장
- 같은 트랜잭션 안에서는 같은 엔티티를 반환 - 약간의 조회 성능 향상
- DB Isolation Level이 Read Commit이어도 애플리케이션에서 Repeatable Read 보장
-
트랜잭션을 지원하는 쓰기 지연(transactional write-behind)
- 트랜잭션을 커밋할 때까지 INSERT SQL을 모음
- JDBC BATCH SQL 기능을 사용해서 한번에 SQL 전송
- UPDATE, DELETE로 인한 로우(ROW)락 시간 최소화
- 트랜잭션 커밋 시 UPDATE, DELETE SQL 실행하고, 바로 커밋
-
지연 로딩(Lazy Loading)
지연 로딩: 객체가 실제 사용될 때 로딩 즉시 로딩: JOIN SQL로 한번에 연관된 객체까지 미리 조회
-
데이터 접근 추상화와 벤더 독립성
표준
ORM은 객체와 RDB 두 기둥 위에 있는 기술이다.