[JAVA]JPA

신동혁·2022년 8월 12일
0

JAVA

목록 보기
14/16

1.JPA란?

JPA(Java Persistence API)는 자바에서 제공하는 관계형 데이터베이스 처리용 API다. JPA는 라이브러리가 아니라 인터페이스다. 즉, JPA 자체가 특정 기능을 하지 못한다. JPA는 인터페이스로 단지 RDBMS(관계형 데이터베이스)를 어떻게 사용해야 하는지(Java ORM 기술)에 대한 명세(프로그램의 구조와 기능을 상세하게 기술한 문서)로 볼 수 있다. 이런 JPA인터페이스를 상속받은 구현체들은 Hibernate, EclipseLink, Apache OpenJPA 등이 있다. 이 구현체들을 ORM 프레임워크라고 한다.

자바 JPA는 javax.persistence 패키지로 제공된다.

1.1 ORM

JPA는 ORM이고, ORM은 Persistence Framework의 한 종류다.

  • Persistence Framework : persistence는 영속성이라는 뜻으로 persistence framework는 데이터를 DB에 영구저장함으로써 데이터에 영속성(사라지지 않게)을 부여해주는 장치라고 생각할 수 있다.
    • ORM(Object Relational Mapping) : 객체와 RDBMS객체(테이블)를 매핑하는 기술. 즉, 자바 객체를 RDBMS객체로 만들거나 RDBMS객체를 자바 객체로 만든다는 의미다.
    • SQL Mapper : 객체와 SQL문을 매핑하는 기술.

1.2 JPA 장점

  • DB마다 다른 sql문을 몰라도 된다 ( 특정 DB에 종속되지 않음 ) :
    데이터베이스마다 sql문법이 조금씩 다르다. 그럼 사용하는 데이터베이스가 달라질 때마다 그에 맞춰서 sql문법을 공부해야 한다. 예를 들어 mysql에서는 varchar, oracle에서는 varchar2라는 타입을 문자열 타입으로 쓴다. 자바에서 mysql을 이용하려고 하는데 JDBC를 이용해 쿼리문을 작성할 때 varchar2타입을 사용하면 문제가 생긴다. 하지만 JPA는 DB별 방언 처리기(Hibernate,EclipseLink 등등) 설정만 해주면 알아서 sql문법에 맞게 sql쿼리문을 생성해준다. 이를 통해 sql문법에 대한 부담을 줄일 수 있다.

  • 반복적인 SQL문 작성이 필요없다 ( 생산성 향상 ) :
    JPA는 간단한 sql 쿼리문에 대한 메서드를 제공한다. 간단한 예를 들어본다.

Member oneFindMember = em.find(Member.class, "id1");

위 코드에서 find메서드가 찾고자 하는 엔티티에서 primary key를 이용해 검색하는 기능을 제공한다. 즉, 자주 쓰이는 select쿼리문을 미리 메서드로 만들어 놓은 것이다. JDBC처럼 직접 select쿼리문을 작성할 필요가 없다.( 복잡한 sql문은 JPQL을 이용해 따로 생성해야됨 )

  • 필드 수정에 따른 SQL문 수정을 안해도 된다 ( 유지보수 용이 )
    필드값이 수정되었을 때 매핑된 엔티티만 수정하면 된다.

2.JPA 실행구조

JPA실행구조도

3.JPA Entity 구성요소

전체적인 흐름은 다음과 같다. DB의 테이블과 같은 구조를 지닌 Entity라는 객체를 만든다. 이 Entity와 DB의 테이블을 매핑시키면 직접 DB테이블을 건들지 않고 Entity를 활용해 DB를 조작하는 것과 같은 효과를 낼 수 있다.

  • @Entity : DB테이블과 매핑되는 클래스를 의미한다.
    • @Entity(name = "사용할엔티티명") : JPA에서 사용할 엔티티명을 설정한다. 지정하지 않으면 클래스명을 사용하게 된다.
  • @Table : Entity와 매핑할 테이블을 지정한다.
    • @Table(name = "사용할테이블명") : 매핑할 테이블명을 설정한다. 지정하지 않으면 엔티티명을 사용하게 된다.
  • @Id : Primary key로 설정한다
  • @GeneratedValue : 제약조건을 설정한다.
    • @GeneratedValue(strategy = GenerationType.IDENTITY) : mysql의 auto_increment설정. 즉, 값이 중복되지 않고 알아서 +1되도록 설정한다.
  • @Column : 필드로 설정한다
    • @Column(name = "사용할컬럼명') : DB에 저장될 컬럼명을 설정한다.

4.JPA 클래스, 인터페이스 구성요소

  • Persistence : createEntityManagerFactory메서드로 EntityManageFactory객체를 생성
  • EntityManagerFactory : createEntityManager메서드로 EntityManager객체를 생성. Application 실행 시 하나만 생성됨
  • EntityManager : getTransaction메서드로 EntityTransaction객체를 생성. 데이터를 DB에 저장하기 전에 Persistent Context에 저장해 관리함.
  • EntityTransaction : EntityManager와 일대일 관계로 EntityManager들의 작업은 EntityTransaction 클래스에 의해서 유지됨. 데이터를 변경하는 모든 작업은 EntityTransaction안에서 이루어져야 한다.(검색은 제외!)
  • Entity : 영속객체로 데이터베이스에 기록될 객체다
  • Query : sql문장 직접 타이핑할 수 있게함
  • JPA클래스관계
  • Persistent Context(영속성 컨텍스트) : Entity가 DB에 영구저장되도록 지원하는 환경. EntityManager로 접근가능함
    • persistent context와 관련된 entity상태종류
      • 비영속(new) : 아직 persistent context에 저장되지 않음
      • 영속(managed) : persistent context에 저장됨
      • 준영속(detached) : persistent context에 저장되었다가 분리됨
      • 삭제(removed) : persistent context, DB에서 삭제됨
    • persistent context 사용이유
      • 버퍼메모리와 같은 느낌의 성능 향상 ( 쓰기지연 ) : Application과 DB사이에 중간다리같은 역할로 Application에서 persistent context로 DB에 저장할 내용을 모았다가 한번에 DB로 저장하는 식의 동작으로 Application과 DB사이를 오가는 시간을 줄여 성능을 향상시킨다.
      • 1차캐시 : persistent context에 저장되는 캐시로 EntityManager의 find메서드를 이용할 때 DB까지 접속해 검색할 필요없이 빠르게 1차캐시를 조회해 값이 있으면 반환한다.
  • persistentcontext

5.JPQL

JPA는 미리 sql문을 메서드로 만들어 편하게 사용하지만, 모든 상황에 관한 메서드를 만들어놓지는 않는다. 복잡한 sql문은 직접 sql문을 작성할 수 밖에 없다. JPA에서 사용하는 객체지향 쿼리 언어를 JPQL이라고 하고, JPA에서 JPQL을 이용해 SQL문을 작성한다. JPQL실행을 위해서 Query객체가 필요하고, EntityManager의 createQuery메서드를 이용해 Query객체를 만들 수 있다. 또한 이 문장은 Entity를 이용해 작성하지 DB에 있는 테이블을 이용하는 식이 아니다. 즉, select문을 작성한다고 했을 때 문장이 'select m from Member m'라고 한다면 여기서

  • JPQL을 실행하기 위한 쿼리 객체

    • TypeQuery : 반환할 타입을 명확하게 지정할 수 없을 때 사용

    • Query : 반환할 타입을 명확하게 지정할 수 있을 때 사용

    • @NamedQuery : Entity에서 미리 작성

      • 사전에 미리 파싱되므로 실행 속도가 좋다

      • Application 로딩 시에 미리 문법체크가 이뤄진다

      • 사전에 DB에 접근했던 SQL문장들은 캐싱을 해놓는다

        @NamedQuery(query = "쿼리문", name = "호출할때이름")
  • 쿼리 메서드

    • createQuery() : JPQL문을 이용한다.

      // Member타입을 리턴해 List형식으로 저장하는 JPQL쿼리문 작성 예시
      List<Member> 객체명 = EntityManager객체명.createQuery("JPQL쿼리문", Member.class).getResultList();
    • createNamedQuery() : @NamedQuery를 이용한다.

      // Employee엔티티에 작성된 NamedQuery가 있고
      // @NamedQuery(query = "select e from Employee e where e.eid = :eid", name = "Employee.findbyEmpno") 내용이 다음과 같을 때 
      // Employee타입으로 한개 결과값을 받아 e1객체에 넣기
      Employee e1 = (Employee)EntityManager객체명.createNamedQuery("Employee.findbyEmpno").setParameter("eid", 7369).getSingleResult();
  • 문법

    • JPQL에는 엔티티명을 사용해야 한다. 만약 엔티티명이 따로 지정되지 않았다면 테이블명을 사용한다.
      별칭(Alias)이 필수다.( 예를 들어 테이블 전체내용이 필요해 sql문의 all('*')을 쓰고 싶다면 all대신 테이블 별칭을 사용한다. ex)select m from Member m )

    • ':' : 이름 기준 파라미터

    • '?' : 위치 기준 파라미터

    • 조인 : foreign key설정된 클래스의 원래 엔티티명을 작성하는 것이 아니라, '별칭.클래스에서참조된멤버변수명 별칭' 이런식으로 작성해야 한다.

      // sql문법에서는 "select * from Member m left join Team t on m.teamId=t.teamId where m.teamId=1"로 작성할 내용을 다음과 같이 작성한다.
      // ( Member테이블이 Team테이블의 teamId를 foregin key로 참조하고 있음 )
      List<Member> datas2 = em.createQuery("select m from Member m left join m.teamId t on m.teamId=t.teamId where m.teamId=1", Member.class).getResultList();
  • 페이징API

    • query.setFirstResult(10);
    • query.setMaxResults(20);
    • query.getResultList();
    • setFirstResult(int startPosition) : 조회 시작 위치(0부터 시작)
    • setMaxResults(int maxResult) : 조회활 데이터 수

6.JPA 실행순서

  1. EntityManagerFactory객체 생성
  2. EntityManager객체 생성
  3. EntityTransaction객체 생성 및 시작
  4. EntityTransaction내용 작성 및 저장
  5. EntityTransaction객체 커밋
  6. 반환

6.1 EntityManagerFactory객체 생성

EntityManagerFactory emf = Persistence.createEntityManagerFactory("dbinfo");

6.2 EntityManager객체 생성

EntityManager em = emf.createEntityManager();

6.3 EntityTransaction객체 생성 및 시작

EntityTransaction tx =em.getTransaction();
tx.begin();

6.4 EntityTransaction내용 작성 및 저장

트랜잭션에 대한 내용을 작성한 뒤 EntityManager의 메서드들을 사용하는데 용도에 대해 알아본다.

  • persist() : Application에서 작성한 트랜잭션을 Persistent Context로 저장
  • flush() : Persistent Context에 저장된 트랜잭션을 DB로 임시저장
  • clear() : Persistent Context에 저장된 트랜잭션을 모두 삭제
Team t1 = Team.builder()
				.teamName("농구팀")
				.members(new ArrayList<>()) // 빌더패턴에서는 이렇게 초기화를 안하면 그냥 null로 인식해버림
				.build();
em.persist(t1);
em.flush();
em.clear();

6.5 EntityTransaction객체 커밋

EntityTransaction의 메서드로 DB에 영구저장하는 단계다.

  • commit() : Application에서 작성한 트랜잭션, Persistent Context에 저장된 트랜잭션, DB에 임시저장된 트랜잭션을 모두 DB에 영구저장
tx.commit();

6.6 반환

더 이상 사용하지 않는 EntityManger객체와 EntityManagerFactory객체를 반환한다.

em.close();
emf.close();

7. 지연로딩( Lazy loading )

fetch설정을 통해 데이터가 필요하지 않은 부분에 대해서는 데이터를 불러오지 않다가, 데이터가 직접적으로 필요할 때 뒤늦게 데이터를 불러오게 할 수 있다. 이를 통해 필요없는 데이터는 불러오지 않게 효율적인 설정이 가능하다.

  • (fetch = FetchType.LAZY)

8. @OneToMany

1:N 관계를 의미한다

  • OneToMany 속성
    • targetEntity : 관계를 맺을 Entity 클래스를 설정
    • cascade : 해당 Entity가 수정될 때 해당 Entity와 관계를 맺고 있는 Entity들도 수정될 지 등을 설정
    • fetch : 해당 Entity와 관계를 맺고 있는 Entity들의 데이터를 미리 가져오는가 나중에 가져오는가를 설정. JPA레이어에서 처리함.
    • mappedBy : 관계의 주체가 되는 쪽에서 설정
    • orphanRemoval : cascade와 같은 작업이지만 수준이 다름. DB레이어에서 처리함.

9. @ManyToOne

N:1 관계를 의미한다

  • @JoinColumn(name = "테이블에서 참조할 필드명") : joincolumn의 name속성 값으로 엔티티에서 필드명을 써야하는지, DB테이블에서 필드명을 써야하는지 헷갈리는 경우가 많았는데 후자를 적는 것이다!
profile
개발취준생

0개의 댓글