[DEV] J-Jay

스프링 부트 ORM (1) 본문

Back-end/Spring

스프링 부트 ORM (1)

J-Jay 2023. 9. 3. 16:50
728x90

ORM(Object-relational mapping)

ORM은 자바의 객체와 데이터베이스를 연결하는 프로그래밍 기법이다.

예를 들어 데이터베이스에 나이(age)와 성명(name) 컬럼에 20, J-Jay 라는 값이 들어있다고 보고, 

이것을 자바에서 사용하려면 어떻게 해야 할까? 보통은 아래처럼 SQL이라는 언어로 데이터를 꺼내 사용을 한다.

SELECT age
     , name
  FROM EMP

하지만 ORM이 있다면 데이터베이스의 값을 마치 객체처럼 사용이 가능하다.

쉽게 말해 SQL을 전혀 몰라도 자바 언어로만 데이터베이스에 접근해서 원하는 데이터를 받아올 수 있다.

즉, 객체와 데이터베이스를 연결해 자바 언어로만 데이터베이스를 다룰 수 있게 하는 도구를 ORM이라고 한다.

 

ORM의 장점과 단점

  장점 단점
1 SQL을 직접 작성하지 않고 사용하는 언어로
데이터베이스에 접근 가능하다.
프로젝트의 복잡성이 커질수록 사용 난이도도
올라간다.
2 객체지향적으로 코드를 작성할 수 있어 비지니스 로직에만
집중 가능하다.
복잡하고 무거운 쿼리는 ORM으로 해결이 불가능한
경우도 있다.
3 DBMS가 추상화되어 있기 때문에 Oracle에서 MySQL,
PostgreSQL로 전환 한다고 해도 추가로 드는 작업이 거의 없다. 즉 DBMS에 대한 종속성이 줄어든다.
 
4 매핑하는 정보가 명확하기 떄문에 ERD에 대한 의전도를 낮출 수 있고 유지보수에 유리하다  

JPA와 Hibernate

DBMS에도 여러 종류가 있는 것처럼 ORM에도 여러 종류가 있다.

자바는 JPA(Java Persistence API)를 표준으로 사용한다. JPA는 자바에서 관계형 데이터 베이스를 사용하는 방식을 정한

인터페이스이며, 실제 사용을 위해서는 ORM 프로임워크를 추가로 선택해야 한다.

대표적으로는 Hibernate를 많이 사용한다.

Hibernate는 JPA 인터페이스를 구현한 구현체이자 자바용 ORM 프레임워크이다. 내부족으로는 JDBC API를 사용한다.

Hibernate의 목표는 자바 객체를 통해 데이터베이스 종류에 상관없이 데이터베이스를 자유자재로 사용할 수 있게 하는데 있다.

 

  • JPA: 자바 객체와 데이터베이스를 연결해 데이터를 관리한다.
    객체 지향 도메인 모델과 데이터베이스의 다리 역할을 한다.
  • Hibernate : JPA의 인터페이스를 구현한다. 내부적으로는 JDBC API를 사용한다.


Entity

엔티티는 데이터베이스의 테이블과 매핑되는 객체를 의미한다. 엔티티는 본질적으로는 자바 객체이므로 일반 객체와 

다르지 않지만, 데이터베이스의 테이블과 직접 연결된다는 특별한 특징이 있어 구분지어 부른다.

즉, 엔티티는 객체이긴 하지만 데이터베이스에 영향을 미치는 쿼리를 실행하는 객체이다.

 

Entity Manager

엔티티 매니저는 엔티티를 관리해 데이터베이스와 어플리케이션 사이에서 객체를 생헝, 수정, 삭제 하는 역할을 한다.

그리고 이런 엔티티 매니저를 만드는 곳이 엔티티 매니저 팩토리 이다.

예를  들어, 회원 2명이 동시에 회원 가입을 하려는 경우 엔티티 매니저는 다음과 같이 업무를 처리한다.

회원 1의 요청에 대해 가입 처리를 할 엔티티 매니저를 엔티티 매니저 팩토리가 생성하면 이를 통해 가입 처리를 해 데이터베이스에 회원 정보를 저장한다. 회원 2도 마찬가지이다.

그리고 회원 1, 2를 위해 생성된 엔티티 매니저는 필요한 시점에 데이터베이스와 연결한 뒤, 쿼리를 실행한다.

 

 

스프링 부트에서도 직접 엔티티 매니저 팩토리를 만들어서 관리하지는 않는다.

스프링 부트는 내부에서 엔티티 매니저 팩토리를 하나만 생성해서 관리하고 @Persistence Context 또는

@Autowried 어노테이션을 사용해서 엔티티 매니저를 사용한다.

 

스프링 부트가 엔티티 매니저를 사용하는 방법

@PersistenceContext
EntityManager entityManager; //프록시 엔티티 매니저, 필요할 때 진짜 엔티티 매니저 호출

그리고 스프링 부트는 기본적으로 빈은 하나만 생성해서 공유하므로 동시성 문제가 발생할 수 있다. 

그래서 실제로는 엔티티 매니저가 아닌 실제 엔티티 매니저와 연결하는 프록시(가짜) 엔티티 매니저를 사용한다.

필요할 때 데이터베이스 트랜잭션과 관련된 실제 엔티티 매니저를 호출 하는 것이다.

※ 쉽게 말해 엔티티 매니저는 Spring Data JPA에서 관리하므로 직접 생성하거나 관리할 필요가 없다.


영속성 컨텍스트

엔티티 매니저는 영속성 컨텍스트에 저장한다는 특징이 있다.

영속성 컨텍스트는 JPA에 중요한 특징 중 하나로, 엔티티를 관리하는 가상의 공간이다. 이를 통해 데이터 베이스에서

효과적으로 데이터를 가져오며, 엔티티를 편하게 사용할 수 있다.

 

영속성 컨텍스트에는 1차 캐시, 쓰기 지연, 변경 감지, 지연 로딩이라는 특징이 있다.

기존에는 데이터 조작을 위해 쿼리를 직접 작성해야 했지만 스프링 부트에서는 이런 쿼리를 자바코드로 작성하고 이를 JPA가 알아서 쿼리로 변경해주는 것이 매우 편리하다. 

 

1차 캐시

영속성 컨텍스트는 내부에 1차 캐시를 가지고 있다. 이때 캐시의 Key는 엔티티의 @Id 어노테이션이 달린 기본키 역할을 하는 식별자이며 값은 엔티티이다. 엔티티를 조회하면 1차 캐시에서 데이터를 조회하고 값이 있으면 반환한다.

값이 없으면 데이터베이스에서 조회해 1차 캐시에 저장한 다음 반환한다. 이를 통해 캐시된 데이터를 조회할 때에는 데이터베이스를 거치지 않아도 되므로 매우 빠르게 데이터를 조회할 수 있다.

 

쓰기 지연(Transactional wirte-behind)

쓰기 지연은 트랜잭션을 커밋하기 전까지는 데이터베이스에 실제로 질의문을 보내지 않고 쿼리를 모았다가 트랜잭션을 커밋하면 모았던 쿼리를 한번에 실행하는 것을 의미한다. 예시로, 데이터 추가 쿼리가 3개라고 가정하면 영속성 컨텍스트는 트랜잭션을 커밋하는 시점에 3개의 쿼리를 한꺼번에 전송한다.

이를 통해 적당한 묶음으로 쿼리를 요청할 수 있어  DBMS의 부담을 줄일수 있다.

 

변경감지

트랜잭션을 커밋하면 1차 캐시에 저장되어 있는 엔티티의 값과 현재 엔티티의 값을 비교해서 변경된 값이 있다면 변경 사항을 감지해 변경된 값을 데이터베이스에 자동으로 반영한다.

이를 통해 쓰기 지연과 마찬가지로 적당한 묶음으로 쿼리를 요청할 수 있고, 데이터베이스 시스템의 부담을 줄일 수 있다.

 

지연 로딩(Lazy loading)

지연 로딩은 쿼리로 요청한 데이터를 애플리케이션에 바로 로딩하는 것이 아니라 필요할 때 쿼리를 날려 데이터를 조회하는 것을 의미한다. (반대로 조회할 때 쿼리를 보내 연관된 모든 데이터를 가져오는 즉시 로딩도 있다.)

 

※ 위와 같은 특징들을 갖는 공통점은 모두 데이터베이스의 접근을 최소화해 성능을 높일수 있다는 점이다.


Entity의 상태

엔티티는 4가지의 상태를 가진다.

  1. 분리(detached): 영속성 컨텍스트가 관리하지 않음
  2. 관리(managed): 영속성 컨텍스트가 관리함
  3. 비영속(transient): 영속성 컨텍스트와 전혀 관계 없음
  4. 삭제(removed): 삭제

이 상태들은 특정 메소드를 호출해 변경할수 있으며, 필요에 따라 엔티티의 상태를 조절해 데이터를 올바르게 유지하고,

관리할 수 있다.

public class EntitiManageT {
	@Autowired
	EntitiyManager entitymanger;
    
	public void test() {
		//1. 엔티티 매니저가 엔티티를 관리하지 않는 상태(비영속)
		Member member = new Member(1L, "J-Jay")
		//2. 엔티티 매니저 관리상태(관리)
		entitymanger.persist(member);
		//3. 엔티티 객체가 분리된 상태(분리)
		entitymanger.detach(member);
		//4. 엔티티 객체가 삭제된 상태(삭제)
		entitymanger.remove(member);
	}
}