[프로그래밍] JPA

[JPA] JPA?

JHVan 2024. 4. 29. 15:49

JPA

 

 Java Persistence API, 자바 진영의 ORM(Object Relational Mapping) 기술 표준

ORM(Object Relational Mapping)은 객체와 관계형 데이터베이스 간의 호환성 문제를 해결하는 기술이다.

이 기술을 통해 개발자는 객체 지향 프로그래밍 언어로 작성된 코드와 관계형 데이터베이스 사이의 '다리' 역할을 하는 매핑 레이어를 이용할 수 있다.

 

ORM을 사용하면 개발자는 객체는 객체대로, 관계형 데이터베이스는 관계형 데이터베이스대로 설계할 수 있다.

이렇게 하면, 객체 지향 프로그래밍의 장점을 살리는 동시에, 관계형 데이터베이스의 강력한 데이터 관리 능력을 활용할 수 있다.

 

ORM 프레임워크는 이 두 세계를 연결하는 중요한 역할을 한다.

객체 모델과 데이터베이스 스키마 사이의 매핑 정보를 정의하면, ORM 프레임워크가 이 정보를 바탕으로 필요한 SQL을 자동으로 생성하고 실행한다. 이 과정에서 개발자는 데이터베이스의 복잡한 쿼리를 직접 작성할 필요 없이, 객체의 메소드 호출과 같은 간단한 방법으로 데이터를 조작할 수 있다.

 

대부분의 대중적인 프로그래밍 언어에는 하나 이상의 ORM 기술이 포함되어 있으며, 이를 통해 개발자는 보다 직관적이고 효율적인 방식으로 데이터베이스 관련 작업을 처리할 수 있다.

 

JPA 는 어플리케이션과 JDBC 사이에서 동작함.

JPA 의 위치

JPA를 사용해서 MEMBER 엔티티를 저장하는 과정을 나열하면,

  1. 먼저, 개발자는 MEMBER 엔티티를 저장하기 위해 MEMBER DAO(Data Access Object) 내의 persist 메서드를 호출하고, 이 메서드에 엔티티를 넘긴다.
  2. JPA는 넘겨받은 엔티티를 분석한다. 이 때, 엔티티의 상태와 매핑 정보를 확인하여 어떤 SQL을 생성해야 할지 결정한다.
  3. 엔티티 정보를 바탕으로 INSERT SQL을 생성한다. 이 과정에서 객체 모델과 관계형 데이터베이스 사이의 패러다임 불일치 문제를 해결한다.
  4. 생성된 INSERT SQL은 JDBC API를 통해 데이터베이스에 전달된다. JDBC는 자바에서 데이터베이스에 접근할 수 있도록 해주는 API다.
  5. 마지막으로, JDBC API를 통해 전달된 INSERT SQL이 실제 데이터베이스에서 실행되어 MEMBER 엔티티가 데이터베이스에 저장된다.

개발자는 이 과정의 복잡한 SQL을 직접 작성하거나 JDBC 코드를 직접 다루지 않아도 되며, 객체 지향적인 방식으로 데이터를 관리할 수 있다.

JPA의 장점은 이 과정의 복잡성을 추상화하고, 패러다임 불일치 문제를 해결해준다.

 

JPA를 사용하여 MEMBER 엔티티를 저낭하는 과정을 나열하면:

  1. findby(식별자) 메서드를 통해 특정 식별자를 가진 MEMBER 조회를 요청한다.
  2. JPA는 요청받은 MEMBER에 대한 정보를 분석하여 적절한 SELECT SQL을 자동으로 생성한다.
  3. 생성된 SELECT SQL은 JDBC API를 통해 데이터베이스에 전달된다.
  4. 데이터베이스는 SQL을 실행한 후 결과를 ResultSet 형태로 반환한다.
  5. JPA는 ResultSet을 분석하여 객체에 매핑한다. 이 과정에서 객체와 관계형 데이터베이스 간의 패러다임 불일치 문제를 해결한다.
  6. 최종적으로 JDBC API를 통해 데이터베이스로부터 받은 결과가 객체로 매핑되어 반환된다.

개발자는 이 과정 에서 복잡한 SQL 작성이나 ResultSet 처리 과정 없이도 객체 지향적인 방식으로 데이터를 쉽게 조회할 수 있다.

JPA는 이 과정 을 추상화하여 패러다임 불일치 문제를 해결하고, 데이터 접근을 보다 단순화한다.


그래서 JPA 는 

JPA는 Java Persistence API의 약자로, 자바에서 사용하는 객체 관계 매핑(ORM)을 위한 표준 명세다.

이 명세는 인터페이스와 표준 문서 집합으로 구성되어 있으며, 개발자가 데이터베이스와 상호 작용하는 방법을 표준화한다.

JPA 자체는 인터페이스 모음이며, 실제 동작은 이 인터페이스를 구현한 구현체(Provider)에 의해 이루어진다. JPA 2.1 표준 명세를 구현한 대표적인 세 가지 구현체는 다음과 같다:

  1. Hibernate: 가장 널리 사용되는 JPA 구현체 중 하나로, 강력한 기능과 유연성을 제공한다. 많은 프로젝트와 프레임워크에서 기본적으로 사용되며, 오랜 기간 동안 발전해온 신뢰성 있는 구현체다.
  2. EclipseLink: Oracle이 주도하는 구현체로, Java EE 서버의 표준 JPA 구현체로 사용된다. 성능과 확장성 측면에서 좋은 평가를 받으며, 다양한 추가 기능을 제공한다.
  3. DataNucleus: 다양한 데이터 저장소를 지원하는 유연성이 특징인 구현체다. JDO(Java Data Objects)와 JPA를 모두 지원하며, NoSQL 데이터베이스를 포함한 다양한 데이터 소스와의 연동이 가능하다.

이 구현체들은 JPA 표준을 따르며, 개발자가 소스 코드 레벨에서 일관된 API를 사용할 수 있게 해준다. 대부분 Hibernate를 사용하지만, 구현체 간의 차이는 주로 성능, 확장성, 지원 기능에서 나타나므로, 프로젝트의 요구 사항에 맞춰 적절한 구현체를 선택할 수 있다.

Hibernate
Hibernate는 자바를 위한 인기 있는 오픈 소스 객체 관계 매핑(ORM) 도구.
객체 지향 도메인 모델을 관계형 데이터베이스에 매핑하는 프레임워크를 제공하여
개발자가 데이터베이스 작업과 데이터 영속성 관리를 보다 효율적으로 할 수 있게 함.

Hibernate는 데이터베이스와의 복잡한 상호 작용을 추상화하고 처리하여
개발자가 애플리케이션의 비즈니스 로직에 더 집중할 수 있음. 

데이터 쿼리 및 검색 기능: Hibernate는 SQL과 유사한 강력한 쿼리 언어(HQL - Hibernate Query Language)
그러나 HQL은 완전히 객체 지향적이며 상속, 다형성, 연관과 같은 개념을 이해함.

투명한 영속성: Hibernate 설정을 통해 객체의 상태가 개발자가 명시적인 SQL 처리를 요구하지 않고도 
데이터베이스에 자동으로 영속화.

캐싱: Hibernate는 데이터베이스 접근을 줄이는 데 도움이 되는 강력한 내부 캐싱 메커니즘 사용.
또한, 이는 second-level cache providers와 함께 구성될 수 있음.

트랜잭션 관리: Hibernate는 JTA(Java Transaction API)와 통합되며 
애플리케이션의 트랜잭션 관리 시스템과 통합될 수 있는 유연한 트랜잭션 관리 시스템을 포함.

자동 키 생성: @TableGenerator 및 @SequenceGenerator와 같이
Hibernate는 데이터베이스 기본 키를 자동으로 생성하는 다양한 전략을 지원.

 

JPA 의 장점

 

생산성

저장: repository.save(Entity)

조회: repository .findby~(~)

수정: member.set~(~)

삭제: repository .remove(Entity)

만 하면 나머지 과정을 JPA가 전부 처리해줌.

 이 과정 들에서 발생하는 패러다임 불일치 문제도 자동으로 해결해줌.

 

유지보수성

엔티티에 추가만 하면 JPA 가 알아서 필요한 쿼리들을 만들어준다.

유지보수성 측면에서 볼 때, 엔티티에 항목을 추가하는 경우 JPA의 동작 과정:

  1. 개발자가 변경사항이 발생한 Entity 클래스에 새로운 필드를 추가한다. 
  2. JPA는 엔티티 클래스의 변경 사항을 인지한다. 이는 엔티티 클래스와 데이터베이스 테이블 간의 매핑 정보를 기반으로 한다.
  3. 필요한 경우, JPA 구현체는 데이터베이스 스키마 자동 생성 기능을 사용하여 데이터베이스 테이블에 새로운 컬럼을 추가할 수 있다. 이 기능은 개발 단계에서 주로 사용되며, 운영 환경에서는 직접 데이터베이스 스키마 변경을 관리하는 것이 일반적이다.
  4. 개발자가 엔티티에 대해 CRUD(Create, Read, Update, Delete) 작업을 수행할 때, JPA는 엔티티의 현재 상태를 분석하여 적절한 SQL 쿼리를 동적으로 생성한다. 예를 들어, 새로운 필드가 추가된 엔티티를 저장하려 할 때 JPA는 해당 필드를 포함하는 INSERT 또는 UPDATE SQL 쿼리를 생성한다.
  5. 생성된 SQL 쿼리는 JDBC API를 통해 데이터베이스에 전송되어 실행된다. 이 과정에서 엔티티의 변경 사항이 데이터베이스에 반영된다.

JPA는 엔티티 클래스의 변경 사항을 자동으로 처리하여, 개발자가 복잡한 SQL 쿼리를 직접 작성하고 관리하는 수고를 덜어준다. 따라서 유지보수성이 향상되며, 애플리케이션의 데이터 모델 변경이 더욱 용이해진다.

 

 

JPA 와 상속 - 저장

 jpa.persist(entity) 메소드만 사용하면 JPA 가 JDBC API를 활용하여 엔티티를 데이터베이스에 저장.

JPA 와 상속 - 조회

jpa.find(EntityClass, id) 메소드를 사용하여 데이터베이스에서 엔티티를 조회

 

JPA와 연관관계, 객체 그래프 탐색

객체스럽게 저장을 하면, JPA 가 team 의 PK 값을 꺼내 외래키에 저장하는 과정을 자동으로 진행함.

객체의 참조를 통한 연관관계를 DB의 외래키에 의한 연관관계로 자동으로 변환.

.get() 을 사용하여 연관된 엔티티를 가져올 수 있는데, LAZY 로딩이냐 EAGER 로딩이냐에 따라 처음부터 가져올지, get 명령어 실행 시 가져올지를 정할 수 있음.

 

신뢰할 수 있는 엔티티, 계층

엔티티간 연관관계가 적절히 생성되어 있다면, .get() 을 통해 필요할 때마다 필요한 엔티티 데이터를 DB에서 계속 가져올 수 있음.

엔티티의 생명주기 관리

JPA에서 엔티티의 생명주기는 크게 4가지 상태로 구분됨. (New, Managed,Detached,Removed)

엔티티 매니저는 이러한 상태 전환을 관리하며, 이를 통해 데이터의 일관성과 신뢰성을 유지.

영속 객체의 상태

  • New : 엔티티가 생성되었지만, 아직 데이터베이스에 저장되지 않은 상태.
  • Managed : 엔티티가 엔티티 매니저에 의해 관리되는 상태로, 이 상태의 엔티티는 데이터베이스와 동기화.
  • Detached : 엔티티가 엔티티 매니저의 관리를 벗어나 데이터베이스와의 동기화가 중단된 상태.
  • Removed : 엔티티가 삭제되어 데이터베이스에서도 삭제될 예정인 상태.

 

동일한 트랜잭션에서 조회한 엔티티는 같음을 보장함.

 

JPA 의 성능 최적화 기능

1차 캐시와 동일성 보장

1차 캐시(First-Level Cache)

계층과 계층 사이에 엔티티 연관관계가 추가되면, buffer writing 이 가능하고, cache 조회가 가능

1차 캐시는 엔티티 매니저(Entity Manager) 범위에 존재하는 캐시.

엔티티 매니저는 작업 단위마다 생성되며, 이 캐시에 영속된 엔티티가 저장됨.

주된 목적은 데이터베이스와의 불필요한 호출을 줄이는 것.

 

동작 방식:

  • 같은 엔티티 매니저 내에서 엔티티를 조회할 때 먼저 1차 캐시를 확인.
  • 캐시에 엔티티가 이미 존재하면 데이터베이스 조회를 스킵하고 캐시된 엔티티를 반환.
  • 네트워크 비용이나 데이터베이스 부하를 줄여 성능을 향상시킴.

예시:

  • find 메소드를 사용하여 같은 엔티티를 다시 조회할 경우, 첫 번째 조회에서 데이터베이스에서 가져온 엔티티가 1차 캐시에 저장.
  • 후에 동일한 엔티티를 다시 조회하면 1차 캐시에서 바로 엔티티를 반환.

동일성 보장(Identity Guarantee)

동일성 보장은 같은 트랜잭션 및 엔티티 매니저 내에서 동일한 엔티티에 대해 여러 번의 조회 또는 접근이 이루어져도, 항상 같은 자바 인스턴스를 반환함을 보장함.

동일성의 중요성

  • 애플리케이션의 데이터 일관성을 유지하는 데 중요함.
  • 같은 엔티티에 대해 다른 부분에서 변경이 있을 경우, 이 변경이 애플리케이션 전체에서 일관되게 반영되어야 하기 때문.

구현 방법

  • 기본적으로 1차 캐시를 통해 구현.
  • 엔티티가 처음 로드될 때 1차 캐시에 저장되고, 동일한 엔티티 매니저 내에서 해당 엔티티에 대한 모든 후속 조회는 캐시된 인스턴스를 반환.
  • 따라서 애플리케이션 내에서 엔티티의 동일성이 보장.

트랜잭션을 지원하는 쓰기 지연

 

쓰기 지연 (Write Behind)

쓰기 지연은 Entity가 변경 되었을 때 바로 데이터베이스에 쓰지 않고, 컨텍스트 내에 변경 내역을 쌓아두었다가 (쓰기 지연 SQL 저장소에 저장), 트랜잭션이 커밋될 때 한 번에 데이터베이스에 쓰는 방법이다.

이 방법은 네트워크 사용량 및 데이터베이스 부하를 줄이고, SQL의 배치 처리를 가능하게 하여 성능을 향상시킨다.

동작 원리:

  • 엔티티의 상태 변화가 발생하면, 해당 변화를 쓰기 지연 저장소에 기록.
  • 트랜잭션 커밋 시, 모아둔 변경 사항들이 데이터베이스로 전송되어 반영.

장점 

  • 트랜잭션 동안 데이터베이스로의 쓰기 작업을 최소화하여 성능을 향상시킬 수 있음.

지연 로딩 (Lazy Loading)

지연 로딩은 관계가 있는 엔티티들을 로딩할 때, 그 관계 엔티티를 실제 사용하기 전까지 로딩을 미루는 전략.

즉, 필요할 때까지 데이터를 로딩하지 않고, 프록시 객체를 사용해 필요한 시점에 로딩하는 방식이고, 이는 불필요한 데이터 로드를 방지한다.

동작 원리:

  • 엔티티를 조회할 때 해당 엔티티와 연관된 다른 엔티티들은 즉시 로딩되지 않고, 해당 엔티티에 접근하는 시점에 로딩.

장점:

  • 초기 로딩 시에 필요하지 않은 데이터를 불러오지 않아서, 성능을 개선하고, 리소스 사용을 최적화.

 

'[프로그래밍] JPA' 카테고리의 다른 글

[JPA] JPA의 기본 키 생성 전략  (0) 2024.05.04
[JPA] Entity Mapping  (0) 2024.05.04
[JPA] JPA의 영속성 컨텍스트  (0) 2024.05.03
[JPA] JPA 와 DB Dialect  (0) 2024.05.01
[JPA] 와 SQL  (0) 2024.04.23