Lee's Grow up

[JAVA/JPA] JPA 와 ORM 기본 개념 Hibernate ORM 본문

PROGRAMMING/JAVA

[JAVA/JPA] JPA 와 ORM 기본 개념 Hibernate ORM

효기로그 2019. 12. 5. 09:34
반응형

해당 내용은 인프런의 자바 ORM 표준 JPA 프로그래밍-기본편 - 김영한 의 내용을 기반으로 정리해서 작성한 글입니다.
자세한 내용은 해당 강의 또는 책을 구매하셔서 자세히 알아보시는걸 추천합니다.

ORM 이란?


  • ORM(Object-relational mapping)의 약자로써, 객체와 관계의 연결을 시켜주는 것을 말한다. 즉 Java와 같은 객체 지향 언어에서의 객체와 Oracle와 같은 RDB를 연결 시켜주는 방식을 말하며, Hibernate는 Java에서 사용하는 ORM의 open source Framework의 한 종류이다.

JPA 란?


JPA(Java Persistent API)의 약자로 말 그대로 자바에서 사용하는 ORM 기술에 대한 API 표준 명세를 뜻합니다.
JPA는 ORM을 사용하기 위한 인터페이스를 모아둔 것입니다.

등장 배경


위에서 ORM과 JPA에 대해서 간략하게 어떤 내용인지 설명을 했고, 그렇다면 왜 등장했을까?
ORM의 정의처럼 현재 프로그래밍 방식인 객체지향적 방식과 DB의 관계지향적 방식의 불일치
페러다임의 불일치로 인해 문제가 발생하며, 프로그래밍을 객체보다 테이블을 우선시 작성하며, 객체를 단순히 데이터 전달의 목적으로( DTO, VO 등 ) 사용

페러다임의 불일치로 발생하는 문제

  1. 객체다운 모델링 문제

    • 간단하게 객체지향 적으로 모델링을했다고 가정하고, 객체의 필드로 객체를 가지는 경우

      public class Member {
        private String name;
        private Department dept;
      
        getter()/setter()
      }

      위와 같이 객체지향적으로 사용을 할경우 Member클래스와 Department 클래스를 모두 사용할 때, DB에서 MEMBER 테이블과 DEPT 테이블을 조인해 결과를 가지고 왔다고 가정했을 경우 로직에서 new Member()로 인스턴스하고 값 매핑시켜주고, 추가로 new Departemt() 를 통해서 또 매핑시켜주고..이런 동작이 필요하게 됨
      그래서 MemberDepartemtDTO 대충 이런식으로 큰 클래스를 넣고 거기서 Member클래스의 필드와 Departemt 필드를 다 넣어서 사용 이는 객체지향적이지 못한 방식의 프로그래밍

  2. 객체의 탐색 범위 문제
    SQL은 데이터가 쿼리문에서 조회해온 데이터로 한정적이기 때문에 엔티티 신뢰에 문제가 발생
    간단하게 설명해 SQL 쿼리를 통해 MEMBER 테이블에서 name을 가져오는 쿼리를 실행했다고 가정하고, 매핑되는 Member 객체는 field로 String name, int age를 가지고 있다고 가정해봅니다.

      public void process() {
        Member member = memberDAO.getMember(memberId);
        member.getName() // 가능
        member.getAge() // 값이 없음
      }

    위 예제를 보면 우리는 member객체를 통해서 getName()는 가능하고 getAge()는 값이 없다는걸 당연하다고 생각할 것입니다.
    왜?? 우리는 현재 쿼리가 어떻게 동작하고 어떤 값을 가져오는지 알고 있다는 가정하에 있으니까 가능합니다. 그러나 다른 사람이 작성한 코드의 경우, 예전에 만들어두고 까먹은 경우
    해당 값이 어떤 값을 매핑하는지는 보통 해당 Service -> DAO -> SQL 을 탐색해서 해당 쿼리문을 보고 받아오는 값을 확인합니다.

  1. 객체의 동일성 보장이 안된다

    public void process() {
      Member member1 = memberDAO.getMember(100);
      Member member2 = memberDAO.getMember(100);
      System.out.println(member1 == member2 ) // false
    }

    위와 같이 실행했을 경우 member1과 member2는 동일한 객체일까요? 아쉽게도 JDBC API 메커니즘에 의해 전혀 새로운 객체로 취급받습니다. 그렇다면 우리가 흔히 생각하는 객체지향적으로 봤을때 두 객체는 같아합니다. 예제는 컬렉션을 기준으로 하겠습니다.

    public void process() {
      Member member1 = memberList.get(100);
      Member member2 = memberList.get(100);
      System.out.println(member1 == member2 ) // true
    }

    위 둘 예제에서 객체의 동일성이 보장이 안되는 문제가 발생합니다.

  1. 객체지향의 장점인 ( 추상화, 캡슐화, 상속, 다형성 ) 을 활용하지 못함
    예를들어 상속관계인 객체의 경우 자바 컬렉션에서는

    public void process() {
      Book book = list.get(bookId);
      Item item = list.get(bookId);
    }

    위와 같이 다형성등을 사용해 간단하게 사용 가능하지만, DB에 저장하기 위해서는

    • 객체를 분해
    • INSERT INTO ITEM...
    • INSERT INTO BOOK...
      위와 같은 방식으로 동작하며 조회시에도 복잡한 작업이 많이 요구됩니다. 그래서 DB에 저장할 객체에는 상속 관게를 사용하지 않는 문제점 발생

JPA를 통한 문제 해결

JPA를 사용하면 패러다임의 불일치에 대한 불필요한 처리를 해결해준다.

  1. 객체답게 사용 가능
    아래의 방식처럼 객체간 연관관계가 끊기는 방식으로 동작하는 기존의 테이블의 데이터 중심의 설계에서

    public class Member {
         String name;
        String deptId;
    }
    public void process(){  
        Member member = new member();  
        Department dept = memberDAO.getDepartment(member.getDeptId());  
    }

    아래와 같이 객체의 연관관계를 사용할 수 있게 해줍니다. 단, JPA의 핵심이기한 객체지향적 모델링이 요구됩니다.
    그만큼 모델링 자체를 객체지향적인 관점에서 해야 하기 때문에 제일 어려운 부분입니다. 또한 객체관의 관계가 단방향인지, 양방향인지, 관계의 주인은 누구인지를 잘 파악하고 사용하셔야 합니다.

    public void process(){  
      Member member = new member();  
      Department dept = member.getDept();  
    }
  1. 객체의 탐색 범위 문제 해결
    위 예제처럼 동일한 상황에 SQL에 의존적이지 않고 객체의 필드를 필요하면 사용이 가능

    public void process() {
      Member member = memberDAO.find(memberId);  
      member.getDepartemt();  
      member.getAge(); //가능
    }

    또한 객체 그래프 탐색가능

    public void process() {
      Member.setTema(new Team());  
      jpa.persist(member); // 객체를 DB 지향적으로 teamId값이 아닌 객체 자체를 넣음
    
      // 조회도 가능  
      Member member = jpa.find(member.class, memberid)  
      Team tem = member.getTeam()
    }

    여기서 persist(),find()는 JPA에서 제공하는 메소드이고, 위와 같이 동작이 가능한 이유는 지연 로딩을 제공하기 때문입니다. 자세한 내용은 아래에 추가로 설명하겠습니다.

  2. 객체의 동일성 보장 ( 단 같은 트랜잭션의 단위 -> jpa에서는 트랜잭션 단위가 중요함 )
    JPA에서 중요한 EntityManager(영속성 컨텍스트)가 트랜잭션 단위로 생성과 소멸이 되기 때문

JPA의 장점


위 객체지향과 관계모델의 페러다임 불일치 해결과 함께 제공해주는 JPA의 장점입니다.

  1. SQL 중심적 개발에서 객체 중심 개발 -> SQL에 의존적이지 않음 -> 생산성 증가
    따로 SQL문 작성이 필요없이 몇개의 메소드로 동작함. 나머진 자동으로 SQL문 생성해줌
  2. 페러다임 불일치 해결
  3. 성능 향상
    • 1차 캐시를 사용
      단 트랜잭션 단위에서만 지원함
      어플리케이션 자체에서 DB Isolation LevelRead Commit 이여도 Repeatable Read을 보장
    • 트랜잭션을 지원하는 쓰기 지연
      JDBC batch() 기능, 여러개의 비슷한 쿼리를 한번에 묶어서 보냄 (버퍼처럼 )
      update, delete시 DB Lock를 최소화 하기 위해 커밋 직전에 실행 위 insert도 동일
    • 지연 로딩
      Member 객체만 사용할 경우 SQL이 MEMBER 테이블에서만 조회 하다가 getDepartemt() 시점에 DEPARTMENT 테이블을 참조해 값을 가져옴
      그러나 매번 두 테이블을 같이 쓰는 경우 불필요하게 select문이 많아질 수 있기 때문에, 즉시로딩 즉 member/team을 한번에 가져오는 방식도 지원해준다. 필요한 방식으로 사용하면 됩니다
  4. 데이터 접근 추상화와 벤더 독립성
    데이터 접근에 대한 SQL문을 몰라도 되고, Oracle,MySQL 등 특정 DB에 종속적이지 않게 사용이 가능, 이는 persistence.xml에 옵션으로 dialect 설정을 통해 자동으로 쿼리를 완성해줌, 이는 장점이자 단점이 될 수도 있을거 같다는 생각이 듭니다.

JPA의 단점


  1. 성능이 떨어진다. 자동으로 쿼리를 완성해준다는 부분이 원하는 쿼리를 수행하지 않을 경우도 존재한다는 말이고, 성능도 좋지 못했다고 합니다. 그러나 지금은 여러가지 보안을 하여서 잘만 이해하고 사용하면 1차 캐쉬등을 통해서 더 좋은 성능을 낼 수 있다고 생각합니다. 또한 전세계적으로 사용중이기 때문에 꾸준한 발전이 되고 있습니다.

  2. 세밀함이 떨어진다. 자동 SQL 완성으로인헤 통계를 구하는등의 세밀한 작업이 떨어집니다. 그래서 JPQL이라는 별도의 언어를 제공해주고 또한 SQL 자체도 수행 가능하도록 지원하고 있습니다.

  3. 러닝커브 사용하기 위해 비용이 많이 들어갑니다.. 제대로 사용하기 위해 시간을 투자해야한다.

반응형
Comments