강의 링크
자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런
sql 중심 개발의 문제점.
- 무한 반복, 지루한 코드
- 객체의 CRUD에 대한 지속적으로 해야함
- java → sql , java ← sql
악덕 기획자가.. 엔티티 필드를 추가, 삭제해달라고 하면 쿼리문 전체를 수정해야한다.
객체와 관계형 데이터베이스의 차이
개발자는 == sql mapper
개발자는 데이터베이스에서 데이터를 조회 후 스프링 서버에서 객체로 바꾸고 , 클라이언트는 그 객체를 조회한다.
따라서 개발자는 객체와 RDB 사이 중간에 mapper 역할을 한다 → 객체와 RDB가 어떤 차이가 있는지 알아야 둘을 연결을 하든 말든 할 것이다.
- 차이
- 상속
- 연관관계
- 데이터 타입 및 식별 방법
1. 상속
- 객체 상속 관계
- 부모 클래스의 속성과 메서드를 자식 클래스가 이어받아, 추가적인 기능을 구현하거나 기존의 행동을 수정할 수 있다.
- RDB 상속 관계
- 그러면 조회를 하려면..
- 조인 sql을 작성
- 각 객체를 생성
- 객체 안에 값을 넣어준다..
- 모든 케이스 마다 sql문을 생성해주어야 하며, 위 과정을 반복해야한다.
- 그러면 조회를 하려면..
- RDB는 따로 상속 관계를 명시하여 사용하지 않는다. 슈퍼타입 서브타입으로 나누고 이를 join 쿼리 사용하여 관계를 만들 수 있다.
- RDB가 아니라 **JAVA Collection에 저장 후 사용하는게 더 쉽잖아.**
1. java에서 객체로 저장한 데이터를 collection에 넣는다.
2. 여기서 해당 id를 조회한다.
1. list.add(album) , list.get(id)
join query를 작성할 필요가 없다.
2. 연관관계
- 객체
- 참조를 사용한다.
- RDB
- 테이블은 외래 키를 사용한다. (join을 사용)
- 따라서 객체를 RDB 연관관계에 맞게 entity를 설계한다.
- 하지만 객체는 참조를 기반으로 연관관계를 맺기 때문에, 해당 entity에 연관된 entity 객체의 참조값을 지정하게 된다.
class Member { String id; Team team; // 연관관계에 참조값을 지정. String username; }
개발자가 모든 SQL 쿼리를 객체에 매핑할 경우
- Member를 새롭게 저장하고 싶을 경우에 번거로움
- Team을 참조값으로 갖고 있기 때문에 Team에서 team_id를 조회한 후에 Member entity에 insert 해야하는 로직을 추가해야한다.
- Member를 객체를 조회하는 경우에 번거로움
- join 쿼리를 사용해서 해당 Member와 Team 데이터를 모두 가져옴
- Member , Team 객체를 만들고 객체 안에 데이터를 삽입함.
- setTeam을 이용하여 연관관계를 매핑함.
member.setTeam(team)
- 자유롭게 객체 그래프를 탐색하기 번거롭다.entity에 대한 신뢰문제가 생긴다. (계층을 쉽게 파악 할 수 없다)
- 조회 가능 여부를 판단하려면 find에서 탐색 범위를 확인해야한다.
- 탐색 범위를 개발자는 계속 지정해주어야한다.
public void process() { Member member = memberDAO.find(memberId); member.getTeam() //?? member.getOrder().getDelivery() //?? }
- DAO를 어떻게 설정했느냐에 따라서 탐색 범위가 제한된다.
- JAVA Collection을 사용하는게 더 쉽잖아.
list.add(member) Long memberId = 1; Member member = list.get(memberId); Team team = member.getTeam(); Order order = member.getOrder(); Delivery delivery1 = order.getDelivery() Delivery delivery2 = member.getTeam().getOrder.getDelivery()
데이터 타입 및 식별
public void process() {
Long memberId = 1;
Member member1 = memberDAO.find(memberId);
Member member2 = memberDAO.find(memberId);
System.out.println(member1 == member2) // false
}
- 데이터는 같지만 인스턴스는 다르다.
- JAVA Collection을 사용하면…
list.add(member)
Long memberId = 1;
Member member1 = list.get(memberId);
Member member2 = list.get(memberId);
System.out.println(member1 == member2) // true
- 데이터가 같으면 같다고 식별.
결론
RDB를 객체 지향적으로 모델링 할수록 매핑 작업만 늘어나게된다.
SQL 매핑 비용이 너무 많이들게된다.
객체를 자바 컬렉션에 저장 하듯이 DB에 저장할 수는 없을까?
JPA
- Java Persistence API
- 자바 진영의 ORM 기술 표준
ORM
- Object-relational mapping(객체 관계 매핑)
- 객체는 객체대로 설계
- 관계형 데이터베이스는 관계형 대로 설계
- ORM 프레임워크가 중간에서 매핑
JPA는 애플리케이션과 JDBC 사이에서 동작
개발자가 원래 직접 jdbc api를 사용했다면 → jpa가 대신 jdbc api를 사용한다.
- jdbc란?
- 자바를 이용한 DB접속과 SQL문장의 실행, 그리고 실행 결과로 얻어진 데이터의 핸들링을 제공하는 방법과 절차에 관한 규약.
- 자바에서 데이터베이스에 접속할 수 있도록하는 자바 api
JPA 동작 - 저장
`
- 회원 객체 entity를 분석한다.`
- 바로 insert sql을 만들어준다.
- JDBC API를 사용한다.
- 패러다임 불일치를 해결해준다.
마치 자바 collection에 저장하듯 ! 데이터를 저장한다.
JAP 동작 - 조회
- select sql 생성한다.
- JDBC API를 사용한다.
- ResultSet 매핑
- 패러다임 불일치를 해결해준다.
JPA는 표준 명세
- JPA는 인터vp이스의 모음이다.
- JPA가 데이터베이스 작업을 위한 다양한 규약과 메소드를 정의하는 인터페이스로 구성되어있다.
- EntityManager , EntityTransaction, Query(쿼리 실행) , CriteriaBuilder(쿼리 생성)..
- JPA가 데이터베이스 작업을 위한 다양한 규약과 메소드를 정의하는 인터페이스로 구성되어있다.
- JAP 2.1 표준 명세를 구현한 3가지 구현체
- 하이버네이트, EclipseLink, DataNucleus
JPA를 왜 사용해야 하는가?
- SQL 중심적인 개발에서 객체 중심으로 개발
- 생산성(persist , remove, setName.. 간편함), 유지보수(필드만 추가하면 알아서 수정), 성능
- 패러다임 불일치 해결
- 데이터 접근 추상화와 벤더 독립성
- 표준
JPA와 상속 - 저장
- 객체 상속 관계를 → 관계형 타입으로 매핑하는 부분을 자동화해준다.
JPA와 상속 - 조회
- findById → JPA가 join 쿼리를 작성해서 jdbc api를 사용하여 해당 객체를 반환받게된다.
JPA와 연관관계, 객체 그래프 탐색
setTeam → pserisit 명령어를 이용하여 연관관계를 저장한다.
이후 find 명령어 후 member.getTeaml()을 이용하면 해당 객체 그래프 탐색이 가능해진다.
find 시에 전체 쿼리를 던지지 않고 JPA 내부에서 성능 최적화를 거친 후 쿼리를 보내게 된다.
JPA의 성능 최적화 기능
- 1차 캐시와 동일성 보장
- 같은 트랜잭션 안에서는 같은 엔티티를 반환한다.
- DB Isolation Level이 Read Commit이라도 어플리케이션에서 Repeatable Read를 보장한다.
- 여러번 조회하면 commit이 중간에 완료되었을 시에 데이터가 달라질 수 있다.
- 위와 같이 커밋한 데이터만 읽을 수 있는 상황이라도 어플리케이션에서는 한 번 조회한 데이터를 반복해서 조회해도 같은 데이터가 조회된다. (일관성을 보장한다)
- 트랜잭션을 지원하는 쓰기 지연 - INSERT
- 트랜잭션이 commit 될 때까지 INSERT SQL을 모음
- JDBC BATCH SQL 기능을 이용해 한번에 SQL 전송
tracsaction.begin(); em.persist(memberA); em.persist(memberA); em.persist(memberA); // 여기 까지는 INSERT SQL을 보내지 않음 tracsaction.commit(); // 여기서 배치 전
[중복되는건 기억하는 기능(캐싱) , 모아서 보내는게 보내짐 (버퍼 라이팅) ] → 성능 향상이 이루어질 수 있음.
- 지연 로딩과 즉시 로딩
- 지연 로딩 : 객체가실제 사용될 때 로딩
- 즉시 로딩 : JOIN SQL로 한번에 연관된 객체까지 미리 join문 사용
신뢰할 수 있는 엔티티, 계층
jpa의 조회는 엔티티를 신뢰할 수 있는지(해당 속성을 가져오는지) 어떻게 알 수 있는걸까?
→ 지연로딩을 이용한다.
JPA의 데이터 비교
find를 이용할 경우 동일한 트랜잭션에서 조회한 에티티는 같음을 보장한다.
- meber에 대한 동일한 트랜잭션
- 두 번 find가 실행되지만 실제 sql 쿼리는 1번 실행된다. (같은 트랜잭션 안에서만!)
Member member1 = jpa,find(member.class, memberId);
Member member2 = jpa,find(member.class, memberId);
System.out.println(member1 == member2) // true