티스토리 뷰
<추천글>[DB] Database Layer 접근 방식 (JDBC, Query Mapper, ORM)
hun.ca 2021. 9. 27. 16:28ApplicationLayer 에서 Database Layer에 접근하는 방법 3가지(Persistence Framework)
1) JDBC Template(또는 순수 jdbc)
2) Query Mapper(mybatis)
3) ORM(JPA)
cf. Persistence(영속성): 데이터를 생성한 프로세스가 종료되어도 데이터가 사라지지 않게 하는 특성(생성된 데이터를 보관되어야한다는 성질을 의미함)
1. JDBC
JDBC(Java Database Connectivity)는 썬 마이크로시스템즈에서 데이터베이스에 접근을 제공하는 자바 표준 API이다. 위 그림과 같이 JDBC 인터페이스는 다양한 JDBC Driver 구현체를 통해서 동일한 인터페이스로 구현이 되어 있다. 예를 들어, MySQL JDBC 드라이버, Oracle JDBC 드라이버, MS-SQL JDBC 드라이버 등의 구현체들이 있고 이들을 통합된 인터페이스로 제공한다. 따라서 JDBC API 사용자는 드라이버 구현체에 신경쓰지 않고 같은 코드로 DB에 접근할 수 있다.
[JDBC 사용 순서]
1) 어떤 드라이버를 선택할지 명시하고 해당 드라이버와 Connection 맺는다.
2) 실제 쿼리를 보내기 위해 Statement 가져온다.
3) Statement에서 제공하는 다양한 함수를 통해 쿼리를 보낸다. 그 중에서도 ResultSet을 반환하는 함수가 있는데, 이는 쿼리 결과를 가져오는 객체이다. 이 ResultSet을 순회하면서 데이터들을 가져올 수 있다.
4) Statement와 Connection을 닫아준다.(반납한다)
[JDBC의 단점]
- Connection을 맺고 Statement를 가져오는 등 반복적인 코드가 많고 코드가 복잡해진다.
- 그래서 Spring에서는 JDBC Temlplate을 제공한다.
[JDBC Template]
- JDBC Template은 자바에서 제공하는 자바 표준이 아니라 Spring에서 제공하는 것이다.
- JDBC Template은 반복적인 잡업을 대신 처리해주는 역할을 한다.(등장배경)
- spring-boot-starter-data-jdbc 의존성을 추가하면 스프링에부트서 제공하는 JDBC Template을 사용할 수 있다.
- JDBC Template은 Bean으로써 컨테이너에서 관리된다.
- 결론적으로 JDBC Tempalate을 사용하면 Connection을 맺는 과정, 예외처리 부분 등 반복적으로 사용되는 코드를 줄일 수 있고 바로 쿼리를 보낼 수 있다.
- 반복적인 작업을 대신 처리할 뿐 트랜잭션을 관리해주진 않는다. 즉, 하나의 커넥션에 대한 일련의 쿼리 묶음을 실행할 방법은 직접 구현해야 한다. TransactionSynchronizationManager를 사용하는 것은 트랜잭션을 사용하는 한가지 방법이 될 수 있다.
cf. @SpringBootTest를 테스트 클래스에 추가하면 테스트시에도 컨테이너가 올라간다. 따라서 test코드에서도 @Autowired를 이용해 JdbcTemplate을 이용할 수 있다.
cf. starter 모듈 : Spring boot의 "Auto Configuration"이 제공되는 모듈을 의미한다. 따라서 @Configuration이 붙은 메타 설정 파일을 작성하지 않고도 Spring Boot가 알아서 의존관계 설정을 맺어준다.
Auto Configuration을 위해서 @EnableAutoConfiguration 어노테이션이 필요하며 @SpringBootApplication 에서는 기본적으로 제공된다. 따라서 @Autowired를 사용해 바로 Bean을 사용할 수 있으며 Connection 획득, SQL 실행, 예외처리 등 반복적이고 개발 외적인 다양한 작업을 Spring Boot가 대신 처리해준다.
cf. NamedParameterJdbcTemplate : 스프링에서 제공하는 JdbcTemplate의 일종이다.
NamedParameterJdbcTemplate는 DataAccessException으로 따로 타입화 해놓음으로써 보편적인 예외들을 추상화 시키고 예외처리에 이점을 준다.
2. Query Mapper
JDBC Template을 사용하더라도, 커넥션을 맺고 끊는 과정 등 반복적인 작업을 대신해줄 뿐이지 자바 코드상에 SQL구문을 작성해야 한다. 따라서 자바 코드상에 쿼리가 여전히 포함되고, 이는 유지보수에 부정적이다.(관리하기가 힘들다) 쿼리를 변경, 추가하기 위해 소스코드를 변경시켜야 한다는 단점이 있다.
이러한 이유 때문에 Query Mapper라는 것이 등장했고, 이중에 대표적인게 MyBatis이다. 자바 표준이 아니다. 즉, 자바에 국한된 것이 아니고 다른 언어에서도 지원된다. Query Mapper의 가장 큰 목적은 DAO 또는 레포지토리(소스코드)로 부터 쿼리를 분리시키는 것이다.
MyBatis는 Spring과 전혀관계가 없는 Persistence Framework(SQL Mapper)이다. 즉, Data Access Layer에 속하는 프레임워크이다.(애플리케이션 프레임워크가 아님) 하지만 Spring 애플리케이션에서 MyBatis를 쉽게 사용하도록 하기 위해 연동 모듈로 mybatis-spring을 제공한다. Spring에서 이와 같이 연동모듈을 제공하기 때문에 컨테이너가 로드되면서 Spring Boot Auto Configuration 기능이 수행되고 Bean 설정 파일 작성 없이도 자동으로 의존관계 설정이 맺어진다.
MyBatis를 사용함으로써 쿼리를 추가/삭제할 때 애플리케이션 코드를 새로 컴파일해야 할 필요가 없고 유지보수가 쉬워진다.
3. JPA(Java Persistence API) -> ORM (RDB를 사용할 경우만 해당)
자바 어플리케이션에서 '관계형 데이터베이스'를 사용하는 방식을 정의한 인터페이스이다. 여기서 중요하게 여겨야 할 부분은, JPA는 말 그대로 인터페이스라는 점이다. 즉, 구현체가 아닌 인터페이스일 뿐이다.(자바표준)
참고) JPA는 ORM이 아니다. 관계형 데이터베이스를 사용할 경우에만 ORM 역할을 하며 JPA와 NoSQL을 사용할 때에는 ORM이 아니다.
JPA를 정의한 javax.persistence 패키지의 대부분은 interface, enum, Exception, Annotation으로 이루어져 있다. 예를 들어, JPA의 핵심이 되는 EntityManager는 아래와 같이 javax.persistence.EntityManager 라는 파일에 interface로 정의되어 있다.
cf. javax: 자바에서 공식적으로 제공하는 패키지
JPA가 인터페이스로 존재하는 이유는 POJO를 지향하기 위한 것에 있다. 즉, hibernate라는 특정 기술에 종속적이지 않게 ORM을 제공하기 위해 자바는 표준 API인 JPA라는 인터페이스를 두어 보다 순수한 자바 객체를 설계하도록 하기 위함이다. 이렇듯 새로운 엔터프라이즈 기술을 도입하면서도 POJO를 유지하는 방법을 PSA라고 한다.
JPA의 대표적인 구현체로는 Hibernate, EclipseLink, DataNucleus, OpenJPA, TopLink Essentials 등이 있다. 그 중에서도 대표적인 구현체가 Hibernate이다. 그리고 이 구현체들이 ORM Framework이다.
Hibernate는 내부적으로 JPQL을 SQL로 변환해서 JDBC API를 사용한다. JPA는 자바의 객체와 RDB의 테이블을 매핑시켜주면서 객체가 변경되면 테이블도 변경된다는 특징이 있다. 즉, RDB의 테이블을 자바의 객체를 다루듯이 연산하는 것이 가장 핵심적인 특징이다. 따라서 애플리케이션 개발자가 쿼리를 작성하지 않고도 자바 객체 내용을 바꿈으로써 테이블과 같은 상태로 유지시킬 수 있다.(트랜잭션 내부에서만)
Spring boot는 JPA를 보다 편하게 사용할 수 있도록 spring-boot-starter-data-jpa 모듈을 제공한다. spring-boot-starter-data-jpa 모듈을 사용하면 Spring Boot의 Auto Configuration 을 통해 @Configuration 파일을 따로 작성해줄 필요 없이 Bean으로 관리되고, 애플리케이션 개발자는 @Autowired를 통해 쉽게 사용할 수 있다.
[JPA의 @Transactional]
@Transactional : 메소드를 단위로 메소드가 시작할 때 트랜잭션이 열리고 메소드가 끝날 때 커밋이 발생한다. @Transactional 처리된 메소드에서는 더티체킹을 통해서 해당 entity(테이블과 매핑되는 객체)의 변경사항을 감지하고 있다가, entity의 속성이 변경되고 트랜잭션이 커밋하게 되면(함수가 끝나면) 자동으로 쿼리가 발생한다. (영속성 컨텍스트 안에서 변경감지) 이렇듯 더티체킹을 감지하는 역할은 EntityManager가 수행한다.
[JPA 를 사용해야 하는 이유]
1) 생산성 증진
SQL에 의존적이지 않은 개발을 할 수 있다. 즉, 객체 중심으로 생산적인 개발이 가능하다. 예를 들어, 테이블에 컬럼이 추가될 경우, 이 테이블과 관련된 쿼리문이 변경될 여지가 크다. 하지만 JPA를 이용하면 객체의 내용만 변경해주면 된다는 특징이 있기 때문에 쿼리문을 바꿀 필요가 없어진다. 또한 영속성 컨텍스트 안에서 캐싱을 통해 엔티티를 관리하기 때문에 변경이 발생한 엔티티만 쿼리가 날라간다.
2) 객체와 RDB의 패러다임 불일치 해결
객체지향 프로그래밍은 추상화, 캡슐화, 상속, 다형성 등을 제공하지만, RDB는 이러한 특징을 제공하지 않으며 RDB의 데이터중심으로 구조화되어 있다. 따라서 객체지향 프로그래밍과 RDB의 패러다임이 불일치하므로 OOP의 특징을 제대로 활용할 수 없게 되는 것이다. 하지만 JPA를 활용하면 Entity가 RDB의 테이블과 완벽하게 매핑되면서 추상화, 캡슐화, 상속, 다형성 등을 모두 사용할 수 있게 된다. 단, FK의 관리 주체의 혼동때문에 객체와 RDB의 패러다임 불일치는 단점이 될 수도 있다.
cf. JPA 연관관계 단점 : https://jh-labs.tistory.com/155
정리
1) JDBC(자바 표준)
- 자바 표준으로써 애플리케이션 개발자가 직접 Connection을 맺고 종료하는 등의 작업을 처리해줘야 한다. 이러한 반복적인 작업을 Spring에서 대신 처리해주기 위해 JDBC Template이 등장했다.(자바표준이 아님)
- spring-boot-starter-data-jdbc 모듈을 추가하면 Spring Boot가 제공하는 Auto Configuration을 이용해 JDBC Template을 이용할 수 있다.
- 가장 큰 문제점은 소스코드 상에 쿼리가 직접적으로 들어간다는 점이다. 따라서 쿼리 변경/추가로 인해 소스코드를 변경해야하며 이는 애플리케이션을 끄고 켜야하는 과정이 수반된다. (유지보수에 매우 비효율적)
2) Query Mapper(MyBatis)
- JDBC Template을 이용하더라도 자바 코드안에 쿼리문이 섞여있기 때문에 쿼리문을 변경시키려면 자바코드의 변경이 발생한다. 따라서 유지보수 측면에서 부정적이다.
- 이러한 문제점을 해결하고자 애플리케이션 코드와 쿼리를 분리시키기 위한 목적으로 Query Mapper가 등장했고, 그 중에서도 MyBatis가 많이 사용된다.
- MyBatis는 Spring과 전혀 무관하다. 하지만 Spring은 Spring 애플리케이션 개발자에게 편리성을 제공하기 위해 mybatis-spring 연동모듈을 제공하며, Spring Boot의 Auto Configuration이 적용된다.
- JDBC와 다르게 소스코드와 쿼리를 분리시킴으로써 애플리케이션 재시작할 필요 없이 쿼리를 추가/삭제할 수 있다.
- 하지만 쿼리 결과를 보다 객체 지향적으로 사용할 수 없다는 점이 단점이다.
3) ORM (JPA + Hibernate)
- 자바 코드와 SQL을 분리시키더라도, Repository 또는 DAO는 SQL을 위주로 코딩할 수 밖에 없다. 이는 '객체지향의 특징을 제대로 활용하지 못하는 문제점'으로 이어진다. (JDBC, Mybatis 사용하면 쿼리결과를 객체로 가져오는게 아님. 'entity'라는 용어가 JPA에서 등장했다는 점을 알고 있어야 한다.)
- 즉, SQL에 종속적이지 않고, 객체지향적으로 코드를 작성하고자 ORM(Object Relation Mapping)이 등장한다.
- JPA는 자바 어플리케이션에서 '관계형 데이터베이스'를 사용하는 방식을 정의한 인터페이스일 뿐이고 JPA를 구현한 구현체 중 하나가 Hibernate이다.
- Spring Boot에 마찬가지로 연동모듈로 spring-boot-starter-data-jpa를 지원하며 Auto Configuration이 적용된다.
- JDBC, Mybatis와는 다르게 JPA는 entity라는 개념을 도입해 쿼리 결과를 보다 객체처럼 다룰 수 있다. 또한 영속성 컨텍스트에서 entity를 관리함으로써 entity를 캐싱한다.
cf. JDBC, JPA는 자바 표준입니다.
Reference
- https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html
- https://meetup.toast.com/posts/152
- https://www.baeldung.com/the-persistence-layer-with-spring-and-jpa
'[ Basic ] > # 데이터베이스' 카테고리의 다른 글
<추천글>[DB] Index 종류와 카디널리티 (0) | 2022.01.11 |
---|---|
<추천글>[DB] DB Index 자료구조 B-Tree 기본개념 (0) | 2022.01.03 |
[DB] Index 적용 및 성능분석, 실행계획 분석 (0) | 2021.12.16 |
<추천글>[DB] Transaction의 Isolation level을 나눠 둔 이유 (0) | 2021.11.29 |
[DB] 실행계획 항목 분석 기초 (0) | 2021.10.13 |
- Total
- Today
- Yesterday
- golang
- argocd
- GitOps
- Stream
- Linux
- db
- Java
- RDB
- Kotlin
- kafka
- ubuntu
- rolling update
- ci/cd
- 코틀린
- spring
- 카프카
- 컨트롤러
- github actions
- Kubernetes
- jvm
- docker
- LFCS
- Non-Blocking
- K8s
- Controller
- CICD
- go
- 쿠버네티스
- container
- 우분투
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |