단의 개발 블로그

JPA 본문

Web/Spring

JPA

danso 2024. 9. 5. 21:37

 

이전에 기술한 데이터베이스 게시글에서 JPA에 대해서 부족한 부분이 있어서 다시 찾아서 정리했다. 

JPA란

JPA는 Java에서 ORM을 기술 표준으로 사용하는 인터페이스의 모음이다. 이 말은 java를 통해 데이터베이스와 같은 영속 계층을 처리함을 의미한다. 즉 자바 코드로 DB와 주고 받고 한다는 뜻이다. 우리가 사용하는 JPA는 RDBMS만 적용되는 규격이고, 인터페이스이기 때문에 Hibernate, Open JPA등이 해당 기술을 구현했는데, 주로 Hibernate를 많이 사용한다. 

해당 기술을 사용하면 애플리케이션에서 많이 사용되는 CRUD를 간편하게 처리할 수 있다. 우리가 정의한 Entity를 간단한 메소드 호출로 처리할 수 있다. 또한 네이티브 SQL도 사용할 수 있기 때문에 복잡한 구문이 사용될 경우 직접 SQL을 작성하여 호출할 수 도 있다.

https://dbjh.tistory.com/77

ORM

ORM이란 Object Relational Mapping의 약자로, 객체지향 패러다임을 RDBMS에 보존하는 기술을 의미한다. 우리 입장에서 본다면 java로 작성한 코드를 RDBMS로 매핑해주는 개념이다. 객체 지향에서 사용하는 클래스와 데이터베이스에서 사용하는 데이터 타입이 비슷하다. 예를 들어, 클래스와 테이블, 인스턴스와 레코드 같이 유사한 특징을 갖고 있다.

https://dbjh.tistory.com/77

Persistence

영속성은 JPA를 사용하면 많이 접하게 되는 단어다. 데이터를 생성한 프로그램이 종료되어도 사라지지 않는 데이터 특성을 의미한다. 이 말은 데이터는 메모리에만 존재하고 서비스가 꺼지면 해당 데이터가 없어진다. 따라서 우리는 서비스를 유지할 때 어떤 데이터를 얻게 되면 파일이나 DB에 저장하여 사용한다.

생성 및 접속

  • 요청 시 JPA는 EntityManagerFactory에서 EntityManager 생성
  • EntityManager는 내부에서 커넥션 풀을 사용하여 DB에 접속

영속성 컨텍스트

  • 위에서 언급한 데이터가 영구히 저장되는 환경을 뜻한다.
  • EntityManager.persist(entity)를 하면 영속성 컨텍스트를 통해 엔티티를 영속화 하는 것을 의미한다.
  • 해당 영속화가 완료되면 DB에 저장된다.

생명주기

  • 비영속(new/transient)
    • 영속화 전 상태
  • 영속(managed)
    • 영속성 컨텍스트에 저장된 상태
    • 엔티티가 영속성 컨텍스트에 의해 관리됨
    • 애플리케이션에서 모든 작업이 완료되고 트랜잭션으로 묶인 작업 단위가 커밋 되면 그때 DB에 저장함
  • 준영속(detached)
    • 영속성 컨텍스트에 저장되었다가 분리된 상태
  • 삭제(remove)
    • 영속성 컨텍스트에서 삭제된 상태, 영속과 마찬가지로 트랜잭션이 완료되고 커밋 되면 DB에서도 삭제함

캐시

  • 내부에 존재하는 캐시를 1차 캐시라고 함
  • @Id로 매핑한 식별자로 Entity 인스턴스를 관리함
  • JPA를 사용하여 데이터를 찾을 때 1차 캐시를 먼저 찾고, 없다면 DB에서 찾음 그 후 다시 1차 캐시에 해당 내용을 저장

https://ict-nroo.tistory.com/130

https://velog.io/@devtel/JPA-%EC%98%81%EC%86%8D%EC%84%B1persistence%EC%9D%B4%EB%9E%80

Entity

JPA에서 관리하는 객체를 의미한다. 상단에 @Entity를 붙여줌으로 해당 클래스가 엔티티를 위한 클래스 임을 지정한다.

https://dev-gorany.tistory.com/114

Querydsl

데이터베이스 시스템은 각 벤더사마다 문법이 조금씩 다르다. querydsl은 하나의 통일된 문법으로 쿼리를 날릴 수 있게 해준다. 쉽게 사용하는 CRUD에서 복잡한 쿼리가 발생할 경우 사용한다. 기본적으로 컴파일 시점에 문법오류를 확인할 수 있으며, IDE의 도움을 받아서 작성도 가능하다. 모든 쿼리에 내용이 함수 형태로 제공되서 편리하다. 하지만 코드 라인수가 길어지는 단점이 있다.

https://dev-gorany.tistory.com/114

 

JPQL

보통 Repository에 @Query 어노테이션으로 사용한다. ?나 :로 변수를 바인딩한다. 하지만 String 형태로 작성하기 때문에 작성자에 의존적이며, 런타임 단계에서 오류가 발생하기 때문에 문제를 찾기가 쉽지 않다.

https://velog.io/@cho876/JPQL-vs-query-DSL

Proxy

EntityManager가 DB에서 데이터를 조회하면 사용하는 두개의 메소드가 존재한다. 

  • find() : 데이터베이스를 통해서 실제 엔티티를 조회함
  • getReference() : 데이터베이스 조회를 나중에 하는 가짜 객체

실제 클래스 상속을 받아 만들어진다. 사용할 때는 똑같이 사용하며 실제 객체의 참조를 보관한다. 프록시란 용어는 JPA뿐만 아니라 다양한 곳에서도 사용하는 용어다. 만약 타입체크를 해야한다면 instance of를 사용해서 비교해야 한다. 준 영속 상태일 때 프록시를 초기화 하면 문제가 발생한다. 즉시로딩, 지연로딩에서 쓰이게 된다.

https://velog.io/@jin0849/JPA-%ED%94%84%EB%A1%9D%EC%8B%9CProxy

지연로딩(Lazyloading) & 즉시로딩(EagerLoading)

객체는 다른 객체와 연관관계를 맺는다. 만약 Team과 Member가 연관 관계를 맺는다. Member가 연관관계의 주인이고, 해당 데이터를 가져온다고 가정했을 때 해당 컬럼에 @ManyToOne(fetch = FetchType.LAZY)라는 연관관계를 지정하게 된다. 여기서 지연로딩, 즉시로딩은 fetch 값으로 지정해서 동작하게 된다.

Lazyloading 

만약 지연로딩을 지정하고 member를 조회하면 member를 조회한다. 

Hibernate: 
    select
        member0_.MEMBER_ID as MEMBER_I1_0_0_,
        member0_.TEAM_ID as TEAM_ID3_0_0_,
        member0_.USERNAME as USERNAME2_0_0_ 
    from
        Member member0_ 
    where
        member0_.MEMBER_ID=?

member 객체로부터 team 객체를 요구하면 그때 team객체를 가져온다.

Hibernate: 
    select
        team0_.TEAM_ID as TEAM_ID1_1_0_,
        team0_.name as name2_1_0_ 
    from
        Team team0_ 
    where
        team0_.TEAM_ID=?

EagerLoading

즉시 로딩은 지연로딩과 다르게 member 호출시점부터 team을 가져온다.

Hibernate: 
    select
        member0_.MEMBER_ID as MEMBER_I1_0_0_,
        member0_.TEAM_ID as TEAM_ID3_0_0_,
        member0_.USERNAME as USERNAME2_0_0_,
        team1_.TEAM_ID as TEAM_ID1_1_1_,
        team1_.name as name2_1_1_ 
    from
        Member member0_ 
    left outer join
        Team team1_ 
            on member0_.TEAM_ID=team1_.TEAM_ID 
    where
        member0_.MEMBER_ID=?

https://velog.io/@jin0849/JPA-%EC%A6%89%EC%8B%9C%EB%A1%9C%EB%94%A9EAGER%EA%B3%BC-%EC%A7%80%EC%97%B0%EB%A1%9C%EB%94%A9LAZY

 

JPA N + 1

JPA를 사용하여 SQL 로그를 보면 여러번 SELECT가 실행된다. 개발자가 의도하지 않은 쿼리가 나가는데 이런 현상을 N+1 문제라고 불린다. 연관관계 조회 시 데이터의 갯수 만큼 조회 쿼리가 추가로 발생하는 것이다. findAll()로 모든 팀을 조회하면 select * from team이 실행되고, 각 팀에 속한 멤버를 조회하는 쿼리가 발생한다. select * from member ~.. 

엔티티 조회 시 테이블을 객체로 매핑하는 과정에서 조회하는 엔티티가 가진 대상 엔티티는 추가로 쿼리를 날려서 조회 후 Team엔티티에 매핑한다.

많은 데이터를 가져올 때 즉시 사용하지 않는 경우 join을 하게되면 효율성이 떨어지고, 이를 방지하기 위해 호출시점에 불러오는 지연로딩을 사용하게 된다. 만약 해당 문제도 해결이 필요하면 fetch join, entity graph, batch fetching 등을 사용하여 해결할 수 있다.

https://programmer93.tistory.com/83#google_vignette

 

@ManyToMany

관계형 데이터베이스는 테이블 2개 만으로 다대다 관계를 표현할 수 없다. 왜냐하면 데이터가 중복해서 발생한다. 예를들어 유저와 상품이라는 테이블이 있는데 이를 다대다 매핑을 하면 한명의 유저는 여러개의 상품을 구매하고, 하나의 상품은 여러 회원이 구매할 수 있다. 관계형 데이터베이스에서는 이 데이터를 표현할 수 없다. 따라서 일대다, 다대일 관계로 해당 연관관계를 표현한다. 유저와 상품 사이에 구매내역이라는 객체를 추가해서 해당 관계를 매핑한다.

https://u-it.tistory.com/348

 

 

커스터마이징

repository 구현체를 생성할 때 정의된 메소드를 찾아 분석한다. DSL (Domain Specific Language)를 정의하고 있어 시그니처에 표현된다. 아래와 같이 사용하여 다양한 SQL문법을 사용할 수 있다.

동사+생략가능한 처리대상+By단어+서술어
findUserByUsernameAndCreateAtBetween()

 

아래는 다양한 서술어 목록이다.

  • IsAfter, After, IsGreaterThan, GreaterThan
  • IsGreaterThanEqual, GreaterThanEqual
  • IsBefore, Before, IsLessThan, LessThan
  • IsLessThanEqual, LessThanEqual
  • IsBetween, Between
  • IsNull, Null
  • IsNotNull, NotNull
  • IsIn, In
  • IsNotIn, NotIn
  • IsStartingWith, StartingWith, StartWith
  • IsEndingWith, EndingWith, EndsWith
  • IsContaining, Containing, Contains
  • IsLike, Like
  • IsNotLike, NotLike
  • IsTrue, True
  • IsFalse, False
  • Is, Equals
  • IsNot, Not
  • IgnoringCase, IgnoreCase

'Web > Spring' 카테고리의 다른 글

Security 사용하기  (3) 2024.10.08
Security  (0) 2024.10.08
데이터 베이스  (0) 2024.09.04
사용자 요청 처리하기  (0) 2024.09.03
간단한 웹 요청 만들기  (0) 2024.05.08