ORM

ORM은 Object Relational Mapping의 약자이다.
ORM은 데이터베이스의 테이블을 객체로 변환하여 개발자가 데이터베이스를 객체 지향적으로 조작할 수 있게 해주는 것을 말한다. ORM 프레임워크로는 Hibernate, 아래의 이미지는 ORM이 자바 객체와 데이터 베이스 테이블 사이의 상호작용을 중개하는지 보여주는 이미지이다.

ORM

ORM을 사용해야 하는 이유

ORM을 사용해야하는 이유는 여러가지가 있지만, 주로 개발의 생산성, 유지보수성, 그리고 객체 지향적 설계와의 일관성을 위해서 사용한다.

  • 개발의 생산성 향상 : ORM을 사용하면 데이터 베이스 테이블을 직접 다루는 것보다는 더 빠르게 개발할 수 있고, CRUD작업을 위한 코드를 간소화 시킬 수 있다. 또한 직접 데이터 베이스 작업을 위해 SQL 쿼리를 직접 작성하지 않아도 되기 때문에 코드가 더 간결하고 관리하기 쉽다.

  • 유지보수성과 확장성 : 데이터 베이스 작업을 재사용 가능한 클래스와 메소드로 캡슐화하기 때문에 코드의 중복을 줄일 수 있고 모듈화하기 편하다. 그리고 데이터베이스 구조가 변경되더라도 ORM 매핑 정보를 갱신하는 것으로 대부분의 대응이 가능하기 때문에 확장성이 좋다.

  • 객체지향적 설계와의 일관성 : ORM은 데이터베이스를 객체로 다루게 해줌으로써 OOP원칙을 데이터베이스 작업에도 적용할 수 있으며, 도메인 중심의 설계가 가능하고 비즈니스 로직과 데이터 베이스 로직을 분리할 수 있도록 도와준다. ** OOP: “Object-Oriented Programming”의 약어로 객체지향 프로그래밍을 말함.

  • 기술적 독립성 : 코드를 굳이 변경하지 않더라도 다른 유형의 데이터베이스로 전환할 수 있는 유연성을 제공하며, 이로 인해 다양한 환경에서 쉽게 배포할 수 있다.

ORM 사용을 고려해야하는 상황

  1. ORM은 빠른 개발과 프로토타이핑에 유리함
  2. 주로 CRUD 작업을 주를 이루는 애플리케이션에 적합함
  3. 애플리케이션의 대부분이 객체 지향적으로 설계되어 있고, 이를 데이터베이스 작업과 일치시키고 싶을 때 유용

ORM 사용을 피해야 하는 상황

  1. 고성능이 필요한 복잡한 쿼리에는 사용하는게 효율적이지 않을 수 있음(성능이 중요한 시스템)
  2. 고도의 최적화가 필요한 상황에는 ORM의 추상화가 오히려 방해가 될수 있음
  3. 데이터베이스 설계가 애플리케이션 설계를 주도하는 경우 ORM을 사용하면 설계의 복잡성이 증가할 수 있음.

JPA(Java Persistence API)

JPA는 자바 어플리케이션에서 ORM을 구현하기 위한 인터페이스다. JPA는 자바 표준 명세로서 데이터 베이스 작업을 위한 API를 정의하며 이를 구현한 여러 ORM프레임워크(Hibernate, EclipesLink, OpenJPA)가 있다.

따라서 JPA는 ORM기법을 자바언어에 적용한것으로 ORM의 개념을 개발자가 사용할 수 있도록 표준화한 API이다.

JPA를 왜 사용하는가? (장점)

  • ORM의 표준 인터페이스를 제공하기 때문에 이 표준화로 인해 개발자들의 학습곡선이나, 일관된 방식으로 데이터를 처리 할 수 있다.
  • 복잡한 SQL 쿼리나 반복적인 CRUD를 해결해주기 때문에 개발 생산성과 유지보수 향상에 용이하다.
  • JPA는 여러 데이터베이스를 지원하기 때문에 코드를 변경하지 않고도 다른 종류의 데이터베이스로 전환하는 것이 가능하다.
  • JPA 구현체들은 쿼리 결과 캐싱, 지연 로등(lazy loading) 등의 기능을 제공하여 성능을 최적화시킨다.

JPA 단점

*장점은 왜 사용하는가에서 충분히 설명했기 때문에 넘어가도록 하겠다.

  1. 복잡한 쿼리에는 제한적이다.
    • JPA는 복잡한 쿼리나 특히 조인이 많은 쿼리에서 성능문제를 많이 겪는데, ORM의 자동화된 쿼리 생성 기능이 최적화되지 않은 쿼리를 만들 수 있기 때문이다.
  2. N+1 문제가 있다.
    • JPA의 지연 로딩(lazy loading)은 “N+1”문제를 일으키는데 JPA가 관계를 맺고 있는 엔티티를 불러올 때, 각 엔티티에 대한 별도의 쿼리를 실행하는 문제를 말한다. 이로 인해 예상치 못한 많은 수의 데이터베이스 쿼리를 발생시키기 때문에 성능에 문제가 생길 수 있다.
  3. 캐싱 문제가 있다.
    • JPA 구현체는 1차 캐시(First Level Cache)와 2차 캐시(Second Level Cache)를 사용는데 이 캐시 메커니즘은 복잡하며, 잘못 관리하면 성능저하와 데이터 일관성 문제를 일으킬 수 있다. ** 1차 캐시 : 한 세션 또는 트랜잭션 동안 엔티티의 상태를 유지하는 캐시를 말하며, 엔티티 매니저(Hibernate의 Session)가 생성되고, 세션이 종료할때 소멸한다. 보통 같은 세션 내에 동일한 엔티티에 반복된 데이터베이스 조회 요청을 줄인다. ** 2차 캐시 : 여러 세션 또는 트랜젝션 간의 공유되는 캐시를 말하며 애플리케이션 레벨에서 공유되기 때문에 재사용을 최적화되어 있다.
  4. JPA는 강력하고 유연하지만, 그만큼 복잡하다. 따라서 올바르게 사용하기 위해서는 JPA 자체뿐만 아니라 ORM의 개념, JPQL, JPA 생명주기, 트랜잭션 관리 등 다양한 측면에 대한 이해가 충분해야하기 때문에 학습 곡선이 좀 있는 편이다.

JPA의 단점에 대한 극복 방법

  1. 성능 최적화 전력 방법
    • 극도로 최적화가 필요한 복잡한 쿼리의 경우 JPA의 NativeQuery를 사용하여 직접 SQL쿼리를 작성하자.
  2. N+1 문제 해결
    • 패치 조인(Fatch Join)을 사용하여 관련 엔티티를 한 번의 쿼리로 불러올 수 있다.
    • 배치 사이즈 설정하여 관련 엔티티를 일정 수량만큼 한 번에 로드할 수 있다.
  3. 적절한 로딩(loding) 선택
    • 지연 로딩 vs 즉시 로딩 : 엔티티의 로딩 전략을 적절하게 선택하고 필요에 따라 조정해야는데, 지연 로딩은 성능 최적화에 도움이 될 수 있지만, 즉시 로딩이 필요한 경우도 있기 때문에 적절하게 선택해야댄다.