본 게시글은 김영한님의 [자바 ORM 표준 JPA 프로그래밍 - 기본편]을 수강하며 작성한 글입니다.
1. SQL 중심적인 개발의 문제점
1) 무한 반복, 지루한 코드
객체에 새 필드를 추가하게 될 경우 쿼리문을 전부 수정해야 한다.
예를 들어 Member 클래스와 이와 관련된 쿼리문이 존재한다.
만약에 객체에 tel이라는 필드가 새로 추가된다면 이와 연관된 모든 쿼리문을 수정해야 한다.
2. 패러다임의 불일치
객체 vs 관계형 데이터베이스
보통 객체를 관계형 데이터 베이스에 저장한다. 이 과정에서 객체를 SQL로 변환해야 한다.
즉 SQL Mapper 역할을 개발자가 수행한다.
객체와 관계형 데이터베이스의 차이
1. 상속
- SQL을 통해 테이블에 저장하는 과정
객체를 분해한다.
INSERT INTO **ITEM**… 수행
INSERT INTO **ALBUM**… 수행
→ 슈퍼타입과 서브타입의 table에 모두 INSERT를 수행해야 한다. - 자바 컬렉션에 저장하는 과정
list.add(album);
2. 연관관계
객체는 레퍼런스를 가질 수 있지만, RDB는 PK, FK를 사용하여 join을 사용해야 한다.
- 객체
- 참조(Reference)를 사용하여 연관 관계를 찾는다.(Member.getTeam())
- 양뱡향 참조를 할 수 없다. (Member에서 Team으로 참조는 가능하지만, Team에서 Member로는 불가능하다.)
- RDB
- 외래키(FK)를 사용하여 Join 쿼리를 통해 연관 관계를 찾는다.(JOIN ON M.TEAM_ID = T.TEAM_ID)
- 양방향 참조가 가능하다.(테이블은 Team에서 Member로도 fk로 join을 통해 참조 가능하다.)
서로 다른 방식을 갖는 객체와 RDB는 다음과 같이 연관관계를 맺는다.
해당 연관관계를 통해 조회를 수행할 경우 다음과 같은 상당히 복잡한 과정을 거치게 된다.
반면에 자바 컬렉션에서 연관관계를 맺는다면 다음과 같이 관리할 수 있다.
- 객체 저장
list.add(member); - 객체 조회
Team team = member.getTeam();
Member member = list.get(memberId);
3. 데이터 타입
4. 데이터 식별 방법
3) 객체 그래프 탐색
기본적으로 객체는 자유롭게 객체 그래프를 탐색할 수 있어야 한다.
Member는 Team과 Order와 연관관계를 맺고 있다.
위와 같은 SQL 문을 수행할 경우 SQL 문에 의해 Member와 Team에 대한 값만 반환받는다.
따라서 member.getTeam()을 수행했을 땐 값을 반환받지만, member.getOrder()을 수행했을 땐 값을 반환받지 못한다. 즉, 필드는 있지만 값은 없게되는 상황이 발생한다.
→ SQL 문에 의해 탐색 범위가 결정된다.
이는 엔티티 신뢰 문제로 이어지게 된다.
Member와 Team이 연관 관계를 맺고, Member와 Order, Delivery가 연관 관계를 맺은 것처럼 보이지만, 실제 memberDao 파일을 살펴보지 않을 경우 어떤 값이 반환되는지 알 수 없다.
즉 다음 계층의 코드를 확인해야 하는 번거로움이 발생한다.
하지만 모든 객체를 미리 로딩할 수는 없으므로 다음과 같이 조회문 여러개를 만든다.
이는 케이스가 많을 경우 점점 복잡한 코드가 쓰여지게 되며 진정한 의미의 계층 분할이 어렵다.
4) 비교하기
객체 & RDB 연관관계
memberId를 설정하고 member 객체를 두 번 조회할 경우 두 객체는 같을까? 정답은 다르다이다.
실제 getMember() 함수를 살펴보면, SQL 문을 통해 member를 조회한 후 새로운 Member 인스턴스를 생성하기 때문이다.
→ 즉, 같은 데이터이지만 다른 인스턴스인 경우이다.
자바 컬렉션에서 조회
식별자가 같을 때 컬렉션에서의 두 객체의 참조값은 같기 때문에 두 객체는 같다.
→ 즉, 같은 주소값을 바라보게 되기 때문에 같은 반환 값을 갖고 있어 Entity를 신뢰할 수 있다.
객체답게 모델링을 할수록 매핑 작업만 늘어나게 되며 개발자들은 객체지향 설계를 일정 부분 포기하게 된다.
그렇다면, 객체를 자바 컬렉션에 저장 하듯이 DB에 저장할 순 없을까? 해당 고민의 결과가 바로 JPA이다.
2. JPA 소개
1) JPA란?
- Java Persistence API
- 자바 진영의 ORM 기술 표준
2) ORM이란?
- Object-relational mapping(객체 관계 매핑)
- 객체는 객체대로 설계하고 RDB는 RDB대로 설계한다.
- ORM 프레임워크가 중간에서 매핑해준다.
- 대중적인 언어에는 대부분 ORM 기술이 존재한다.
3) JPA는 애플리케이션과 JDBC 사이에서 동작
개발자가 직접 JDBC API를 사용하는게 아닌 JPA를 사용하면 JPA가 JDBC API를 호출하는 방식이다.
JPA 동작 - 저장
MemberDao에서 Member 객체 저장을 JPA에게 요청하면 JPA는 Entity를 분석 후 INSERT SQL을 생성하고 JDBC API를 사용하여 DB에 저장한다.
이때 중요한 것은 패러다임의 불일치를 해결해준다는 것이다.
JPA 동작 - 조회
MemberDao에서 Member 객체 조회를 JPA에게 요청하면 JPA는 SELECT SQL을 생성하고 JDBC API를 사용하여 DB로부터 결과를 받아온다. 해당 결과를 ResultSet에 매핑을 수행한 뒤 Entity Object로 반환한다.
여기서도 중요한 것은 패러다임의 불일치를 해결해준다는 것이다.
4) JPA 역사
5) JPA는 표준 명세
JPA는 실제 구현체가 아닌 인터페이스의 모음이다. JPA 2.1의 표준 명세를 구현한 3가지 구현체는 Hibernate, EclipseLink, DataNucleus이다.
해당 강의에서는 JPA 표준 인터페이스의 Hibernate 구현체를 사용한다.
6) JPA를 왜 사용해야 하는가?
1. SQL 중심적인 개발에서 객체 중심으로 개발
2. 생산성 - JPA와 CRUD
저장: jpa.persist(member)
조회: Member member = jpa.find(memberId)
수정: member.setName(”변경할 이름”)
삭제: jpa.remove(member)
3. 유지보수 - 필드 변경 시 모든 SQL 수정
기존 객체의 필드가 수정되면 쿼리도 일일이 수정해야 한다. 하지만 JPA를 사용하게 되면 필드는 수정하더라도 쿼리를 수정할 일은 없다.
4. 패러다임의 불일치 해결
4-1. JPA와 상속
- 저장 시
개발자가 할 일: jpa.persist(album);
나머진 JPA가 처리: INSERT INTO ITEM… INSERT INTO ALBUM…
- 조회 시
개발자가 할 일: Album album = jpa.find(Album.class, albumId);
나머진 JPA가 처리: SELECT I.*, A.* FROM ITEM I JOIN ALBUM A ON I.ITEM_ID = A.ITEM_ID
4-2. 연관관계, 객체 그래프 탐색
- 연관관계 저장
객체를 테이블에 맞게 모델링 하기 위해 복잡한 과정을 거쳤었지만, JPA에서는 간단한 코드로 연관관계 설정이 가능하다.
member.setTeam(team);
jpa.persist(member);
- 객체 그래프 탐색
객체 그래프를 탐색할 때도 SELECT문을 통해 탐색을 진행했지만, JPA에서는 간단한 코드만으로 탐색이 가능하다.
Member member = jpa.find(Member.class, memberId);
Team team = member.getTeam();
4-3. 신뢰할 수 있는 엔티티, 계층
모든 객체를 미리 로딩해야 하는 SQL 주도 개발에서는 객체 내 연관관계를 신뢰할 수 없었다. 연관관계를 미리 설정해주지 않는다면 객체를 참조하는 데 문제가 있었지만 JPA에서는 이런 문제점이 해결됐다. 즉, JPA를 사용해서 가져온 객체는 신뢰하고 사용할 수 있게 된다.
동일한 트랜잭션에서 조회한 엔티티는 같음을 보장한다.
5. 성능
6. 데이터 접근 추상화와 벤더 독립성
7. 표준
7) JPA의 성능 최적화 기능
1. 1차 캐시와 동일성(Identity) 보장
같은 트랜잭션 안에서는 같은 엔티티를 반환한다. 이는 약간의 조회 성능 향상을 기대할 수 있다.
DB Isolation Level이 Read Commit이어도 애플리케이션에서 Repeatable Read를 보장한다.
첫 번째 SQL문을 수행한 뒤 같은 SQL문을 수행할 경우 캐시 메모리에 저장되어 있던 값을 가져온다.
즉, 결과적으로 SQL이 1번만 실행된다.
2. 트랜잭션을 지원하는 쓰기 지연(transactional write-behind)
트랜잭션을 커밋할 때까지 INSERT SQL을 모아준다.
JDBC BATCH SQL 기능을 사용해서 한 번에 SQL을 전송한다.
3. 지연 로딩(Lazy Loading)
- 지연 로딩 : 객체가 실제 사용될 때 로딩
- 즉시 로딩: JOIN SQL로 한번에 연관된 객체까지 미리 조회
보통 개발 시에는 지연 로딩으로 셋팅을 해놓고, 후에 성능 최적화를 진행할 때 필요한 부분에 한하여 즉시 로딩으로 변경한다.
'Courses > JPA' 카테고리의 다른 글
[자바 ORM 표준 JPA 프로그래밍 - 기본편] 4. 엔티티 매핑 (0) | 2023.10.12 |
---|---|
[자바 ORM 표준 JPA 프로그래밍 - 기본편] 3. 영속성 관리 - 내부 동작 방식 (0) | 2023.10.02 |
[자바 ORM 표준 JPA 프로그래밍 - 기본편] 2. JPA 시작하기 (1) | 2023.10.01 |