[JPA] JPA

leehyunjon·2022년 7월 10일
0

JPA

목록 보기
3/10

ORM

ORM(Object Relational Mapping)이란?

객체와 데이터베이스 테이블을 매핑시켜주는 프레임워크이다.

즉, 객체를 데이터베이스에 자동으로 영속화 해주는것이다.

종류에는 JPA, Hibernate 등이 있다.


JPA

JPA란

JPA(Java Persistence Api)란?

현재 자바의 ORM기술 표준으로써 인터페이스의 모음이다.

JPA가 직접 동작하는 것이 아니라 Hibernate와 같은 구현체가 직접 동작한다.

참조 : https://suhwan.dev/2019/02/24/jpa-vs-hibernate-vs-spring-data-jpa/

JPA의 동작 과정

JPA는 애플리케이션과 JDBC사이에서 동작하며 JPA내부에서 JDBC API를 사용하여 데이터베이스와 통신한다.

참조 : https://gmlwjd9405.github.io/2019/08/04/what-is-jpa.html

저장

JPA의 데이터 저장

참조 : https://suhwan.dev/2019/02/24/jpa-vs-hibernate-vs-spring-data-jpa/

개발자

  • JPA에게 저장하려는 Member객체(Entity Object)를 전달한다.

JPA

  1. 전달받은 Member객체를 분석.
  2. Insert SQL 생성
  3. 생성한 SQL을 JDBC API를 통해 데이터베이스에 전달.

조회

JPA의 데이터 조회

참조 : https://suhwan.dev/2019/02/24/jpa-vs-hibernate-vs-spring-data-jpa/

개발자

  • JPA에게 조회하려는 Member객체의 PK나 조건 인자를 전달한다.

JPA

  1. 전달받은 인자로 Select SQL 생성
  2. JDBC API를 통해 SQL을 데이터베이스에 전달.
  3. 결과를 ResultSet으로 매핑.
  4. Member객체(Entity Object)로 결과 반환.

JPA를 사용하는 이유

1. SQL 중심적인 개발에서 객체 중심적인 개발

2. 생산성

  • 쿼리를 작성하고 JDBC API에 연결하는 지루하고 반복적인 일을 JPA가 대신 처리한다.
  • CREATE TABLE과 같은 DDL문 자동 생성
  • 간단한 CRUD를 제공한다
    • 저장 : jpa.persist(member);
    • 조회 : jpa.find(Member.class, 1);
    • 수정 : member.setName("name2");
    • 삭제 : jpa.remove(member);

3. 유지보수

기존

  • Entity객체가 수정될 때 관련된 SQL을 모두 수정했어야한다.

JPA

  • Entity객체의 수정할 부분만 수정한다.
    • JPA가 수정한 부분에 대한 SQL을 자동 생성한다.

4. 패러다임 불일치 해결

패러다임 불일치란?
데이터베이스는 데이터 중심 설계, 객체는 객체지향 설계이므로 데이터베이스는 객체가 가지는 상속, 연관관계 등의 개념이 없기 때문에 데이터베이스와 객체의 지향하는 점이 다르다는 것.

1) 상속

객체 상속 모델

참조 : https://ultrakain.gitbooks.io/jpa/content/chapter1/chapter1.3.html

테이블 설계

참조 : https://ultrakain.gitbooks.io/jpa/content/chapter1/chapter1.3.html

객체 모델 코드

public class Item{
	int id;
    String name;
   	int price;
}

public class Album extend Item{
	String artist;
}

public class Movie extend Item{
	String director;
    String actor;
}

public class Book extend Item{
	String author
    String isbn;
}

관련 SQL

-- Album 저장
INSERT INTO ITEM ... //부모 테이블 저장 후
INSERT INTO ALBUM ... //자식 테이블 저장

-- Movie 저장
INSERT INTO ITEM ... //부모 테이블 저장 후
INSERT INTO MOVIE ... //자식 테이블 저장

객체에서 테이블로 데이터를 처리하기 위해서는

  1. (저장)부모 객체에서 부모 데이터를 꺼내어 INSERT SQL 작성
  2. (저장)자식 객체에서 자식 데이터를 꺼내서 INSERT SQL 작성
  3. (조회)자식과 부모 테이블을 조인하여 자식 객체를 생성한다.

JPA 상속

JPA에서는 상속과 관련된 패러다임 불일치 문제를 개발자는 자바 컬렉션에 객체를 저장하듯 JPA에 저장하려는 객체를 저장하면 JPA가 처리해준다.

저장
jpa.persis(album)시 JPA는 Item 테이블 Insert쿼리와 Album 테이블 Insert쿼리를 생성한다.

조회
jpa.find(Album.class, 1)시 JPA는 Album테이블 과 Item 테이블을 조인하여 Album의 PK가 1인 row를 Album객체로 반환한다.


2) 연관관계

연관관계를 맺을 때 객체는 참조, 테이블은 외래키(fk)가 필요하다.

테이블에 맞춰 모델링

public class Member{
	int id; //pk
    int team_id; //연관관계 외래키(fk)
    String name;
}

public class Team{
	int id; //pk
    String name;
}

객체는 참조를 통해 연관 객체를 조회하지만 FK를 저장하면 조회가 복잡해진다.
데이터 중심으로 모델링이 되었기 때문에 객체지향의 특징을 잃게 된다.

객체에 맞춰 모델링

public class Member{
	int id;
    Team team;	//연관 객체 참조
    String name;
}

public class Team{
	int id;
    String name;
}

테이블은 객체 참조가 아닌 FK가 필요하기 때문에 개발자가 중간에서 변환 역할을 해줘야 한다.

JPA 연관관계

저장

member.setTeam(team); //연관 객체 참조
jpa.persist(member); //연관 객체를 참조한 객체를 JPA에 저장시 함께 저장

연관 객체를 참조한 객체를 JPA를 통해 저장하게 된다면 JPA에서는 자동으로 참조한 객체를 외래키로 변환해주어 함께 저장해준다.

조회

Member member = jpa.find(Member.class, 1);	//특정 엔티티 조회
Team team = member.getTeam(); //조회한 객체의 연관 객체를 가져올 수 있다.

특정 객체를 조회하고 연관관계가 맺어진 객체를 조회할 때 외래키를 참조로 변환하는 일을 JPA가 처리한다.


3) 객체 그래프 탐색

객체에서 회원이 소속된 팀의 정보를 조회할 때 참조를 사용해서 정보를 조회할 수 있는데 이를 객체 그래프 탐색이라고 한다.

member.getTeam().getTeamName(); //자유로운 객체 그래프 탐색

SQL으로 조회를 다루게 된다면 SQL에 따라 실행할 수 있는 객체 그래프 탐색이 제한된다.

public class MemberService{
	//...
    
    public findMemberBySQL(){
    	Member member = memberDAO.find(memberId);
        member.getTeam();
        member.getTeam().getName(); //??
        member.getTeam().getConnection(); //??
    }
    
    public findMemberByJPA(){
    	Member member = jpa.find(Member.class, 1);
        member.getTeam().getName(); //true
        member.getTeam().getConnection(); //true
    }
}

SQL시 DAO에서 어떤 쿼리를 날렸는지 확인하기전까지는 알수가 없다. 그렇기 때문에 관련된 객체를 모두 잘 가져왔는지 반환된 Entity를 신뢰할수 없다.

JPA시 실제 객체를 사용 시점까지 데이터베이스 조회를 미루게 된다.(지연로딩)
실제 객체를 사용하는 시점에 데이터베이스에 참조 객체를 조회하는 Select SQL을 요청하여 참조 객체를 반환받게 된다.
(지연로딩은 별다로 사용여부를 설정할 수 있다)

Member member = jpa.find(Member.class, 1);
member.getTeam();
member.getTeam().getName(); //Team을 사용하는 시점에 SELECT TEAM 요청

4) 비교

데이터베이스는 PK의 값으로 각 ROW를 비교
객체는 동일성(identity)비교와 동등성(Equality)비교

동일성, 동등성 비교

  • 동일성 비교 : == 비교, 객체 인스턴스의 주소값 비교
  • 동등성 비교 : equal()비교, 객체 내부의 값 비교
//SQL을 이용한 MemberDAO의 findMember()
Member member1 = memberDAO.findMember(1);
Member member2 = memberDAO.findMember(1);

member1 == member2; //false

SQL은 동일한 PK를 가지고 조회하여도 서로 다른 객체로 반환된다.

//JPA이용
Member member1 = jpa.find(Member.class, 1);
Member member2 = jpa.find(Member.class, 2);

member1 == member2; //true

JPA는 같은 트랜잭션일 때 같은 조회가 되는 것을 보장한다.(동일성 비교 성공)


5. 성능 최적화 기능

JPA를 사용해서 성능을 개선할 수 있는 기능들이 존재한다.

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

1차 캐시란, 동일한 트랜잭션에서 JPA를 통해 동일한 엔티티를 조회한다면 첫번째 조회때 SELECT SQL을 요청하고 그 다음 조회부터는 영속성 컨텍스트에 저장된 엔티티를 조회하여 가져온다.

즉, 동일한 트랜잭션에서 동일한 엔티티 조회는 한 번의 SQL문만 요청된다.

동일성이란, 동일한 트랜잭션에서 JPA를 통해 동일한 엔티티를 조회한다면 반환된 객체에 대해서는 동일한 인스턴스 주소를 가지게 되어 ==비교가 가능하다.

즉, 동일한 트랜잭션에서 동일한 엔티티 조회의 결과 객체는 동일한 인스턴스 주소를 가진다.

Member member1 = jpa.find(Member.class, 1); //첫번째 조회에서 SELECT SQL 요청
Member member2 = jpa.find(Member.class, 1); //두번째 조회에서는 SELECT SQL 요청이 되지 않는다.

member1 == member2; // 동일성 비교 true

쓰기 지연

쓰기 지연이란, 여러 SQL이 요청될때, SQL 요청때마다 데이터베이스에 요청하는 것이 아닌 SQL을 메모리에 저장해 두었다가 트랜잭션이 커밋되는 순간 한번에 데이터베이스에 요청한다.

즉, 한번에 SQL을 모아서 데이터베이스에 요청한다.

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

jpa.persist(member1); //INSERT member1 저장
jpa.persist(member2); //INSERT member2 저장
jpa.persist(member3); //INSERT member3 저장

transaction.commit(); //commit되는 순간 데이터베이스에 3개의 INSERT SQL 전달
  • UPDATE, DELETE
transaction.begin(); //트랜잭션 시작

//member.setName("newName")
jpa.persist(member); //수정된 엔티티객체 저장, UPDATE member 저장
jpa.remove(member2); //DELETE member2 저장

//비즈니스로직수행(); //비즈니스 로직 수행 중 데이터베이스 로우 락이 걸리지 않는다.

transaction.commit(); //commit되는 순간 저장된 UPDATE, DELETE SQL 전달

UPDATE, DELETE시 발생하는 row락 시간을 최소화.

지연 로딩

지연 로딩이란 실제로 사용하는 객체를 사용할 때 SELECT SQL을 요청하는 전략.

Member member = jpa.find(Member.class, 1); //SELECT MEMBER

Team team = member.getTeam();
team.getName(); //SELECT TEAM

연관관계를 가지고 있는 객체를 바로 불러오는 것이 아닌, 연관 객체를 사용하려고 할 시점에 SELECT SQL을 요청.

불필요한 연관관계를 불러오지 않을 수 있다.

만약 연관관련 객체를 항상 불러와야 할 때는 즉시로딩전략을 사용한다.

즉시 로딩이란 요청한 객체뿐만 아니라 연관관계 객체까지 한번에 조회하는 SQL을 요청한다.

Member member = jpa.find(Member.class, 1); 
//SELECT * FROM MEMBER JOIN TEAM ...

Team team = member.getTeam();
team.getName();

객체를 조회할 때 요청하는 테이블 뿐만 아니라 연관관계 테이블까지 조인으로 묶어서 한번에 불러온다.


6. 데이터 접근 추상화와 벤더 독립성

어떤 데이터베이스를 사용함에 따라서 사용법이 달라지는 것이 아니라 JPA에 다른 데이터베이스를 사용한다고 알려주기만 하면 된다.

참조 : https://ultrakain.gitbooks.io/jpa/content/chapter1/chapter1.3.html

즉, 데이터베이스 기술에 종속되지 않는다.


Reference

https://gmlwjd9405.github.io/2019/08/04/what-is-jpa.html

https://ultrakain.gitbooks.io/jpa/content/chapter1/chapter1.3.html

profile
내 꿈은 좋은 개발자

0개의 댓글