국내 DB에는 price 타입이 int이고 해외 DB 에는 price 타입이 float이다.
동일한 JPA Entity를 사용함으로 코드 중복을 제거하고 각 레이어 코드 내부에 if, else와 같은 키워드를 사용하지 않음으로 해결했다.
그런데! JPA Entity의 BigDecimal이 어떻게 DB int, float 타입을 자동으로 매핑 처리가 되는걸까?
@Getter
@Entity
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "bigdecimal_test")
public class BigDecimalTestEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private BigDecimal price;
}
create bigdecimal_test
(
id bigint auto_increment,
price int
)
create bigdecimal_test
(
id bigint auto_increment,
price float
)
위와 같이 Entity와 DB가 정의되어있다고 가정한다.
JdbcType
데이터베이스의 타입을 Hibernate에서 JdbcType 클래스로 매핑
IntegerJdbcType, BigIntJdbcType, NumericJdbcType, LocalDateJdbcType, JsonJdbcType, EnumJdbcType, …
데이터 save
데이터 find
db numeric 타입은 int, float의 상위 타입이라 자동 형변환
NumericJdbcType은 DecimalJdbcType을 상속해서 동일한 메소드 사용
BasicBinder.java
@Override
public final void bind(PreparedStatement st, J value, int index, WrapperOptions options) throws SQLException {
if ( value == null ) {
...
}
else {
if ( JdbcBindingLogging.LOGGER.isTraceEnabled() ) { JdbcBindingLogging.logBinding( index,
jdbcType.getDefaultSqlTypeCode(),
getJavaType().extractLoggableRepresentation(value )
);
}
doBind( st, value, index, options );
}
}
DecimalJdbcType.java
@Override
public <X> ValueBinder<X> getBinder(final JavaType<X> javaType) {
return new BasicBinder<>( javaType, this ) {
@Override
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
st.setBigDecimal( index, javaType.unwrap( value, BigDecimal.class, options ) );
}
...
};
}
현재 Binder가 알고있는 DecimalJdbcType에서 doBind가 수행된다.
st.setBigDecimal에서 BigDecimal 자바 타입을 → DB Numeric 타입으로 변환한다.
st.setBigDecimal 메소드 주석 참고
BasicExtractor.java
@Override
public J extract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
final J value = doExtract( rs, paramIndex, options );
if ( value == null || rs.wasNull() ) {
... }
else {
if ( JdbcExtractingLogging.LOGGER.isTraceEnabled() ) {
JdbcExtractingLogging.logExtracted(
paramIndex,
getJdbcType().getDefaultSqlTypeCode(),
getJavaType().extractLoggableRepresentation( value )
);
}
return value;
}
}
DecimalJdbcType.java
@Override
public <X> ValueExtractor<X> getExtractor(final JavaType<X> javaType) {
return new BasicExtractor<>( javaType, this ) {
@Override
protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
return javaType.wrap( rs.getBigDecimal( paramIndex ), options );
}
....
};
}
현재 Extractor가 알고있는 DecimalJdbcType에서 doExtract가 수행된다.
rs.getBigDecimal에서 db의 데이터를 BigDecimal로 변환해서 가져온다.
rs.getBigDecimal 메소드 주석 참고