대부분의 애플리케이션은 객체 지향 언어를 사용하여 개발하고, 데이터베이스는 관계형 DB를 사용!!
애플리케이셔는 객체로 개발하고 데이터는 관계형 DB에 저장
SQL 중심적인 개발의 문제점
- 무한 반복, 지루한 반복적인 코드 ex) CRUD, 자바객체를 SQL, SQL를 자바객체로
- 객체에 필드 추가시 모든 SQL를 변경해야할 수 있다.
- SQL에 의존적인 개발을 피하기 힘들다.
페러다임의 불일치
- 객체 지향 프로그래밍은 추상화, 캡슐화, 정보은닉, 상속, 다형성 등 시스템의 복잡성을 제어할 수 있는 다양한 장치 제공!
- 객체를 SQL로 변환해서 RDB에 저장한다. - SQL 변환(매퍼) = 개발자
- 객체와 관계형 데이터 베이스의 차이
-- 상속 : 객체에는 상속 개념이 있지만 관계형 데이터베이스에는 없음. RDB에 상속을 표현하여 설계한다면 등록시 여러 SQL문이 필요하며, 조회시 여러 JOIN 발생
-- 연관관계 : 객체는 참조를 사용, 테이블은 외래키를 사용
데이터 타입, 데이터 식별 방법
-- 객체 그래프를 탐색 : 객체는 자유롭게 객체 그래프를 탐색할 수 있어야 한다. 하지만 SQL 경우 처음 실행한 SQL에 따라 탐색 범위가 결정 - 엔티티에 대한 신뢰문제가 생김.
-- 모든 객체를 미리 로딩할 수 없다. 상황에 따라 동일한 객체 조회 메서드를 여러개 생성해야함. - 계층 분할이 어려움.(물리적으로는 분할되어 있지만 논리적으로는 분할되어 있지 않음.)
-- 비교문제 (sql의 경우 new해서 객체를 생성해서 조회한 결과를 반환, 자바 컬렉션의 경우는 )
-- 객체답게 모델링할 수록 매핑작업만 늘어난다.
객체를 자바 컬렉션에 저장하듯이 DB에 저장할 순 없을까?
답 : JPA !!!
JPA
- JPA : Java Persistence API, 자바 진영의 ORM 기술 표준 - 인터페이스 (구현체 Hibernate)
- ORM : Object-relational mapping(객체 관계 매핑), 객체는 객체대로, RDB는 RDB로 설계, 이 둘의 차이를 ORM 프레임워크가 중간에 매핑, 애플리케이션과 JDBC 중간에서 동작
JPA를 왜 사용해야할까?
- SQL 중심적인 개발에서 객체 중심으로 개발
- 생산성, 유지보수
- 페러다임의 불일치 해결
- 성능
생산성 - 각 메서드가 SQL을 알아서 생성하고 데이터베이스에 접근함.
- 저장 : persist
- 조회 : find
- 수정 : setXX("변경내용") - 객체와 동일
- 삭제 : remove
유지보수
- 기존에는 필드 변경시 모든 SQL를 수정해야했다면, JPA는 필드만 변경되면 JPA가 알아서 해줌.
JPA와 패러다임의 불일치 해결
- 상속 : 조회시 여러 SQL문을 생성하는 것과 저장시 JOIN문제를 알아서 해결해줌.
- 연관관계
- 객체 그래프 탐색 : 자유로운 객체 그래프 탐색이 가능.
- 비교하기 : 동일한 트랜잭션에서 조회한 엔티티는 같음을 보장.
성능최적화
- 1차 캐시와 동일성 보장
- 같은 트랜잭션 안에서는 같은 엔티티를 반환 - 약갼의 조회 성능 향상
Member member1 = jpa.find(Member.class, memberId);
Member member2 = jpa.find(Member.class, memberId);
- DB Isolation Level이 Read Commit이어도 애플리케이션에서 Repeatable Read 보장 ?? - 공부예정
- 트랜잭션을 지원하는 쓰기 지연
- 트랜잭션을 커밋할 때까지 INSERT SQL을 모은다.
- JDBC BATCH SQL 기능을 사용해서 한번에 SQL 전송
em.persist(member1);
em.persist(member2);
em.persist(member3);
transaction.commit();
- UPDATE, DELETE로 인한 로우락 시간 최소화
- 트랜잭션 커밋시 UPDATE, DELETE SQL 실행하고 바로 커밋
changeMember(member1);
deleteMember(member2);
비즈니스_로직_수행();
transaction.commit();
- 지연 로딩
Member member = memberRepository.find(memberId);
Team team = member.getTeam();
String teamName = team.getName();
상황에 따라 팀을 사용하고, 팀을 사용안할 때가 있어 팀을 사용할 때 DB에서 가져온다.
- 즉시 로딩 : JOIN SQL로 한번에 연관된 객체까지 미리 조회
Member member = memberRepository.find(memberId);
// SELECT M.*, T.* FROM MEMBER JOIN TEAM ...
Team team = member.getTeam();
String teamName = team.getName();
// SELECT * FROM TEAM;
멤버을 사용할때 거의 팀도 사용하여 한번에 가져오는게 좋음
개발 시 다 지연로딩으로 개발 -> 최적화 필요시 즉시로딩으로 변경
참고문헌
https://www.inflearn.com/course/스프링-db-2/dashboard