[JPA] 1.1 패러다임의 불일치

PADO·2020년 11월 5일
4

JPA

목록 보기
1/1
post-thumbnail

JPA란?

JPA를 사용하면 객체를 데이터베이스에 저장하고 관리할 때, 개발자가 직접 SQL을 작성하는 것이 아니라 JPA가 제공하는 API를 사용하면 된다. 그러면 JPA가 개발자 대신에 적절한 SQL을 생성해서 데이터베이스에 전달한다.

1.1.1 JPA가 제공하는 CRUD API

저장 기능

jpa.persist(member); // 저장

persist()메소드는 객체를 데이터베이스에 저장한다.

이 메소드를 호출하면 JPA가 객체와 매핑정보를 보고 적절한 INSERT SQL을 생성해서 데이터베이스에 전달한다.

조회 기능

String memberId = "memberId";
Member member = jpa.find(Member.class, memberId); // 조회

find()메소드는 객체 하나를 데이터베이스에서 조회한다.

JPA는 객체와 매핑정보를 보고 적절한 SELECT SQL을 생성해서 데이터베이스에 전달하고 그 결과로 Member 객체를 생성해서 반환한다.

수정 기능

Member member = jpa.find(Member.class, memberId);
member.setName("이름변경"); // 수정

JPA는 별도의 수정 메소드를 제공하지 않는다. 대신 객체를 조회해 값을 변경만 하면 트랜잭션을 커밋할 때 데이터베이스에 적절한 UPDATE SQL이 전달 된다.

연관된 객체 조회

Member member = jpa.find(Member.class, memberId);
Team team = member.getTeam(); // 연관된 객체 조회

JPA는 연관된 객체를 사용하는 시점에 적절한 SELECT SQL을 실행한다. 따라서 JPA를 사용하면 연관된 객체를 마음껏 조회할 수 있다.

이와 같이 JPA는 SQL을 개발자 대신 작성해서 실행 해주는 것 이상의 기능들을 제공한다.


1.1.2 연관관계

객체는 참조를 사용해서 다른 객체와 연관관계를 가지고 참조에 접근해서 연관된 객체를 조회한다. 반면에 테이블은 외래 키를 사용해서 다른 테이블과 연관관계를 가지고 조인을 사용해서 연관된 테이블을 조회한다.

따라서 객체지향 모델링을 사용하면 객체를 테이블에 저장하거나 조회 하기가 쉽지 않다. Member 객체는 team 필드로 연관관계를 맺고 MEMBER 테이블은 TEAM_ID 외래 키로 연관관계를 맺기 때문인데, 객체 모델은 외래 키가 필요 없고 단지 참조만 있으면 된다. 반면에 테이블은 참조가 필요 없고 외래 키만 있으면 된다. 결국, 개발자가 중간에서 변환 역할을 해야 한다.

  • 저장
  • 조회

이런 과정들은 모두 패러다임 불일치를 해결하려고 소모하는 비용이다. 만약 자바 컬렉션에 회원 객체를 저장한다면 이런 비용이 전혀 들지 않는다.

JPA와 연관관계

JPA는 연관관계와 관련된 패러다임의 불일치 문제를 해결해준다.

member.setTeam(team); // 회원과 팀 연관관계 설정
jpa.persist(member); // 회원과 연관관계 함께 저장

개발자는 회원과 팀의 관계를 설정하고 회원 객체를 저장하면 된다. JPA는 team의 참조를 외래 키로 변환해서 적절한 INSERT SQL을 데이터베이스에 전달한다.

객체를 조회할 때 외래 키를 참조로 변환하는 일도 JPA가 처리해 준다.

Member member = jpa.find(Member.class, memberId);
Team team = member.getTeam();

1.1.3 객체 그래프 탐색

SQL을 직접 다루면 처음 실행하는 SQL에 따라 객체 그래프를 어디까지 탐색할 수 있는지 정해진다.

이것은 객체지향 개발자에게 큰 제약이다. 비지니스 로직에 따라 사용하는 객체 그래프가 다른데 언제 끊어질지 모를 객체 그래프를 함부로 탐색할 수는 없기 때문이다.

class MemberService {
	...
	public void process() {
		
		Member member = memberDAO.find(memberId);
		member.getTeam(); // member -> team 객체 그래프 탐색이 가능한가?
		member.getOrder().getDelivery(); // ??

MemberServicememberDAO를 통해서 member 객체를 조회했지만, 이 객체와 연관된 Team, Order, Delivery방향으로 객체 그래프를 탐색할 수 있을지 없을지는 이 코드만 보고는 예측할 수 없다. 결국 데이터 접근 계층인 DAO 를 열어서 SQL을 직접 확인해야 한다. 이것은 엔티티가 SQL에 논리적으로 종속되어서 발생하는 문제다.

JPA와 객체 그래프 탐색

JPA를 사용하면 객체 그래프를 마음껏 탐색할 수 있다.

member.getOrder().getOrderItem()... // 자유로운 객체 그래프 탐색

JPA는 연관된 객체를 사용하는 시점에 적절한 SELECT SQL을 실행한다. 따라서 JPA를 사용하면 연관된 객체를 신뢰하고 마음껏 조회할 수 있다. 이 기능은 실제 객체를 사용하는 시점까지 데이터베이스 조회를 미룬다고 해서 지연 로딩이라고 한다.

JPA는 연관된 객체를 즉시 함께 조회할지 아니면 실제 사용되는 시점에 지연해서 조회할지를 간단한 설정으로 정의할 수 있다. 만약 MemberOrder를 즉시 함께 조회 하겠다고 설정하면 JPA는 Member를 조회할 때 다음 SQL을 실행해서 연관된 Order도 함께 조회한다.

SELECT M.*, O.*
	FROM MEMBER M
	JOIN ORDER O ON M.MEMBER_ID = O.MEMBER_ID

1.1.4 비교

데이터베이스는 기본 키의 값으로 각 row를 구분한다. 반면에 객체는 동일성 비교와 동등성 비교라는 두 가지 비교 방법이 있다.

  • 동일성 비교: ==. 객체 인스턴스의 주소 값을 비교한다.
  • 동등성 비교. equals(). 객체 내부의 값을 비교한다.
class MemberDAO {

	public Member getMember(String memberId) {
		String sql = "SELECT * FROM MEMBER WHERHE MEMBER_ID = ?";
		...
		// JDBC API, SQL 실행
		return new Member(...);
	}
}
String memberId = "memberId";
Member member1 = memberDAO.getMember(memberId);
Member member2 = memberDAO.getMember(memberId);

member1 == member2; // 다르다.

기본 키 값이 같은 회원 객체를 두 번 조회했다. 그런데 둘은 동일성(==) 비교 시 false가 반환된다.

왜냐하면 member1과 member2는 같은 데이터베이스 로우에서 조회했지만, 객체 측면에서 볼 때 둘은 다른 인스턴스기 때문이다. (MemberDAO.getMember()를 호출할 때마다 new Member()로 인스턴스가 새로 생성된다.)

이러한 패러다임의 불일치 문제를 해결하기 위해 데이터베이스의 같은 로우를 조회할 때마다 같은 인스턴스를 반환하도록 구현하는 것은 쉽지 않다.

JPA와 비교

JPA는 같은 트랜잭션일 때 같은 객체가 조회되는 것을 보장한다. 그러므로 다음 코드에서 member1과 member2는 동일성 비교(==)에 성공한다.

String memberId = "memberId";
Member member1 = jpa.find(Member.class, memberId);
Member member2 = jpa.find(Member.class, memberId);

member1 == member2; // 같다.

1.1.5 정리

객체 모델과 관계형 데이터베이스 모델은 지향하는 패러다임이 서로 다르다. 문제는 객체지향 애플리케이션답게 정교한 객체 모델링을 할수록 패러다임의 불일치 문제는 더 커지고, 결국 데이터 중심의 모델로 변해간다. JPA는 패러다임의 불일치 문제를 해결해주고 정교한 객체 모델링을 유지하게 도와준다.

0개의 댓글