JPA 프로그래밍 기본기
JPA를 공부하면서 정리를 한 내용들 입니다.
-참고 김영한 개발자님 : [토크ON세미나] JPA 프로그래밍 기본기 다지기 | T아카데미 |
SQL 중심적인 개발의 문제점
1.객체 CRUD의 무한 반복
public class member {
private String memberId;
private String name;
...
}
INSERT INTO MEMBER(MEMBER_ID, NAME) VALUES
SELECT MEMBER_ID, NAME FROM MEMBER M
UPDATE MEMBER SET ...
기존에 기획자가 요구했던 형식이 위와 같이 되어있는 상황에서
추가적으로 필드를 요구한다면 개발자는 하나하나 해당 필드를 추가해야 합니다.
public class member {
private String memberId;
private String name;
<strong>private String tel;</strong>
...
}
INSERT INTO MEMBER(MEMBER_ID, NAME, <strong>TEL</strong>) VALUES
SELECT MEMBER_ID, NAME, <strong>TEL</strong> FROM MEMBER M
UPDATE MEMBER SET ... <strong>TEL</strong>
2.엔티티 신뢰 문제
class MemberService {
...
public void process(String id) {
Member member = memberDAO.find(id);
member.getTeam(); // ???
member.getOrder().getDelivery(); // ???
}
}
이것은 객체지향적인 코드입니다.
그러나 Member 객체를 가져왔음에도 불구하고,
team과 order와 delivery라는 것을 가져온다는 것을 직접 확인하는지를
보장이 되지 않는 이상 이런식으로 짜면 안됩니다.
3.계층형 아키텍쳐에서 진정한 의미의 계층 분할이 어렵다.
물리적으로는 분할되어 있지만 논리적으로는 분할되었는지 모릅니다.
4.SQL에 의존적인 개발을 피하기 어렵다.
어떤 쿼리를 작성했느냐에 따라서 비즈니스 로직이 다 달라져버립니다.
패러다임의 불일치
객체 vs 관계형 데이터베이스
객체를 영구 보관하는 저장소는 RDB, NoSQL, File, OODB처럼 다양한 방법이 있습니다.
그러나 현실적인 대안은 RDB와 NoSQL (관계형 데이터베이스) 뿐입니다.
객체를 관계형 데이터 베이스에 저장하기 위해서는 SQL로 변환해서 저장해야 합니다.
즉, 개발자가 SQL 매퍼 일을 너무 많이 하게 됩니다.
객체와 관계형 데이터베이스의 차이
연관관계
- 객체의 연관관계에는 방향성이 있다.
-
테이블의 연관관계는 방향성이 없다.
- 그런 이유로 보통은 아래와 같이 객체를 테이블에 맞추어 모델링합니다.
class Member {
String id;
Long teamId; //TEAM_ID FK 컬럼 사용
String username;
}
class Team {
Long id; //TEAM_ID PK 사용
String name;
}
보통은 이렇게 해야 쿼리를 쉽게 짤 수 있기 때문에 이런식으로 작성합니다.
- 객체다운 모델링은 아래와 같습니다.
class Member {
String id;
Team team;
String username;
Team getTeam() {
return team;
}
}
class Team {
Long id;
String name;
}
Member에 Team의 값이 아니라 오브젝트가 있는 것이 더 객체지향적인 것 입니다.
- 객체 그래프 탐색 - 객체는 자유롭게 객체 그래프를 탐색할 수 있어야 합니다.
객체를 자바 컬렉션에 저장하듯이 DB에 저장할 수는 없을까? 그에 대한 질문을 1980년대부터 시작을 합니다.
그에 대한 해답으로 JPA(Java Persistence API)가 나오게 됩니다.
ORM(Object-relational mapping) 이란?
- Object-relational mapping (객체 관계 매핑)
- 객체는 객체대로 설계하고, 관계형 데이터베이스는 관계형 데이터베이스대로 설계합니다.
- ORM 프레임워크가 중간에서 매핑해줍니다.
- 대중적인 언어에는 대부분 ORM 기술이 존재합니다.
- ORM은 객체와 RDB 두 기둥 위에 있는 기술입니다.
JPA (Java Persistence API)
- 현재 자바 진영의 ORM 기술 표준으로, 인터페이스의 모음입니다. 즉, 실제로 동작하는 것이 아닙니다.
- JPA 인터페이스를 구현한 대표적인 오픈소스가 Hibernate라고 할 수 있습니다.
- JPA 2.1 표준 명세를 구현한 3가지 구현체: Hibernate, EclipseLink, DataNucleus
JPA의 동작과정
JPA는 애플리케이션과 JDBC 사이에서 동작합니다.
개발자가 JPA를 사용하면, JPA 내부에서 JDBC API를 사용하여 SQL을 호출하여 DB와 통신합니다.
즉, 개발자가 직접 JDBC API를 쓰는 것이 아닙니다.
왜 JPA를 사용해야하는가?
1.SQL 중심적인 개발에서 객체 중심으로 개발
2.생산성 - JPA와 CRUD
간단한 CRUD
- 저장: jpa.persist(member)
- 조회: Member member = jpa.find(memberId)
- 수정: member.setName(“변경할 이름”)
- 삭제: jpa.remove(member)
특히, 수정이 굉장히 간단해집니다.
객체를 변경하면 알아서 DB에 UPDATE Query가 나갑니다.
3.유지보수
- 기존: 필드 변경 시 모든 SQL을 수정해야 한다.
- JPA: 필드만 추가하면 된다. SQL은 JPA가 처리하기 때문에 손댈 것이 없다.
4.Object와 RDB간의 패러다임 불일치를 해결
- JPA와 상속
* 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
- JPA와 연관관계
- 객체의 참조로 연관관계 저장 가능 member.setTeam(team); jpa.persist(member);
- JPA와 객체 그래프 탐색
신뢰할 수 있는 엔티티, 계층
class MemberService {
...
public void process() {
/* 직접 구현한 DAO에서 객체를 가져온 경우 */
Member member1 = memberDAO.find(memberId);
member1.getTeam(); // 엔티티를 신뢰할 수 없음
member1.getOrder().getDelivery();
/* JPA를 통해서 객체를 가져온 경우 */
Member member2 = jpa.find(Member.class, memberId);
member2.getTeam(); // 자유로운 객체 그래프 탐색
member2.getOrder().getDelivery();
}
}
-
내가 아닌 다른 개발자가 직접 구현한 DAO에서 가져오는 경우
- DAO에서 직접 어떤 쿼리를 날렸는지 확인하지 않는 이상, 그래프 형태의 관련된 객체들을 모두 잘 가져왔는지 알 수가 없다.
즉, 반환한 엔티티를 신뢰하고 사용할 수 없다.
- DAO에서 직접 어떤 쿼리를 날렸는지 확인하지 않는 이상, 그래프 형태의 관련된 객체들을 모두 잘 가져왔는지 알 수가 없다.
-
JPA를 통해서 가져오는 경우
- 객체 그래프를 완전히 자유롭게 탐색할 수 있게 된다.
-
JPA와 비교하기 동일한 트랜잭션에서 조회한 엔티티는 같음을 보장한다.
String memberId = "100"; Member member1 = jpa.find(Member.class, memberId); // DB에서 가져옴 Member member2 = jpa.find(Member.class, memberId); // 1차 캐시에서 가져옴 member1 == member2; //같다.
JPA의 성능 최적화 기능
- 중간 계층이 있는 경우 아래의 방법으로 성능을 개선할 수 있는 기능이 존재한다.
- 모아서 쓰는 버퍼링 기능
- 읽을 때 쓰는 캐싱 기능
- JPA도 JDBC API와 DB 사이에 존재하기 때문에 위의 두 기능이 존재한다.