JPA Entity BigDecimal 타입 DB int, float 자동 맵핑

janjanee·2024년 9월 4일
0

JPA

목록 보기
2/2

국내 DB에는 price 타입이 int이고 해외 DB 에는 price 타입이 float이다.

동일한 JPA Entity를 사용함으로 코드 중복을 제거하고 각 레이어 코드 내부에 if, else와 같은 키워드를 사용하지 않음으로 해결했다.

그런데! JPA Entity의 BigDecimal이 어떻게 DB int, float 타입을 자동으로 매핑 처리가 되는걸까?

샘플 Entity 클래스 및 DDL

Entity

@Getter
@Entity
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "bigdecimal_test")
public class BigDecimalTestEntity {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  private BigDecimal price;
}

국내 DB DDL

create bigdecimal_test
(
    id                 bigint auto_increment,
    price              int
)

해외 DB DDL

create bigdecimal_test
(
    id                 bigint auto_increment,
    price              float
)

위와 같이 Entity와 DB가 정의되어있다고 가정한다.


hibernate의 JavaType , JdbcType 클래스

  • JavaType
    • Entity 클래스의 속성 타입을 Hibernate에서 JavaType 클래스로 매핑
    • IntegerJavaType, LongJavaType, BigDecimalJavaType, LocalDateJavaType, UUIDJavaType, …
  • JdbcType

    • 데이터베이스의 타입을 Hibernate에서 JdbcType 클래스로 매핑

    • IntegerJdbcType, BigIntJdbcType, NumericJdbcType, LocalDateJdbcType, JsonJdbcType, EnumJdbcType, …

hibernate가 value 타입을 변환하는 과정

  • 데이터 save

    • BigDecimal(Entity Java Type) → BigDecimalJavaType(Hibernate Type) → NumericJdbcType/DecimalJdbcType(Hibernate Type) → DB 타입으로 자동 형변환 (int or float)
  • 데이터 find

    • DB(int or float) → NumericJdbcType/DecimalJdbcType(Hibernate Type) → BigDecimalJavaType(Hibernate Type) → BigDecimal(Entity Java Type)
  • db numeric 타입은 int, float의 상위 타입이라 자동 형변환

  • NumericJdbcType은 DecimalJdbcType을 상속해서 동일한 메소드 사용

hibernate-core-6.5.2.Final.jar 소스 코드 분석

데이터 save

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 );
	}
}
  • 데이터를 저장할 때 BasicBinder 클래스에서 doBind 메소드를 실행하여 현재 바인더 클래스가 알고있는 JavaType -> JdbcType 으로 value를 바인딩한다.

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 메소드 주석 참고

    • Sets the designated parameter to the given java. math. BigDecimal value. The driver converts this to an SQL NUMERIC value when it sends it to the database.

데이터 find

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;
		}
	}
  • 데이터를 가져올 때 BasicExtractor의 extract 메소드 내부의 doExtract를 호출하여
    현재 Extractor가 알고있는 JdbcType → JavaType으로 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 메소드 주석 참고

    • Retrieves the value of the designated column in the current row of this ResultSet object as a java. math. BigDecimal with full precision.
profile
얍얍 개발 펀치

0개의 댓글