[JDBC] Datasource Proxy 에 대하여

글쓴이 Engineer Myoa 날짜

JPA 구현체인 Hibernate 의 문제점

Java 의 ORM 인 JPA(Java Persistence API)와 그 구현체 중 하나인 Hibrernate 를 사용하다보면 몇 가지 불편사항이 생긴다.

  • Native Query 를 사용하지 않으면 (JPA) 쿼리 실행 계획이 어떻게 되는지 알 수 없다
  • Hibernate debug log 를 이용하면 Bulk insert 여부를 알 수 없다
  • Hibernate debug log 를 이용하면 Binding variable 파악을 위한 불편이 생긴다
  • Query 의 execute 결과를 계획하기 어렵다

 

사실 어떻게 보면 단편적 혹은 극단적으로 작성한 사항일 수 있다.

하지만 비즈니스 로직을 작성하다보면 위 같은 사항들이 얼마나 불편한지 알게된다.

 

어떻게든 해결하는 방법은 있다.

예를들면 Binding variable 같은 문제는

INSERT INTO TBL_TEST VALUES ( ?, ?, ? )
logging:
  level:
    org:
      hibernate:
        type:
          descriptor:
            sql: trace

로 해결한다던지….

 

properties 나 log4j configuration class 를 바꾸는 등

어차피 소스코드를 변경해야한다면 한 번에 해결하는편이 좋지 않을까?

위에 나열한 대부분의 단점을 해결할 수 있는 방법이 있다.

 

Datasource Proxy 란

 

Java 에서 ‘우아하게’ (혹은 그럴듯하게) DB에 접근하는 과정은 다음과 같다.

Application -> DataSource -> JDBC Driver -> Physical DB

풀이하자면,
Application 에서는
DataSource 로부터 Connection 정보를 설정하고
DB Connectivity Driver 를 이용해
Physical DB 에 연결한다.

이 DataSource 와 Application 사이에 Proxy 를 두는것이다

연결을 가로채어 Persistence 와 Gathering 등 Database 와의 일련의 모든 로직들을 까볼 수 있다.

다음과 같이 말이다.

Application -> DataSourceProxy -> DataSource -> JDBC Driver -> Physical DB

 

DataSourceProxy 설정방법

먼저 DataSourceProxy 는 Spring 내에 기본적으로 탑재돼있지 않기 때문에 별도의 의존성이 필요하다.

본 글에서는 ttddyy 님의 datasource-proxy (https://github.com/ttddyy/datasource-proxy) 를 이용하도록 한다. Github의 README.md 를 참고하여 dependency 설정을 해주도록 한다. (maven, gradle 등 빌드툴에 맞게)

 

이후 DataSource 를 Bean 을 설정하는 부분을 DataSourceProxy 객체를 반환하도록 작성해야 한다.

예를들어 Spring 에서는 기본 DataSource 를 Bean 을 등록할 때,

@Bean
public DataSource dataSource() {
    DataSource dataSource = DataSourceBuilder.create()
    .url(...)
    .username(...)
    .password(...)
    .build()

    return dataSource;
}

위 처럼 Configuration class 에 method 를 작성한다.

 

이 DataSource 를 반환하는 method 에서 DataSourceProxy 객체를 반환하도록, 아주 약간만 수정해주면 된다.

@Bean
public DataSource dataSource() {
    DataSource dataSource = DataSourceBuilder.create()
    .url(...)
    .username(...)
    .password(...)
    .build()

    DataSourceProxy dataSourceProxy = ProxyDataSourceBuilder
    .create(dataSource)
    .name("dataSource")
    .logQueryToSysOut()
    .asJson()
    .countQuery()
    .afterQuery((execInfo, queryInfoList) -> {
        System.out.println("Elapsed time : " + execInfo.getElapsedTime() + " ms\n");
    })
    .build();

    return dataSourceProxy;
}

정말 간단하게, DataSourceProxyBuilder 에 이전에 생성한 dataSource 객체를 담고 qualifier (bean name) 을 지정해준다.

이후 필요한 설정들을 빌더에 추가로 덧붙여준다. 아래는 내가 사용하는 설정이다.

  • logQueryToSysOut() : System.out 으로 수행된 query 를 출력한다.
    e.g.)

    {“name”:”dataSource”, “connection”:5, “time”:85, “success”:true, “type”:”Prepared”, “batch”:true, “querySize”:1, “batchSize”:100, “query”: ~~~~

  • asJson() : 위 출력될 query 데이터의 형식을 json 형식으로 지정한다.
  • countQuery() : 실행된 query 의 count 를 보여준다.
  • afterQuery() : query 가 실행된 이후의 동작을 지정한다. Java8 의 Consumer 를 받는다.
    Consume 을 위해 제공되는 객체는 ExecInfo 와 QueryInfoList 2개이다. (위 코드 참고)
    보통 query elasped time 을 찍는다.

위 설정을 마치고 query 가 수행되면 아래와 같이 로그가 남을것이다.

{"name":"dataSource", "connection":5, "time":85, "success":true, "type":"Prepared", "batch":true, "querySize":1, "batchSize":100, "query": [~~~~

]}
Elapsed time : ~~ms

 

말 그대로 로그이니, 디버깅에 사용하고 싶다면 JSON Beautifier 등을 이용해 이쁘게 보도록 하자

 

 

주의사항

이 DataSourceProxy 을 이용한 콘셉트는 DataSource 위에 추상계층이 있는 것이다.

따라서 ORM(여기서는 JPA) 를 구현한 라이브러리를 이용할때는 해당 로그를 꺼야, 이중으로 로그가 나오는 것을 막는다

 

예를 들어 hibernate 라면 application properties 에서

#        show_sql: true
#        use_sql_comments: true
#        format_sql: true

다음 3줄을 코멘트 처리하거나 지워주도록 하자.

 

 

참고문헌


2개의 댓글

답글 남기기

Avatar placeholder

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다