[자바 ORM 표준 JPA 프로그래밍] - 1. JPA 소개

쓰옹·2023년 3월 23일
0

JPA

목록 보기
1/4

김영한, ⌜자바 ORM 표준 JPA 프로그래밍⌟ 과
인프런 강의 <자바 ORM 표준 JPA 프로그래밍 - 기본편> 으로
공부한 JPA를 정리한 내용이다.

JPA 이전엔 SQL로 관계형 데이터베이스를 관리하였다.
MyBatis나 스프링의 JdbcTemplate와 같은 SQL매퍼를 사용하며 JDBC API 사용 코드를 많이 줄일 수 있었지만 여전히 그런 과정은 비생산적이었다.

JPA는 자바진영의 ORM 기술 표준으로
객체와 관계형 데이터베이스 간의 차이를 중간에서 해결해준다.
어플리케이션을 객체 중심으로 개발하여 생산성과 유지보수가 이전보다 확연이 좋아졌고 테스트 작성도 편리해졌다.

패러다임의 불일치 문제

객체지향 프로그래밍 ; 추상화, 캡슐화, 정보은닉, 상속, 다형성 등 복잡성 제어 장치 제공
VS
관계형 데이터베이스 ; 데이터 중심 구조화, 집합적 사고 요구, 추상화/상속/다형성 개념 無

상속

데이터베이스는 상속이라는 기능이 없고 그나마 유사한 형태로 슈퍼타입 서브타입 관계를 사용해 테이블을 설계할 수 있다.
이렇게 되면 INSERT문 작성 시 부모 객체에서 부모데이터만 꺼내 작성하고 자식객체에서 자식데이터만 꺼내 따로따로 작성해야한다.
조회시에도 테이블을 조인해서 조회하고 객체를 생성해야한다.

연관관계

객체는 참조해서 연관된 객체를 조회하고 참조가 있는 방향으로만 조회할 수 있지만
테이블은 외래 키를 사용해 연관관계를 가지고 조인으로 연관된 테이블을 조회하고 외래 키 하나로 연관된 테이블 모두에서 조회가 가능하다

테이블에 맞춘 객체 모델링

class Member {
	String id;   // MEMBER_ID 컬럼
    Long teamId; // TEAM_ID FK 컬럼
    String username;
}

class Team{
	Long id;  // TEAM_ID PK 사용
    String name;
}

객체를 테이블에 저장/조회 시에는 편리하다 그러나
객체는 연관된 객체의 참조를 보관해야 참조를 통해 연관된 객체를 찾을 수 있는데 외래 키 값을 그대로 보관하는 teamId 필드는 Team team = member.getTeam(); 같이 참조를 통해 연관객체를 찾을 수 없다.
== 객체지향의 특징을 잃어버리게 된다.

객체지향 모델링

class Member {
	String id;   // MEMBER_ID 컬럼
    Team team;   // 참조로 연관관계 맺음
    String username;
}

class Team{
	Long id;  // TEAM_ID PK 사용
    String name;
}

연관된 팀을 조회할 수는 있지만 객체를 테이블에 저장하거나 조회하기 쉽지 않다.

# 생성
INSERT INTO MEMBER (
	MEMBER_ID,
    TEAM_ID,
    USERNAME
) VALUES ...

# 조회
SELECT M.*, T.*
	FROM MEMBER M
    JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID

그리고 객체를 생성해서 직접 연관관계를 설정해야한다.

public Member find(String memberId) {
	// SQL 실행
    ...
    // 객체 생성
    ...
    // 연관관계 설정
    member.setTeam(team);
    return member;
}

객체 그래프 탐색

참조를 통해 연관된 객체를 검색하는 것
SQL을 직접 다루면 처음 실행하는 SQL에 따라 객체 그래프를 어디까지 탐색할 수 있는지 정해짐

class MemberService {
	...
    public void process() {
    	Member member = memberDAO.find(memberId);
        member.getTeam(); // ???
        member.getOrder(); // ???
    }
}

위 코드를 보면 memberDAO를 통해 member 객체를 조회하고 거기서 Team과 Order를 탐색한다. 하지만 위 코드만 보고서는 객체 탐색이 가능한지 예측이 불가능하다. 결국 DAO를 열어서 SQL을 직접 확인해야한다.
엔티티 신뢰에 대한 문제가 생기게 된다.
모든 객체를 미리 로딩해서 메모리에 올려두는 것은 노노하다
사용하지도 않는 데이터를 올려두면 어마무시한 쿼리양이...!
결국 DAO에 Member 조회 메서드, Member&Team조회 메서드 등 상황에 따라 여러 벌 만들어야 한다.

JPA에서는

연관된 객체를 사용하는 시점에 select문이 실행되기 때문에 연관된 객체를 신뢰하고 조회가 가능하다.

  • 지연로딩: 실제 객체를 사용하는 시점까지 데이터베이스 조회를 미룸
class Member {
	private Order order;
    
    public Order getOrder() {
    	return order;
    }
}

== 지연로딩 사용
Member member = jpa.find(Member.class, memberId); // SELECT MEMBER SQL

Order order = member.getOrder();
order.getOrderDate(); // Order 사용 시점에 SELECT ORDER SQL

연관된 객체를 즉시 조회할지 지연 조회할지는 설정할 수 있다.

비교 (데이터식별)

데이터베이스 -> 기본키의 값으로 각 로우를 구분
객체 -> 동일성(identity)비교와 동등성(equality)비교 방법

  • 동일성비교: ==비교. 객체 인스턴스 주소 값 비교
  • 동등성비교: equals() 객체 내부 값 비교

DAO에서 getMember() SQL을 작성하면 결과적으로 new Member();로 메서드 호출시마다 새로운 인스턴스가 생성된다.

Long memberId = 100;
Member member1 = memberDAO.getMember(memberId);
Member member2 = memberDAO.getMember(memberId);

member1 == member2; // false

따라서 같은 데이터베이스 로우에서 조회했지만 동일성비교는 실패한다.

JPA에서는

같은 트랜잭션일 때 같은 객체가 조회되는 것을 보장한다.

Long memberId = 100;
Member member1 = jpa.find(Member.class, memberId); // SQL실행
Member member2 = jpa.find(Member.class, memberId); // 캐싱

member1 == member2; // true

마무리

JPA는 객체 모델과 관계형 데이터베이스 모델의 패러다임 불일치 문제를 해결해주고 정교한 객체 모델링을 유지하게 도와준다. 어플리케이션을 SQL이 아닌 객체 중심으로 개발할 수 있게 해주기 떄문에 생산성과 유지보수가 좋아지고 테스트 작성도 편리해진다.

profile
기록하자기록해!

0개의 댓글