SQL 중심적인 개발에서 JPA로

MinSeong Kang·2022년 7월 24일
0

spring

목록 보기
9/18

대부분의 애플리케이션은 객체 지향 언어를 사용하여 개발하고, 데이터베이스는 관계형 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와 패러다임의 불일치 해결

  1. 상속 : 조회시 여러 SQL문을 생성하는 것과 저장시 JOIN문제를 알아서 해결해줌.
  2. 연관관계
  3. 객체 그래프 탐색 : 자유로운 객체 그래프 탐색이 가능.
  4. 비교하기 : 동일한 트랜잭션에서 조회한 엔티티는 같음을 보장.

성능최적화

  1. 1차 캐시와 동일성 보장
  • 같은 트랜잭션 안에서는 같은 엔티티를 반환 - 약갼의 조회 성능 향상
Member member1 = jpa.find(Member.class, memberId); // SQL
Member member2 = jpa.find(Member.class, memberId); // 캐시
  • DB Isolation Level이 Read Commit이어도 애플리케이션에서 Repeatable Read 보장 ?? - 공부예정
  1. 트랜잭션을 지원하는 쓰기 지연
  • 트랜잭션을 커밋할 때까지 INSERT SQL을 모은다.
  • JDBC BATCH SQL 기능을 사용해서 한번에 SQL 전송
// 트랜잭션 시작
em.persist(member1);
em.persist(member2);
em.persist(member3);
// Insert SQL을 데이터 베이스에 반영하지 않음.

transaction.commit(); // 커밋 순간 데이터베이스에 Insert SQL을 모아서 보냄.
  • UPDATE, DELETE로 인한 로우락 시간 최소화
  • 트랜잭션 커밋시 UPDATE, DELETE SQL 실행하고 바로 커밋
// 트랜잭션 시작
changeMember(member1);
deleteMember(member2);
비즈니스_로직_수행();
// 비즈니스 로직 수행 동안 DB 로우 락이 걸리지 않음.

transaction.commit(); // 커밋 순간 데이터베이스에 UPDATE, DELETE SQL을 보낸다.
  1. 지연 로딩
  • 지연 로딩 : 객체가 실제 사용될때 사용
Member member = memberRepository.find(memberId);
// SELECT * FROM MEMBER;

Team team = member.getTeam();
String teamName = team.getName();
// SELECT * FROM TEAM;

상황에 따라 팀을 사용하고, 팀을 사용안할 때가 있어 팀을 사용할 때 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

0개의 댓글