본문 바로가기
DB

[MyBatis] MyBatis 설정과 log4jdbc-log4j2 적용기

by soro.k 2023. 3. 31.

들어가기 전에

이번에 프로젝트를 본격적으로 시작하면서 MyBatis 설정을 끝냈는데 혹시나 다음 번에 설정해야 할 일이 있을 때 내가 정리한 문서를 참고하기 위해서 기록해 보기로 했다. log4jdbc-log4j2도 처음 알게 된 라이브러리인데 도움이 될 것 같아서 같이 정리하려고 한다.

 

 

 log4jdbc-log4j2

 

Log4jdbc-log4j2

This project was imported from https://code.google.com/archive/p/log4jdbc-log4j2/ Original License: Apache License 2.0 log4jdbc-log4j2 is a modification of log4jdbc to natively use Log4j 2 (or SLF4J as usual), that supports JDBC 4.1 to JDBC 3, includes all

log4jdbc.brunorozendo.com

 

SQL문을 실행할 때 관련 정보를 로깅할 수 있는 JDBC 라이브러리이다. Maven repository에서 검색해 보거나 위의 사이트를 들어가면 바로 2013년이라는 날짜가 보여서 나처럼 정보를 잘못 찾은 건가 당황할 수 있지만 이게 맞다. 굉장히 오래 전에 업데이트가 되고 이후로는 되지 않았지만 아직도 잘 쓰이고 있는 것 같다.

 

아래와 같이 입력된 정보를 UI적으로 편하게 볼 수 있도록 해주기도 하고 혹은 쿼리문의 형태, Connection 정보까지 많은 기록들을 로그로 확인할 수 있다.

 

시작하기

1. 의존성 추가

implementation 'org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4.1:1.16'
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.1'
runtimeOnly 'com.mysql:mysql-connector-j'

testImplementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter-test:3.0.1'
  • log4jdbc-log4j2
  • mybatis, mybatis-test
  • 사용하는 DB 관련 의존성

 

2. application.yml

spring:
    datasource:
        driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
        url: jdbc:log4jdbc:[DB]://[HOST]:[PORT]/[DATABASE]?useSSL=false
        username: [USERNAME]
        password: [PASSWORD]

    logging:
    	level:
            jdbc:
                sqlonly: off
                sqltiming: debug
                audit: off
                resultset: off
                resultsettable: debug
                connection: off

mybatis:
    mapper-locations: mapper/**/*.xml
    configuration:
    	[설정 정보 등록]

❶ datasource 설정

 

❷ logger 설정

6개의 logger가 있는데 모두 사용하지 않으면 실제 데이터베이스의 반환 값이 출력되고 하나라도 ERROR, INFO, DEBUG로 설정되면 log4jdbc가 활성화돼서 작업 내역을 로깅해 준다고 한다.

Logger 설명
jdbc.sqlonly SQL만 기록한다. 쿼리문에서 바인딩된 데이터로 대체된 인수가 표시된다.
jdbc.sqltiming SQL을 실행하는 데 걸린 시간에 대한 통계까지 포함하여 로깅한다.
jdbc.audit ResultSets를 제외한 모든 JDBC 호풀을 기록한다. 일반적으로 특정 JDBC 문제를 추적하지 않는 한 필요하지 않다.
jdbc.resultset ResultSet 개체에 대한 모든 호출이 기록된다.
jdbc.resultsettable JDBC 결과를 테이블로 기록한다.
jdbc.connection Connection에 관련된 정보로 누수 문제를 추적할 때 용이하다.

 

❸ mybatis settings

  • mapper-locations는 resources 하위에 mapper 파일들이 위치할 디렉토리를 명시해 주면 된다. 나는 resources 하위에 mapper 폴더를 만들었고 추후에 도메인 별로 나눌 가능성이 있을까봐 저렇게 지정해뒀다.

  • configuration에는 여러 가지 정보들을 설정할 수 있다. 
설정 설명 기본값
cacheEnabled 설정에서 각 매퍼에 설정된 캐시를 전역적으로 사용할지 말지에 대한 여부 true
lazyLoadingEnabled 지연로딩을 사용할지에 대한 여부. 사용하지 않는다면 모두 즉시 로딩할 것이다. 이 값은 fetchType 속성을 사용해서 대체할 수 있다. false
aggressiveLazyLoading 활성화되면 모든 메서드 호출은 객체의 모든 lazy properties 을 로드한다. 그렇지 않으면 각 property 가 필요에 따라 로드된다. (lazyLoadTriggerMethods 참조). false (3.4.1 부터 true)
multipleResultSetsEnabled 한 개의 구문에서 여러 개의 ResultSet을 허용할지의 여부(드라이버가 해당 기능을 지원해야 함) true
useColumnLabel 컬럼명 대신에 컬럼라벨을 사용. 드라이버마다 조금 다르게 작동한다. 문서와 간단한 테스트를 통해 실제 기대하는 것처럼 작동하는지 확인해야 한다. true
useGeneratedKeys 생성키에 대한 JDBC 지원을 허용. 지원하는 드라이버가 필요하다. true로 설정하면 생성키를 강제로 생성한다. 일부 드라이버(예를들면, Derby)에서는 이 설정을 무시한다. false
autoMappingBehavior 마이바티스가 칼럼을 필드/프로퍼티에 자동으로 매핑할지와 방법에 대해 명시. PARTIAL은 간단한 자동매핑만 할뿐 내포된 결과에 대해서는 처리하지 않는다. FULL은 처리가능한 모든 자동매핑을 처리한다. PARTIAL (NONE/PARTIAL/FULL)
autoMappingUnknownColumnBehavior 자동매핑 대상 중 알 수 없는 칼럼(이나 알 수 없는 프로퍼티 타입)을 발견했을 때 행위를 명시
  • NONE: 아무것도 하지 않음
  • WARNING: 경고 로그를 출력('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior'의 로그레벨은 WARN이어야 한다.)
  • FAILING: 매핑이 실패한다. (SqlSessionException예외를 던진다.)
NONE (NONE/WARNING/FAILING)
defaultExecutorType 디폴트 실행자(executor) 설정. SIMPLE 실행자는 특별히 하는 것이 없다. REUSE 실행자는 PreparedStatement를 재사용한다. BATCH 실행자는 구문을 재사용하고 수정을 배치처리한다. SIMPLE(SIMPLE/REUSE/BATCH)
defaultStatementTimeout 데이터베이스로의 응답을 얼마나 오래 기다릴지를 판단하는 타임아웃을 설정 설정되지 않음
defaultFetchSize 결과를 가져오는 크기를 제어하는 힌트처럼 드라이버에 설정한다. 이 파라미터는 쿼리설정으로 변경할 수 있다. 설정되지 않음
defaultResultSetType 각 구문의 설정을 생략할 때 스크롤 하는 방법을 지정한다. (3.5.2 부터) 설정되지 않음(FORWARD_ONLY/SCROLL_SENSITIVE/SCROLL_INSENSITIVE/DEFAULT)
safeRowBoundsEnabled 중첩구문내 RowBound사용을 허용 허용한다면 false로 설정 false
safeResultHandlerEnabled 중첩구문내 ResultHandler사용을 허용 허용한다면 false로 설정 true
mapUnderscoreToCamelCase 전통적인 데이터베이스 칼럼명 형태인 A_COLUMN을 CamelCase형태의 자바 프로퍼티명 형태인 aColumn으로 자동으로 매핑하도록 함 false
localCacheScope 마이바티스는 순환참조를 막거나 반복된 쿼리의 속도를 높히기 위해 로컬캐시를 사용한다. 디폴트 설정인 SESSION을 사용해서 동일 세션의 모든 쿼리를 캐시한다. localCacheScope=STATEMENT 로 설정하면 로컬 세션은 구문 실행할때만 사용하고 같은 SqlSession에서 두 개의 다른 호출 사이에는 데이터를 공유하지 않는다. SESSION(SESSION/STATEMENT)
jdbcTypeForNull JDBC타입을 파라미터에 제공하지 않을때 null값을 처리한 JDBC타입을 명시한다. 일부 드라이버는 칼럼의 JDBC타입을 정의하도록 요구하지만 대부분은 NULL, VARCHAR 나 OTHER 처럼 일반적인 값을 사용해서 동작한다. OTHER
lazyLoadTriggerMethods 지연로딩을 야기하는 객체의 메소드를 명시 equals,clone,hashCode,toString
defaultScriptingLanguage 동적으로 SQL을 만들기 위해 기본적으로 사용하는 언어를 명시 org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
defaultEnumTypeHandler Enum에 기본적으로 사용되는 TypeHandler 를 지정합니다. (3.4.5 부터) org.apache.ibatis.type.EnumTypeHandler
callSettersOnNulls 가져온 값이 null일 때 setter나 맵의 put 메소드를 호출할지를 명시 Map.keySet() 이나 null값을 초기화할때 유용하다. int, boolean 등과 같은 원시타입은 null을 설정할 수 없다는 점은 알아두면 좋다. false
returnInstanceForEmptyRow MyBatis 는 기본적으로 모든 열들의 행이 NULL 이 반환되었을 때 null을 반환한다. 이 설정을 사용하면 MyBatis가 대신 empty 인스턴스를 반환한다. nested results(collection 또는 association) 에도 적용된다. 3.4.2 부터 false
logPrefix 마이바티스가 로거(logger) 이름에 추가할 접두사 문자열을 명시 설정하지 않음
logImpl 마이바티스가 사용할 로깅 구현체를 명시 이 설정을 사용하지 않으면 마이바티스가 사용할 로깅 구현체를 자동으로 찾는다. 설정하지 않음(SLFJ2/LOG4J2/JDK_LOGGING/COMMONGS_LOGGING/STDOUT_LOGGING/NO_LOGGING)
proxyFactory 마이바티스가 지연로딩을 처리할 객체를 생성할 때 사용할 프록시 툴을 명시 JAVASSISTS (MyBatis 3.3 이상 버전)
vfsImpl VFS 구현체를 명시  
useActualParamName 메소드 시그니처에 명시된 실제 이름으로 구문파라미터를 참조하는 것을 허용 이 기능을 사용하려면 프로젝트를 자바 8의 -parameters옵션을 사용해서 컴파일해야만 한다.(마이바티스 3.4.1이상의 버전) true
configurationFactory Configuration 인스턴스를 제공하는 클래스를 지정한다. 반환된 Configuration 인스턴스는 역직렬화 된 객체의 지연로딩 속성들을 불러오는 데 사용된다. 이 클래스는 static Configuration getConfiguration() 메서드를 가져야 한다. (3.2.3 부터) 설정하지 않음
shrinkWhitespacesInSql SQL에서 여분의 whitespace 문자들을 삭제한다. 이는 SQL의 리터럴 문자열에도 영향을 미친다. (Since 3.5.5) false
defaultSqlProviderType Provider method를 가지고 있는 sql provider class를 지정한다. (3.5.6 부터). 이 클래스는 sql provider annotation(예: @SelectProvider)의 type (혹은 value)속성이 누락되었을때 기본으로 적용된다. 설정하지 않음
nullableOnForEach 'foreach' 태그에서 'nullable' 속성의 기본값을 지정한다. (3.5.9 부터) false
argNameBasedConstructorAutoMapping 생성자 자동 매핑을 적용할 때 칼럼 순서가 아닌 칼럼 이름과 생성자 인수들의 이름을 기반으로 매핑한다. (3.5.10 부터) false

 

 

3. log4jdbc.log4j2.properties

log4jdbc.log4j2의 속성 파일을 만들어야 하는데 기본값은 properties였지만 가독성을 위해 yml로 변경했었는데 설정값이 적용이 안돼서 다시 properties로 변경했다.

log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator
log4jdbc.dump.sql.maxlinelength=0
log4jdbc.drivers=com.mysql.cj.jdbc.Driver
log4jdbc.auto.load.popular.drivers=false
  • spylogdelegator.name : 4jdbc-log4j2 기능을 사용하기 위해 사용할 클래스 이름을 지정해준다. 참고로 다른 값은 없이 무조건 net.sf.log4jdbc.log.slf4j.Sl4jSpyLogDelegator로 설정해야 한다.
  • dump.sql.maxlinelength : 기본값이 90이기 때문에 제한없이 출력하기 위해 0으로 설정한다.
  • Mysql을 사용하는 경우, drivers에는 com.mysql.jdbc.Driver만 등록되어있기 때문에 auto.load.popluar.drivers를 false로 설정하고 drivers를 com.mysql.cj.jdbc.Driver로 설정해야 한다. 아마 이 라이브러리가 계속해서 업데이트되고 있지 않기 때문에 드라이버도 업데이트되지 않은 것 같다. 설정하지 않으면 아래와 같은 메시지를 만날 수 있다.

나같은 경우에는 yml 파일로 만들고 나서 설정값을 제대로 작성했는데도 사라지지 않아 properties로 변경했는데 그제서야 이 메시지가 사라지게 됐다. 

 

속성 설명 기본값
log4jdbc.drivers 둘 이상의 드라이버를 지정해야 하는 경우 공백 없이 쉼표로 구분해야 한다. 대부분의 유명한 JDBC 드라이버는 기본적으로 이미 로드되어 있기 때문에 이 옵션은 일반적으로 필요하지 않다. log4jdbc가 아직 래핑하지 않은 하나 이상의 추가 JDBC 드라이버를 포함해야 하는 경우 이 옵션을 사용해야 한다.  
log4jdbc.auto.load.popular.drivers 자주 사용되는 드라이버가 자동으로 로드되는 기능을 비활성화하려면 false로 설정한다. false이면 드라이버를 로드하기 위해 log4jdbc.drivers 속성을 설정해야 한다. true
log4jdbc.debug.stack.prefix 어플리케이션 패키지 이름과 일치하는 regex로 설정되지 않을 경우 log4jdbc로 호출된 실제 클래스가 디버그 출력에 사용된다.  
log4jdbc.sqltiming.warn.threshold 밀리초 시간 값을 지정하며 지정된 시간 또는 실행하는 데 더 많은 시간이 걸리는 SQL이 sqltiming 로그에 경고 수준으로 기록된다. 이 기능을 작동하게 하려면 sqltiming 로그를 활성화해야 한다. 그리고 로깅 레벨이 DEBUG로 활성화되어야 한다.  
log4jdbc.sqltiming.error.threshold 밀리초 시간 값을 지정하며 지정된 시간 또는 실행하는 데 더 많은 시간이 걸리는 SQL이 sqltiming 로그에 오류 수준으로 기록된다. 이 기능을 작동하게 하려면 sqltiming 로그를 활성화해야 한다. 그리고 로깅 레벨이 DEBUG로 활성화되어야 한다.  
log4jdbc.dump.booleanastrueflase SQL에서 boolean값을 dump할 때 true 또는 false로 dump한다. 설정하지 않으면 1 또는 0으로 dump된다. false
log4jdbc.dump.sql.maxlinelength SQL을 dump할 때 한 번에 출력되는 행의 수를 제한할 수 있다. 0으로 설정하면 제한없이 출력된다. 90
log4jdbc.dump.fulldebugstacktrace DEBUG 모드에서 dump하는 경우 전체 stack trace를 dump한다. SQL에 대한 호출 체인을 추적할 때 유용할 수 있다. false
log4jdbc.dump.sql.select SQL select문을 출력하지 않으려면 false로 설정한다. true
log4jdbc.dump.sql.insert SQL insert문을 출력하지 않으려면 false로 설정한다. true
log4jdbc.dump.sql.update SQL update문을 출력하지 않으려면 false로 설정한다. true
log4jdbc.dump.sql.delete SQL delete문을 출력하지 않으려면 false로 설정한다. true
log4jdbc.dump.sql.create SQL create문을 출력하지 않으려면 false로 설정한다. true
log4jdbc.dump.sql.addsemicolon SQL 끝에 세미콜론을 추가하여 출력하고 싶을 때 true로 설정한다. 추후에 스크립트를 만들 때 유용할 수 있다. false
log4jdbc.spylogdelegator.name 사용할 SpyLogDelegator의 정규화된 클래스 이름이다. log4jdbc-log4j2를 사용하려면 이 속성을 net.sf.log4jdbc.log.slf4j.Sl4jSpyLogDelegator로 설정해야 한다. 표준 log4jdbc 구현에는 없는 새로운 속성이다. net.sf.log4jdbc.log.slf4j.Sl4jSpyLogDelegator
log4jdbc.statement.warn 명령문이 로그에서 사용될 때 로그에 경고를 표시하려면 true로 설정한다. false
log4jdbc.trim.sql 로깅된 SQL을 트리밍하지 않으려면 false로 설정헌다. true
log4jdbc.trim.sql.extrablanklines 로깅된 SQL에서 여분의 빈 줄을 트리밍하지 않으려면 false로 설정한다(기본적으로 한 행에 둘 이상의 빈 줄이 발생하면 연속된 줄은 하나의 빈 줄로 축소된다). true
log4jdbc.suppress.generated.keys.exception Statement.getGeneratedKeys() 메서드에서 생성된 모든 예외를 무시하려면 true로 설정한다. false

 

4. 관련 설정 및 클래스 생성

❶ @MapperScan의 basePackageClasses 설정

@SpringBootApplication
@MapperScan(basePackageClasses = TestApplication.class)
public class TestApplication {

	public static void main(String[] args) {
		SpringApplication.run(TestApplication.class, args);
	}

}

 

 ❷ mapper 인터페이스 생성

@Mapper
public interface MemberMapper {
	Optional<Member> findByEmail(String email);
}

 

❸ mapper.xml 생성

namespace에 매핑되는 mapper 클래스의 경로를 기입해야 한다. 그리고 아래와 같이 해당 쿼리문의 id를 위에 만든 인터페이스에 생성한 메서드 이름과 일치시킨다.

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.test.member.mapper.MemberMapper">

    <select id="findByEmail" parameterType="String" resultType="com.test.member.domain.Member">
        SELECT *
        FROM member
        WHERE email = #{email}
    </select>

</mapper>

 

 

마무리

프로젝트에 어떤 설정이 필요하고 왜 필요한지를 충분히 고민한 후에 적용하는 것이 중요한 것 같다. 이번에도 서로 어떤 설정이 필요하고 왜 필요한가에 대해 이야기를 나누다 보니까 정말 필요한 선택들을 하게 됐고 결과적으로 깔끔하게 원하는 방향대로 설정할 수 있었다.

 

 

참고