JPA란?

미미·2024년 4월 17일
0

Spring

목록 보기
2/3
post-thumbnail

Java Persistence API, 자바 진영의 ORM 기술 표준

💡 ORM? (Object-relational mapping, 객체 관계 매핑 )
객체는 객체대로 설계, 관계형 데이터베이스는 관계형 데이터베이스대로 설계한것을 중간에서 매핑해주는 역할을 한다.


JPA가 필요한 이유?

스프링을 이용하여 서버를 개발시 언어는 객체 지향언어 ex) java, koltin

데이터베이스는 관계형 DB를 사용한다. ex) Oracle, MySQL

따라서 우리는 객체를 관계형 DB에 관리한다. → SQL문을 작성

SQL 중심적인 개발의 문제점

→ 무한 반복, 지루한 코드 🥲

ex) 만약 다음과 같은 Member 객체가 있다고 가정하자.

public class Member {
	private String memberId;
	private String name;
	...
}

간단한 CRUD SQL문을 작성해보면 다음과 같다.

**INSERT** INTO MEMBER( MEMBER_ID, NAME) **VALUES
SELECT MEMBER_ID, NAME FROM MEMBER M
UPDATE MEMBER SET**

이때, 전화번호 필드를 나중에 추가하고싶다면 어떻게 해야할까?

객체에 새로운 필드를 추가할것이다.

public class Member {
	private String memberId;
	private String name;
	private String tel; //필드 추가
	...
}

그리고 SQL문에 하나하나 그 필드를 추가해줘야한다.

INSERT INTO MEMBER( MEMBER_ID, NAME, TEL) VALUES
SELECT MEMBER_ID, NAME, TEL FROM MEMBER M
UPDATE MEMBER SET … TEL = ?

일일이 모두 추가하다보니 효율성도 떨어지고 실수가 발생할 가능성이 높아진다.

패러다임의 불일치

객체와 관계형 데이터베이스의 차이

상속

객체와 관계형 데이터베이스에서 상속관계를 표현하면 다음과 같다.

객체 상속 관계

Table 슈퍼타입 서브타입 관계

  • 슈퍼타입 엔티티 : 상호 배타적인 더 작은 그룹으로 분할 시킬 수 있는 엔티티를 의미한다.
  • 서브타입 엔티티 : 슈퍼타입 내의 분해된 엔티티를 의미한다.
  • 모든 필드들을 모두 한 테이블에서 관리한다면 DTYPE에 따라 어떤 필드들은 NULL값이 된다. → 공통속성들을 슈터타입 엔티티로 묶고 나머지를 서브타입 엔티티로 나누어 관리한다.

💡 이때 Album을 저장하려고 한다면?

  1. 자바 컬렉션에 저장

    list.add(album);

  2. DB에 저장

    객체 분해 → INSERT INTO ITEM …, → INSERT INTO ALBUM …

    (슈퍼타입과 서브타입에 모두 저장해주어야함)


💡 조회하려고 한다면?
  1. 자바 컬렉션에서 조회

    • Album album = list.get(albumId);
    • Item item = list.get(albumId); // 부모타입으로 조회 후 다형성 활용
  2. DB에서 조회

    각각의 테이블에 따른 조인 SQL 작성 → 각각의 객체 생성 → … 굉장히 복잡함


연관관계

객체는 참조를 사용
member.getTeam()

객체다운 모델링

<class Member {
	String id;
	Team team; //참조로 연관관계를 맺음
	String username;
	
	Team getTeam() {
			return team;
	}
}

class Team {
	Long id;
	String name;
}

객체는 연관관계를 참조로 맺어 타입이 클래스인 필드로 저장한다.


테이블은 외래 키를 사용

JOIN ON M.TEAM_ID = T.TEAM_ID

객체를 테이블에 맞추어 모델링

class Member {
	String id;
	Long teamId; //TEAM_ID FK 칼럼 사용
	String username;
}

class Team {
	Long id;
	String name;
}

테이블은 연관관계를 외래키로 맺기때문에 타입이 Long인 키값이 필드에 저장된다.

비교

자바 컬렉션에서 조회

String memberId = "100";
Member member1 = list.get(memberId);
Member member2 = list.get(memberId);

member1 == member2; //같다

자바 컬렉션에서 조회는 같은 참조, 인스턴스가 같다

DB에서 조회

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

member1 == member2; //오류
  • 데이터는 같지만 인스턴스는 다름
  • getMember()메서드를 실행할때마다 다른 인스턴스값을 반환

객체 그래프 탐색

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

하지만 RDB에서는 처음 실행하는 SQL에 따라 탐색 범위가 결정된다.

ex) 다음과 SQL문으로 조회했을 경우

SELECT M.*, T.*
	FROM MEMBER M
	JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
  • member.getTeam(); → 조회 성공
  • member.getOrder(); → Order를 조인하여 조회하지 않았기 때문에 조회를 실패한다.

이렇게 처음 실행하는 SQL에 따라 조회 결과가 달라지면 엔티티 신뢰 문제가 발생한다.

그렇다고 모든 객체를 조인하여 미리 로딩할 수는 없다. → Query가 너무 많아지고 성능이 떨어진다.

따라서 상황에 따라 동일한 조회 메서드를 여러번 생성 해야한다.

  • memberDAO.getMember(); // Member만 조회
  • memberDAO.getMemberWithTeam(); // Member와 Team 조회
  • memberDAO.getMemberWithOrderWithDelivery(); // Member,Order,Delivery 조회

객체 답게 모델링 할수록 매핑 작업만 늘어나게된다.

→ JPA를 사용한다면 객체를 자바 컬렉션에 저장 하듯이 DB에 저장할 수 있다 ‼️

JPA

JPA는 애플리케이션과 JDBC 사이에서 동작한다.

💡 JDBC(Java Database Connectivity)는 Java 기반 애플리케이션의 데이터를 데이터베이스에 저장 및 업데이트하거나, 데이터베이스에 저장된 데이터를 Java에서 사용할 수 있도록 하는 자바 API이다.

  • JPA는 인터페이스의 모음이다
  • JPA 2.1 표준 명세를 구현한 3가지 구현체에는 HIbernate, EclipseLink, DataNucleus가 있다.
  • 이중에서 Hibernate가 가장 보편적으로 사용된다.

JPA를 왜 사용해야 하는가?

  1. SQL 중심적인 개발에서 객체 중심으로 개발할 수 있다.

  2. 생산성성능이 향상되고 유지보수가 용이하다.

    -> 기존에는 필드 변경시 모든 SQL 수정해야했지만 JPA를 사용한다면 필드만 추가하면 된다.

  3. 패러다임의 불일치를 해결할 수 있다.

  4. 데이터 접근 추상화와 벤더 독립성 → DB를 교체해도 문제가 되지않는다.

  5. 표준이다.

JPA의 성능 최적화 기능

1. 1차 캐시와 동일성(identity) 보장

String memberId = "100";
Member m1 = jpa.find(Member.class, memberId); //SQL
Member m2 = jpa.find(Member.class, memberId); //캐시
  • 같은 트랜잭션 안에서는 같은 엔티티를 반환한다. → 약간의 조회 성능 향상
  • 처음 조회할때는 SQL을 실행하여 DB에서 가져오지만 (같은 트랙잭션 안에서) 한번 조회한 객체는 캐시에 저장된 객체를 가져온다.

2. 트랜잭션을 지원하는 쓰기 지연(transactional write-behind)

트랜잭션을 커밋할 때까지 INSERT SQL을 모은다.

JDBC BATCH SQL 기능을 사용하여 트랜잭션을 커밋하는 순간 한번에 SQL을 전송한다.

→ 여러번 통신을 하지않으므로 네트워크 통신비용이 낮아진다.

transaction.begin()/ //트랜잭션 시작

em.persits(memberA);
em.persits(memberB);
em.persits(memberC);
// 여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.

transaction.commit(); // 트랜잭션 커밋
//커밋하는 순간 데이터베이스에 INSERT SQL을 모아서 보낸다.

3. 지연 로딩(Lazy Loading)

  • 지연 로딩 : 객체가 실제 사용될 때 로딩

  • 즉시 로딩 : JOIN SQL로 한번에 연관된 객체까지 미리 조회










본 포스팅은 김영한님의 자바 ORM 표준 JPA 프로그래밍 - 기본편 강의를 참고하여 작성되었습니다.

0개의 댓글