Post

(Spring JDBC) JdbcOperations

Spring Data JDBC를 사용할 때, CrudRepository와 자동생성 쿼리 만으로는 커버가 되지 않는 경우가 반드시 생기고,
이 경우 @Query 보다는 JdbcOperations를 쓰는 것이 낫다. (see [Spring Data JDBC] docs )

따라서 CrudRepository를 아래와 같이 Dao로 확장해서 관리하는 것이 좋다.

1
2
interface MerchantInfoRepository 
    : MyJdbcRepository<MerchantInfo, MerchantInfo.CompositeKey>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Repository
class MerchantInfoDao(
    private val merchantInfoRepository: MerchantInfoRepository,
    private val jdbcOperations: NamedParameterJdbcOperations
): MerchantInfoRepository by merchantInfoRepository {  // delegate 처리해서 interface 노출
    fun upsert(merchantInfo: MerchantInfo): Int {
        return jdbcOperations.update(
            upsertQuery,
            ObjectSqlParameterSource(merchantInfo)
        )
    }
    private val upsertQuery = """
        MERGE INTO mrc
        USING DUAL
        ON (mrc_no = :merchantNo...)    // bind는 반드시 $가 아니라 :로. (prepared statement)
        ..."""
}

:param bind

다음 3가지 방법 사용 할 수 있다.

1
2
3
4
5
6
7
8
9
1.
BeanPropertySqlParameterSource(obj)

2.
MapSqlParameterSource()
    .addValue("param1", param1)

3.
mapOf("param1" to param1)

여기서 LocalDateTime 같은 타입에 대한 Converter를 자동으로 적용하려면, 아래 2가지 방법 사용 할 수 있다.

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
31
32
33
34
1.
objectMapper.converValue(map으로 변환)
// objectMapper에 Converter가 등록되어 있어야 한다. (보통은 Auto-config)
// 이 방법도 나쁘지 않다.

2.
// ObjectSqlParameterSource를 직접 정의하고 override해서 Convert 처리
class ObjectSqlParameterSource(
    val obj: Any
): BeanPropertySqlParameterSource(obj) {
    override fun getValue(paramName: String): Any? {
        return when (val value = super.getValue(paramName)) {
            is LocalDateTime -> LocalDateTimeToStringConverter.convert(value)
            is CustomLocalDate -> CustomLocalDateToStringConverter.convert(value)
            is CardNumber -> CardNumberToStringConverter.convert(value)
            is CurrencyCode -> CurrencyCodeToStringConverter.convert(value)
            else -> value
        }
    }
    
    /**
     * BeanPropertySqlParameterSource.getSqlType을 보면 
     * JavaType이 Date이면 SqlType을 Timestamp로 만들어버린다.
     * 따라서 반드시 override 필요함.
     */
    override fun getSqlType(paramName: String): Int {
        val sqlType = super.getSqlType(paramName)
        return if (sqlType == Types.TIMESTAMP) {
            Types.VARCHAR
        } else {
            sqlType
        }
    }
}

참고

spring-jdbc-tips/spring-jdbc-core.md at master · benelog/spring-jdbc-tips

This post is licensed under CC BY 4.0 by the author.