SQL 중심적인 개발의 문제점
객체 지향 프로그래밍과 관계형 데이터베이스의 괴리감
객체 지향의 특성
객체 지향 프로그래밍 언어는 추상화, 캡슐화, 정보은닉, 상속, 다형성 등과 같은 여러 특성을 가지고 있어 복잡한 시스템을 효율적으로 구성할 수 있다.
이러한 특성을 통해, 개발자는 더 나은 설계와 유지보수가 가능하다.
관계형 데이터베이스의 특성
반면에 관계형 데이터베이스는 테이블, 행, 열 등으로 데이터를 정의하고 SQL을 사용하여 데이터를 관리한다.
이는 객체 지향의 특성과는 맞지 않으며, 때로는 데이터를 객체에 매핑하기 위해 추가적인 작업이 필요하다.
SQL Mapper와 객체-데이터베이스 불일치 문제
Mapper의 한계
JPA가 등장하기 전에는 대개 SQL Mapper를 사용하여 객체와 데이터베이스 사이의 매핑을 수행했다.
이 경우, 객체의 필드가 변경되면 관련된 SQL 쿼리도 모두 수정해야 했다.
이러한 수동적인 작업은 유지보수에 큰 부담을 주었다.
객체의 대한 내용을 SQL query에 맞게 Mapper라는 것을 작성해서 DB와 객체 사이에 존재한다.
객체 <-SQL Mapper-> RDB
만약 Mapper를 작성한 상태에서, 필드가 추가된다면 SQL 또한 하나하나 수정해줘야 한다.
객체와 RDB의 차이
객체는 상속, 연관관계, 데이터 타입, 데이터 식별 방법 등에서 RDB와 차이를 보인다.
예를 들어, 객체는 상속을 통한 다형성을 가질 수 있지만, RDB에서는 상속 개념이 없어 일반적으로 슈퍼타입-서브타입 관계를 사용하여 비슷한 효과를 낼 수 있다.
성능과 로딩 전략
로딩의 문제
엔티티의 신뢰성 문제가 발생할 수 있다.
즉, DAO를 통해 어떤 SQL문이 실행되는지, 어떤 데이터가 유효한지를 직접 확인해야 한다.
그렇다고 모든 객체를 미리 로딩할 수는 없다. 그래서 실무에서는 필요한 객체만 로딩하는 전략을 사용한다.
예시
memberDAO.getMember(); // Member만 조회
memberDAO.getMemberWithTeam(): //Member와 Team을 함께 조회
memberDAO.getMemberWithOrderWithDelivery(); // Order, Delivery, Member를 함께 조회
객체 동일성과 JPA
객체 동일성 문제
SQL로 데이터를 조회한 후 DAO에서 객체를 생성할 경우, 동일한 데이터에 대해서도 다른 객체가 생성될 수 있다.
이는 객체 동일성을 보장하지 못하는 문제를 야기할 수 있다.
실제 비교
SQL로 데이터를 꺼낸 후에 DAO에서 return new Member(...) 로 꺼내면 member1 == member2가 다르다
JPA의 해결 방안
JPA (Java Persistence API)는 이러한 문제를 해결하기 위해 등장했다.
JPA를 사용하면, 객체를 자바 컬렉션에 저장하듯이 데이터베이스에 저장할 수 있다.
이로 인해 개발자는 객체 지향적인 방법으로 데이터를 다룰 수 있으며, 여러 복잡한 매핑 작업을 JPA가 대신 처리해 준다.
Java Persistence API (JPA)와 그 필요성
ORM과 JPA
ORM (Object Relational Mapping): 객체와 데이터베이스 테이블 간의 매핑을 처리하는 프로그래밍 기술이다.
JPA (Java Persistence API): Java에서 ORM을 적용하기 위한 표준 명세입니다. 즉, 인터페이스의 모음으로 구성되어 있다.
객체를 자바 컬렉션에 저장하듯이 DB에 저장하는 방법이다.
Hibernate: 하이버네이트는 JPA를 만든 사람이 JPA를 만들기 전 오픈소스이다.
JPA와 기존 방식의 차이
기존 방식: JAVA 애플리케이션 <-- JDBC API --> DB
JPA 사용: JAVA 애플리케이션 <-- JPA --> <-- JDBC API --> DB
JPA는 JDBC API를 자동화해 주며, 이는 MyBatis와 JDBC Template과도 유사한 점이 있다.
JDBC API는 DB와 연결하기 위해 당연히 필요한 것이고, JPA는 이를 자동으로 만들어줌
JPA의 주요 특징
1. 패러다임의 불일치 해결: JPA가 객체를 분석하고 적절한 SQL을 자동으로 생성한다.
2. 유지보수: 객체의 필드를 수정할 경우, 관련 SQL을 모두 수정할 필요가 없다. JPA가 이를 자동으로 처리한다.
JPA의 성능 최적화 기능
같은 트랜잭션 내 일관성
JPA는 같은 트랜잭션 내에서는 같은 엔티티를 반환한다. 이로 인해 약간의 조회 성능이 향상될 수 있다.
버퍼링과 캐싱
버퍼링: JPA는 여러 작업을 모아서 한 번에 DB에 전송할 수 있다.
캐싱: 이미 조회한 객체는 캐시에 저장되어, 다시 조회할 필요가 없다.
쓰기 지연 (Write-behind caching)
JPA는 트랜잭션을 지원하는 쓰기 지연을 제공한다.
transaction.begin();
em.persist(memberA);
em.persist(memberB);
em.persist(memberC);
// 커밋하는 순간 데이터베이스에 INSERT SQL 모아서 보냄
transaction.commit();
지연 로딩 vs 즉시 로딩
즉시 로딩 (Eager Loading): 연관된 객체까지 미리 조회하는데, JOIN SQL을 사용한다.
지연 로딩 (Lazy Loading): 객체가 실제로 사용될 때까지 로딩을 미룬다.
언제 사용하나?
객체와 팀이 있고 팀을 자주 사용한다면 즉시 로딩을 사용한다.
팀을 가끔만 사용한다면 지연 로딩을 사용하여 성능을 최적화한다.
'JPA > ORM 표준 JPA' 카테고리의 다른 글
[ORM 표준 JPA] 6. 연관관계 매핑 1 (0) | 2023.09.23 |
---|---|
[ORM 표준 JPA] 5. 엔티티 매핑2 - 기본키 전략 (0) | 2023.09.20 |
[ORM 표준 JPA] 4. 엔티티 매핑1 - 객체와 테이블 매핑 (0) | 2023.09.18 |
[ORM 표준 JPA] 3. 영속성 관리 - 내부 동작 방식 (0) | 2023.09.16 |
[ORM 표준 JPA 프로그래밍] 2. JPA와 Hibernate: 데이터베이스와의 다리 역할 (0) | 2023.09.13 |