From 14535a68496f90bd0b06a661898759e298e60934 Mon Sep 17 00:00:00 2001 From: Tran Ngoc Nhan Date: Sat, 27 Jul 2024 20:37:30 +0700 Subject: [PATCH] act: improve converter --- .../ArrayToArrayGenericOracleConverter.java | 8 +- ...rayToCollectionGenericOracleConverter.java | 5 +- ...llectionToArrayGenericOracleConverter.java | 5 +- .../support/DefaultOracleConverters.java | 5 + .../io/spring/jdbc/oracle/utils/Mappers.java | 14 +++ .../support/DefaultOracleConvertersTest.java | 119 ++++++++++++++++++ 6 files changed, 152 insertions(+), 4 deletions(-) diff --git a/spring-jdbc-oracle-core/src/main/java/io/spring/jdbc/oracle/converter/support/ArrayToArrayGenericOracleConverter.java b/spring-jdbc-oracle-core/src/main/java/io/spring/jdbc/oracle/converter/support/ArrayToArrayGenericOracleConverter.java index 8f91262..8b33e1a 100644 --- a/spring-jdbc-oracle-core/src/main/java/io/spring/jdbc/oracle/converter/support/ArrayToArrayGenericOracleConverter.java +++ b/spring-jdbc-oracle-core/src/main/java/io/spring/jdbc/oracle/converter/support/ArrayToArrayGenericOracleConverter.java @@ -3,6 +3,7 @@ import io.spring.jdbc.oracle.converter.ConvertKey; import io.spring.jdbc.oracle.converter.GenericOracleConverter; import io.spring.jdbc.oracle.converter.OracleConverters; +import io.spring.jdbc.oracle.utils.Mappers; import java.util.Arrays; import java.util.List; import org.springframework.core.convert.TypeDescriptor; @@ -22,8 +23,11 @@ public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { this.converter.matches(sourceType, targetType); - return Object[].class.isAssignableFrom(sourceType.getType()) - && Object[].class.isAssignableFrom(targetType.getType()); + var source = Mappers.resolveArrayTypeDescriptor(sourceType).getType(); + var target = Mappers.resolveArrayTypeDescriptor(targetType).getType(); + + return Object[].class.isAssignableFrom(source) + && Object[].class.isAssignableFrom(target); } @Override diff --git a/spring-jdbc-oracle-core/src/main/java/io/spring/jdbc/oracle/converter/support/ArrayToCollectionGenericOracleConverter.java b/spring-jdbc-oracle-core/src/main/java/io/spring/jdbc/oracle/converter/support/ArrayToCollectionGenericOracleConverter.java index c0a1444..87f7c79 100644 --- a/spring-jdbc-oracle-core/src/main/java/io/spring/jdbc/oracle/converter/support/ArrayToCollectionGenericOracleConverter.java +++ b/spring-jdbc-oracle-core/src/main/java/io/spring/jdbc/oracle/converter/support/ArrayToCollectionGenericOracleConverter.java @@ -3,6 +3,7 @@ import io.spring.jdbc.oracle.converter.ConvertKey; import io.spring.jdbc.oracle.converter.GenericOracleConverter; import io.spring.jdbc.oracle.converter.OracleConverters; +import io.spring.jdbc.oracle.utils.Mappers; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collection; @@ -27,7 +28,9 @@ public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { this.sourceType = sourceType; this.targetType = targetType; - return Object[].class.isAssignableFrom(sourceType.getType()) + var source = Mappers.resolveArrayTypeDescriptor(sourceType).getType(); + + return Object[].class.isAssignableFrom(source) && Collection.class.isAssignableFrom(targetType.getType()); } diff --git a/spring-jdbc-oracle-core/src/main/java/io/spring/jdbc/oracle/converter/support/CollectionToArrayGenericOracleConverter.java b/spring-jdbc-oracle-core/src/main/java/io/spring/jdbc/oracle/converter/support/CollectionToArrayGenericOracleConverter.java index 8610ab4..9527fb4 100644 --- a/spring-jdbc-oracle-core/src/main/java/io/spring/jdbc/oracle/converter/support/CollectionToArrayGenericOracleConverter.java +++ b/spring-jdbc-oracle-core/src/main/java/io/spring/jdbc/oracle/converter/support/CollectionToArrayGenericOracleConverter.java @@ -3,6 +3,7 @@ import io.spring.jdbc.oracle.converter.ConvertKey; import io.spring.jdbc.oracle.converter.GenericOracleConverter; import io.spring.jdbc.oracle.converter.OracleConverters; +import io.spring.jdbc.oracle.utils.Mappers; import io.spring.jdbc.oracle.utils.Validators; import java.lang.reflect.Array; import java.util.Collection; @@ -25,8 +26,10 @@ public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { this.sourceType = sourceType; this.targetType = targetType; + var target = Mappers.resolveArrayTypeDescriptor(targetType).getType(); + return Collection.class.isAssignableFrom(sourceType.getType()) - && Object[].class.isAssignableFrom(targetType.getType()); + && Object[].class.isAssignableFrom(target); } @Override diff --git a/spring-jdbc-oracle-core/src/main/java/io/spring/jdbc/oracle/converter/support/DefaultOracleConverters.java b/spring-jdbc-oracle-core/src/main/java/io/spring/jdbc/oracle/converter/support/DefaultOracleConverters.java index 317c454..1ed5459 100644 --- a/spring-jdbc-oracle-core/src/main/java/io/spring/jdbc/oracle/converter/support/DefaultOracleConverters.java +++ b/spring-jdbc-oracle-core/src/main/java/io/spring/jdbc/oracle/converter/support/DefaultOracleConverters.java @@ -108,6 +108,11 @@ public void addConverter(OracleConverter converter) { throw new ValueException(DETERMINE_EXCEPTION.formatted(converter)); } + if (this.converterCaches.containsKey(convertKey)) { + + return; + } + this.converterCaches.put(convertKey, new ConvertAdapter(converter)); } diff --git a/spring-jdbc-oracle-core/src/main/java/io/spring/jdbc/oracle/utils/Mappers.java b/spring-jdbc-oracle-core/src/main/java/io/spring/jdbc/oracle/utils/Mappers.java index 9b671c6..87101b6 100644 --- a/spring-jdbc-oracle-core/src/main/java/io/spring/jdbc/oracle/utils/Mappers.java +++ b/spring-jdbc-oracle-core/src/main/java/io/spring/jdbc/oracle/utils/Mappers.java @@ -7,6 +7,7 @@ import org.springframework.beans.BeanUtils; import org.springframework.core.ResolvableType; import org.springframework.core.convert.TypeDescriptor; +import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.ReflectionUtils; @@ -65,4 +66,17 @@ public static Object convertValue(TypeProperty typeProperty, Object value) { return ReflectionUtils.invokeMethod(method, instance, value); } + public static TypeDescriptor resolveArrayTypeDescriptor(TypeDescriptor typeDescriptor) { + + var sourceElementType = typeDescriptor.getElementTypeDescriptor(); + + if (sourceElementType == null) { + + return typeDescriptor; + } + + var resolveClass = ClassUtils.resolvePrimitiveIfNecessary(sourceElementType.getType()); + return TypeDescriptor.array(TypeDescriptor.valueOf(resolveClass)); + } + } \ No newline at end of file diff --git a/spring-jdbc-oracle-test/src/test/java/io/spring/jdbc/oracle/converter/support/DefaultOracleConvertersTest.java b/spring-jdbc-oracle-test/src/test/java/io/spring/jdbc/oracle/converter/support/DefaultOracleConvertersTest.java index f2db2e0..3134844 100644 --- a/spring-jdbc-oracle-test/src/test/java/io/spring/jdbc/oracle/converter/support/DefaultOracleConvertersTest.java +++ b/spring-jdbc-oracle-test/src/test/java/io/spring/jdbc/oracle/converter/support/DefaultOracleConvertersTest.java @@ -1,8 +1,13 @@ package io.spring.jdbc.oracle.converter.support; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import io.spring.jdbc.oracle.converter.ConvertKey; +import io.spring.jdbc.oracle.converter.GenericOracleConverter; +import io.spring.jdbc.oracle.converter.OracleConverter; import io.spring.jdbc.oracle.converter.OracleConverters; +import io.spring.jdbc.oracle.exception.ValueException; import java.math.BigDecimal; import java.sql.Timestamp; import java.time.LocalDate; @@ -12,12 +17,15 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.Set; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.core.convert.TypeDescriptor; +import org.springframework.util.ReflectionUtils; @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) class DefaultOracleConvertersTest { @@ -30,6 +38,87 @@ void return_null_if_input_is_null(TypeDescriptor sourceType, TypeDescriptor targ .isNull(); } + @Test + void throw_exception_when_adding_null_converter() { + + assertThatExceptionOfType(ValueException.class) + .isThrownBy(() -> oracleConverters.addGenericConverter(null)); + + assertThatExceptionOfType(ValueException.class) + .isThrownBy(() -> oracleConverters.addConverter(null)); + } + + @SuppressWarnings("unchecked") + @Test + void do_nothing_when_adding_replicate_converter() { + + oracleConverters.addGenericConverter(new ClonedNumberToString()); + oracleConverters.addConverter(new ClonedLocalDatetimeToTimestampOracleConverter()); + + var converterCachesField = ReflectionUtils.findField( + DefaultOracleConverters.class, + "converterCaches" + ); + ReflectionUtils.makeAccessible(converterCachesField); + var converterCaches = + (Map) ReflectionUtils.getField( + converterCachesField, + oracleConverters + ); + assertThat(converterCaches) + .hasSize(2); + + var genericConvertersField = ReflectionUtils.findField( + DefaultOracleConverters.class, + "genericConverters" + ); + ReflectionUtils.makeAccessible(genericConvertersField); + var genericConverters = (Set) ReflectionUtils.getField( + genericConvertersField, + oracleConverters + ); + assertThat(genericConverters) + .hasSize(5); + } + + record ClonedLocalDatetimeToTimestampOracleConverter() implements + OracleConverter { + + @Override + public Timestamp convert(LocalDateTime source) { + + return Optional.ofNullable(source) + .map(Timestamp::valueOf) + .orElse(null); + } + + } + + record ClonedNumberToString() implements GenericOracleConverter { + + @Override + public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { + + return Number.class.isAssignableFrom(sourceType.getType()) + && String.class.isAssignableFrom(targetType.getType()); + } + + @Override + public ConvertKey getConvertKey() { + + return new ConvertKey(Number.class, String.class); + } + + @Override + public Object convert(Object source) { + + return Optional.ofNullable((Number) source) + .map(Number::toString) + .orElse(null); + } + + } + @Nested class DetermineJavaClassForJdbcTypeCode { @@ -132,6 +221,36 @@ void convert_Collection_to_Collection() { return_null_if_input_is_null(source, listTarget); } + @Test + void convert_Array_to_Array() { + + var source = TypeDescriptor.valueOf(int[].class); + var target = TypeDescriptor.valueOf(String[].class); + + var output = oracleConverters.convert(new Object[0], source, target); + assertThat(output).isEqualTo(new Object[0]); + + output = oracleConverters.convert(new int[]{1, 2}, source, target); + assertThat(output).isEqualTo(new String[]{"1", "2"}); + + return_null_if_input_is_null(source, target); + } + + @Test + void convert_Collection_to_Array() { + + var source = TypeDescriptor.valueOf(Collection.class); + var target = TypeDescriptor.valueOf(int[].class); + + var output = oracleConverters.convert(Collections.emptyList(), source, target); + assertThat(output).isEqualTo(new int[0]); + + output = oracleConverters.convert(List.of(1, 2), source, target); + assertThat(output).isEqualTo(new int[]{1, 2}); + + return_null_if_input_is_null(source, target); + } + } @Nested