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

 

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

interface MerchantInfoRepository : NfcJdbcRepository<MerchantInfo, MerchantInfo.CompositeKey>

@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.
BeanPropertySqlParameterSource(obj)

2.
MapSqlParameterSource()
    .addValue("param1", param1)
    
3.
mapOf("param1" to param1)

 

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

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   

 

'Java Stack > Persistence' 카테고리의 다른 글

[MyBatis] 객체 안의 객체 매핑하기 (ResultMap과 DTO)  (0) 2022.03.12
[MyBatis] Cache  (0) 2022.03.08
redis - java persistence  (0) 2020.03.10
[Spring] DB 관련 : Mybatis  (3) 2020.03.04