Spring/JPA

JPA 소개 #1

묠니르묘묘 2022. 2. 15. 18:52

 

관계형 데이터베이스 = 대중적이고 안전한 데이터 저장소

자바로 개발하는 애플리케이션 대부분 관계형 데이터베이스 사용한다.

DB 관리하려면 SQL을 사용해야 한다.

그래서 다음과 같은 원리로 사용하게 된다.

JDBC API와 SQL

JDBC API를 사용해 SQL을 DB에 전달하기에 개발자는 SQL을 다뤄야 한다.

 

 

🥲 SQL을 직접 사용할 때 발생하는 문제점

1. 반복

회원 조회 기능 → SQL 작성

회원 등록 기능 → SQL 작성

회원 수정기능 → SQL 작성

회원 삭제 기능 → SQL 작성

.......  → SQL 작성

 

이렇게 DB에 CRUD하거나 여러 테이블을 사용하려면 반복적이고 유사한 SQL과 JDBC API를 코드로 작성해야 한다.

 

 

2. SQL에 의존적 개발

회원 객체를 관리하는 MemberDAO 완성함.

그 후 회원의 연락처도 저장해달라고 요구사항 추가됨.

  • 등록 코드 변경( 클래스 필드추가, INSERT SQL 수정, 연락처 값 등록 SQL 전달 등 여러가지 SQL과 JDBC API 수정 )
  • 조회 코드 변경 ( SQL과 JDBC API 수정 )
  • 수정 코드 변경 ( SQL과 JDBC API 수정 )

회원은 어떤 한 팀에 필수로 소속되어야 한다는 요구사항 추가됨.

  • 여러가지 코드와 SQL, JDBC API 수정

 

 

이렇게 기존의 서비스에서 요구사항이 추가된다면 SQL에 의존적인 상황에서는 계속 코드와 SQL을 바꾸고 확인하고 바꾸고를 반복하여야 한다.

 

 

3. 이런 문제점이 있는데 만약 JPA를 사용한다면?

이제 엄청난 시간과 코드를 줄일 수 있다.

개발자가 직접 SQL을 작성하는 것이 아니라 JPA가 제공하는 API를 사용하면 알아서 적절한 SQL을 생성해서 DB에 전달한다.

  • 저장 기능
jpa.persist(member); // 저장

 

persist() 메서드는 객체를 DB에 저장한다.

즉, JPA가 객체와 매핑정보를 보고 적절한 INSERT SQL을 생성해 DB에 전달한다는 뜻이다.

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

find() 메서드는 객체 하나를 DB에서 조회한다.

즉, JPA는 객체와 매핑정보를 보고 적절한 SELECT SQL을 생성해서 DB에 전달하고 결과를 Member 객체를 생성해서 반환한다.

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

JPA는 수정 메서드를 제공하지 않는다.

대신 객체를 조회해서 값을 변경만 하면 트랜잭션을 커밋할 때 DB에 적절한 UPDATE SQL이 전달된다.

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

JPA는 연관된 객체를 사용하는 시점에 적절한 SELECT SQL을 실행한다.

 

 

 

🥲 패러다임의 불일치

애플리케이션 발전  → 내부 복잡성 증가  → 유지보수하기 어려운 애플리케이션

 

 

객체지향 프로그래밍

  • 추상화, 캡슐화, 정보은닉, 상속, 다형성 등
  • 시스템의 복잡성을 제어할 수 있는 다양한 장치 제공

 

 

관계형 데이터베이스

  • 데이터 중심으로 구조화
  • 집합적인 사고 요구
  • 객체지향에서의 추상화, 상속, 다형성 같은 개념 없음

 

 

객체와 관계형 데이터베이스는 지향하는 목적이 다르므로 표현 방법도 다름!

이것이 객체와 관계형 데이터베이스의 패러다임 불일치 문제라고 한다!

애플리케이션 객체지향 언어 개발 -> 데이터는 관계형 데이터베이스 저장 -> 패러다임 불일치로 개발자가 중간다리 역할해야함!

 

 

 

1. 상속

객체는 상속이 있지만, 테이블은 없다.

그래서 개발자는 이런 패러다임 불일치를 해결하려고 SQL과 JDBC API를 엄청난 시간을 들여 방대한 코드를 작성한다.

 

이런 것들을 JPA가 개발자 대신 해결한다.

앞서 말했던 persist() 메서드로 상속된 객체도 저장하고, find() 메서드로 상속된 객체도 조회가 가능하다.

매번 방대한 코드를 작성할 필요없이 간단하게 처리할 수 있다.

 

 

 

2. 연관관계

객체는 참조를 사용해서 다른 객체와 연관관계를 가지고 참조에 접근해서 연관된 객체를 조회한다.

테이블은 외래키를 사용해서 다른 테이블과 연관관계를 가지고 조인을 사용해서 연관된 테이블을 조회한다.

이렇게나 달라보이는데 특히 객체는 참조가 있는 방향으로만 조회 가능하다.

Member → Team 조회는 되지만 Team → Member 조회는 불가능하다.

테이블은 외래키로 둘 다 조회가능하다.

 

이런 패러다임 불일치를 또 해결하려면 소모하는 비용이 엄청나다.

이것 역시 JPA가 해결해준다.

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

개발자는 회원과 팀의 관계를 설정하고 회원 객체를 저장한다.

JPA는 team의 참조를 외래키로 변환해서 적절한 INSERT SQL을 DB에 전달한다.

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

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

 

 

 

3. 객체 그래프 탐색

객체는 자유롭게 객체 그래프를 탐색할 수 있어야 한다.

객체 연관관계

이렇게 Team은 member.getTeam(); 으로 조회하고,

OrderItem은 member.getOrder().getOrderItem(); 처럼 조회하듯이 조회하는 것을 객체 그래프 탐색이라 한다.

 

그렇다면 member에서 다른 범위들을 마음대로 검색할 수 있을까? 답은 불가능이다.

처음 실행한 SQL에 따라 탐색 범위가 결정되기 때문이다. (Team만 조인했다면 다른 것들은 조회 불가능)

그래서 member 객체에서 다른 것들을 조회하기전에 데이터 접근 계층인 DAO를 찾아봐야 한다.

결국 엔티티가 SQL에 논리적으로 종속적이라는 것이다.

 

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

JPA는 연관된 객체를 사용하는 시점에 적절한 SELECT SQL을 실행한다.

JPA는 연관된 객체를 즉시 함께 조회할지, 실제 사용 시점에 지연해서 조회할지 설정할 수 있다.

(지연 로딩 : 실제 객체를 사용하는 시점까지 DB 조회를 미룬다.)

 

 

 

4. 비교

데이터베이스는 기본키의 값으로 각 row를 구분한다.

객체는 동일성 비교 (==) 과 동등성 비교 (equals())라는 두 가지 비교 방법이 있다.

( == : 객체 인스턴스 주소값 비교 , equals() 메서드 : 객체 내부의 값 비교 )

 

같은 회원 객체를 조회해도 객체 측면과 DB 측면은 불일치 하기 때문에 DB의 같은 row를 같은 인스턴스로 반환하는건 쉽지 않다.

트랜잭션이 동시 실행되는 상황이면 더욱 어려워진다.

 

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

 

 

 

 

5. 정리

객체와 관계형 데이터베이스는 지향하는 패러다임이 다르다.

이러한 불일치를 해결해주면서 서로의 장점을 쓸 수 있는 것이 JPA이다.

 

 

 


자바 ORM 표준 JPA 프로그래밍 / 김영한 지음 / 에이콘출판주식회사 출판