From 1a801a11068345222e1d0a15cae49238a66186d0 Mon Sep 17 00:00:00 2001 From: Andreas Hufler Date: Wed, 4 Sep 2024 09:26:26 +0200 Subject: [PATCH 1/9] wip --- pom.xml | 12 ++++ .../toolbox/persistence/WrappedValue.java | 5 ++ .../persistence/identity/Identity.java | 14 +++++ .../javatype/WrappedLongJavaType.java | 60 +++++++++++++++++++ .../javatype/WrappedStringJavaType.java | 57 ++++++++++++++++++ .../persistence/type/ToolboxTypeConfig.java | 12 ++++ 6 files changed, 160 insertions(+) create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/persistence/WrappedValue.java create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/Identity.java create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/WrappedLongJavaType.java create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/WrappedStringJavaType.java create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/persistence/type/ToolboxTypeConfig.java diff --git a/pom.xml b/pom.xml index 01001da..752f026 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,11 @@ + + org.springframework.boot + spring-boot-starter-data-jpa + + org.springframework.boot spring-boot-starter-validation @@ -41,6 +46,13 @@ 1.9.0 + + + org.projectlombok + lombok + true + + org.springframework.boot diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/WrappedValue.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/WrappedValue.java new file mode 100644 index 0000000..e7c9ee3 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/WrappedValue.java @@ -0,0 +1,5 @@ +package it.aboutbits.springboot.toolbox.persistence; + +public interface WrappedValue { + T value(); +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/Identity.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/Identity.java new file mode 100644 index 0000000..4b41e83 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/Identity.java @@ -0,0 +1,14 @@ +package it.aboutbits.springboot.toolbox.persistence.identity; + +import it.aboutbits.springboot.toolbox.persistence.WrappedValue; + +import java.io.Serializable; + +/** + * The Identity interface represents a serializable entity that wraps a value. + * A class implementing Identity is considered to be the primary key of an entity type. + * + * @param the type of the value being wrapped + */ +public interface Identity extends WrappedValue, Serializable { +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/WrappedLongJavaType.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/WrappedLongJavaType.java new file mode 100644 index 0000000..46b4382 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/WrappedLongJavaType.java @@ -0,0 +1,60 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype; + +import it.aboutbits.springboot.toolbox.persistence.WrappedValue; +import lombok.SneakyThrows; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.AbstractClassJavaType; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; + +import java.lang.reflect.InvocationTargetException; +import java.sql.Types; + +public abstract class WrappedLongJavaType> extends AbstractClassJavaType { + protected WrappedLongJavaType(Class type) { + super(type); + } + + @Override + public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) { + return indicators.getTypeConfiguration() + .getJdbcTypeRegistry() + .getDescriptor(Types.BIGINT); + } + + @Override + public X unwrap(T id, Class aClass, WrapperOptions wrapperOptions) { + var javaTypeClass = getJavaTypeClass(); + + if (id == null) { + return null; + } + if (javaTypeClass.isAssignableFrom(aClass)) { + return (X) id; + } + if (Long.class.isAssignableFrom(aClass)) { + return (X) id.value(); + } + throw unknownUnwrap(aClass); + } + + @SneakyThrows({InstantiationException.class, IllegalAccessException.class, InvocationTargetException.class}) + @Override + public T wrap(X value, WrapperOptions wrapperOptions) { + var clazz = getJavaTypeClass(); + + if (value == null) { + return null; + } + if (clazz.isInstance(value)) { + return (T) value; + } + if (value instanceof Long longValue) { + return (T) clazz.getConstructors()[0].newInstance(longValue); + } + if (value instanceof String stringValue) { + return (T) clazz.getConstructors()[0].newInstance(Long.parseLong(stringValue)); + } + throw unknownWrap(value.getClass()); + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/WrappedStringJavaType.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/WrappedStringJavaType.java new file mode 100644 index 0000000..ce6bb2d --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/WrappedStringJavaType.java @@ -0,0 +1,57 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype; + +import it.aboutbits.springboot.toolbox.persistence.WrappedValue; +import lombok.SneakyThrows; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.AbstractClassJavaType; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; + +import java.lang.reflect.InvocationTargetException; +import java.sql.Types; + +public abstract class WrappedStringJavaType> extends AbstractClassJavaType { + protected WrappedStringJavaType(Class type) { + super(type); + } + + @Override + public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) { + return indicators.getTypeConfiguration() + .getJdbcTypeRegistry() + .getDescriptor(Types.VARCHAR); + } + + @Override + public X unwrap(T id, Class aClass, WrapperOptions wrapperOptions) { + var javaTypeClass = getJavaTypeClass(); + + if (id == null) { + return null; + } + if (javaTypeClass.isAssignableFrom(aClass)) { + return (X) id; + } + if (String.class.isAssignableFrom(aClass)) { + return (X) id.value(); + } + throw unknownUnwrap(aClass); + } + + @SneakyThrows({InstantiationException.class, IllegalAccessException.class, InvocationTargetException.class}) + @Override + public T wrap(X value, WrapperOptions wrapperOptions) { + var clazz = getJavaTypeClass(); + + if (value == null) { + return null; + } + if (clazz.isInstance(value)) { + return (T) value; + } + if (value instanceof String stringValue) { + return (T) clazz.getConstructors()[0].newInstance(stringValue); + } + throw unknownWrap(value.getClass()); + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/type/ToolboxTypeConfig.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/type/ToolboxTypeConfig.java new file mode 100644 index 0000000..53bb8e2 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/type/ToolboxTypeConfig.java @@ -0,0 +1,12 @@ +package it.aboutbits.springboot.toolbox.persistence.type; + +import org.hibernate.annotations.JavaTypeRegistrations; + +@JavaTypeRegistrations({ + /*@JavaTypeRegistration( + javaType = ID.class, + descriptorClass = IdJavaType.class + )*/ +}) +public abstract class ToolboxTypeConfig { +} From b9cb44c48a4cfd1c030b5c4cab08e9eeb935e358 Mon Sep 17 00:00:00 2001 From: Andreas Hufler Date: Thu, 5 Sep 2024 11:26:51 +0200 Subject: [PATCH 2/9] add full support for custom types --- pom.xml | 28 ++- .../AbstractCustomTypeContributor.java | 38 +++++ .../persistence/AutoRegisteredJavaType.java | 6 + .../persistence/CustomTypeContributor.java | 7 + .../boot/type/CustomTypeConfiguration.java | 79 +++++++++ .../CustomTypeConfigurationRegistrar.java | 28 +++ .../boot/type/RegisterCustomTypes.java | 15 ++ .../toolbox/persistence/WrappedValue.java | 5 - .../persistence/identity/Identified.java | 5 + .../persistence/identity/Identity.java | 4 +- .../javatype/EmailAddressJavaType.java | 11 ++ .../persistence/javatype/IbanJavaType.java | 11 ++ .../javatype/ScaledBigDecimalJavaType.java | 11 ++ .../base/WrappedBigDecimalJavaType.java | 61 +++++++ .../javatype/base/WrappedDoubleJavaType.java | 60 +++++++ .../javatype/base/WrappedFloatJavaType.java | 60 +++++++ .../javatype/base/WrappedIntegerJavaType.java | 60 +++++++ .../{ => base}/WrappedLongJavaType.java | 6 +- .../base/WrappedScaledBigDecimalJavaType.java | 62 +++++++ .../javatype/base/WrappedShortJavaType.java | 60 +++++++ .../{ => base}/WrappedStringJavaType.java | 6 +- .../persistence/type/ToolboxTypeConfig.java | 12 -- .../reflection/util/RecordReflectionUtil.java | 61 +++++++ .../springboot/toolbox/type/CustomType.java | 5 + .../springboot/toolbox/type/EmailAddress.java | 2 +- .../springboot/toolbox/type/Iban.java | 2 +- .../toolbox/type/ScaledBigDecimal.java | 2 +- .../type/jackson/CustomTypeDeserializer.java | 161 ++++++++++++++++++ .../type/jackson/CustomTypeSerializer.java | 38 +++++ .../type/mvc/CustomTypePropertyEditor.java | 74 ++++++++ 30 files changed, 944 insertions(+), 36 deletions(-) create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/boot/persistence/AbstractCustomTypeContributor.java create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/boot/persistence/AutoRegisteredJavaType.java create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/boot/persistence/CustomTypeContributor.java create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeConfiguration.java create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeConfigurationRegistrar.java create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/boot/type/RegisterCustomTypes.java delete mode 100644 src/main/java/it/aboutbits/springboot/toolbox/persistence/WrappedValue.java create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/Identified.java create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/EmailAddressJavaType.java create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/IbanJavaType.java create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/ScaledBigDecimalJavaType.java create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedBigDecimalJavaType.java create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedDoubleJavaType.java create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedFloatJavaType.java create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedIntegerJavaType.java rename src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/{ => base}/WrappedLongJavaType.java (88%) create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedScaledBigDecimalJavaType.java create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedShortJavaType.java rename src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/{ => base}/WrappedStringJavaType.java (87%) delete mode 100644 src/main/java/it/aboutbits/springboot/toolbox/persistence/type/ToolboxTypeConfig.java create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/reflection/util/RecordReflectionUtil.java create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/type/CustomType.java create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/type/jackson/CustomTypeDeserializer.java create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/type/jackson/CustomTypeSerializer.java create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/type/mvc/CustomTypePropertyEditor.java diff --git a/pom.xml b/pom.xml index 752f026..5d1303f 100644 --- a/pom.xml +++ b/pom.xml @@ -25,12 +25,17 @@ org.springframework.boot spring-boot-starter-data-jpa - + org.springframework.boot spring-boot-starter-validation + + org.springframework.boot + spring-boot-starter-json + + org.projectlombok @@ -38,19 +43,26 @@ true + + + + org.reflections + reflections + 0.10.2 + + commons-validator commons-validator 1.9.0 - - - - - org.projectlombok - lombok - true + + + commons-logging + commons-logging + + diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/persistence/AbstractCustomTypeContributor.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/persistence/AbstractCustomTypeContributor.java new file mode 100644 index 0000000..0aa6944 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/boot/persistence/AbstractCustomTypeContributor.java @@ -0,0 +1,38 @@ +package it.aboutbits.springboot.toolbox.boot.persistence; + +import lombok.SneakyThrows; +import org.hibernate.boot.model.TypeContributions; +import org.hibernate.boot.model.TypeContributor; +import org.hibernate.service.ServiceRegistry; +import org.hibernate.type.descriptor.java.JavaType; +import org.reflections.Reflections; +import org.reflections.util.ConfigurationBuilder; + +import java.lang.reflect.InvocationTargetException; +import java.util.Set; + +public abstract class AbstractCustomTypeContributor implements TypeContributor { + private final Reflections reflections; + + protected AbstractCustomTypeContributor(String... packageNames) { + var packageToScan = new ConfigurationBuilder().forPackages(packageNames); + reflections = new Reflections(packageToScan); + } + + @SneakyThrows({InstantiationException.class, IllegalAccessException.class, InvocationTargetException.class}) + @Override + public void contribute(TypeContributions typeContributions, ServiceRegistry serviceRegistry) { + var types = findAllTypes(); + + for (var type : types) { + typeContributions.contributeJavaType( + (JavaType) type.getConstructors()[0].newInstance() + ); + } + } + + @SuppressWarnings("rawtypes") + private Set> findAllTypes() { + return reflections.getSubTypesOf(AutoRegisteredJavaType.class); + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/persistence/AutoRegisteredJavaType.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/persistence/AutoRegisteredJavaType.java new file mode 100644 index 0000000..393520c --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/boot/persistence/AutoRegisteredJavaType.java @@ -0,0 +1,6 @@ +package it.aboutbits.springboot.toolbox.boot.persistence; + +import org.hibernate.type.descriptor.java.JavaType; + +public interface AutoRegisteredJavaType extends JavaType { +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/persistence/CustomTypeContributor.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/persistence/CustomTypeContributor.java new file mode 100644 index 0000000..c7c4208 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/boot/persistence/CustomTypeContributor.java @@ -0,0 +1,7 @@ +package it.aboutbits.springboot.toolbox.boot.persistence; + +public final class CustomTypeContributor extends AbstractCustomTypeContributor { + public CustomTypeContributor() { + super("it.aboutbits.springboot.toolbox"); + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeConfiguration.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeConfiguration.java new file mode 100644 index 0000000..f6b9e25 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeConfiguration.java @@ -0,0 +1,79 @@ +package it.aboutbits.springboot.toolbox.boot.type; + +import it.aboutbits.springboot.toolbox.type.CustomType; +import it.aboutbits.springboot.toolbox.type.jackson.CustomTypeDeserializer; +import it.aboutbits.springboot.toolbox.type.jackson.CustomTypeSerializer; +import it.aboutbits.springboot.toolbox.type.mvc.CustomTypePropertyEditor; +import jakarta.annotation.PostConstruct; +import lombok.extern.slf4j.Slf4j; +import org.reflections.Reflections; +import org.reflections.util.ConfigurationBuilder; +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.InitBinder; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + +@Slf4j +@Configuration +public class CustomTypeConfiguration { + public static final String LIBRARY_BASE_PACKAGE_NAME = "it.aboutbits.springboot.toolbox"; + + private String[] packageNamesToScan; + private Reflections reflections; + + public void setAdditionalTypePackages(String[] additionalTypePackages) { + var tmp = new ArrayList(); + tmp.add(LIBRARY_BASE_PACKAGE_NAME); + tmp.addAll(Arrays.asList(additionalTypePackages)); + + this.packageNamesToScan = tmp.toArray(new String[0]); + + var packageToScan = new ConfigurationBuilder().forPackages(packageNamesToScan); + reflections = new Reflections(packageToScan); + } + + @PostConstruct + public void init() { + log.info("CustomTypeConfiguration enabled. Scanning: {}", Arrays.toString(packageNamesToScan)); + } + + @ControllerAdvice + public class CustomTypePropertyBinder { + @InitBinder + public void initBinder(WebDataBinder binder) { + var types = findAllCustomTypeRecords(); + for (var clazz : types) { + binder.registerCustomEditor(clazz, new CustomTypePropertyEditor<>(clazz)); + } + } + } + + @Bean + public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() { + var types = findAllCustomTypeRecords(); + + var deserializers = types.stream() + .map(CustomTypeDeserializer::new) + .toList() + .toArray(new CustomTypeDeserializer[types.size()]); + + + return builder -> builder + .serializers(new CustomTypeSerializer()) + .deserializers(deserializers); + } + + @SuppressWarnings("rawtypes") + private Set> findAllCustomTypeRecords() { + return reflections.getSubTypesOf(CustomType.class).stream() + .filter(Record.class::isAssignableFrom) + .collect(Collectors.toSet()); + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeConfigurationRegistrar.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeConfigurationRegistrar.java new file mode 100644 index 0000000..33b3fe0 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeConfigurationRegistrar.java @@ -0,0 +1,28 @@ +package it.aboutbits.springboot.toolbox.boot.type; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.type.AnnotationMetadata; + +import java.util.Objects; + +public class CustomTypeConfigurationRegistrar implements ImportBeanDefinitionRegistrar { + + @Override + public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { + var attributes = new AnnotationAttributes( + Objects.requireNonNull( + metadata.getAnnotationAttributes( + RegisterCustomTypes.class.getName() + ) + ) + ); + var value = attributes.getStringArray("additionalTypePackages"); + + var builder = BeanDefinitionBuilder.genericBeanDefinition(CustomTypeConfiguration.class); + builder.addPropertyValue("additionalTypePackages", value); + registry.registerBeanDefinition("CustomTypeConfiguration", builder.getBeanDefinition()); + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/RegisterCustomTypes.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/RegisterCustomTypes.java new file mode 100644 index 0000000..2f2754a --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/RegisterCustomTypes.java @@ -0,0 +1,15 @@ +package it.aboutbits.springboot.toolbox.boot.type; + +import org.springframework.context.annotation.Import; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Import({CustomTypeConfigurationRegistrar.class}) +public @interface RegisterCustomTypes { + String[] additionalTypePackages() default ""; +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/WrappedValue.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/WrappedValue.java deleted file mode 100644 index e7c9ee3..0000000 --- a/src/main/java/it/aboutbits/springboot/toolbox/persistence/WrappedValue.java +++ /dev/null @@ -1,5 +0,0 @@ -package it.aboutbits.springboot.toolbox.persistence; - -public interface WrappedValue { - T value(); -} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/Identified.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/Identified.java new file mode 100644 index 0000000..3743d96 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/Identified.java @@ -0,0 +1,5 @@ +package it.aboutbits.springboot.toolbox.persistence.identity; + +public interface Identified> { + ID getId(); +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/Identity.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/Identity.java index 4b41e83..0d666b1 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/Identity.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/Identity.java @@ -1,6 +1,6 @@ package it.aboutbits.springboot.toolbox.persistence.identity; -import it.aboutbits.springboot.toolbox.persistence.WrappedValue; +import it.aboutbits.springboot.toolbox.type.CustomType; import java.io.Serializable; @@ -10,5 +10,5 @@ * * @param the type of the value being wrapped */ -public interface Identity extends WrappedValue, Serializable { +public interface Identity extends CustomType, Serializable { } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/EmailAddressJavaType.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/EmailAddressJavaType.java new file mode 100644 index 0000000..496c029 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/EmailAddressJavaType.java @@ -0,0 +1,11 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype; + +import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedStringJavaType; +import it.aboutbits.springboot.toolbox.type.EmailAddress; + +public final class EmailAddressJavaType extends WrappedStringJavaType implements AutoRegisteredJavaType { + public EmailAddressJavaType() { + super(EmailAddress.class); + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/IbanJavaType.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/IbanJavaType.java new file mode 100644 index 0000000..7347b03 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/IbanJavaType.java @@ -0,0 +1,11 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype; + +import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedStringJavaType; +import it.aboutbits.springboot.toolbox.type.Iban; + +public final class IbanJavaType extends WrappedStringJavaType implements AutoRegisteredJavaType { + public IbanJavaType() { + super(Iban.class); + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/ScaledBigDecimalJavaType.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/ScaledBigDecimalJavaType.java new file mode 100644 index 0000000..5819f43 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/ScaledBigDecimalJavaType.java @@ -0,0 +1,11 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype; + +import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedBigDecimalJavaType; +import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; + +public final class ScaledBigDecimalJavaType extends WrappedBigDecimalJavaType implements AutoRegisteredJavaType { + public ScaledBigDecimalJavaType() { + super(ScaledBigDecimal.class); + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedBigDecimalJavaType.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedBigDecimalJavaType.java new file mode 100644 index 0000000..166d3e9 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedBigDecimalJavaType.java @@ -0,0 +1,61 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype.base; + +import it.aboutbits.springboot.toolbox.type.CustomType; +import lombok.SneakyThrows; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.AbstractClassJavaType; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; + +import java.lang.reflect.InvocationTargetException; +import java.math.BigDecimal; +import java.sql.Types; + +public abstract class WrappedBigDecimalJavaType> extends AbstractClassJavaType { + protected WrappedBigDecimalJavaType(Class type) { + super(type); + } + + @Override + public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) { + return indicators.getTypeConfiguration() + .getJdbcTypeRegistry() + .getDescriptor(Types.DOUBLE); + } + + @Override + public X unwrap(T id, Class aClass, WrapperOptions wrapperOptions) { + var javaTypeClass = getJavaTypeClass(); + + if (id == null) { + return null; + } + if (javaTypeClass.isAssignableFrom(aClass)) { + return (X) id; + } + if (BigDecimal.class.isAssignableFrom(aClass)) { + return (X) id.value(); + } + throw unknownUnwrap(aClass); + } + + @SneakyThrows({InstantiationException.class, IllegalAccessException.class, InvocationTargetException.class}) + @Override + public T wrap(X value, WrapperOptions wrapperOptions) { + var clazz = getJavaTypeClass(); + + if (value == null) { + return null; + } + if (clazz.isInstance(value)) { + return (T) value; + } + if (value instanceof BigDecimal bigDecimal) { + return (T) clazz.getConstructors()[0].newInstance(bigDecimal); + } + if (value instanceof String stringValue) { + return (T) clazz.getConstructors()[0].newInstance(new BigDecimal(stringValue)); + } + throw unknownWrap(value.getClass()); + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedDoubleJavaType.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedDoubleJavaType.java new file mode 100644 index 0000000..2f88ae6 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedDoubleJavaType.java @@ -0,0 +1,60 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype.base; + +import it.aboutbits.springboot.toolbox.type.CustomType; +import lombok.SneakyThrows; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.AbstractClassJavaType; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; + +import java.lang.reflect.InvocationTargetException; +import java.sql.Types; + +public abstract class WrappedDoubleJavaType> extends AbstractClassJavaType { + protected WrappedDoubleJavaType(Class type) { + super(type); + } + + @Override + public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) { + return indicators.getTypeConfiguration() + .getJdbcTypeRegistry() + .getDescriptor(Types.DOUBLE); + } + + @Override + public X unwrap(T id, Class aClass, WrapperOptions wrapperOptions) { + var javaTypeClass = getJavaTypeClass(); + + if (id == null) { + return null; + } + if (javaTypeClass.isAssignableFrom(aClass)) { + return (X) id; + } + if (Double.class.isAssignableFrom(aClass)) { + return (X) id.value(); + } + throw unknownUnwrap(aClass); + } + + @SneakyThrows({InstantiationException.class, IllegalAccessException.class, InvocationTargetException.class}) + @Override + public T wrap(X value, WrapperOptions wrapperOptions) { + var clazz = getJavaTypeClass(); + + if (value == null) { + return null; + } + if (clazz.isInstance(value)) { + return (T) value; + } + if (value instanceof Double doubleValue) { + return (T) clazz.getConstructors()[0].newInstance(doubleValue); + } + if (value instanceof String stringValue) { + return (T) clazz.getConstructors()[0].newInstance(Double.parseDouble(stringValue)); + } + throw unknownWrap(value.getClass()); + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedFloatJavaType.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedFloatJavaType.java new file mode 100644 index 0000000..513079b --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedFloatJavaType.java @@ -0,0 +1,60 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype.base; + +import it.aboutbits.springboot.toolbox.type.CustomType; +import lombok.SneakyThrows; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.AbstractClassJavaType; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; + +import java.lang.reflect.InvocationTargetException; +import java.sql.Types; + +public abstract class WrappedFloatJavaType> extends AbstractClassJavaType { + protected WrappedFloatJavaType(Class type) { + super(type); + } + + @Override + public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) { + return indicators.getTypeConfiguration() + .getJdbcTypeRegistry() + .getDescriptor(Types.DOUBLE); + } + + @Override + public X unwrap(T id, Class aClass, WrapperOptions wrapperOptions) { + var javaTypeClass = getJavaTypeClass(); + + if (id == null) { + return null; + } + if (javaTypeClass.isAssignableFrom(aClass)) { + return (X) id; + } + if (Float.class.isAssignableFrom(aClass)) { + return (X) id.value(); + } + throw unknownUnwrap(aClass); + } + + @SneakyThrows({InstantiationException.class, IllegalAccessException.class, InvocationTargetException.class}) + @Override + public T wrap(X value, WrapperOptions wrapperOptions) { + var clazz = getJavaTypeClass(); + + if (value == null) { + return null; + } + if (clazz.isInstance(value)) { + return (T) value; + } + if (value instanceof Float floatValue) { + return (T) clazz.getConstructors()[0].newInstance(floatValue); + } + if (value instanceof String stringValue) { + return (T) clazz.getConstructors()[0].newInstance(Float.parseFloat(stringValue)); + } + throw unknownWrap(value.getClass()); + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedIntegerJavaType.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedIntegerJavaType.java new file mode 100644 index 0000000..3b53e55 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedIntegerJavaType.java @@ -0,0 +1,60 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype.base; + +import it.aboutbits.springboot.toolbox.type.CustomType; +import lombok.SneakyThrows; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.AbstractClassJavaType; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; + +import java.lang.reflect.InvocationTargetException; +import java.sql.Types; + +public abstract class WrappedIntegerJavaType> extends AbstractClassJavaType { + protected WrappedIntegerJavaType(Class type) { + super(type); + } + + @Override + public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) { + return indicators.getTypeConfiguration() + .getJdbcTypeRegistry() + .getDescriptor(Types.BIGINT); + } + + @Override + public X unwrap(T id, Class aClass, WrapperOptions wrapperOptions) { + var javaTypeClass = getJavaTypeClass(); + + if (id == null) { + return null; + } + if (javaTypeClass.isAssignableFrom(aClass)) { + return (X) id; + } + if (Integer.class.isAssignableFrom(aClass)) { + return (X) id.value(); + } + throw unknownUnwrap(aClass); + } + + @SneakyThrows({InstantiationException.class, IllegalAccessException.class, InvocationTargetException.class}) + @Override + public T wrap(X value, WrapperOptions wrapperOptions) { + var clazz = getJavaTypeClass(); + + if (value == null) { + return null; + } + if (clazz.isInstance(value)) { + return (T) value; + } + if (value instanceof Integer integerValue) { + return (T) clazz.getConstructors()[0].newInstance(integerValue); + } + if (value instanceof String stringValue) { + return (T) clazz.getConstructors()[0].newInstance(Integer.parseInt(stringValue)); + } + throw unknownWrap(value.getClass()); + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/WrappedLongJavaType.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedLongJavaType.java similarity index 88% rename from src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/WrappedLongJavaType.java rename to src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedLongJavaType.java index 46b4382..efc5288 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/WrappedLongJavaType.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedLongJavaType.java @@ -1,6 +1,6 @@ -package it.aboutbits.springboot.toolbox.persistence.javatype; +package it.aboutbits.springboot.toolbox.persistence.javatype.base; -import it.aboutbits.springboot.toolbox.persistence.WrappedValue; +import it.aboutbits.springboot.toolbox.type.CustomType; import lombok.SneakyThrows; import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.java.AbstractClassJavaType; @@ -10,7 +10,7 @@ import java.lang.reflect.InvocationTargetException; import java.sql.Types; -public abstract class WrappedLongJavaType> extends AbstractClassJavaType { +public abstract class WrappedLongJavaType> extends AbstractClassJavaType { protected WrappedLongJavaType(Class type) { super(type); } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedScaledBigDecimalJavaType.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedScaledBigDecimalJavaType.java new file mode 100644 index 0000000..9472cb0 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedScaledBigDecimalJavaType.java @@ -0,0 +1,62 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype.base; + +import it.aboutbits.springboot.toolbox.type.CustomType; +import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; +import lombok.SneakyThrows; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.AbstractClassJavaType; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; + +import java.lang.reflect.InvocationTargetException; +import java.math.BigDecimal; +import java.sql.Types; + +public abstract class WrappedScaledBigDecimalJavaType> extends AbstractClassJavaType { + protected WrappedScaledBigDecimalJavaType(Class type) { + super(type); + } + + @Override + public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) { + return indicators.getTypeConfiguration() + .getJdbcTypeRegistry() + .getDescriptor(Types.DOUBLE); + } + + @Override + public X unwrap(T id, Class aClass, WrapperOptions wrapperOptions) { + var javaTypeClass = getJavaTypeClass(); + + if (id == null) { + return null; + } + if (javaTypeClass.isAssignableFrom(aClass)) { + return (X) id; + } + if (ScaledBigDecimal.class.isAssignableFrom(aClass)) { + return (X) id.value(); + } + throw unknownUnwrap(aClass); + } + + @SneakyThrows({InstantiationException.class, IllegalAccessException.class, InvocationTargetException.class}) + @Override + public T wrap(X value, WrapperOptions wrapperOptions) { + var clazz = getJavaTypeClass(); + + if (value == null) { + return null; + } + if (clazz.isInstance(value)) { + return (T) value; + } + if (value instanceof ScaledBigDecimal scaledBigDecimalValue) { + return (T) clazz.getConstructors()[0].newInstance(scaledBigDecimalValue); + } + if (value instanceof String stringValue) { + return (T) clazz.getConstructors()[0].newInstance(new ScaledBigDecimal(stringValue)); + } + throw unknownWrap(value.getClass()); + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedShortJavaType.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedShortJavaType.java new file mode 100644 index 0000000..757bc3e --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedShortJavaType.java @@ -0,0 +1,60 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype.base; + +import it.aboutbits.springboot.toolbox.type.CustomType; +import lombok.SneakyThrows; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.AbstractClassJavaType; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; + +import java.lang.reflect.InvocationTargetException; +import java.sql.Types; + +public abstract class WrappedShortJavaType> extends AbstractClassJavaType { + protected WrappedShortJavaType(Class type) { + super(type); + } + + @Override + public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) { + return indicators.getTypeConfiguration() + .getJdbcTypeRegistry() + .getDescriptor(Types.BIGINT); + } + + @Override + public X unwrap(T id, Class aClass, WrapperOptions wrapperOptions) { + var javaTypeClass = getJavaTypeClass(); + + if (id == null) { + return null; + } + if (javaTypeClass.isAssignableFrom(aClass)) { + return (X) id; + } + if (Short.class.isAssignableFrom(aClass)) { + return (X) id.value(); + } + throw unknownUnwrap(aClass); + } + + @SneakyThrows({InstantiationException.class, IllegalAccessException.class, InvocationTargetException.class}) + @Override + public T wrap(X value, WrapperOptions wrapperOptions) { + var clazz = getJavaTypeClass(); + + if (value == null) { + return null; + } + if (clazz.isInstance(value)) { + return (T) value; + } + if (value instanceof Short shortValue) { + return (T) clazz.getConstructors()[0].newInstance(shortValue); + } + if (value instanceof String stringValue) { + return (T) clazz.getConstructors()[0].newInstance(Short.parseShort(stringValue)); + } + throw unknownWrap(value.getClass()); + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/WrappedStringJavaType.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedStringJavaType.java similarity index 87% rename from src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/WrappedStringJavaType.java rename to src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedStringJavaType.java index ce6bb2d..32eea0f 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/WrappedStringJavaType.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedStringJavaType.java @@ -1,6 +1,6 @@ -package it.aboutbits.springboot.toolbox.persistence.javatype; +package it.aboutbits.springboot.toolbox.persistence.javatype.base; -import it.aboutbits.springboot.toolbox.persistence.WrappedValue; +import it.aboutbits.springboot.toolbox.type.CustomType; import lombok.SneakyThrows; import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.java.AbstractClassJavaType; @@ -10,7 +10,7 @@ import java.lang.reflect.InvocationTargetException; import java.sql.Types; -public abstract class WrappedStringJavaType> extends AbstractClassJavaType { +public abstract class WrappedStringJavaType> extends AbstractClassJavaType { protected WrappedStringJavaType(Class type) { super(type); } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/type/ToolboxTypeConfig.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/type/ToolboxTypeConfig.java deleted file mode 100644 index 53bb8e2..0000000 --- a/src/main/java/it/aboutbits/springboot/toolbox/persistence/type/ToolboxTypeConfig.java +++ /dev/null @@ -1,12 +0,0 @@ -package it.aboutbits.springboot.toolbox.persistence.type; - -import org.hibernate.annotations.JavaTypeRegistrations; - -@JavaTypeRegistrations({ - /*@JavaTypeRegistration( - javaType = ID.class, - descriptorClass = IdJavaType.class - )*/ -}) -public abstract class ToolboxTypeConfig { -} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/reflection/util/RecordReflectionUtil.java b/src/main/java/it/aboutbits/springboot/toolbox/reflection/util/RecordReflectionUtil.java new file mode 100644 index 0000000..38e721a --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/reflection/util/RecordReflectionUtil.java @@ -0,0 +1,61 @@ +package it.aboutbits.springboot.toolbox.reflection.util; + +import java.lang.reflect.Constructor; + +public final class RecordReflectionUtil { + private RecordReflectionUtil() { + } + + @SuppressWarnings("unchecked") + public static Constructor getCanonicalConstructor(Class recordClass) { + if (!recordClass.isRecord()) { + throw new IllegalArgumentException("Class must be a record: " + recordClass.getName()); + } + + var constructors = recordClass.getDeclaredConstructors(); + + for (var constructor : constructors) { + if (parametersAreEqual(recordClass, constructor)) { + return (Constructor) constructor; + } + } + + throw new IllegalStateException("Canonical constructor not found for the record: " + recordClass.getName()); + } + + @SuppressWarnings("unchecked") + public static Constructor getConstructorForType( + Class recordClass, + Class type + ) { + if (!recordClass.isRecord()) { + throw new IllegalArgumentException("Class must be a record: " + recordClass.getName()); + } + + var constructors = recordClass.getDeclaredConstructors(); + + for (var constructor : constructors) { + if (constructor.getParameterCount() == 1 && constructor.getParameterTypes()[0].equals(type)) { + return (Constructor) constructor; + } + } + + throw new IllegalStateException("No constructor for type (" + type.getName() + ") found for the record: " + recordClass.getName()); + } + + private static boolean parametersAreEqual(Class recordClass, Constructor constructor) { + var recordComponents = recordClass.getRecordComponents(); + var parameterTypes = constructor.getParameterTypes(); + + if (recordComponents.length != parameterTypes.length) { + return false; + } + + for (int i = 0; i < recordComponents.length; i++) { + if (!parameterTypes[i].equals(recordComponents[i].getType())) { + return false; + } + } + return true; + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/type/CustomType.java b/src/main/java/it/aboutbits/springboot/toolbox/type/CustomType.java new file mode 100644 index 0000000..fd9e12e --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/type/CustomType.java @@ -0,0 +1,5 @@ +package it.aboutbits.springboot.toolbox.type; + +public interface CustomType { + T value(); +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/type/EmailAddress.java b/src/main/java/it/aboutbits/springboot/toolbox/type/EmailAddress.java index 4c2869b..78245aa 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/type/EmailAddress.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/type/EmailAddress.java @@ -15,7 +15,7 @@ * @param value the email address string * @throws IllegalArgumentException if the provided email address is not in a valid format */ -public record EmailAddress(String value) implements Comparable { +public record EmailAddress(String value) implements CustomType, Comparable { public EmailAddress(String value) { if (value == null || EmailAddressValidator.isNotValid(value)) { throw new IllegalArgumentException("Value is not a valid email address: " + value); diff --git a/src/main/java/it/aboutbits/springboot/toolbox/type/Iban.java b/src/main/java/it/aboutbits/springboot/toolbox/type/Iban.java index 0098da5..a180661 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/type/Iban.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/type/Iban.java @@ -11,7 +11,7 @@ * * @param value the IBAN value, which must be a valid and non-null string */ -public record Iban(String value) implements Comparable { +public record Iban(String value) implements CustomType, Comparable { public Iban(String value) { if (value == null || IbanValidator.isNotValid(value.toUpperCase())) { throw new IllegalArgumentException("Value is not a valid IBAN: " + value); diff --git a/src/main/java/it/aboutbits/springboot/toolbox/type/ScaledBigDecimal.java b/src/main/java/it/aboutbits/springboot/toolbox/type/ScaledBigDecimal.java index 3ffc5ec..8256bee 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/type/ScaledBigDecimal.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/type/ScaledBigDecimal.java @@ -14,7 +14,7 @@ */ public record ScaledBigDecimal( @NonNull BigDecimal value -) implements Comparable { +) implements CustomType, Comparable { private static final MathContext MATH_CONTEXT = new MathContext(15, RoundingMode.HALF_UP); public static final ScaledBigDecimal ZERO = new ScaledBigDecimal(0); diff --git a/src/main/java/it/aboutbits/springboot/toolbox/type/jackson/CustomTypeDeserializer.java b/src/main/java/it/aboutbits/springboot/toolbox/type/jackson/CustomTypeDeserializer.java new file mode 100644 index 0000000..170d237 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/type/jackson/CustomTypeDeserializer.java @@ -0,0 +1,161 @@ +package it.aboutbits.springboot.toolbox.type.jackson; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil; +import it.aboutbits.springboot.toolbox.type.CustomType; +import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.math.BigDecimal; +import java.util.function.Function; + +public class CustomTypeDeserializer> extends JsonDeserializer { + private final Class customType; + private final Constructor constructor; + private final Function typeConverter; + + public CustomTypeDeserializer(Class customType) { + this.customType = customType; + + this.constructor = RecordReflectionUtil.getCanonicalConstructor(customType); + + this.typeConverter = getTypeConverter( + constructor.getParameterTypes()[0] + ); + } + + @Override + public Class handledType() { + return customType; + } + + @Override + public T deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { + var value = typeConverter.apply(jsonParser); + + try { + return constructor.newInstance(value); + } catch ( + IllegalAccessException + | InvocationTargetException + | InstantiationException e) { + throw new IOException(e); + } + } + + private static Function getTypeConverter(Class wrappedType) { + if (String.class.isAssignableFrom(wrappedType)) { + return getStringConverter(); + } else if (Short.class.isAssignableFrom(wrappedType)) { + return getShortConverter(); + } else if (Integer.class.isAssignableFrom(wrappedType)) { + return getIntegerConverter(); + } else if (Long.class.isAssignableFrom(wrappedType)) { + return getLongConverter(); + } else if (Float.class.isAssignableFrom(wrappedType)) { + return getFloatConverter(); + } else if (Double.class.isAssignableFrom(wrappedType)) { + return getDoubleConverter(); + } else if (BigDecimal.class.isAssignableFrom(wrappedType)) { + return getBigDecimalConverter(); + } else if (ScaledBigDecimal.class.isAssignableFrom(wrappedType)) { + return getScaledBigDecimalConverter(); + } else { + throw new CustomTypeDeserializerException("Value type not supported: " + wrappedType.getName()); + } + } + + private static Function getScaledBigDecimalConverter() { + return jsonParser -> { + try { + return new ScaledBigDecimal(jsonParser.getDecimalValue()); + } catch (IOException e) { + throw new CustomTypeDeserializerException("Failed to read value as ScaledBigDecimal.", e); + } + }; + } + + private static Function getBigDecimalConverter() { + return jsonParser -> { + try { + return jsonParser.getDecimalValue(); + } catch (IOException e) { + throw new CustomTypeDeserializerException("Failed to read value as BigDecimal.", e); + } + }; + } + + private static Function getDoubleConverter() { + return jsonParser -> { + try { + return jsonParser.getDoubleValue(); + } catch (IOException e) { + throw new CustomTypeDeserializerException("Failed to read value as Double.", e); + } + }; + } + + private static Function getFloatConverter() { + return jsonParser -> { + try { + return jsonParser.getFloatValue(); + } catch (IOException e) { + throw new CustomTypeDeserializerException("Failed to read value as Float.", e); + } + }; + } + + private static Function getLongConverter() { + return jsonParser -> { + try { + return jsonParser.getLongValue(); + } catch (IOException e) { + throw new CustomTypeDeserializerException("Failed to read value as Long.", e); + } + }; + } + + private static Function getIntegerConverter() { + return jsonParser -> { + try { + return jsonParser.getIntValue(); + } catch (IOException e) { + throw new CustomTypeDeserializerException("Failed to read value as Integer.", e); + } + }; + } + + private static Function getShortConverter() { + return jsonParser -> { + try { + return jsonParser.getShortValue(); + } catch (IOException e) { + throw new CustomTypeDeserializerException("Failed to read value as Short.", e); + } + }; + } + + private static Function getStringConverter() { + return jsonParser -> { + try { + return jsonParser.getValueAsString(); + } catch (IOException e) { + throw new CustomTypeDeserializerException("Failed to read value as String.", e); + } + }; + } + + public static final class CustomTypeDeserializerException extends RuntimeException { + public CustomTypeDeserializerException(String message) { + super(message); + } + + public CustomTypeDeserializerException(String message, Throwable cause) { + super(message, cause); + } + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/type/jackson/CustomTypeSerializer.java b/src/main/java/it/aboutbits/springboot/toolbox/type/jackson/CustomTypeSerializer.java new file mode 100644 index 0000000..4101800 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/type/jackson/CustomTypeSerializer.java @@ -0,0 +1,38 @@ +package it.aboutbits.springboot.toolbox.type.jackson; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import it.aboutbits.springboot.toolbox.type.CustomType; + +import java.io.IOException; + +public class CustomTypeSerializer extends JsonSerializer> { + @SuppressWarnings("unchecked") + @Override + public Class> handledType() { + return (Class>) (Class) CustomType.class; + } + + @Override + public void serialize( + CustomType customType, + JsonGenerator jsonGenerator, + SerializerProvider serializerProvider + ) throws IOException { + var value = customType.value(); + + if (value instanceof String stringValue) { + jsonGenerator.writeString( + stringValue + ); + return; + } + + jsonGenerator.writeRawValue( + String.valueOf( + value + ) + ); + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/type/mvc/CustomTypePropertyEditor.java b/src/main/java/it/aboutbits/springboot/toolbox/type/mvc/CustomTypePropertyEditor.java new file mode 100644 index 0000000..093eddd --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/type/mvc/CustomTypePropertyEditor.java @@ -0,0 +1,74 @@ +package it.aboutbits.springboot.toolbox.type.mvc; + +import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil; +import it.aboutbits.springboot.toolbox.type.CustomType; +import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; +import lombok.NonNull; +import lombok.SneakyThrows; +import org.springframework.lang.Nullable; + +import java.beans.PropertyEditorSupport; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.math.BigDecimal; +import java.util.function.Function; + +public final class CustomTypePropertyEditor> extends PropertyEditorSupport { + private final Constructor constructor; + private final Function typeConverter; + + @SneakyThrows + public CustomTypePropertyEditor(@NonNull Class customType) { + this.constructor = RecordReflectionUtil.getCanonicalConstructor(customType); + this.typeConverter = getTextToTypeConverter( + constructor.getParameters()[0].getType() + ); + } + + @SuppressWarnings("unchecked") + @Override + @Nullable + public String getAsText() { + var value = (T) getValue(); + + return value == null ? null : String.valueOf(value.value()); + } + + @Override + public void setAsText(String text) throws IllegalArgumentException { + var value = typeConverter.apply(text); + + try { + setValue( + constructor.newInstance(value) + ); + } catch ( + IllegalAccessException + | InvocationTargetException + | InstantiationException e) { + throw new IllegalArgumentException(e); + } + } + + private static Function getTextToTypeConverter(Class wrappedType) { + if (String.class.isAssignableFrom(wrappedType)) { + return text -> text; + } else if (Short.class.isAssignableFrom(wrappedType)) { + return Short::parseShort; + } else if (Integer.class.isAssignableFrom(wrappedType)) { + return Integer::parseInt; + } else if (Long.class.isAssignableFrom(wrappedType)) { + return Long::parseLong; + } else if (Float.class.isAssignableFrom(wrappedType)) { + return Float::parseFloat; + } else if (Double.class.isAssignableFrom(wrappedType)) { + return Double::parseDouble; + } else if (BigDecimal.class.isAssignableFrom(wrappedType)) { + return BigDecimal::new; + } else if (ScaledBigDecimal.class.isAssignableFrom(wrappedType)) { + return ScaledBigDecimal::new; + } else { + throw new IllegalArgumentException("Unable to convert text to type: " + wrappedType.getName()); + } + } +} From d243cef47746aca94605ba47c44dfc3b05e0ca8a Mon Sep 17 00:00:00 2001 From: Andreas Hufler Date: Thu, 5 Sep 2024 14:06:17 +0200 Subject: [PATCH 3/9] replace old vulnerable lib with new version --- pom.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pom.xml b/pom.xml index 5d1303f..700bb1b 100644 --- a/pom.xml +++ b/pom.xml @@ -58,6 +58,10 @@ commons-validator 1.9.0 + + commons-collections + commons-collections + commons-logging commons-logging @@ -65,6 +69,14 @@ + + + org.apache.commons + commons-collections4 + 4.4 + + + org.springframework.boot From cb40ddfb03b5c91cfcbe362b0733fcb9f6c39ce7 Mon Sep 17 00:00:00 2001 From: Andreas Hufler Date: Thu, 5 Sep 2024 15:01:55 +0200 Subject: [PATCH 4/9] add swagger customizers --- pom.xml | 14 ++- .../boot/type/RegisterCustomTypes.java | 12 +- ...nericSingleValueWrapperModelConverter.java | 57 +++++++++ ...cSingleValueWrapperPropertyCustomizer.java | 93 +++++++++++++++ .../type/swagger/IdentityModelConverter.java | 56 +++++++++ .../swagger/IdentityPropertyCustomizer.java | 108 ++++++++++++++++++ .../identity/{Identity.java => EntityId.java} | 2 +- .../persistence/identity/Identified.java | 2 +- .../type/jackson/CustomTypeDeserializer.java | 24 ++-- .../type/mvc/CustomTypePropertyEditor.java | 24 ++-- 10 files changed, 367 insertions(+), 25 deletions(-) create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/GenericSingleValueWrapperModelConverter.java create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/GenericSingleValueWrapperPropertyCustomizer.java create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/IdentityModelConverter.java create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/IdentityPropertyCustomizer.java rename src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/{Identity.java => EntityId.java} (85%) diff --git a/pom.xml b/pom.xml index 700bb1b..b9c2daf 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.springframework.boot spring-boot-starter-parent - 3.3.2 + 3.3.3 @@ -76,6 +76,12 @@ 4.4 + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + 2.6.0 + @@ -89,7 +95,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.11.0 + 3.13.0 ${java.version} ${java.version} @@ -98,7 +104,7 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.2.1 + 3.5.0 checkstyle.xml @@ -122,7 +128,7 @@ com.puppycrawl.tools checkstyle - 10.3.4 + 10.18.1 diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/RegisterCustomTypes.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/RegisterCustomTypes.java index 2f2754a..a2884ce 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/RegisterCustomTypes.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/RegisterCustomTypes.java @@ -1,5 +1,9 @@ package it.aboutbits.springboot.toolbox.boot.type; +import it.aboutbits.springboot.toolbox.boot.type.swagger.GenericSingleValueWrapperModelConverter; +import it.aboutbits.springboot.toolbox.boot.type.swagger.GenericSingleValueWrapperPropertyCustomizer; +import it.aboutbits.springboot.toolbox.boot.type.swagger.IdentityModelConverter; +import it.aboutbits.springboot.toolbox.boot.type.swagger.IdentityPropertyCustomizer; import org.springframework.context.annotation.Import; import java.lang.annotation.ElementType; @@ -9,7 +13,13 @@ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) -@Import({CustomTypeConfigurationRegistrar.class}) +@Import({ + GenericSingleValueWrapperModelConverter.class, + GenericSingleValueWrapperPropertyCustomizer.class, + IdentityModelConverter.class, + IdentityPropertyCustomizer.class, + CustomTypeConfigurationRegistrar.class +}) public @interface RegisterCustomTypes { String[] additionalTypePackages() default ""; } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/GenericSingleValueWrapperModelConverter.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/GenericSingleValueWrapperModelConverter.java new file mode 100644 index 0000000..cdfcbe0 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/GenericSingleValueWrapperModelConverter.java @@ -0,0 +1,57 @@ +package it.aboutbits.springboot.toolbox.boot.type.swagger; + +import io.swagger.v3.core.converter.AnnotatedType; +import io.swagger.v3.core.converter.ModelConverter; +import io.swagger.v3.core.converter.ModelConverterContext; +import io.swagger.v3.oas.models.media.Schema; +import it.aboutbits.springboot.toolbox.persistence.identity.EntityId; +import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil; +import it.aboutbits.springboot.toolbox.type.CustomType; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.util.Iterator; + +@Component +public class GenericSingleValueWrapperModelConverter implements ModelConverter { + + @Override + public Schema resolve( + AnnotatedType annotatedType, + ModelConverterContext context, + Iterator chain + ) { + + var type = annotatedType.getType(); + + if (type instanceof Class clazz && CustomType.class.isAssignableFrom(clazz) && !EntityId.class.isAssignableFrom( + clazz)) { + var constructor = RecordReflectionUtil.getCanonicalConstructor(clazz); + var wrappedType = constructor.getParameters()[0].getType(); + + if (Short.class.isAssignableFrom(wrappedType)) { + return context.resolve(new AnnotatedType(Short.TYPE)); + } + if (Integer.class.isAssignableFrom(wrappedType)) { + return context.resolve(new AnnotatedType(Integer.TYPE)); + } + if (Long.class.isAssignableFrom(wrappedType)) { + return context.resolve(new AnnotatedType(Long.TYPE)); + } + if (Float.class.isAssignableFrom(wrappedType)) { + return context.resolve(new AnnotatedType(Float.TYPE)); + } + if (Double.class.isAssignableFrom(wrappedType)) { + return context.resolve(new AnnotatedType(Double.TYPE)); + } + if (BigDecimal.class.isAssignableFrom(wrappedType)) { + return context.resolve(new AnnotatedType(Double.TYPE)); + } + if (String.class.isAssignableFrom(wrappedType)) { + return context.resolve(new AnnotatedType(String.class)); + } + } + + return chain.hasNext() ? chain.next().resolve(annotatedType, context, chain) : null; + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/GenericSingleValueWrapperPropertyCustomizer.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/GenericSingleValueWrapperPropertyCustomizer.java new file mode 100644 index 0000000..9a9b6f6 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/GenericSingleValueWrapperPropertyCustomizer.java @@ -0,0 +1,93 @@ +package it.aboutbits.springboot.toolbox.boot.type.swagger; + +import com.fasterxml.jackson.databind.type.SimpleType; +import io.swagger.v3.core.converter.AnnotatedType; +import io.swagger.v3.oas.models.media.Schema; +import it.aboutbits.springboot.toolbox.persistence.identity.EntityId; +import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil; +import it.aboutbits.springboot.toolbox.type.CustomType; +import lombok.extern.slf4j.Slf4j; +import org.springdoc.core.customizers.PropertyCustomizer; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; + +@Slf4j +@Component +public class GenericSingleValueWrapperPropertyCustomizer implements PropertyCustomizer { + @Override + public Schema customize(Schema property, AnnotatedType annotatedType) { + var type = annotatedType.getType(); + + if (type instanceof SimpleType simpleType && CustomType.class.isAssignableFrom(simpleType.getRawClass()) && !EntityId.class.isAssignableFrom( + simpleType.getRawClass())) { + var rawClass = simpleType.getRawClass(); + + var displayName = rawClass.getSimpleName(); + + var constructor = RecordReflectionUtil.getCanonicalConstructor(rawClass); + var wrappedType = constructor.getParameters()[0].getType(); + + + if (Short.class.isAssignableFrom(wrappedType)) { + property.type("integer"); + property.format(""); + property.setDescription(displayName); + property.setProperties(null); + property.set$ref(null); + return property; + } + if (Integer.class.isAssignableFrom(wrappedType)) { + property.type("integer"); + property.format("int32"); + property.setDescription(displayName); + property.setProperties(null); + property.set$ref(null); + return property; + } + if (Long.class.isAssignableFrom(wrappedType)) { + property.type("integer"); + property.format("int64"); + property.setDescription(displayName); + property.setProperties(null); + property.set$ref(null); + return property; + } + if (Float.class.isAssignableFrom(wrappedType)) { + property.type("number"); + property.format("float"); + property.setDescription(displayName); + property.setProperties(null); + property.set$ref(null); + return property; + } + if (Double.class.isAssignableFrom(wrappedType)) { + property.type("number"); + property.format("double"); + property.setDescription(displayName); + property.setProperties(null); + property.set$ref(null); + return property; + } + if (BigDecimal.class.isAssignableFrom(wrappedType)) { + property.type("number"); + property.format(""); + property.setDescription(displayName); + property.setProperties(null); + property.set$ref(null); + return property; + } + if (String.class.isAssignableFrom(wrappedType)) { + property.type("string"); + property.format(null); + property.setDescription(displayName); + property.setProperties(null); + property.set$ref(null); + return property; + } + log.warn("Property {} of type WrappedValue: Can not resolve parameter type!", property.getName()); + } + + return property; + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/IdentityModelConverter.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/IdentityModelConverter.java new file mode 100644 index 0000000..7d81fc7 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/IdentityModelConverter.java @@ -0,0 +1,56 @@ +package it.aboutbits.springboot.toolbox.boot.type.swagger; + +import io.swagger.v3.core.converter.AnnotatedType; +import io.swagger.v3.core.converter.ModelConverter; +import io.swagger.v3.core.converter.ModelConverterContext; +import io.swagger.v3.oas.models.media.Schema; +import it.aboutbits.springboot.toolbox.persistence.identity.EntityId; +import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.util.Iterator; + +@Component +public class IdentityModelConverter implements ModelConverter { + + @Override + public Schema resolve( + AnnotatedType annotatedType, + ModelConverterContext context, + Iterator chain + ) { + + var type = annotatedType.getType(); + + if (type instanceof Class clazz && EntityId.class.isAssignableFrom(clazz)) { + var constructor = RecordReflectionUtil.getCanonicalConstructor(clazz); + + var wrappedType = constructor.getParameters()[0].getType(); + + if (Short.class.isAssignableFrom(wrappedType)) { + return context.resolve(new AnnotatedType(Short.TYPE)); + } + if (Integer.class.isAssignableFrom(wrappedType)) { + return context.resolve(new AnnotatedType(Integer.TYPE)); + } + if (Long.class.isAssignableFrom(wrappedType)) { + return context.resolve(new AnnotatedType(Long.TYPE)); + } + if (Float.class.isAssignableFrom(wrappedType)) { + return context.resolve(new AnnotatedType(Float.TYPE)); + } + if (Double.class.isAssignableFrom(wrappedType)) { + return context.resolve(new AnnotatedType(Double.TYPE)); + } + if (BigDecimal.class.isAssignableFrom(wrappedType)) { + return context.resolve(new AnnotatedType(Double.TYPE)); + } + if (String.class.isAssignableFrom(wrappedType)) { + return context.resolve(new AnnotatedType(String.class)); + } + } + + return chain.hasNext() ? chain.next().resolve(annotatedType, context, chain) : null; + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/IdentityPropertyCustomizer.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/IdentityPropertyCustomizer.java new file mode 100644 index 0000000..62b5e65 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/IdentityPropertyCustomizer.java @@ -0,0 +1,108 @@ +package it.aboutbits.springboot.toolbox.boot.type.swagger; + +import com.fasterxml.jackson.databind.type.SimpleType; +import io.swagger.v3.core.converter.AnnotatedType; +import io.swagger.v3.oas.models.media.Schema; +import it.aboutbits.springboot.toolbox.persistence.identity.EntityId; +import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; +import org.springdoc.core.customizers.PropertyCustomizer; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; + +@Slf4j +@Component +public class IdentityPropertyCustomizer implements PropertyCustomizer { + @Override + public Schema customize(Schema property, AnnotatedType annotatedType) { + var type = annotatedType.getType(); + + if (type instanceof SimpleType simpleType && EntityId.class.isAssignableFrom(simpleType.getRawClass())) { + String displayName = null; + + var bindings = simpleType.getBindings(); + var boundType = bindings.getBoundType(0); + + if (boundType == null) { + var rawClass = simpleType.getRawClass(); + displayName = resolveDisplayName(rawClass); + } + + var constructor = RecordReflectionUtil.getCanonicalConstructor(simpleType.getRawClass()); + + var wrappedType = constructor.getParameters()[0].getType(); + + + if (Short.class.isAssignableFrom(wrappedType)) { + property.type("integer"); + property.format(""); + property.setDescription(displayName); + property.setProperties(null); + property.set$ref(null); + return property; + } + if (Integer.class.isAssignableFrom(wrappedType)) { + property.type("integer"); + property.format("int32"); + property.setDescription(displayName); + property.setProperties(null); + property.set$ref(null); + return property; + } + if (Long.class.isAssignableFrom(wrappedType)) { + property.type("integer"); + property.format("int64"); + property.setDescription(displayName); + property.setProperties(null); + property.set$ref(null); + return property; + } + if (Float.class.isAssignableFrom(wrappedType)) { + property.type("number"); + property.format("float"); + property.setDescription(displayName); + property.setProperties(null); + property.set$ref(null); + return property; + } + if (Double.class.isAssignableFrom(wrappedType)) { + property.type("number"); + property.format("double"); + property.setDescription(displayName); + property.setProperties(null); + property.set$ref(null); + return property; + } + if (BigDecimal.class.isAssignableFrom(wrappedType)) { + property.type("number"); + property.format(""); + property.setDescription(displayName); + property.setProperties(null); + property.set$ref(null); + return property; + } + if (String.class.isAssignableFrom(wrappedType)) { + property.type("string"); + property.format(null); + property.setDescription(displayName); + property.setProperties(null); + property.set$ref(null); + return property; + } + log.warn("Property {} of type EntityId: Can not resolve parameter type!", property.getName()); + } + + return property; + } + + @NonNull + private static String resolveDisplayName(Class rawClass) { + var parent = rawClass.getEnclosingClass(); + if (parent != null) { + return parent.getSimpleName() + "." + rawClass.getSimpleName(); + } + return rawClass.getSimpleName(); + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/Identity.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/EntityId.java similarity index 85% rename from src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/Identity.java rename to src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/EntityId.java index 0d666b1..1d89385 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/Identity.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/EntityId.java @@ -10,5 +10,5 @@ * * @param the type of the value being wrapped */ -public interface Identity extends CustomType, Serializable { +public interface EntityId extends CustomType, Serializable { } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/Identified.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/Identified.java index 3743d96..caeb5d9 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/Identified.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/Identified.java @@ -1,5 +1,5 @@ package it.aboutbits.springboot.toolbox.persistence.identity; -public interface Identified> { +public interface Identified> { ID getId(); } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/type/jackson/CustomTypeDeserializer.java b/src/main/java/it/aboutbits/springboot/toolbox/type/jackson/CustomTypeDeserializer.java index 170d237..b405351 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/type/jackson/CustomTypeDeserializer.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/type/jackson/CustomTypeDeserializer.java @@ -50,23 +50,29 @@ public T deserialize(JsonParser jsonParser, DeserializationContext deserializati private static Function getTypeConverter(Class wrappedType) { if (String.class.isAssignableFrom(wrappedType)) { return getStringConverter(); - } else if (Short.class.isAssignableFrom(wrappedType)) { + } + if (Short.class.isAssignableFrom(wrappedType)) { return getShortConverter(); - } else if (Integer.class.isAssignableFrom(wrappedType)) { + } + if (Integer.class.isAssignableFrom(wrappedType)) { return getIntegerConverter(); - } else if (Long.class.isAssignableFrom(wrappedType)) { + } + if (Long.class.isAssignableFrom(wrappedType)) { return getLongConverter(); - } else if (Float.class.isAssignableFrom(wrappedType)) { + } + if (Float.class.isAssignableFrom(wrappedType)) { return getFloatConverter(); - } else if (Double.class.isAssignableFrom(wrappedType)) { + } + if (Double.class.isAssignableFrom(wrappedType)) { return getDoubleConverter(); - } else if (BigDecimal.class.isAssignableFrom(wrappedType)) { + } + if (BigDecimal.class.isAssignableFrom(wrappedType)) { return getBigDecimalConverter(); - } else if (ScaledBigDecimal.class.isAssignableFrom(wrappedType)) { + } + if (ScaledBigDecimal.class.isAssignableFrom(wrappedType)) { return getScaledBigDecimalConverter(); - } else { - throw new CustomTypeDeserializerException("Value type not supported: " + wrappedType.getName()); } + throw new CustomTypeDeserializerException("Value type not supported: " + wrappedType.getName()); } private static Function getScaledBigDecimalConverter() { diff --git a/src/main/java/it/aboutbits/springboot/toolbox/type/mvc/CustomTypePropertyEditor.java b/src/main/java/it/aboutbits/springboot/toolbox/type/mvc/CustomTypePropertyEditor.java index 093eddd..6310057 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/type/mvc/CustomTypePropertyEditor.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/type/mvc/CustomTypePropertyEditor.java @@ -53,22 +53,28 @@ public void setAsText(String text) throws IllegalArgumentException { private static Function getTextToTypeConverter(Class wrappedType) { if (String.class.isAssignableFrom(wrappedType)) { return text -> text; - } else if (Short.class.isAssignableFrom(wrappedType)) { + } + if (Short.class.isAssignableFrom(wrappedType)) { return Short::parseShort; - } else if (Integer.class.isAssignableFrom(wrappedType)) { + } + if (Integer.class.isAssignableFrom(wrappedType)) { return Integer::parseInt; - } else if (Long.class.isAssignableFrom(wrappedType)) { + } + if (Long.class.isAssignableFrom(wrappedType)) { return Long::parseLong; - } else if (Float.class.isAssignableFrom(wrappedType)) { + } + if (Float.class.isAssignableFrom(wrappedType)) { return Float::parseFloat; - } else if (Double.class.isAssignableFrom(wrappedType)) { + } + if (Double.class.isAssignableFrom(wrappedType)) { return Double::parseDouble; - } else if (BigDecimal.class.isAssignableFrom(wrappedType)) { + } + if (BigDecimal.class.isAssignableFrom(wrappedType)) { return BigDecimal::new; - } else if (ScaledBigDecimal.class.isAssignableFrom(wrappedType)) { + } + if (ScaledBigDecimal.class.isAssignableFrom(wrappedType)) { return ScaledBigDecimal::new; - } else { - throw new IllegalArgumentException("Unable to convert text to type: " + wrappedType.getName()); } + throw new IllegalArgumentException("Unable to convert text to type: " + wrappedType.getName()); } } From a8bb5cff198339d5c5d92cbc180e9205e66e42a1 Mon Sep 17 00:00:00 2001 From: Andreas Hufler Date: Thu, 5 Sep 2024 15:04:27 +0200 Subject: [PATCH 5/9] move swagger modifications to a dedicated annotation and rename the customizers --- .../boot/type/RegisterCustomTypes.java | 12 +----------- ...ter.java => CustomTypeModelConverter.java} | 2 +- ...java => CustomTypePropertyCustomizer.java} | 2 +- ...erter.java => EntityIdModelConverter.java} | 2 +- ...r.java => EntityIdPropertyCustomizer.java} | 2 +- .../swagger/RegisterCustomSwaggerTypes.java | 19 +++++++++++++++++++ 6 files changed, 24 insertions(+), 15 deletions(-) rename src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/{GenericSingleValueWrapperModelConverter.java => CustomTypeModelConverter.java} (96%) rename src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/{GenericSingleValueWrapperPropertyCustomizer.java => CustomTypePropertyCustomizer.java} (97%) rename src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/{IdentityModelConverter.java => EntityIdModelConverter.java} (97%) rename src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/{IdentityPropertyCustomizer.java => EntityIdPropertyCustomizer.java} (98%) create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/RegisterCustomSwaggerTypes.java diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/RegisterCustomTypes.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/RegisterCustomTypes.java index a2884ce..5e4643e 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/RegisterCustomTypes.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/RegisterCustomTypes.java @@ -1,9 +1,5 @@ package it.aboutbits.springboot.toolbox.boot.type; -import it.aboutbits.springboot.toolbox.boot.type.swagger.GenericSingleValueWrapperModelConverter; -import it.aboutbits.springboot.toolbox.boot.type.swagger.GenericSingleValueWrapperPropertyCustomizer; -import it.aboutbits.springboot.toolbox.boot.type.swagger.IdentityModelConverter; -import it.aboutbits.springboot.toolbox.boot.type.swagger.IdentityPropertyCustomizer; import org.springframework.context.annotation.Import; import java.lang.annotation.ElementType; @@ -13,13 +9,7 @@ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) -@Import({ - GenericSingleValueWrapperModelConverter.class, - GenericSingleValueWrapperPropertyCustomizer.class, - IdentityModelConverter.class, - IdentityPropertyCustomizer.class, - CustomTypeConfigurationRegistrar.class -}) +@Import(CustomTypeConfigurationRegistrar.class) public @interface RegisterCustomTypes { String[] additionalTypePackages() default ""; } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/GenericSingleValueWrapperModelConverter.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/CustomTypeModelConverter.java similarity index 96% rename from src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/GenericSingleValueWrapperModelConverter.java rename to src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/CustomTypeModelConverter.java index cdfcbe0..655b6e9 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/GenericSingleValueWrapperModelConverter.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/CustomTypeModelConverter.java @@ -13,7 +13,7 @@ import java.util.Iterator; @Component -public class GenericSingleValueWrapperModelConverter implements ModelConverter { +public class CustomTypeModelConverter implements ModelConverter { @Override public Schema resolve( diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/GenericSingleValueWrapperPropertyCustomizer.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/CustomTypePropertyCustomizer.java similarity index 97% rename from src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/GenericSingleValueWrapperPropertyCustomizer.java rename to src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/CustomTypePropertyCustomizer.java index 9a9b6f6..0e5f194 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/GenericSingleValueWrapperPropertyCustomizer.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/CustomTypePropertyCustomizer.java @@ -14,7 +14,7 @@ @Slf4j @Component -public class GenericSingleValueWrapperPropertyCustomizer implements PropertyCustomizer { +public class CustomTypePropertyCustomizer implements PropertyCustomizer { @Override public Schema customize(Schema property, AnnotatedType annotatedType) { var type = annotatedType.getType(); diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/IdentityModelConverter.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/EntityIdModelConverter.java similarity index 97% rename from src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/IdentityModelConverter.java rename to src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/EntityIdModelConverter.java index 7d81fc7..9863063 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/IdentityModelConverter.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/EntityIdModelConverter.java @@ -12,7 +12,7 @@ import java.util.Iterator; @Component -public class IdentityModelConverter implements ModelConverter { +public class EntityIdModelConverter implements ModelConverter { @Override public Schema resolve( diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/IdentityPropertyCustomizer.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/EntityIdPropertyCustomizer.java similarity index 98% rename from src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/IdentityPropertyCustomizer.java rename to src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/EntityIdPropertyCustomizer.java index 62b5e65..12367b3 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/IdentityPropertyCustomizer.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/EntityIdPropertyCustomizer.java @@ -14,7 +14,7 @@ @Slf4j @Component -public class IdentityPropertyCustomizer implements PropertyCustomizer { +public class EntityIdPropertyCustomizer implements PropertyCustomizer { @Override public Schema customize(Schema property, AnnotatedType annotatedType) { var type = annotatedType.getType(); diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/RegisterCustomSwaggerTypes.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/RegisterCustomSwaggerTypes.java new file mode 100644 index 0000000..38c0b43 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/RegisterCustomSwaggerTypes.java @@ -0,0 +1,19 @@ +package it.aboutbits.springboot.toolbox.boot.type.swagger; + +import org.springframework.context.annotation.Import; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Import({ + CustomTypeModelConverter.class, + CustomTypePropertyCustomizer.class, + EntityIdModelConverter.class, + EntityIdPropertyCustomizer.class +}) +public @interface RegisterCustomSwaggerTypes { +} From 215b2715badb69675ca1d86e88bd65757dbcbc9c Mon Sep 17 00:00:00 2001 From: Andreas Hufler Date: Fri, 6 Sep 2024 09:51:37 +0200 Subject: [PATCH 6/9] replace deprecated classscanner --- pom.xml | 11 ++--- .../AbstractCustomTypeContributor.java | 16 +++---- .../boot/type/CustomTypeConfiguration.java | 11 +++-- .../reflection/util/ClassScannerUtil.java | 42 +++++++++++++++++++ 4 files changed, 61 insertions(+), 19 deletions(-) create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/reflection/util/ClassScannerUtil.java diff --git a/pom.xml b/pom.xml index b9c2daf..68764cd 100644 --- a/pom.xml +++ b/pom.xml @@ -42,15 +42,16 @@ lombok true - - + + - org.reflections - reflections - 0.10.2 + io.github.classgraph + classgraph + 4.8.175 + diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/persistence/AbstractCustomTypeContributor.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/persistence/AbstractCustomTypeContributor.java index 0aa6944..5b6690a 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/boot/persistence/AbstractCustomTypeContributor.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/boot/persistence/AbstractCustomTypeContributor.java @@ -1,38 +1,38 @@ package it.aboutbits.springboot.toolbox.boot.persistence; +import it.aboutbits.springboot.toolbox.reflection.util.ClassScannerUtil; import lombok.SneakyThrows; import org.hibernate.boot.model.TypeContributions; import org.hibernate.boot.model.TypeContributor; import org.hibernate.service.ServiceRegistry; import org.hibernate.type.descriptor.java.JavaType; -import org.reflections.Reflections; -import org.reflections.util.ConfigurationBuilder; import java.lang.reflect.InvocationTargetException; import java.util.Set; public abstract class AbstractCustomTypeContributor implements TypeContributor { - private final Reflections reflections; + private final ClassScannerUtil.ClassScanner classScanner; protected AbstractCustomTypeContributor(String... packageNames) { - var packageToScan = new ConfigurationBuilder().forPackages(packageNames); - reflections = new Reflections(packageToScan); + classScanner = ClassScannerUtil.getScannerForPackages(packageNames); } @SneakyThrows({InstantiationException.class, IllegalAccessException.class, InvocationTargetException.class}) @Override public void contribute(TypeContributions typeContributions, ServiceRegistry serviceRegistry) { - var types = findAllTypes(); + var types = findAllRelevantTypes(); for (var type : types) { typeContributions.contributeJavaType( (JavaType) type.getConstructors()[0].newInstance() ); } + + classScanner.close(); } @SuppressWarnings("rawtypes") - private Set> findAllTypes() { - return reflections.getSubTypesOf(AutoRegisteredJavaType.class); + private Set> findAllRelevantTypes() { + return classScanner.getSubTypesOf(AutoRegisteredJavaType.class); } } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeConfiguration.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeConfiguration.java index f6b9e25..83e5550 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeConfiguration.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeConfiguration.java @@ -1,13 +1,12 @@ package it.aboutbits.springboot.toolbox.boot.type; +import it.aboutbits.springboot.toolbox.reflection.util.ClassScannerUtil; import it.aboutbits.springboot.toolbox.type.CustomType; import it.aboutbits.springboot.toolbox.type.jackson.CustomTypeDeserializer; import it.aboutbits.springboot.toolbox.type.jackson.CustomTypeSerializer; import it.aboutbits.springboot.toolbox.type.mvc.CustomTypePropertyEditor; import jakarta.annotation.PostConstruct; import lombok.extern.slf4j.Slf4j; -import org.reflections.Reflections; -import org.reflections.util.ConfigurationBuilder; import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -26,7 +25,7 @@ public class CustomTypeConfiguration { public static final String LIBRARY_BASE_PACKAGE_NAME = "it.aboutbits.springboot.toolbox"; private String[] packageNamesToScan; - private Reflections reflections; + private ClassScannerUtil.ClassScanner classScanner; public void setAdditionalTypePackages(String[] additionalTypePackages) { var tmp = new ArrayList(); @@ -35,8 +34,7 @@ public void setAdditionalTypePackages(String[] additionalTypePackages) { this.packageNamesToScan = tmp.toArray(new String[0]); - var packageToScan = new ConfigurationBuilder().forPackages(packageNamesToScan); - reflections = new Reflections(packageToScan); + classScanner = ClassScannerUtil.getScannerForPackages(packageNamesToScan); } @PostConstruct @@ -64,6 +62,7 @@ public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() { .toList() .toArray(new CustomTypeDeserializer[types.size()]); + classScanner.close(); return builder -> builder .serializers(new CustomTypeSerializer()) @@ -72,7 +71,7 @@ public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() { @SuppressWarnings("rawtypes") private Set> findAllCustomTypeRecords() { - return reflections.getSubTypesOf(CustomType.class).stream() + return classScanner.getSubTypesOf(CustomType.class).stream() .filter(Record.class::isAssignableFrom) .collect(Collectors.toSet()); } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/reflection/util/ClassScannerUtil.java b/src/main/java/it/aboutbits/springboot/toolbox/reflection/util/ClassScannerUtil.java new file mode 100644 index 0000000..bc67ac7 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/reflection/util/ClassScannerUtil.java @@ -0,0 +1,42 @@ +package it.aboutbits.springboot.toolbox.reflection.util; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ScanResult; +import lombok.NonNull; + +import java.util.Set; +import java.util.stream.Collectors; + +public final class ClassScannerUtil { + private ClassScannerUtil() { + } + + public static ClassScanner getScannerForPackages(String... packages) { + return new ClassScanner(packages); + } + + public static final class ClassScanner { + private final ScanResult scanResult; + + private ClassScanner(String... packages) { + try (var result = new ClassGraph() + .enableAllInfo() + .acceptPackages(packages) + .scan()) { + this.scanResult = result; + } + } + + @SuppressWarnings("unchecked") + public Set> getSubTypesOf(@NonNull Class clazz) { + return scanResult.getSubclasses(clazz).loadClasses() + .stream() + .map(item -> (Class) item) + .collect(Collectors.toSet()); + } + + public void close() { + scanResult.close(); + } + } +} From f50e118d85e7595ec088293e931fa920c5c7d439 Mon Sep 17 00:00:00 2001 From: Andreas Hufler Date: Mon, 9 Sep 2024 09:47:32 +0200 Subject: [PATCH 7/9] add testing and fix bugs --- pom.xml | 50 ++- .../boot/type/CustomTypeConfiguration.java | 46 +-- .../CustomTypeConfigurationRegistrar.java | 4 +- .../toolbox/boot/type/CustomTypeScanner.java | 43 +++ .../boot/type/RegisterCustomTypes.java | 2 +- .../swagger/CustomTypeModelConverter.java | 7 + .../swagger/CustomTypePropertyCustomizer.java | 19 + .../type/swagger/EntityIdModelConverter.java | 8 + .../swagger/EntityIdPropertyCustomizer.java | 18 + .../base/WrappedBigDecimalJavaType.java | 21 +- .../base/WrappedBigIntegerJavaType.java | 68 ++++ .../javatype/base/WrappedDoubleJavaType.java | 17 +- .../javatype/base/WrappedFloatJavaType.java | 19 +- .../javatype/base/WrappedIntegerJavaType.java | 19 +- .../javatype/base/WrappedLongJavaType.java | 15 +- .../base/WrappedScaledBigDecimalJavaType.java | 24 +- .../javatype/base/WrappedShortJavaType.java | 19 +- .../javatype/base/WrappedStringJavaType.java | 12 +- .../reflection/util/ClassScannerUtil.java | 9 +- .../toolbox/type/ScaledBigDecimal.java | 5 + .../type/jackson/CustomTypeDeserializer.java | 14 + .../type/mvc/CustomTypePropertyEditor.java | 4 + .../aboutbits/springboot/toolbox/TestApp.java | 18 + .../CustomTypeBindingsForControllerTest.java | 190 ++++++++++ .../EntityIdBindingsForControllerTest.java | 91 +++++ .../boot/mvc/body/BodyWithEmailAddress.java | 8 + .../boot/mvc/body/BodyWithEntityId.java | 8 + .../toolbox/boot/mvc/body/BodyWithIban.java | 8 + .../mvc/body/BodyWithScaledBigDecimal.java | 8 + .../controller/CustomTypeTestController.java | 64 ++++ .../controller/EntityIdTestController.java | 30 ++ .../boot/persistence/CustomTypeJpaTest.java | 76 ++++ .../boot/persistence/EntityIdJpaTest.java | 54 +++ .../impl/jpa/CustomTypeTestModel.java | 62 ++++ .../jpa/CustomTypeTestModelRepository.java | 18 + .../impl/jpa/ReferencedTestModel.java | 25 ++ .../toolbox/boot/swagger/SwaggerTest.java | 36 ++ .../javatype/WrapperTypesJpaTest.java | 337 ++++++++++++++++++ .../impl/javatype/WrapBigDecimalJavaType.java | 11 + .../impl/javatype/WrapBigIntegerJavaType.java | 11 + .../impl/javatype/WrapDoubleJavaType.java | 11 + .../impl/javatype/WrapFloatJavaType.java | 11 + .../impl/javatype/WrapIntegerJavaType.java | 11 + .../impl/javatype/WrapLongJavaType.java | 11 + .../WrapScaledBigDecimalJavaType.java | 11 + .../impl/javatype/WrapShortJavaType.java | 11 + .../impl/javatype/WrapStringJavaType.java | 11 + .../javatype/impl/jpa/WrapperTypesModel.java | 95 +++++ .../impl/jpa/WrapperTypesModelRepository.java | 34 ++ .../javatype/impl/type/WrapBigDecimal.java | 9 + .../javatype/impl/type/WrapBigInteger.java | 9 + .../javatype/impl/type/WrapDouble.java | 7 + .../javatype/impl/type/WrapFloat.java | 7 + .../javatype/impl/type/WrapInteger.java | 7 + .../javatype/impl/type/WrapLong.java | 7 + .../impl/type/WrapScaledBigDecimal.java | 8 + .../javatype/impl/type/WrapShort.java | 7 + .../javatype/impl/type/WrapString.java | 7 + .../toolbox/support/ApplicationTest.java | 17 + .../springboot/toolbox/support/HttpTest.java | 13 + .../toolbox/support/WithPersistence.java | 12 + .../persistence/PostgresTestcontainer.java | 110 ++++++ .../support/persistence/WithPostgres.java | 14 + src/test/resources/application.yml | 62 ++++ ...09-06-create-custom-type-testing-table.yml | 27 ++ ...9-06-create-wrapper-type-testing-table.yml | 42 +++ src/test/resources/db/changelog/master.yml | 7 + 67 files changed, 1986 insertions(+), 90 deletions(-) create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeScanner.java create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedBigIntegerJavaType.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/TestApp.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/CustomTypeBindingsForControllerTest.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/EntityIdBindingsForControllerTest.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithEmailAddress.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithEntityId.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithIban.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithScaledBigDecimal.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/controller/CustomTypeTestController.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/controller/EntityIdTestController.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/CustomTypeJpaTest.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/EntityIdJpaTest.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/impl/jpa/CustomTypeTestModel.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/impl/jpa/CustomTypeTestModelRepository.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/impl/jpa/ReferencedTestModel.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/boot/swagger/SwaggerTest.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/WrapperTypesJpaTest.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapBigDecimalJavaType.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapBigIntegerJavaType.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapDoubleJavaType.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapFloatJavaType.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapIntegerJavaType.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapLongJavaType.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapScaledBigDecimalJavaType.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapShortJavaType.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapStringJavaType.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/jpa/WrapperTypesModel.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/jpa/WrapperTypesModelRepository.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapBigDecimal.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapBigInteger.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapDouble.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapFloat.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapInteger.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapLong.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapScaledBigDecimal.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapShort.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapString.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/support/ApplicationTest.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/support/HttpTest.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/support/WithPersistence.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/support/persistence/PostgresTestcontainer.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/support/persistence/WithPostgres.java create mode 100644 src/test/resources/application.yml create mode 100644 src/test/resources/db/changelog/2024-09-06-create-custom-type-testing-table.yml create mode 100644 src/test/resources/db/changelog/2024-09-06-create-wrapper-type-testing-table.yml create mode 100644 src/test/resources/db/changelog/master.yml diff --git a/pom.xml b/pom.xml index 68764cd..4d78bfe 100644 --- a/pom.xml +++ b/pom.xml @@ -42,7 +42,7 @@ lombok true - + @@ -88,6 +88,54 @@ org.springframework.boot spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-web + test + + + + + org.liquibase + liquibase-core + test + + + org.postgresql + postgresql + test + + + + + org.testcontainers + testcontainers + 1.20.1 + test + + + org.hamcrest + hamcrest-core + + + org.hamcrest + hamcrest-library + + + + + org.testcontainers + junit-jupiter + 1.20.1 + test + + + org.testcontainers + postgresql + 1.20.1 + test diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeConfiguration.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeConfiguration.java index 83e5550..09d4f48 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeConfiguration.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeConfiguration.java @@ -1,11 +1,9 @@ package it.aboutbits.springboot.toolbox.boot.type; -import it.aboutbits.springboot.toolbox.reflection.util.ClassScannerUtil; import it.aboutbits.springboot.toolbox.type.CustomType; import it.aboutbits.springboot.toolbox.type.jackson.CustomTypeDeserializer; import it.aboutbits.springboot.toolbox.type.jackson.CustomTypeSerializer; import it.aboutbits.springboot.toolbox.type.mvc.CustomTypePropertyEditor; -import jakarta.annotation.PostConstruct; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; import org.springframework.context.annotation.Bean; @@ -14,39 +12,22 @@ import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.InitBinder; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Set; -import java.util.stream.Collectors; @Slf4j @Configuration public class CustomTypeConfiguration { - public static final String LIBRARY_BASE_PACKAGE_NAME = "it.aboutbits.springboot.toolbox"; - - private String[] packageNamesToScan; - private ClassScannerUtil.ClassScanner classScanner; - - public void setAdditionalTypePackages(String[] additionalTypePackages) { - var tmp = new ArrayList(); - tmp.add(LIBRARY_BASE_PACKAGE_NAME); - tmp.addAll(Arrays.asList(additionalTypePackages)); - - this.packageNamesToScan = tmp.toArray(new String[0]); - - classScanner = ClassScannerUtil.getScannerForPackages(packageNamesToScan); - } + @ControllerAdvice + public static class CustomTypePropertyBinder { + @SuppressWarnings("rawtypes") + private final Set> types; - @PostConstruct - public void init() { - log.info("CustomTypeConfiguration enabled. Scanning: {}", Arrays.toString(packageNamesToScan)); - } + public CustomTypePropertyBinder(CustomTypeScanner configuration) { + this.types = configuration.findAllCustomTypeRecords(); + } - @ControllerAdvice - public class CustomTypePropertyBinder { @InitBinder public void initBinder(WebDataBinder binder) { - var types = findAllCustomTypeRecords(); for (var clazz : types) { binder.registerCustomEditor(clazz, new CustomTypePropertyEditor<>(clazz)); } @@ -54,25 +35,16 @@ public void initBinder(WebDataBinder binder) { } @Bean - public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() { - var types = findAllCustomTypeRecords(); + public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer(CustomTypeScanner configuration) { + var types = configuration.findAllCustomTypeRecords(); var deserializers = types.stream() .map(CustomTypeDeserializer::new) .toList() .toArray(new CustomTypeDeserializer[types.size()]); - classScanner.close(); - return builder -> builder .serializers(new CustomTypeSerializer()) .deserializers(deserializers); } - - @SuppressWarnings("rawtypes") - private Set> findAllCustomTypeRecords() { - return classScanner.getSubTypesOf(CustomType.class).stream() - .filter(Record.class::isAssignableFrom) - .collect(Collectors.toSet()); - } } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeConfigurationRegistrar.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeConfigurationRegistrar.java index 33b3fe0..b2750bb 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeConfigurationRegistrar.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeConfigurationRegistrar.java @@ -21,8 +21,8 @@ public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionR ); var value = attributes.getStringArray("additionalTypePackages"); - var builder = BeanDefinitionBuilder.genericBeanDefinition(CustomTypeConfiguration.class); + var builder = BeanDefinitionBuilder.genericBeanDefinition(CustomTypeScanner.class); builder.addPropertyValue("additionalTypePackages", value); - registry.registerBeanDefinition("CustomTypeConfiguration", builder.getBeanDefinition()); + registry.registerBeanDefinition("CustomTypeScanner", builder.getBeanDefinition()); } } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeScanner.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeScanner.java new file mode 100644 index 0000000..9d3327f --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeScanner.java @@ -0,0 +1,43 @@ +package it.aboutbits.springboot.toolbox.boot.type; + +import it.aboutbits.springboot.toolbox.reflection.util.ClassScannerUtil; +import it.aboutbits.springboot.toolbox.type.CustomType; +import jakarta.annotation.PostConstruct; +import lombok.extern.slf4j.Slf4j; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + +@Slf4j +public class CustomTypeScanner { + public static final String LIBRARY_BASE_PACKAGE_NAME = "it.aboutbits.springboot.toolbox"; + + private String[] packageNamesToScan; + private ClassScannerUtil.ClassScanner classScanner; + + public void setAdditionalTypePackages(String[] additionalTypePackages) { + var tmp = new ArrayList(); + tmp.add(LIBRARY_BASE_PACKAGE_NAME); + tmp.addAll(Arrays.stream(additionalTypePackages) + .filter(item -> !item.isBlank()) + .collect(Collectors.toSet())); + + this.packageNamesToScan = tmp.toArray(new String[0]); + + classScanner = ClassScannerUtil.getScannerForPackages(packageNamesToScan); + } + + @PostConstruct + public void init() { + log.info("CustomTypeConfiguration enabled. Scanning: {}", Arrays.toString(packageNamesToScan)); + } + + @SuppressWarnings("rawtypes") + public Set> findAllCustomTypeRecords() { + return classScanner.getSubTypesOf(CustomType.class).stream() + .filter(Record.class::isAssignableFrom) + .collect(Collectors.toSet()); + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/RegisterCustomTypes.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/RegisterCustomTypes.java index 5e4643e..4a6d307 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/RegisterCustomTypes.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/RegisterCustomTypes.java @@ -9,7 +9,7 @@ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) -@Import(CustomTypeConfigurationRegistrar.class) +@Import({CustomTypeConfigurationRegistrar.class, CustomTypeConfiguration.class}) public @interface RegisterCustomTypes { String[] additionalTypePackages() default ""; } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/CustomTypeModelConverter.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/CustomTypeModelConverter.java index 655b6e9..f99009a 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/CustomTypeModelConverter.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/CustomTypeModelConverter.java @@ -7,6 +7,7 @@ import it.aboutbits.springboot.toolbox.persistence.identity.EntityId; import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil; import it.aboutbits.springboot.toolbox.type.CustomType; +import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; import org.springframework.stereotype.Component; import java.math.BigDecimal; @@ -38,6 +39,9 @@ public Schema resolve( if (Long.class.isAssignableFrom(wrappedType)) { return context.resolve(new AnnotatedType(Long.TYPE)); } + if (BigDecimal.class.isAssignableFrom(wrappedType)) { + return context.resolve(new AnnotatedType(Long.TYPE)); + } if (Float.class.isAssignableFrom(wrappedType)) { return context.resolve(new AnnotatedType(Float.TYPE)); } @@ -47,6 +51,9 @@ public Schema resolve( if (BigDecimal.class.isAssignableFrom(wrappedType)) { return context.resolve(new AnnotatedType(Double.TYPE)); } + if (ScaledBigDecimal.class.isAssignableFrom(wrappedType)) { + return context.resolve(new AnnotatedType(Double.TYPE)); + } if (String.class.isAssignableFrom(wrappedType)) { return context.resolve(new AnnotatedType(String.class)); } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/CustomTypePropertyCustomizer.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/CustomTypePropertyCustomizer.java index 0e5f194..b2bd284 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/CustomTypePropertyCustomizer.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/CustomTypePropertyCustomizer.java @@ -6,11 +6,13 @@ import it.aboutbits.springboot.toolbox.persistence.identity.EntityId; import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil; import it.aboutbits.springboot.toolbox.type.CustomType; +import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; import lombok.extern.slf4j.Slf4j; import org.springdoc.core.customizers.PropertyCustomizer; import org.springframework.stereotype.Component; import java.math.BigDecimal; +import java.math.BigInteger; @Slf4j @Component @@ -53,6 +55,14 @@ public Schema customize(Schema property, AnnotatedType annotatedType) { property.set$ref(null); return property; } + if (BigInteger.class.isAssignableFrom(wrappedType)) { + property.type("integer"); + property.format("int64"); + property.setDescription(displayName); + property.setProperties(null); + property.set$ref(null); + return property; + } if (Float.class.isAssignableFrom(wrappedType)) { property.type("number"); property.format("float"); @@ -77,6 +87,15 @@ public Schema customize(Schema property, AnnotatedType annotatedType) { property.set$ref(null); return property; } + + if (ScaledBigDecimal.class.isAssignableFrom(wrappedType)) { + property.type("number"); + property.format(""); + property.setDescription(displayName); + property.setProperties(null); + property.set$ref(null); + return property; + } if (String.class.isAssignableFrom(wrappedType)) { property.type("string"); property.format(null); diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/EntityIdModelConverter.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/EntityIdModelConverter.java index 9863063..8a0cfc5 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/EntityIdModelConverter.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/EntityIdModelConverter.java @@ -6,9 +6,11 @@ import io.swagger.v3.oas.models.media.Schema; import it.aboutbits.springboot.toolbox.persistence.identity.EntityId; import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil; +import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; import org.springframework.stereotype.Component; import java.math.BigDecimal; +import java.math.BigInteger; import java.util.Iterator; @Component @@ -37,6 +39,9 @@ public Schema resolve( if (Long.class.isAssignableFrom(wrappedType)) { return context.resolve(new AnnotatedType(Long.TYPE)); } + if (BigInteger.class.isAssignableFrom(wrappedType)) { + return context.resolve(new AnnotatedType(Long.TYPE)); + } if (Float.class.isAssignableFrom(wrappedType)) { return context.resolve(new AnnotatedType(Float.TYPE)); } @@ -46,6 +51,9 @@ public Schema resolve( if (BigDecimal.class.isAssignableFrom(wrappedType)) { return context.resolve(new AnnotatedType(Double.TYPE)); } + if (ScaledBigDecimal.class.isAssignableFrom(wrappedType)) { + return context.resolve(new AnnotatedType(Double.TYPE)); + } if (String.class.isAssignableFrom(wrappedType)) { return context.resolve(new AnnotatedType(String.class)); } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/EntityIdPropertyCustomizer.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/EntityIdPropertyCustomizer.java index 12367b3..968ee9f 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/EntityIdPropertyCustomizer.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/EntityIdPropertyCustomizer.java @@ -5,12 +5,14 @@ import io.swagger.v3.oas.models.media.Schema; import it.aboutbits.springboot.toolbox.persistence.identity.EntityId; import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil; +import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; import org.springdoc.core.customizers.PropertyCustomizer; import org.springframework.stereotype.Component; import java.math.BigDecimal; +import java.math.BigInteger; @Slf4j @Component @@ -59,6 +61,14 @@ public Schema customize(Schema property, AnnotatedType annotatedType) { property.set$ref(null); return property; } + if (BigInteger.class.isAssignableFrom(wrappedType)) { + property.type("integer"); + property.format("int64"); + property.setDescription(displayName); + property.setProperties(null); + property.set$ref(null); + return property; + } if (Float.class.isAssignableFrom(wrappedType)) { property.type("number"); property.format("float"); @@ -83,6 +93,14 @@ public Schema customize(Schema property, AnnotatedType annotatedType) { property.set$ref(null); return property; } + if (ScaledBigDecimal.class.isAssignableFrom(wrappedType)) { + property.type("number"); + property.format(""); + property.setDescription(displayName); + property.setProperties(null); + property.set$ref(null); + return property; + } if (String.class.isAssignableFrom(wrappedType)) { property.type("string"); property.format(null); diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedBigDecimalJavaType.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedBigDecimalJavaType.java index 166d3e9..d9b2382 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedBigDecimalJavaType.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedBigDecimalJavaType.java @@ -1,5 +1,6 @@ package it.aboutbits.springboot.toolbox.persistence.javatype.base; +import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil; import it.aboutbits.springboot.toolbox.type.CustomType; import lombok.SneakyThrows; import org.hibernate.type.descriptor.WrapperOptions; @@ -7,13 +8,18 @@ import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; +import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.math.BigDecimal; import java.sql.Types; public abstract class WrappedBigDecimalJavaType> extends AbstractClassJavaType { + private final transient Constructor canonicalConstructor; + protected WrappedBigDecimalJavaType(Class type) { super(type); + + this.canonicalConstructor = RecordReflectionUtil.getCanonicalConstructor(type); } @Override @@ -23,6 +29,7 @@ public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) { .getDescriptor(Types.DOUBLE); } + @SuppressWarnings("unchecked") @Override public X unwrap(T id, Class aClass, WrapperOptions wrapperOptions) { var javaTypeClass = getJavaTypeClass(); @@ -33,12 +40,14 @@ public X unwrap(T id, Class aClass, WrapperOptions wrapperOptions) { if (javaTypeClass.isAssignableFrom(aClass)) { return (X) id; } - if (BigDecimal.class.isAssignableFrom(aClass)) { - return (X) id.value(); + if (Double.class.isAssignableFrom(aClass)) { + return (X) Double.valueOf(id.value().doubleValue()); } + throw unknownUnwrap(aClass); } + @SuppressWarnings("unchecked") @SneakyThrows({InstantiationException.class, IllegalAccessException.class, InvocationTargetException.class}) @Override public T wrap(X value, WrapperOptions wrapperOptions) { @@ -50,12 +59,10 @@ public T wrap(X value, WrapperOptions wrapperOptions) { if (clazz.isInstance(value)) { return (T) value; } - if (value instanceof BigDecimal bigDecimal) { - return (T) clazz.getConstructors()[0].newInstance(bigDecimal); - } - if (value instanceof String stringValue) { - return (T) clazz.getConstructors()[0].newInstance(new BigDecimal(stringValue)); + if (value instanceof Double doubleValue) { + return canonicalConstructor.newInstance(BigDecimal.valueOf(doubleValue)); } + throw unknownWrap(value.getClass()); } } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedBigIntegerJavaType.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedBigIntegerJavaType.java new file mode 100644 index 0000000..ef2094c --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedBigIntegerJavaType.java @@ -0,0 +1,68 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype.base; + +import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil; +import it.aboutbits.springboot.toolbox.type.CustomType; +import lombok.SneakyThrows; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.AbstractClassJavaType; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.math.BigInteger; +import java.sql.Types; + +public abstract class WrappedBigIntegerJavaType> extends AbstractClassJavaType { + private final transient Constructor canonicalConstructor; + + protected WrappedBigIntegerJavaType(Class type) { + super(type); + + this.canonicalConstructor = RecordReflectionUtil.getCanonicalConstructor(type); + } + + @Override + public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) { + return indicators.getTypeConfiguration() + .getJdbcTypeRegistry() + .getDescriptor(Types.BIGINT); + } + + @SuppressWarnings("unchecked") + @Override + public X unwrap(T id, Class aClass, WrapperOptions wrapperOptions) { + var javaTypeClass = getJavaTypeClass(); + + if (id == null) { + return null; + } + if (javaTypeClass.isAssignableFrom(aClass)) { + return (X) id; + } + if (Long.class.isAssignableFrom(aClass)) { + return (X) Long.valueOf(id.value().longValue()); + } + + throw unknownUnwrap(aClass); + } + + @SuppressWarnings("unchecked") + @SneakyThrows({InstantiationException.class, IllegalAccessException.class, InvocationTargetException.class}) + @Override + public T wrap(X value, WrapperOptions wrapperOptions) { + var clazz = getJavaTypeClass(); + + if (value == null) { + return null; + } + if (clazz.isInstance(value)) { + return (T) value; + } + if (value instanceof Long longValue) { + return canonicalConstructor.newInstance(BigInteger.valueOf(longValue)); + } + + throw unknownWrap(value.getClass()); + } +} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedDoubleJavaType.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedDoubleJavaType.java index 2f88ae6..37a6b36 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedDoubleJavaType.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedDoubleJavaType.java @@ -1,5 +1,6 @@ package it.aboutbits.springboot.toolbox.persistence.javatype.base; +import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil; import it.aboutbits.springboot.toolbox.type.CustomType; import lombok.SneakyThrows; import org.hibernate.type.descriptor.WrapperOptions; @@ -7,12 +8,17 @@ import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; +import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.sql.Types; -public abstract class WrappedDoubleJavaType> extends AbstractClassJavaType { +public abstract class WrappedDoubleJavaType> extends AbstractClassJavaType { + private final transient Constructor canonicalConstructor; + protected WrappedDoubleJavaType(Class type) { super(type); + + this.canonicalConstructor = RecordReflectionUtil.getCanonicalConstructor(type); } @Override @@ -22,6 +28,7 @@ public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) { .getDescriptor(Types.DOUBLE); } + @SuppressWarnings("unchecked") @Override public X unwrap(T id, Class aClass, WrapperOptions wrapperOptions) { var javaTypeClass = getJavaTypeClass(); @@ -35,9 +42,11 @@ public X unwrap(T id, Class aClass, WrapperOptions wrapperOptions) { if (Double.class.isAssignableFrom(aClass)) { return (X) id.value(); } + throw unknownUnwrap(aClass); } + @SuppressWarnings("unchecked") @SneakyThrows({InstantiationException.class, IllegalAccessException.class, InvocationTargetException.class}) @Override public T wrap(X value, WrapperOptions wrapperOptions) { @@ -50,11 +59,9 @@ public T wrap(X value, WrapperOptions wrapperOptions) { return (T) value; } if (value instanceof Double doubleValue) { - return (T) clazz.getConstructors()[0].newInstance(doubleValue); - } - if (value instanceof String stringValue) { - return (T) clazz.getConstructors()[0].newInstance(Double.parseDouble(stringValue)); + return canonicalConstructor.newInstance(doubleValue); } + throw unknownWrap(value.getClass()); } } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedFloatJavaType.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedFloatJavaType.java index 513079b..c862e39 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedFloatJavaType.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedFloatJavaType.java @@ -1,5 +1,6 @@ package it.aboutbits.springboot.toolbox.persistence.javatype.base; +import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil; import it.aboutbits.springboot.toolbox.type.CustomType; import lombok.SneakyThrows; import org.hibernate.type.descriptor.WrapperOptions; @@ -7,21 +8,27 @@ import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; +import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.sql.Types; -public abstract class WrappedFloatJavaType> extends AbstractClassJavaType { +public abstract class WrappedFloatJavaType> extends AbstractClassJavaType { + private final transient Constructor canonicalConstructor; + protected WrappedFloatJavaType(Class type) { super(type); + + this.canonicalConstructor = RecordReflectionUtil.getCanonicalConstructor(type); } @Override public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) { return indicators.getTypeConfiguration() .getJdbcTypeRegistry() - .getDescriptor(Types.DOUBLE); + .getDescriptor(Types.FLOAT); } + @SuppressWarnings("unchecked") @Override public X unwrap(T id, Class aClass, WrapperOptions wrapperOptions) { var javaTypeClass = getJavaTypeClass(); @@ -35,9 +42,11 @@ public X unwrap(T id, Class aClass, WrapperOptions wrapperOptions) { if (Float.class.isAssignableFrom(aClass)) { return (X) id.value(); } + throw unknownUnwrap(aClass); } + @SuppressWarnings("unchecked") @SneakyThrows({InstantiationException.class, IllegalAccessException.class, InvocationTargetException.class}) @Override public T wrap(X value, WrapperOptions wrapperOptions) { @@ -50,11 +59,9 @@ public T wrap(X value, WrapperOptions wrapperOptions) { return (T) value; } if (value instanceof Float floatValue) { - return (T) clazz.getConstructors()[0].newInstance(floatValue); - } - if (value instanceof String stringValue) { - return (T) clazz.getConstructors()[0].newInstance(Float.parseFloat(stringValue)); + return canonicalConstructor.newInstance(floatValue); } + throw unknownWrap(value.getClass()); } } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedIntegerJavaType.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedIntegerJavaType.java index 3b53e55..8c4d933 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedIntegerJavaType.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedIntegerJavaType.java @@ -1,5 +1,6 @@ package it.aboutbits.springboot.toolbox.persistence.javatype.base; +import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil; import it.aboutbits.springboot.toolbox.type.CustomType; import lombok.SneakyThrows; import org.hibernate.type.descriptor.WrapperOptions; @@ -7,21 +8,27 @@ import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; +import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.sql.Types; -public abstract class WrappedIntegerJavaType> extends AbstractClassJavaType { +public abstract class WrappedIntegerJavaType> extends AbstractClassJavaType { + private final transient Constructor canonicalConstructor; + protected WrappedIntegerJavaType(Class type) { super(type); + + this.canonicalConstructor = RecordReflectionUtil.getCanonicalConstructor(type); } @Override public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) { return indicators.getTypeConfiguration() .getJdbcTypeRegistry() - .getDescriptor(Types.BIGINT); + .getDescriptor(Types.INTEGER); } + @SuppressWarnings("unchecked") @Override public X unwrap(T id, Class aClass, WrapperOptions wrapperOptions) { var javaTypeClass = getJavaTypeClass(); @@ -35,9 +42,11 @@ public X unwrap(T id, Class aClass, WrapperOptions wrapperOptions) { if (Integer.class.isAssignableFrom(aClass)) { return (X) id.value(); } + throw unknownUnwrap(aClass); } + @SuppressWarnings("unchecked") @SneakyThrows({InstantiationException.class, IllegalAccessException.class, InvocationTargetException.class}) @Override public T wrap(X value, WrapperOptions wrapperOptions) { @@ -50,11 +59,9 @@ public T wrap(X value, WrapperOptions wrapperOptions) { return (T) value; } if (value instanceof Integer integerValue) { - return (T) clazz.getConstructors()[0].newInstance(integerValue); - } - if (value instanceof String stringValue) { - return (T) clazz.getConstructors()[0].newInstance(Integer.parseInt(stringValue)); + return canonicalConstructor.newInstance(integerValue); } + throw unknownWrap(value.getClass()); } } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedLongJavaType.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedLongJavaType.java index efc5288..4949394 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedLongJavaType.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedLongJavaType.java @@ -1,5 +1,6 @@ package it.aboutbits.springboot.toolbox.persistence.javatype.base; +import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil; import it.aboutbits.springboot.toolbox.type.CustomType; import lombok.SneakyThrows; import org.hibernate.type.descriptor.WrapperOptions; @@ -7,12 +8,17 @@ import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; +import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.sql.Types; public abstract class WrappedLongJavaType> extends AbstractClassJavaType { + private final transient Constructor canonicalConstructor; + protected WrappedLongJavaType(Class type) { super(type); + + this.canonicalConstructor = RecordReflectionUtil.getCanonicalConstructor(type); } @Override @@ -22,6 +28,7 @@ public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) { .getDescriptor(Types.BIGINT); } + @SuppressWarnings("unchecked") @Override public X unwrap(T id, Class aClass, WrapperOptions wrapperOptions) { var javaTypeClass = getJavaTypeClass(); @@ -35,9 +42,11 @@ public X unwrap(T id, Class aClass, WrapperOptions wrapperOptions) { if (Long.class.isAssignableFrom(aClass)) { return (X) id.value(); } + throw unknownUnwrap(aClass); } + @SuppressWarnings("unchecked") @SneakyThrows({InstantiationException.class, IllegalAccessException.class, InvocationTargetException.class}) @Override public T wrap(X value, WrapperOptions wrapperOptions) { @@ -50,11 +59,9 @@ public T wrap(X value, WrapperOptions wrapperOptions) { return (T) value; } if (value instanceof Long longValue) { - return (T) clazz.getConstructors()[0].newInstance(longValue); - } - if (value instanceof String stringValue) { - return (T) clazz.getConstructors()[0].newInstance(Long.parseLong(stringValue)); + return canonicalConstructor.newInstance(longValue); } + throw unknownWrap(value.getClass()); } } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedScaledBigDecimalJavaType.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedScaledBigDecimalJavaType.java index 9472cb0..3450c14 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedScaledBigDecimalJavaType.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedScaledBigDecimalJavaType.java @@ -1,5 +1,6 @@ package it.aboutbits.springboot.toolbox.persistence.javatype.base; +import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil; import it.aboutbits.springboot.toolbox.type.CustomType; import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; import lombok.SneakyThrows; @@ -8,13 +9,17 @@ import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; +import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; -import java.math.BigDecimal; import java.sql.Types; -public abstract class WrappedScaledBigDecimalJavaType> extends AbstractClassJavaType { +public abstract class WrappedScaledBigDecimalJavaType> extends AbstractClassJavaType { + private final transient Constructor canonicalConstructor; + protected WrappedScaledBigDecimalJavaType(Class type) { super(type); + + this.canonicalConstructor = RecordReflectionUtil.getCanonicalConstructor(type); } @Override @@ -24,6 +29,7 @@ public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) { .getDescriptor(Types.DOUBLE); } + @SuppressWarnings("unchecked") @Override public X unwrap(T id, Class aClass, WrapperOptions wrapperOptions) { var javaTypeClass = getJavaTypeClass(); @@ -34,12 +40,14 @@ public X unwrap(T id, Class aClass, WrapperOptions wrapperOptions) { if (javaTypeClass.isAssignableFrom(aClass)) { return (X) id; } - if (ScaledBigDecimal.class.isAssignableFrom(aClass)) { - return (X) id.value(); + if (Double.class.isAssignableFrom(aClass)) { + return (X) Double.valueOf(id.value().value().doubleValue()); } + throw unknownUnwrap(aClass); } + @SuppressWarnings("unchecked") @SneakyThrows({InstantiationException.class, IllegalAccessException.class, InvocationTargetException.class}) @Override public T wrap(X value, WrapperOptions wrapperOptions) { @@ -51,12 +59,10 @@ public T wrap(X value, WrapperOptions wrapperOptions) { if (clazz.isInstance(value)) { return (T) value; } - if (value instanceof ScaledBigDecimal scaledBigDecimalValue) { - return (T) clazz.getConstructors()[0].newInstance(scaledBigDecimalValue); - } - if (value instanceof String stringValue) { - return (T) clazz.getConstructors()[0].newInstance(new ScaledBigDecimal(stringValue)); + if (value instanceof Double doubleValue) { + return canonicalConstructor.newInstance(new ScaledBigDecimal(doubleValue)); } + throw unknownWrap(value.getClass()); } } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedShortJavaType.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedShortJavaType.java index 757bc3e..b5782fa 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedShortJavaType.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedShortJavaType.java @@ -1,5 +1,6 @@ package it.aboutbits.springboot.toolbox.persistence.javatype.base; +import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil; import it.aboutbits.springboot.toolbox.type.CustomType; import lombok.SneakyThrows; import org.hibernate.type.descriptor.WrapperOptions; @@ -7,21 +8,27 @@ import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; +import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.sql.Types; -public abstract class WrappedShortJavaType> extends AbstractClassJavaType { +public abstract class WrappedShortJavaType> extends AbstractClassJavaType { + private final transient Constructor canonicalConstructor; + protected WrappedShortJavaType(Class type) { super(type); + + this.canonicalConstructor = RecordReflectionUtil.getCanonicalConstructor(type); } @Override public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) { return indicators.getTypeConfiguration() .getJdbcTypeRegistry() - .getDescriptor(Types.BIGINT); + .getDescriptor(Types.SMALLINT); } + @SuppressWarnings("unchecked") @Override public X unwrap(T id, Class aClass, WrapperOptions wrapperOptions) { var javaTypeClass = getJavaTypeClass(); @@ -35,9 +42,11 @@ public X unwrap(T id, Class aClass, WrapperOptions wrapperOptions) { if (Short.class.isAssignableFrom(aClass)) { return (X) id.value(); } + throw unknownUnwrap(aClass); } + @SuppressWarnings("unchecked") @SneakyThrows({InstantiationException.class, IllegalAccessException.class, InvocationTargetException.class}) @Override public T wrap(X value, WrapperOptions wrapperOptions) { @@ -50,11 +59,9 @@ public T wrap(X value, WrapperOptions wrapperOptions) { return (T) value; } if (value instanceof Short shortValue) { - return (T) clazz.getConstructors()[0].newInstance(shortValue); - } - if (value instanceof String stringValue) { - return (T) clazz.getConstructors()[0].newInstance(Short.parseShort(stringValue)); + return canonicalConstructor.newInstance(shortValue); } + throw unknownWrap(value.getClass()); } } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedStringJavaType.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedStringJavaType.java index 32eea0f..35b9fd6 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedStringJavaType.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/base/WrappedStringJavaType.java @@ -1,5 +1,6 @@ package it.aboutbits.springboot.toolbox.persistence.javatype.base; +import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil; import it.aboutbits.springboot.toolbox.type.CustomType; import lombok.SneakyThrows; import org.hibernate.type.descriptor.WrapperOptions; @@ -7,12 +8,17 @@ import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; +import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.sql.Types; public abstract class WrappedStringJavaType> extends AbstractClassJavaType { + private final transient Constructor canonicalConstructor; + protected WrappedStringJavaType(Class type) { super(type); + + this.canonicalConstructor = RecordReflectionUtil.getCanonicalConstructor(type); } @Override @@ -22,6 +28,7 @@ public JdbcType getRecommendedJdbcType(JdbcTypeIndicators indicators) { .getDescriptor(Types.VARCHAR); } + @SuppressWarnings("unchecked") @Override public X unwrap(T id, Class aClass, WrapperOptions wrapperOptions) { var javaTypeClass = getJavaTypeClass(); @@ -35,9 +42,11 @@ public X unwrap(T id, Class aClass, WrapperOptions wrapperOptions) { if (String.class.isAssignableFrom(aClass)) { return (X) id.value(); } + throw unknownUnwrap(aClass); } + @SuppressWarnings("unchecked") @SneakyThrows({InstantiationException.class, IllegalAccessException.class, InvocationTargetException.class}) @Override public T wrap(X value, WrapperOptions wrapperOptions) { @@ -50,8 +59,9 @@ public T wrap(X value, WrapperOptions wrapperOptions) { return (T) value; } if (value instanceof String stringValue) { - return (T) clazz.getConstructors()[0].newInstance(stringValue); + return canonicalConstructor.newInstance(stringValue); } + throw unknownWrap(value.getClass()); } } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/reflection/util/ClassScannerUtil.java b/src/main/java/it/aboutbits/springboot/toolbox/reflection/util/ClassScannerUtil.java index bc67ac7..13016ca 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/reflection/util/ClassScannerUtil.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/reflection/util/ClassScannerUtil.java @@ -19,17 +19,16 @@ public static final class ClassScanner { private final ScanResult scanResult; private ClassScanner(String... packages) { - try (var result = new ClassGraph() + var result = new ClassGraph() .enableAllInfo() .acceptPackages(packages) - .scan()) { - this.scanResult = result; - } + .scan(); + this.scanResult = result; } @SuppressWarnings("unchecked") public Set> getSubTypesOf(@NonNull Class clazz) { - return scanResult.getSubclasses(clazz).loadClasses() + return scanResult.getClassesImplementing(clazz).loadClasses() .stream() .map(item -> (Class) item) .collect(Collectors.toSet()); diff --git a/src/main/java/it/aboutbits/springboot/toolbox/type/ScaledBigDecimal.java b/src/main/java/it/aboutbits/springboot/toolbox/type/ScaledBigDecimal.java index 8256bee..5a4dda6 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/type/ScaledBigDecimal.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/type/ScaledBigDecimal.java @@ -206,6 +206,11 @@ public ScaledBigDecimal roundToScale(int scale) { return new ScaledBigDecimal(this.value().setScale(scale, RoundingMode.HALF_UP)); } + @Override + public String toString() { + return this.value().toString(); + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/src/main/java/it/aboutbits/springboot/toolbox/type/jackson/CustomTypeDeserializer.java b/src/main/java/it/aboutbits/springboot/toolbox/type/jackson/CustomTypeDeserializer.java index b405351..1137753 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/type/jackson/CustomTypeDeserializer.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/type/jackson/CustomTypeDeserializer.java @@ -11,6 +11,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.math.BigDecimal; +import java.math.BigInteger; import java.util.function.Function; public class CustomTypeDeserializer> extends JsonDeserializer { @@ -60,6 +61,9 @@ private static Function getTypeConverter(Class wrappedTyp if (Long.class.isAssignableFrom(wrappedType)) { return getLongConverter(); } + if (BigInteger.class.isAssignableFrom(wrappedType)) { + return getBigIntegerConverter(); + } if (Float.class.isAssignableFrom(wrappedType)) { return getFloatConverter(); } @@ -115,6 +119,16 @@ private static Function getFloatConverter() { }; } + private static Function getBigIntegerConverter() { + return jsonParser -> { + try { + return jsonParser.getBigIntegerValue(); + } catch (IOException e) { + throw new CustomTypeDeserializerException("Failed to read value as BigInteger.", e); + } + }; + } + private static Function getLongConverter() { return jsonParser -> { try { diff --git a/src/main/java/it/aboutbits/springboot/toolbox/type/mvc/CustomTypePropertyEditor.java b/src/main/java/it/aboutbits/springboot/toolbox/type/mvc/CustomTypePropertyEditor.java index 6310057..1b9af95 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/type/mvc/CustomTypePropertyEditor.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/type/mvc/CustomTypePropertyEditor.java @@ -11,6 +11,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.math.BigDecimal; +import java.math.BigInteger; import java.util.function.Function; public final class CustomTypePropertyEditor> extends PropertyEditorSupport { @@ -63,6 +64,9 @@ private static Function getTextToTypeConverter(Class wrappedT if (Long.class.isAssignableFrom(wrappedType)) { return Long::parseLong; } + if (BigInteger.class.isAssignableFrom(wrappedType)) { + return BigInteger::new; + } if (Float.class.isAssignableFrom(wrappedType)) { return Float::parseFloat; } diff --git a/src/test/java/it/aboutbits/springboot/toolbox/TestApp.java b/src/test/java/it/aboutbits/springboot/toolbox/TestApp.java new file mode 100644 index 0000000..68f1947 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/TestApp.java @@ -0,0 +1,18 @@ +package it.aboutbits.springboot.toolbox; + +import it.aboutbits.springboot.toolbox.boot.type.RegisterCustomTypes; +import it.aboutbits.springboot.toolbox.boot.type.swagger.RegisterCustomSwaggerTypes; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SuppressWarnings("checkstyle:HideUtilityClassConstructor") +@SpringBootApplication +@RegisterCustomTypes +@RegisterCustomSwaggerTypes +public class TestApp { + + public static void main(String[] args) { + SpringApplication.run(TestApp.class, args); + } + +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/CustomTypeBindingsForControllerTest.java b/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/CustomTypeBindingsForControllerTest.java new file mode 100644 index 0000000..36fcf00 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/CustomTypeBindingsForControllerTest.java @@ -0,0 +1,190 @@ +package it.aboutbits.springboot.toolbox.boot.mvc; + +import com.fasterxml.jackson.databind.ObjectMapper; +import it.aboutbits.springboot.toolbox.boot.mvc.body.BodyWithEmailAddress; +import it.aboutbits.springboot.toolbox.boot.mvc.body.BodyWithIban; +import it.aboutbits.springboot.toolbox.boot.mvc.body.BodyWithScaledBigDecimal; +import it.aboutbits.springboot.toolbox.support.HttpTest; +import it.aboutbits.springboot.toolbox.type.EmailAddress; +import it.aboutbits.springboot.toolbox.type.Iban; +import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; +import lombok.NonNull; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import static org.assertj.core.api.Assertions.assertThat; + +@HttpTest +public class CustomTypeBindingsForControllerTest { + @Autowired + protected MockMvc mockMvc; + + @Autowired + protected ObjectMapper objectMapper; + + @Nested + class EmailAddressType { + @Test + void emailAddressAsPathVariable() throws Exception { + var value = new EmailAddress("herbert@aboutbits.it"); + + var resultAsString = performGetAndReturnResult( + String.format("/test/type/EmailAddress/as-path-variable/%s", value) + ); + + var actual = objectMapper.readValue(resultAsString, EmailAddress.class); + + assertThat(actual).isEqualTo(value); + } + + @Test + void emailAddressAsRequestParameter() throws Exception { + var value = new EmailAddress("herbert@aboutbits.it"); + + var resultAsString = performGetAndReturnResult( + String.format("/test/type/EmailAddress/as-request-parameter?value=%s", value) + ); + + var actual = objectMapper.readValue(resultAsString, EmailAddress.class); + + assertThat(actual).isEqualTo(value); + } + + @Test + void emailAddressAsBody() throws Exception { + var value = new BodyWithEmailAddress( + new EmailAddress("herbert@aboutbits.it") + ); + + var resultAsString = performPostAndReturnResult( + "/test/type/EmailAddress/as-body", + value + ); + + var actual = objectMapper.readValue(resultAsString, BodyWithEmailAddress.class); + + assertThat(actual).isEqualTo(value); + } + } + + @Nested + class IbanType { + @Test + void IbanAsPathVariable() throws Exception { + var value = new Iban("NL63ABNA7864733042"); + + var resultAsString = performGetAndReturnResult( + String.format("/test/type/Iban/as-path-variable/%s", value) + ); + + var actual = objectMapper.readValue(resultAsString, Iban.class); + + assertThat(actual).isEqualTo(value); + } + + @Test + void IbanAsRequestParameter() throws Exception { + var value = new Iban("NL63ABNA7864733042"); + + var resultAsString = performGetAndReturnResult( + String.format("/test/type/Iban/as-request-parameter?value=%s", value) + ); + + var actual = objectMapper.readValue(resultAsString, Iban.class); + + assertThat(actual).isEqualTo(value); + } + + @Test + void IbanAsBody() throws Exception { + var value = new BodyWithIban( + new Iban("NL63ABNA7864733042") + ); + + var resultAsString = performPostAndReturnResult( + "/test/type/Iban/as-body", + value + ); + + var actual = objectMapper.readValue(resultAsString, BodyWithIban.class); + + assertThat(actual).isEqualTo(value); + } + } + + @Nested + class ScaledBigDecimalType { + @ParameterizedTest + @ValueSource(doubles = {-1, 0, 1, -0.001, 0.001, -100_000_000, 100_000_000}) + void ScaledBigDecimalAsPathVariable(double doubleValue) throws Exception { + var value = new ScaledBigDecimal(doubleValue); + + var resultAsString = performGetAndReturnResult( + String.format("/test/type/ScaledBigDecimal/as-path-variable/%s", value) + ); + + var actual = objectMapper.readValue(resultAsString, ScaledBigDecimal.class); + + assertThat(actual).isEqualTo(value); + } + + @ParameterizedTest + @ValueSource(doubles = {-1, 0, 1, -0.001, 0.001, -100_000_000, 100_000_000}) + void ScaledBigDecimalAsRequestParameter(double doubleValue) throws Exception { + var value = new ScaledBigDecimal(doubleValue); + + var resultAsString = performGetAndReturnResult( + String.format("/test/type/ScaledBigDecimal/as-request-parameter?value=%s", value) + ); + + var actual = objectMapper.readValue(resultAsString, ScaledBigDecimal.class); + + assertThat(actual).isEqualTo(value); + } + + @ParameterizedTest + @ValueSource(doubles = {-1, 0, 1, -0.001, 0.001, -100_000_000, 100_000_000}) + void ScaledBigDecimalAsBody(double doubleValue) throws Exception { + var value = new BodyWithScaledBigDecimal( + new ScaledBigDecimal(doubleValue) + ); + + var resultAsString = performPostAndReturnResult( + "/test/type/ScaledBigDecimal/as-body", + value + ); + + var actual = objectMapper.readValue(resultAsString, BodyWithScaledBigDecimal.class); + + assertThat(actual).isEqualTo(value); + } + } + + private @NonNull String performGetAndReturnResult(@NonNull String url) throws Exception { + var requestBuilder = MockMvcRequestBuilders.get(url) + .contentType(MediaType.APPLICATION_JSON); + + return mockMvc.perform(requestBuilder) + .andReturn() + .getResponse() + .getContentAsString(); + } + + private @NonNull String performPostAndReturnResult(@NonNull String url, @NonNull Object body) throws Exception { + var requestBuilder = MockMvcRequestBuilders.post(url) + .content(objectMapper.writeValueAsString(body)) + .contentType(MediaType.APPLICATION_JSON); + + return mockMvc.perform(requestBuilder) + .andReturn() + .getResponse() + .getContentAsString(); + } + +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/EntityIdBindingsForControllerTest.java b/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/EntityIdBindingsForControllerTest.java new file mode 100644 index 0000000..65b414d --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/EntityIdBindingsForControllerTest.java @@ -0,0 +1,91 @@ +package it.aboutbits.springboot.toolbox.boot.mvc; + +import com.fasterxml.jackson.databind.ObjectMapper; +import it.aboutbits.springboot.toolbox.boot.mvc.body.BodyWithEntityId; +import it.aboutbits.springboot.toolbox.boot.persistence.impl.jpa.CustomTypeTestModel; +import it.aboutbits.springboot.toolbox.support.HttpTest; +import lombok.NonNull; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import static org.assertj.core.api.Assertions.assertThat; + +@HttpTest +public class EntityIdBindingsForControllerTest { + @Autowired + protected MockMvc mockMvc; + + @Autowired + protected ObjectMapper objectMapper; + + @Nested + class EntityId { + @Test + void emailAddressAsPathVariable() throws Exception { + var value = new CustomTypeTestModel.ID(512L); + + var resultAsString = performGetAndReturnResult( + String.format("/test/entity-id/CustomTypeTestModel.ID/as-path-variable/%s", value) + ); + + var actual = objectMapper.readValue(resultAsString, CustomTypeTestModel.ID.class); + + assertThat(actual).isEqualTo(value); + } + + @Test + void emailAddressAsRequestParameter() throws Exception { + var value = new CustomTypeTestModel.ID(512L); + + var resultAsString = performGetAndReturnResult( + String.format("/test/entity-id/CustomTypeTestModel.ID/as-request-parameter?value=%s", value) + ); + + var actual = objectMapper.readValue(resultAsString, CustomTypeTestModel.ID.class); + + assertThat(actual).isEqualTo(value); + } + + @Test + void emailAddressAsBody() throws Exception { + var value = new BodyWithEntityId( + new CustomTypeTestModel.ID(512L) + ); + + var resultAsString = performPostAndReturnResult( + "/test/entity-id/CustomTypeTestModel.ID/as-body", + value + ); + + var actual = objectMapper.readValue(resultAsString, BodyWithEntityId.class); + + assertThat(actual).isEqualTo(value); + } + } + + private @NonNull String performGetAndReturnResult(@NonNull String url) throws Exception { + var requestBuilder = MockMvcRequestBuilders.get(url) + .contentType(MediaType.APPLICATION_JSON); + + return mockMvc.perform(requestBuilder) + .andReturn() + .getResponse() + .getContentAsString(); + } + + private @NonNull String performPostAndReturnResult(@NonNull String url, @NonNull Object body) throws Exception { + var requestBuilder = MockMvcRequestBuilders.post(url) + .content(objectMapper.writeValueAsString(body)) + .contentType(MediaType.APPLICATION_JSON); + + return mockMvc.perform(requestBuilder) + .andReturn() + .getResponse() + .getContentAsString(); + } + +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithEmailAddress.java b/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithEmailAddress.java new file mode 100644 index 0000000..49d7c44 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithEmailAddress.java @@ -0,0 +1,8 @@ +package it.aboutbits.springboot.toolbox.boot.mvc.body; + +import it.aboutbits.springboot.toolbox.type.EmailAddress; + +public record BodyWithEmailAddress( + EmailAddress emailAddress +) { +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithEntityId.java b/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithEntityId.java new file mode 100644 index 0000000..06c4a13 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithEntityId.java @@ -0,0 +1,8 @@ +package it.aboutbits.springboot.toolbox.boot.mvc.body; + +import it.aboutbits.springboot.toolbox.boot.persistence.impl.jpa.CustomTypeTestModel; + +public record BodyWithEntityId( + CustomTypeTestModel.ID entityId +) { +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithIban.java b/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithIban.java new file mode 100644 index 0000000..2d24b5d --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithIban.java @@ -0,0 +1,8 @@ +package it.aboutbits.springboot.toolbox.boot.mvc.body; + +import it.aboutbits.springboot.toolbox.type.Iban; + +public record BodyWithIban( + Iban iban +) { +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithScaledBigDecimal.java b/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithScaledBigDecimal.java new file mode 100644 index 0000000..ea2d5e7 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithScaledBigDecimal.java @@ -0,0 +1,8 @@ +package it.aboutbits.springboot.toolbox.boot.mvc.body; + +import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; + +public record BodyWithScaledBigDecimal( + ScaledBigDecimal scaledBigDecimal +) { +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/controller/CustomTypeTestController.java b/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/controller/CustomTypeTestController.java new file mode 100644 index 0000000..dd98121 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/controller/CustomTypeTestController.java @@ -0,0 +1,64 @@ +package it.aboutbits.springboot.toolbox.boot.mvc.controller; + +import it.aboutbits.springboot.toolbox.boot.mvc.body.BodyWithEmailAddress; +import it.aboutbits.springboot.toolbox.boot.mvc.body.BodyWithIban; +import it.aboutbits.springboot.toolbox.boot.mvc.body.BodyWithScaledBigDecimal; +import it.aboutbits.springboot.toolbox.type.EmailAddress; +import it.aboutbits.springboot.toolbox.type.Iban; +import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/test/type") +public class CustomTypeTestController { + @GetMapping("/EmailAddress/as-path-variable/{value}") + public EmailAddress emailAddressAsPathVariable(@PathVariable EmailAddress value) { + return value; + } + + @GetMapping("/EmailAddress/as-request-parameter") + public EmailAddress emailAddressAsRequestParameter(@RequestParam EmailAddress value) { + return value; + } + + @PostMapping("/EmailAddress/as-body") + public BodyWithEmailAddress emailAddressAsBody(@RequestBody BodyWithEmailAddress value) { + return value; + } + + @GetMapping("/Iban/as-path-variable/{value}") + public Iban ibanAsPathVariable(@PathVariable Iban value) { + return value; + } + + @GetMapping("/Iban/as-request-parameter") + public Iban ibanAsRequestParameter(@RequestParam Iban value) { + return value; + } + + @PostMapping("/Iban/as-body") + public BodyWithIban ibanAsBody(@RequestBody BodyWithIban value) { + return value; + } + + @GetMapping("/ScaledBigDecimal/as-path-variable/{value}") + public ScaledBigDecimal scaledBigDecimalAsPathVariable(@PathVariable ScaledBigDecimal value) { + return value; + } + + @GetMapping("/ScaledBigDecimal/as-request-parameter") + public ScaledBigDecimal scaledBigDecimalAsRequestParameter(@RequestParam ScaledBigDecimal value) { + return value; + } + + @PostMapping("/ScaledBigDecimal/as-body") + public BodyWithScaledBigDecimal scaledBigDecimalAsBody(@RequestBody BodyWithScaledBigDecimal value) { + return value; + } +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/controller/EntityIdTestController.java b/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/controller/EntityIdTestController.java new file mode 100644 index 0000000..6cf40bc --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/controller/EntityIdTestController.java @@ -0,0 +1,30 @@ +package it.aboutbits.springboot.toolbox.boot.mvc.controller; + +import it.aboutbits.springboot.toolbox.boot.mvc.body.BodyWithEntityId; +import it.aboutbits.springboot.toolbox.boot.persistence.impl.jpa.CustomTypeTestModel; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/test/entity-id") +public class EntityIdTestController { + @GetMapping("/CustomTypeTestModel.ID/as-path-variable/{value}") + public CustomTypeTestModel.ID customTypeTestModelIdAsPathVariable(@PathVariable CustomTypeTestModel.ID value) { + return value; + } + + @GetMapping("/CustomTypeTestModel.ID/as-request-parameter") + public CustomTypeTestModel.ID customTypeTestModelIdAsRequestParameter(@RequestParam CustomTypeTestModel.ID value) { + return value; + } + + @PostMapping("/CustomTypeTestModel.ID/as-body") + public BodyWithEntityId customTypeTestModelIdAsBody(@RequestBody BodyWithEntityId value) { + return value; + } +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/CustomTypeJpaTest.java b/src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/CustomTypeJpaTest.java new file mode 100644 index 0000000..bd045bf --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/CustomTypeJpaTest.java @@ -0,0 +1,76 @@ +package it.aboutbits.springboot.toolbox.boot.persistence; + +import it.aboutbits.springboot.toolbox.boot.persistence.impl.jpa.CustomTypeTestModel; +import it.aboutbits.springboot.toolbox.boot.persistence.impl.jpa.CustomTypeTestModelRepository; +import it.aboutbits.springboot.toolbox.support.ApplicationTest; +import it.aboutbits.springboot.toolbox.type.EmailAddress; +import it.aboutbits.springboot.toolbox.type.Iban; +import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.beans.factory.annotation.Autowired; + +import static org.assertj.core.api.Assertions.assertThat; + +@ApplicationTest +public class CustomTypeJpaTest { + @Autowired + CustomTypeTestModelRepository repository; + + @Nested + class EmailAddressType { + @Test + void inAndOut_shouldSucceed() { + var item = new CustomTypeTestModel(); + item.setEmail(new EmailAddress("sepp@aboutbits.it")); + + var savedItem = repository.save(item); + + var retrievedItem = repository.findByEmail(savedItem.getEmail()); + + assertThat(retrievedItem).isPresent() + .get() + .usingRecursiveComparison() + .isEqualTo(savedItem); + } + } + + @Nested + class IbanType { + @Test + void inAndOut_shouldSucceed() { + var item = new CustomTypeTestModel(); + item.setIban(new Iban("NL63ABNA7864733042")); + + var savedItem = repository.save(item); + + var retrievedItem = repository.findByIban(savedItem.getIban()); + + assertThat(retrievedItem).isPresent() + .get() + .usingRecursiveComparison() + .isEqualTo(savedItem); + } + } + + @Nested + class ScaledBigDecimalType { + @ParameterizedTest + @ValueSource(doubles = {-1, 0, 1, -0.001, 0.001, -100_000_000, 100_000_000}) + void inAndOut_shouldSucceed(double doubleValue) { + var item = new CustomTypeTestModel(); + item.setAccountBalance(new ScaledBigDecimal(doubleValue)); + + var savedItem = repository.save(item); + + var retrievedItem = repository.findByAccountBalance(savedItem.getAccountBalance()); + + assertThat(retrievedItem).isPresent() + .get() + .usingRecursiveComparison() + .isEqualTo(savedItem); + } + } +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/EntityIdJpaTest.java b/src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/EntityIdJpaTest.java new file mode 100644 index 0000000..59110c1 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/EntityIdJpaTest.java @@ -0,0 +1,54 @@ +package it.aboutbits.springboot.toolbox.boot.persistence; + +import it.aboutbits.springboot.toolbox.boot.persistence.impl.jpa.CustomTypeTestModel; +import it.aboutbits.springboot.toolbox.boot.persistence.impl.jpa.CustomTypeTestModelRepository; +import it.aboutbits.springboot.toolbox.boot.persistence.impl.jpa.ReferencedTestModel; +import it.aboutbits.springboot.toolbox.support.ApplicationTest; +import it.aboutbits.springboot.toolbox.support.WithPersistence; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import static org.assertj.core.api.Assertions.assertThat; + +@ApplicationTest +@WithPersistence +public class EntityIdJpaTest { + @Autowired + CustomTypeTestModelRepository repository; + + @Nested + class OwnId { + @Test + void inAndOut_shouldSucceed() { + var item = new CustomTypeTestModel(); + + var savedItem = repository.save(item); + + var retrievedItem = repository.findById(savedItem.getId()); + + assertThat(retrievedItem).isPresent() + .get() + .usingRecursiveComparison() + .isEqualTo(savedItem); + } + } + + @Nested + class ReferencedId { + @Test + void inAndOut_shouldSucceed() { + var item = new CustomTypeTestModel(); + item.setReferencedId(new ReferencedTestModel.ID(1234L)); + + var savedItem = repository.save(item); + + var retrievedItem = repository.findByReferencedId(savedItem.getReferencedId()); + + assertThat(retrievedItem).isPresent() + .get() + .usingRecursiveComparison() + .isEqualTo(savedItem); + } + } +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/impl/jpa/CustomTypeTestModel.java b/src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/impl/jpa/CustomTypeTestModel.java new file mode 100644 index 0000000..ed6075c --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/impl/jpa/CustomTypeTestModel.java @@ -0,0 +1,62 @@ +package it.aboutbits.springboot.toolbox.boot.persistence.impl.jpa; + +import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.persistence.identity.EntityId; +import it.aboutbits.springboot.toolbox.persistence.identity.Identified; +import it.aboutbits.springboot.toolbox.persistence.javatype.EmailAddressJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.IbanJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.ScaledBigDecimalJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedLongJavaType; +import it.aboutbits.springboot.toolbox.type.EmailAddress; +import it.aboutbits.springboot.toolbox.type.Iban; +import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.JavaType; + +@Entity +@Getter +@Setter +@Table(name = "custom_type_test_model") +public class CustomTypeTestModel implements Identified { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @JavaType(CustomTypeTestModel.ID.JavaType.class) + private ID id; + + @SuppressWarnings("JpaAttributeTypeInspection") + @JavaType(EmailAddressJavaType.class) + private EmailAddress email; + + @SuppressWarnings("JpaAttributeTypeInspection") + @JavaType(IbanJavaType.class) + private Iban iban; + + @SuppressWarnings("JpaAttributeTypeInspection") + @JavaType(ScaledBigDecimalJavaType.class) + private ScaledBigDecimal accountBalance; + + @JavaType(ReferencedTestModel.ID.JavaType.class) + private ReferencedTestModel.ID referencedId; + + public record ID( + Long value + ) implements EntityId { + + @Override + public String toString() { + return String.valueOf(value()); + } + + public static class JavaType extends WrappedLongJavaType implements AutoRegisteredJavaType { + public JavaType() { + super(ID.class); + } + } + } +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/impl/jpa/CustomTypeTestModelRepository.java b/src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/impl/jpa/CustomTypeTestModelRepository.java new file mode 100644 index 0000000..647ea69 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/impl/jpa/CustomTypeTestModelRepository.java @@ -0,0 +1,18 @@ +package it.aboutbits.springboot.toolbox.boot.persistence.impl.jpa; + +import it.aboutbits.springboot.toolbox.type.EmailAddress; +import it.aboutbits.springboot.toolbox.type.Iban; +import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface CustomTypeTestModelRepository extends JpaRepository { + Optional findByEmail(EmailAddress emailAddress); + + Optional findByIban(Iban iban); + + Optional findByAccountBalance(ScaledBigDecimal accountBalance); + + Optional findByReferencedId(ReferencedTestModel.ID otherId); +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/impl/jpa/ReferencedTestModel.java b/src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/impl/jpa/ReferencedTestModel.java new file mode 100644 index 0000000..349bac8 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/impl/jpa/ReferencedTestModel.java @@ -0,0 +1,25 @@ +package it.aboutbits.springboot.toolbox.boot.persistence.impl.jpa; + +import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.persistence.identity.EntityId; +import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedLongJavaType; + +public class ReferencedTestModel { + // we just use this to have and ID we can actually reference + + public record ID( + Long value + ) implements EntityId { + + @Override + public String toString() { + return String.valueOf(value()); + } + + public static class JavaType extends WrappedLongJavaType implements AutoRegisteredJavaType { + public JavaType() { + super(ID.class); + } + } + } +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/boot/swagger/SwaggerTest.java b/src/test/java/it/aboutbits/springboot/toolbox/boot/swagger/SwaggerTest.java new file mode 100644 index 0000000..7e1e12f --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/boot/swagger/SwaggerTest.java @@ -0,0 +1,36 @@ +package it.aboutbits.springboot.toolbox.boot.swagger; + +import it.aboutbits.springboot.toolbox.support.HttpTest; +import lombok.NonNull; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import static org.assertj.core.api.Assertions.assertThat; + +@HttpTest +public class SwaggerTest { + @Autowired + MockMvc mockMvc; + + @Test + void shouldBeAccessible() throws Exception { + var swaggerUiString = performGetAndReturnResult("/docs/swagger-ui/index.html"); + var swaggerSchemaString = performGetAndReturnResult("/docs/api"); + + assertThat(swaggerUiString).isNotBlank(); + assertThat(swaggerSchemaString).isNotBlank(); + } + + private @NonNull String performGetAndReturnResult(@NonNull String url) throws Exception { + var requestBuilder = MockMvcRequestBuilders.get(url) + .contentType(MediaType.APPLICATION_JSON); + + return mockMvc.perform(requestBuilder) + .andReturn() + .getResponse() + .getContentAsString(); + } +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/WrapperTypesJpaTest.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/WrapperTypesJpaTest.java new file mode 100644 index 0000000..c54b8b3 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/WrapperTypesJpaTest.java @@ -0,0 +1,337 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype; + +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.jpa.WrapperTypesModel; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.jpa.WrapperTypesModelRepository; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapBigDecimal; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapBigInteger; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapDouble; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapFloat; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapInteger; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapLong; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapScaledBigDecimal; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapShort; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapString; +import it.aboutbits.springboot.toolbox.support.ApplicationTest; +import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.beans.factory.annotation.Autowired; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import static org.assertj.core.api.Assertions.assertThat; + +@ApplicationTest +public class WrapperTypesJpaTest { + @Autowired + WrapperTypesModelRepository repository; + + @Nested + class WrapBigDecimalType { + @Test + void givenNull_inAndOut_shouldSucceed() { + var item = new WrapperTypesModel(); + item.setBigDecimalValue(null); + + var savedItem = repository.save(item); + + var retrievedItem = repository.findByBigDecimalValue(null); + + assertThat(retrievedItem).isPresent() + .get() + .usingRecursiveComparison() + .isEqualTo(savedItem); + } + + @ParameterizedTest + @ValueSource(doubles = {-1, 0, 1, -0.001, 0.001, -100_000_000, 100_000_000}) + void givenValues_inAndOut_shouldSucceed(double doubleValue) { + var item = new WrapperTypesModel(); + item.setBigDecimalValue(new WrapBigDecimal(BigDecimal.valueOf(doubleValue))); + + var savedItem = repository.save(item); + + var retrievedItem = repository.findByBigDecimalValue(savedItem.getBigDecimalValue()); + + assertThat(retrievedItem).isPresent() + .get() + .usingRecursiveComparison() + .isEqualTo(savedItem); + } + } + + @Nested + class WrapBigIntegerType { + @Test + void givenNull_inAndOut_shouldSucceed() { + var item = new WrapperTypesModel(); + item.setBigIntegerValue(null); + + var savedItem = repository.save(item); + + var retrievedItem = repository.findByBigIntegerValue(null); + + assertThat(retrievedItem).isPresent() + .get() + .usingRecursiveComparison() + .isEqualTo(savedItem); + } + + @ParameterizedTest + @ValueSource(ints = {-1, 0, 1, -100_000_000, 100_000_000}) + void givenValues_inAndOut_shouldSucceed(int intValue) { + var item = new WrapperTypesModel(); + item.setBigIntegerValue(new WrapBigInteger(BigInteger.valueOf(intValue))); + + var savedItem = repository.save(item); + + var retrievedItem = repository.findByBigIntegerValue(savedItem.getBigIntegerValue()); + + assertThat(retrievedItem).isPresent() + .get() + .usingRecursiveComparison() + .isEqualTo(savedItem); + } + } + + @Nested + class WrapDoubleType { + @Test + void givenNull_inAndOut_shouldSucceed() { + var item = new WrapperTypesModel(); + item.setDoubleValue(null); + + var savedItem = repository.save(item); + + var retrievedItem = repository.findByDoubleValue(null); + + assertThat(retrievedItem).isPresent() + .get() + .usingRecursiveComparison() + .isEqualTo(savedItem); + } + + @ParameterizedTest + @ValueSource(doubles = {-1, 0, 1, -0.001, 0.001, -100_000_000, 100_000_000}) + void givenValues_inAndOut_shouldSucceed(double doubleValue) { + var item = new WrapperTypesModel(); + item.setDoubleValue(new WrapDouble(doubleValue)); + + var savedItem = repository.save(item); + + var retrievedItem = repository.findByDoubleValue(savedItem.getDoubleValue()); + + assertThat(retrievedItem).isPresent() + .get() + .usingRecursiveComparison() + .isEqualTo(savedItem); + } + } + + @Nested + class WrapFloatType { + @Test + void givenNull_inAndOut_shouldSucceed() { + var item = new WrapperTypesModel(); + item.setFloatValue(null); + + var savedItem = repository.save(item); + + var retrievedItem = repository.findByFloatValue(null); + + assertThat(retrievedItem).isPresent() + .get() + .usingRecursiveComparison() + .isEqualTo(savedItem); + } + + @ParameterizedTest + @ValueSource(floats = {-1, 0, 1, -0.001f, 0.001f, -100_000_000, 100_000_000}) + void givenValues_inAndOut_shouldSucceed(float floatValue) { + var item = new WrapperTypesModel(); + item.setFloatValue(new WrapFloat(floatValue)); + + var savedItem = repository.save(item); + + var retrievedItem = repository.findByFloatValue(savedItem.getFloatValue()); + + assertThat(retrievedItem).isPresent() + .get() + .usingRecursiveComparison() + .isEqualTo(savedItem); + } + } + + @Nested + class WrapIntegerType { + @Test + void givenNull_inAndOut_shouldSucceed() { + var item = new WrapperTypesModel(); + item.setIntegerValue(null); + + var savedItem = repository.save(item); + + var retrievedItem = repository.findByIntegerValue(null); + + assertThat(retrievedItem).isPresent() + .get() + .usingRecursiveComparison() + .isEqualTo(savedItem); + } + + @ParameterizedTest + @ValueSource(ints = {-1, 0, 1, -100_000_000, 100_000_000}) + void givenValues_inAndOut_shouldSucceed(int intValue) { + var item = new WrapperTypesModel(); + item.setIntegerValue(new WrapInteger(intValue)); + + var savedItem = repository.save(item); + + var retrievedItem = repository.findByIntegerValue(savedItem.getIntegerValue()); + + assertThat(retrievedItem).isPresent() + .get() + .usingRecursiveComparison() + .isEqualTo(savedItem); + } + } + + @Nested + class WrapLongType { + @Test + void givenNull_inAndOut_shouldSucceed() { + var item = new WrapperTypesModel(); + item.setLongValue(null); + + var savedItem = repository.save(item); + + var retrievedItem = repository.findByLongValue(null); + + assertThat(retrievedItem).isPresent() + .get() + .usingRecursiveComparison() + .isEqualTo(savedItem); + } + + @ParameterizedTest + @ValueSource(longs = {-1, 0, 1, -100_000_000, 100_000_000}) + void givenValues_inAndOut_shouldSucceed(long longValue) { + var item = new WrapperTypesModel(); + item.setLongValue(new WrapLong(longValue)); + + var savedItem = repository.save(item); + + var retrievedItem = repository.findByLongValue(savedItem.getLongValue()); + + assertThat(retrievedItem).isPresent() + .get() + .usingRecursiveComparison() + .isEqualTo(savedItem); + } + } + + @Nested + class WrapScaledBigDecimalType { + @Test + void givenNull_inAndOut_shouldSucceed() { + var item = new WrapperTypesModel(); + item.setScaledBigDecimalValue(null); + + var savedItem = repository.save(item); + + var retrievedItem = repository.findByScaledBigDecimalValue(null); + + assertThat(retrievedItem).isPresent() + .get() + .usingRecursiveComparison() + .isEqualTo(savedItem); + } + + @ParameterizedTest + @ValueSource(doubles = {-1, 0, 1, -0.001, 0.001, -100_000_000, 100_000_000}) + void givenValues_inAndOut_shouldSucceed(double doubleValue) { + var item = new WrapperTypesModel(); + item.setScaledBigDecimalValue(new WrapScaledBigDecimal(new ScaledBigDecimal(doubleValue))); + + var savedItem = repository.save(item); + + var retrievedItem = repository.findByScaledBigDecimalValue(savedItem.getScaledBigDecimalValue()); + + assertThat(retrievedItem).isPresent() + .get() + .usingRecursiveComparison() + .isEqualTo(savedItem); + } + } + + @Nested + class WrapShortType { + @Test + void givenNull_inAndOut_shouldSucceed() { + var item = new WrapperTypesModel(); + item.setShortValue(null); + + var savedItem = repository.save(item); + + var retrievedItem = repository.findByShortValue(null); + + assertThat(retrievedItem).isPresent() + .get() + .usingRecursiveComparison() + .isEqualTo(savedItem); + } + + @ParameterizedTest + @ValueSource(shorts = {-1, 0, 1, -10_000, 10_000}) + void givenValues_inAndOut_shouldSucceed(short shortValue) { + var item = new WrapperTypesModel(); + item.setShortValue(new WrapShort(shortValue)); + + var savedItem = repository.save(item); + + var retrievedItem = repository.findByShortValue(savedItem.getShortValue()); + + assertThat(retrievedItem).isPresent() + .get() + .usingRecursiveComparison() + .isEqualTo(savedItem); + } + } + + @Nested + class WrapStringType { + @Test + void givenNull_inAndOut_shouldSucceed() { + var item = new WrapperTypesModel(); + item.setStringValue(null); + + var savedItem = repository.save(item); + + var retrievedItem = repository.findByStringValue(null); + + assertThat(retrievedItem).isPresent() + .get() + .usingRecursiveComparison() + .isEqualTo(savedItem); + } + + @ParameterizedTest + @ValueSource(strings = {"", " ", "test", "some longer test", "\r", "\n"}) + void givenValues_inAndOut_shouldSucceed(String stringValue) { + var item = new WrapperTypesModel(); + item.setStringValue(new WrapString(stringValue)); + + var savedItem = repository.save(item); + + var retrievedItem = repository.findByStringValue(savedItem.getStringValue()); + + assertThat(retrievedItem).isPresent() + .get() + .usingRecursiveComparison() + .isEqualTo(savedItem); + } + } +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapBigDecimalJavaType.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapBigDecimalJavaType.java new file mode 100644 index 0000000..5374d1a --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapBigDecimalJavaType.java @@ -0,0 +1,11 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype; + +import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedBigDecimalJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapBigDecimal; + +public final class WrapBigDecimalJavaType extends WrappedBigDecimalJavaType implements AutoRegisteredJavaType { + public WrapBigDecimalJavaType() { + super(WrapBigDecimal.class); + } +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapBigIntegerJavaType.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapBigIntegerJavaType.java new file mode 100644 index 0000000..99b796c --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapBigIntegerJavaType.java @@ -0,0 +1,11 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype; + +import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedBigIntegerJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapBigInteger; + +public final class WrapBigIntegerJavaType extends WrappedBigIntegerJavaType implements AutoRegisteredJavaType { + public WrapBigIntegerJavaType() { + super(WrapBigInteger.class); + } +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapDoubleJavaType.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapDoubleJavaType.java new file mode 100644 index 0000000..932d68d --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapDoubleJavaType.java @@ -0,0 +1,11 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype; + +import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedDoubleJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapDouble; + +public final class WrapDoubleJavaType extends WrappedDoubleJavaType implements AutoRegisteredJavaType { + public WrapDoubleJavaType() { + super(WrapDouble.class); + } +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapFloatJavaType.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapFloatJavaType.java new file mode 100644 index 0000000..908aab2 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapFloatJavaType.java @@ -0,0 +1,11 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype; + +import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedFloatJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapFloat; + +public final class WrapFloatJavaType extends WrappedFloatJavaType implements AutoRegisteredJavaType { + public WrapFloatJavaType() { + super(WrapFloat.class); + } +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapIntegerJavaType.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapIntegerJavaType.java new file mode 100644 index 0000000..6d5e350 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapIntegerJavaType.java @@ -0,0 +1,11 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype; + +import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedIntegerJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapInteger; + +public final class WrapIntegerJavaType extends WrappedIntegerJavaType implements AutoRegisteredJavaType { + public WrapIntegerJavaType() { + super(WrapInteger.class); + } +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapLongJavaType.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapLongJavaType.java new file mode 100644 index 0000000..c014d7e --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapLongJavaType.java @@ -0,0 +1,11 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype; + +import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedLongJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapLong; + +public final class WrapLongJavaType extends WrappedLongJavaType implements AutoRegisteredJavaType { + public WrapLongJavaType() { + super(WrapLong.class); + } +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapScaledBigDecimalJavaType.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapScaledBigDecimalJavaType.java new file mode 100644 index 0000000..f508865 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapScaledBigDecimalJavaType.java @@ -0,0 +1,11 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype; + +import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedScaledBigDecimalJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapScaledBigDecimal; + +public final class WrapScaledBigDecimalJavaType extends WrappedScaledBigDecimalJavaType implements AutoRegisteredJavaType { + public WrapScaledBigDecimalJavaType() { + super(WrapScaledBigDecimal.class); + } +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapShortJavaType.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapShortJavaType.java new file mode 100644 index 0000000..8a3440e --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapShortJavaType.java @@ -0,0 +1,11 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype; + +import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedShortJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapShort; + +public final class WrapShortJavaType extends WrappedShortJavaType implements AutoRegisteredJavaType { + public WrapShortJavaType() { + super(WrapShort.class); + } +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapStringJavaType.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapStringJavaType.java new file mode 100644 index 0000000..fb9e910 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapStringJavaType.java @@ -0,0 +1,11 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype; + +import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedStringJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapString; + +public final class WrapStringJavaType extends WrappedStringJavaType implements AutoRegisteredJavaType { + public WrapStringJavaType() { + super(WrapString.class); + } +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/jpa/WrapperTypesModel.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/jpa/WrapperTypesModel.java new file mode 100644 index 0000000..b778cf9 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/jpa/WrapperTypesModel.java @@ -0,0 +1,95 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype.impl.jpa; + +import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.persistence.identity.EntityId; +import it.aboutbits.springboot.toolbox.persistence.identity.Identified; +import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedLongJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype.WrapBigDecimalJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype.WrapBigIntegerJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype.WrapDoubleJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype.WrapFloatJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype.WrapIntegerJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype.WrapLongJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype.WrapScaledBigDecimalJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype.WrapShortJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype.WrapStringJavaType; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapBigDecimal; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapBigInteger; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapDouble; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapFloat; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapInteger; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapLong; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapScaledBigDecimal; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapShort; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapString; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.JavaType; + +@Entity +@Getter +@Setter +@Table(name = "wrapper_type_test_model") +public class WrapperTypesModel implements Identified { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @JavaType(ID.JavaType.class) + private ID id; + + @SuppressWarnings("JpaAttributeTypeInspection") + @JavaType(WrapBigDecimalJavaType.class) + private WrapBigDecimal bigDecimalValue; + + @SuppressWarnings("JpaAttributeTypeInspection") + @JavaType(WrapBigIntegerJavaType.class) + private WrapBigInteger bigIntegerValue; + + @SuppressWarnings("JpaAttributeTypeInspection") + @JavaType(WrapDoubleJavaType.class) + private WrapDouble doubleValue; + + @SuppressWarnings("JpaAttributeTypeInspection") + @JavaType(WrapFloatJavaType.class) + private WrapFloat floatValue; + + @SuppressWarnings("JpaAttributeTypeInspection") + @JavaType(WrapIntegerJavaType.class) + private WrapInteger integerValue; + + @SuppressWarnings("JpaAttributeTypeInspection") + @JavaType(WrapLongJavaType.class) + private WrapLong longValue; + + @SuppressWarnings("JpaAttributeTypeInspection") + @JavaType(WrapScaledBigDecimalJavaType.class) + private WrapScaledBigDecimal scaledBigDecimalValue; + + @SuppressWarnings("JpaAttributeTypeInspection") + @JavaType(WrapShortJavaType.class) + private WrapShort shortValue; + + @SuppressWarnings("JpaAttributeTypeInspection") + @JavaType(WrapStringJavaType.class) + private WrapString stringValue; + + public record ID( + Long value + ) implements EntityId { + + @Override + public String toString() { + return String.valueOf(value()); + } + + public static class JavaType extends WrappedLongJavaType implements AutoRegisteredJavaType { + public JavaType() { + super(ID.class); + } + } + } +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/jpa/WrapperTypesModelRepository.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/jpa/WrapperTypesModelRepository.java new file mode 100644 index 0000000..e7c2459 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/jpa/WrapperTypesModelRepository.java @@ -0,0 +1,34 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype.impl.jpa; + +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapBigDecimal; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapBigInteger; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapDouble; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapFloat; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapInteger; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapLong; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapScaledBigDecimal; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapShort; +import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapString; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface WrapperTypesModelRepository extends JpaRepository { + Optional findByBigDecimalValue(WrapBigDecimal value); + + Optional findByBigIntegerValue(WrapBigInteger value); + + Optional findByDoubleValue(WrapDouble value); + + Optional findByFloatValue(WrapFloat value); + + Optional findByIntegerValue(WrapInteger value); + + Optional findByLongValue(WrapLong value); + + Optional findByScaledBigDecimalValue(WrapScaledBigDecimal value); + + Optional findByShortValue(WrapShort value); + + Optional findByStringValue(WrapString value); +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapBigDecimal.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapBigDecimal.java new file mode 100644 index 0000000..5b3b2b8 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapBigDecimal.java @@ -0,0 +1,9 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype.impl.type; + +import it.aboutbits.springboot.toolbox.type.CustomType; + +import java.math.BigDecimal; + +public record WrapBigDecimal(BigDecimal value) implements CustomType { + +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapBigInteger.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapBigInteger.java new file mode 100644 index 0000000..f97a743 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapBigInteger.java @@ -0,0 +1,9 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype.impl.type; + +import it.aboutbits.springboot.toolbox.type.CustomType; + +import java.math.BigInteger; + +public record WrapBigInteger(BigInteger value) implements CustomType { + +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapDouble.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapDouble.java new file mode 100644 index 0000000..467527e --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapDouble.java @@ -0,0 +1,7 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype.impl.type; + +import it.aboutbits.springboot.toolbox.type.CustomType; + +public record WrapDouble(Double value) implements CustomType { + +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapFloat.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapFloat.java new file mode 100644 index 0000000..357b8aa --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapFloat.java @@ -0,0 +1,7 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype.impl.type; + +import it.aboutbits.springboot.toolbox.type.CustomType; + +public record WrapFloat(Float value) implements CustomType { + +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapInteger.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapInteger.java new file mode 100644 index 0000000..73a08af --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapInteger.java @@ -0,0 +1,7 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype.impl.type; + +import it.aboutbits.springboot.toolbox.type.CustomType; + +public record WrapInteger(Integer value) implements CustomType { + +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapLong.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapLong.java new file mode 100644 index 0000000..fc54ee0 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapLong.java @@ -0,0 +1,7 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype.impl.type; + +import it.aboutbits.springboot.toolbox.type.CustomType; + +public record WrapLong(Long value) implements CustomType { + +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapScaledBigDecimal.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapScaledBigDecimal.java new file mode 100644 index 0000000..87746eb --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapScaledBigDecimal.java @@ -0,0 +1,8 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype.impl.type; + +import it.aboutbits.springboot.toolbox.type.CustomType; +import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; + +public record WrapScaledBigDecimal(ScaledBigDecimal value) implements CustomType { + +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapShort.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapShort.java new file mode 100644 index 0000000..5933d1d --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapShort.java @@ -0,0 +1,7 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype.impl.type; + +import it.aboutbits.springboot.toolbox.type.CustomType; + +public record WrapShort(Short value) implements CustomType { + +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapString.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapString.java new file mode 100644 index 0000000..af28f4d --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/type/WrapString.java @@ -0,0 +1,7 @@ +package it.aboutbits.springboot.toolbox.persistence.javatype.impl.type; + +import it.aboutbits.springboot.toolbox.type.CustomType; + +public record WrapString(String value) implements CustomType { + +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/support/ApplicationTest.java b/src/test/java/it/aboutbits/springboot/toolbox/support/ApplicationTest.java new file mode 100644 index 0000000..6763f38 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/support/ApplicationTest.java @@ -0,0 +1,17 @@ +package it.aboutbits.springboot.toolbox.support; + +import it.aboutbits.springboot.toolbox.support.persistence.WithPostgres; +import org.springframework.boot.test.context.SpringBootTest; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@SpringBootTest +@WithPostgres +public @interface ApplicationTest { + +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/support/HttpTest.java b/src/test/java/it/aboutbits/springboot/toolbox/support/HttpTest.java new file mode 100644 index 0000000..723081e --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/support/HttpTest.java @@ -0,0 +1,13 @@ +package it.aboutbits.springboot.toolbox.support; + +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +@ApplicationTest +@AutoConfigureMockMvc +public @interface HttpTest { + +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/support/WithPersistence.java b/src/test/java/it/aboutbits/springboot/toolbox/support/WithPersistence.java new file mode 100644 index 0000000..e42ca53 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/support/WithPersistence.java @@ -0,0 +1,12 @@ +package it.aboutbits.springboot.toolbox.support; + +import it.aboutbits.springboot.toolbox.support.persistence.WithPostgres; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +@WithPostgres +public @interface WithPersistence { + +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/support/persistence/PostgresTestcontainer.java b/src/test/java/it/aboutbits/springboot/toolbox/support/persistence/PostgresTestcontainer.java new file mode 100644 index 0000000..8a21f17 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/support/persistence/PostgresTestcontainer.java @@ -0,0 +1,110 @@ +package it.aboutbits.springboot.toolbox.support.persistence; + +import lombok.extern.log4j.Log4j2; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.utility.DockerImageName; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.StringJoiner; + +@Log4j2 +public class PostgresTestcontainer implements BeforeAllCallback, AfterEachCallback { + public static final PostgreSQLContainer POSTGRES_CONTAINER; + private static final Set TABLES_TO_IGNORE = Set.of( + "databasechangelog", + "databasechangeloglock" + ); + + // See https://www.postgresql.org/docs/current/non-durability.html for details about PostgreSQL CLI parameters + static { + POSTGRES_CONTAINER = new PostgreSQLContainer<>( + DockerImageName.parse("postgres:16").asCompatibleSubstituteFor("postgres")) + .withDatabaseName("app") + .withCommand( + "postgres -c max_connections=500 -c fsync=off -c synchronous_commit=off -c full_page_writes=off -c max_wal_size=2GB -c checkpoint_timeout=20min" + ) + .withUsername("admin") + .withPassword("password") + .withReuse(true); + + POSTGRES_CONTAINER.start(); + } + + @Override + public void beforeAll(ExtensionContext extensionContext) { + System.setProperty("spring.datasource.url", POSTGRES_CONTAINER.getJdbcUrl()); + System.setProperty("spring.datasource.username", POSTGRES_CONTAINER.getUsername()); + System.setProperty("spring.datasource.password", POSTGRES_CONTAINER.getPassword()); + System.setProperty("spring.liquibase.url", POSTGRES_CONTAINER.getJdbcUrl()); + System.setProperty("spring.liquibase.user", POSTGRES_CONTAINER.getUsername()); + System.setProperty("spring.liquibase.password", POSTGRES_CONTAINER.getPassword()); + } + + @Override + public void afterEach(ExtensionContext extensionContext) throws Exception { + var connection = DriverManager.getConnection( + POSTGRES_CONTAINER.getJdbcUrl(), + POSTGRES_CONTAINER.getUsername(), + POSTGRES_CONTAINER.getPassword() + ); + + try { + connection.setAutoCommit(false); + var tablesToClean = loadTablesToClean(connection); + cleanTablesData(tablesToClean, connection); + connection.commit(); + } catch (SQLException exception) { + exception.printStackTrace(); + } + } + + private List loadTablesToClean(Connection connection) throws SQLException { + var databaseMetaData = connection.getMetaData(); + var resultSet = databaseMetaData.getTables( + connection.getCatalog(), null, null, new String[]{"TABLE"}); + + var tablesToClean = new ArrayList(); + while (resultSet.next()) { + var table = new TableData( + resultSet.getString("TABLE_SCHEM"), + resultSet.getString("TABLE_NAME") + ); + + if (!TABLES_TO_IGNORE.contains(table.name())) { + tablesToClean.add(table); + } + } + + return tablesToClean; + } + + private void cleanTablesData(List tablesToClean, Connection connection) throws SQLException { + if (tablesToClean.isEmpty()) { + return; + } + + log.debug("Cleaning Database tables {}", tablesToClean); + + var allTables = new StringJoiner(", "); + for (var table : tablesToClean) { + allTables.add(table.getFullyQualifiedName()); + } + + var statement = String.format("TRUNCATE %s RESTART IDENTITY CASCADE", allTables); + connection.prepareStatement(statement).execute(); + } + + private record TableData(String schema, String name) { + public String getFullyQualifiedName() { + return schema + "." + name; + } + } +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/support/persistence/WithPostgres.java b/src/test/java/it/aboutbits/springboot/toolbox/support/persistence/WithPostgres.java new file mode 100644 index 0000000..9f51713 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/support/persistence/WithPostgres.java @@ -0,0 +1,14 @@ +package it.aboutbits.springboot.toolbox.support.persistence; + +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.parallel.ResourceAccessMode; +import org.junit.jupiter.api.parallel.ResourceLock; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +@ExtendWith({PostgresTestcontainer.class}) +@ResourceLock(value = "Database", mode = ResourceAccessMode.READ_WRITE) +public @interface WithPostgres { +} diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml new file mode 100644 index 0000000..6a029c0 --- /dev/null +++ b/src/test/resources/application.yml @@ -0,0 +1,62 @@ +server: + shutdown: graceful + +spring: + lifecycle: + timeout-per-shutdown-phase: 30s + threads: + virtual: + enabled: true + mvc: + format: + date: yyyy-MM-dd + datasource: + driver-class-name: org.postgresql.Driver + url: jdbc:postgresql://localhost:5432/app?applicationName=ToolboxTestApp + username: root + password: password + hikari: + maximum-pool-size: 10 + schema: public + jpa: + open-in-view: false + properties: + hibernate: + jdbc: + lob: + non-contextual-creation: true + default-schema: ${spring.datasource.hikari.schema} + liquibase: + enabled: true + change-log: "classpath:db/changelog/master.yml" + user: ${spring.datasource.username} + password: ${spring.datasource.password} + default-schema: ${spring.datasource.hikari.schema} + test: + database: + replace: none + sql: + init: + continue-on-error: true + +springdoc: + api-docs: + path: /docs/api + groups: + enabled: true + enabled: true + swagger-ui: + path: /docs/swagger + csrf: + enabled: true + enabled: ${springdoc.api-docs.enabled} + tags-sorter: alpha + operations-sorter: alpha + doc-expansion: none + default-model-expand-depth: 99 + default-models-expand-depth: 99 + +logging: + level: + it.aboutbits: ${LOG_LEVEL:INFO} + root: ${LOG_LEVEL:WARN} diff --git a/src/test/resources/db/changelog/2024-09-06-create-custom-type-testing-table.yml b/src/test/resources/db/changelog/2024-09-06-create-custom-type-testing-table.yml new file mode 100644 index 0000000..9ee001a --- /dev/null +++ b/src/test/resources/db/changelog/2024-09-06-create-custom-type-testing-table.yml @@ -0,0 +1,27 @@ +databaseChangeLog: + - changeSet: + author: Andreas Hufler + id: 2024-09-06-create-custom-type-testing-table + changes: + - createTable: + tableName: custom_type_test_model + columns: + - column: + name: id + autoIncrement: true + type: bigserial + constraints: + nullable: false + primaryKey: true + - column: + name: email + type: text + - column: + name: iban + type: text + - column: + name: account_balance + type: double + - column: + name: referenced_id + type: bigint diff --git a/src/test/resources/db/changelog/2024-09-06-create-wrapper-type-testing-table.yml b/src/test/resources/db/changelog/2024-09-06-create-wrapper-type-testing-table.yml new file mode 100644 index 0000000..1ae8cb5 --- /dev/null +++ b/src/test/resources/db/changelog/2024-09-06-create-wrapper-type-testing-table.yml @@ -0,0 +1,42 @@ +databaseChangeLog: + - changeSet: + author: Andreas Hufler + id: 2024-09-06-create-wrapper-type-testing-table + changes: + - createTable: + tableName: wrapper_type_test_model + columns: + - column: + name: id + autoIncrement: true + type: bigserial + constraints: + nullable: false + primaryKey: true + - column: + name: big_decimal_value + type: double + - column: + name: big_integer_value + type: bigint + - column: + name: double_value + type: double + - column: + name: float_value + type: double + - column: + name: integer_value + type: bigint + - column: + name: long_value + type: bigint + - column: + name: scaled_big_decimal_value + type: double + - column: + name: short_value + type: bigint + - column: + name: string_value + type: text diff --git a/src/test/resources/db/changelog/master.yml b/src/test/resources/db/changelog/master.yml new file mode 100644 index 0000000..ae4ea78 --- /dev/null +++ b/src/test/resources/db/changelog/master.yml @@ -0,0 +1,7 @@ +databaseChangeLog: + - include: + file: 2024-09-06-create-custom-type-testing-table.yml + relativeToChangelogFile: true + - include: + file: 2024-09-06-create-wrapper-type-testing-table.yml + relativeToChangelogFile: true From 6eb2762212c2aef7c971711606a141be54c6493b Mon Sep 17 00:00:00 2001 From: Andreas Hufler Date: Mon, 9 Sep 2024 12:00:33 +0200 Subject: [PATCH 8/9] move things around and some minor improvements --- .../AbstractCustomTypeContributor.java | 17 ++- .../persistence/AutoRegisteredJavaType.java | 2 +- .../persistence/CustomTypeContributor.java | 2 +- .../RegisterCustomTypesWithSwagger.java} | 10 +- .../web}/CustomTypeConfiguration.java | 12 +- .../web}/CustomTypeScanner.java | 26 ++-- .../web/CustomTypeScannerRegistrar.java} | 11 +- ...RegisterCustomTypesWithJacksonAndMvc.java} | 6 +- .../type/swagger/EntityIdModelConverter.java | 64 --------- .../swagger/EntityIdPropertyCustomizer.java | 126 ------------------ .../jackson/CustomTypeDeserializer.java | 2 +- .../jackson/CustomTypeSerializer.java | 2 +- .../mvc/CustomTypePropertyEditor.java | 2 +- .../javatype/EmailAddressJavaType.java | 2 +- .../persistence/javatype/IbanJavaType.java | 2 +- .../javatype/ScaledBigDecimalJavaType.java | 2 +- .../reflection/util/ClassScannerUtil.java | 3 +- .../swagger/CustomTypeModelConverter.java | 11 +- .../swagger/CustomTypePropertyCustomizer.java | 23 +++- .../identity/EntityId.java | 2 +- .../identity/Identified.java | 2 +- .../aboutbits/springboot/toolbox/TestApp.java | 8 +- .../CustomTypeBindingsForControllerTest.java | 8 +- .../EntityIdBindingsForControllerTest.java | 6 +- .../mvc/body/BodyWithEmailAddress.java | 2 +- .../mvc/body/BodyWithEntityId.java | 8 ++ .../mvc/body/BodyWithIban.java | 2 +- .../mvc/body/BodyWithScaledBigDecimal.java | 2 +- .../controller/CustomTypeTestController.java | 8 +- .../controller/EntityIdTestController.java | 6 +- .../persistence/CustomTypeJpaTest.java | 6 +- .../persistence/EntityIdJpaTest.java | 8 +- .../impl/jpa/CustomTypeTestModel.java | 8 +- .../jpa/CustomTypeTestModelRepository.java | 2 +- .../impl/jpa/ReferencedTestModel.java | 6 +- .../swagger/SwaggerTest.java | 2 +- .../boot/mvc/body/BodyWithEntityId.java | 8 -- .../impl/javatype/WrapBigDecimalJavaType.java | 2 +- .../impl/javatype/WrapBigIntegerJavaType.java | 2 +- .../impl/javatype/WrapDoubleJavaType.java | 2 +- .../impl/javatype/WrapFloatJavaType.java | 2 +- .../impl/javatype/WrapIntegerJavaType.java | 2 +- .../impl/javatype/WrapLongJavaType.java | 2 +- .../WrapScaledBigDecimalJavaType.java | 2 +- .../impl/javatype/WrapShortJavaType.java | 2 +- .../impl/javatype/WrapStringJavaType.java | 2 +- .../javatype/impl/jpa/WrapperTypesModel.java | 6 +- 47 files changed, 133 insertions(+), 310 deletions(-) rename src/main/java/it/aboutbits/springboot/toolbox/{boot => autoconfiguration}/persistence/AbstractCustomTypeContributor.java (67%) rename src/main/java/it/aboutbits/springboot/toolbox/{boot => autoconfiguration}/persistence/AutoRegisteredJavaType.java (63%) rename src/main/java/it/aboutbits/springboot/toolbox/{boot => autoconfiguration}/persistence/CustomTypeContributor.java (71%) rename src/main/java/it/aboutbits/springboot/toolbox/{boot/type/swagger/RegisterCustomSwaggerTypes.java => autoconfiguration/swagger/RegisterCustomTypesWithSwagger.java} (52%) rename src/main/java/it/aboutbits/springboot/toolbox/{boot/type => autoconfiguration/web}/CustomTypeConfiguration.java (78%) rename src/main/java/it/aboutbits/springboot/toolbox/{boot/type => autoconfiguration/web}/CustomTypeScanner.java (56%) rename src/main/java/it/aboutbits/springboot/toolbox/{boot/type/CustomTypeConfigurationRegistrar.java => autoconfiguration/web/CustomTypeScannerRegistrar.java} (67%) rename src/main/java/it/aboutbits/springboot/toolbox/{boot/type/RegisterCustomTypes.java => autoconfiguration/web/RegisterCustomTypesWithJacksonAndMvc.java} (63%) delete mode 100644 src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/EntityIdModelConverter.java delete mode 100644 src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/EntityIdPropertyCustomizer.java rename src/main/java/it/aboutbits/springboot/toolbox/{type => }/jackson/CustomTypeDeserializer.java (99%) rename src/main/java/it/aboutbits/springboot/toolbox/{type => }/jackson/CustomTypeSerializer.java (95%) rename src/main/java/it/aboutbits/springboot/toolbox/{type => }/mvc/CustomTypePropertyEditor.java (98%) rename src/main/java/it/aboutbits/springboot/toolbox/{boot/type => }/swagger/CustomTypeModelConverter.java (87%) rename src/main/java/it/aboutbits/springboot/toolbox/{boot/type => }/swagger/CustomTypePropertyCustomizer.java (86%) rename src/main/java/it/aboutbits/springboot/toolbox/{persistence => type}/identity/EntityId.java (86%) rename src/main/java/it/aboutbits/springboot/toolbox/{persistence => type}/identity/Identified.java (54%) rename src/test/java/it/aboutbits/springboot/toolbox/{boot => autoconfiguration}/mvc/CustomTypeBindingsForControllerTest.java (95%) rename src/test/java/it/aboutbits/springboot/toolbox/{boot => autoconfiguration}/mvc/EntityIdBindingsForControllerTest.java (92%) rename src/test/java/it/aboutbits/springboot/toolbox/{boot => autoconfiguration}/mvc/body/BodyWithEmailAddress.java (66%) create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/body/BodyWithEntityId.java rename src/test/java/it/aboutbits/springboot/toolbox/{boot => autoconfiguration}/mvc/body/BodyWithIban.java (60%) rename src/test/java/it/aboutbits/springboot/toolbox/{boot => autoconfiguration}/mvc/body/BodyWithScaledBigDecimal.java (69%) rename src/test/java/it/aboutbits/springboot/toolbox/{boot => autoconfiguration}/mvc/controller/CustomTypeTestController.java (86%) rename src/test/java/it/aboutbits/springboot/toolbox/{boot => autoconfiguration}/mvc/controller/EntityIdTestController.java (81%) rename src/test/java/it/aboutbits/springboot/toolbox/{boot => autoconfiguration}/persistence/CustomTypeJpaTest.java (89%) rename src/test/java/it/aboutbits/springboot/toolbox/{boot => autoconfiguration}/persistence/EntityIdJpaTest.java (79%) rename src/test/java/it/aboutbits/springboot/toolbox/{boot => autoconfiguration}/persistence/impl/jpa/CustomTypeTestModel.java (86%) rename src/test/java/it/aboutbits/springboot/toolbox/{boot => autoconfiguration}/persistence/impl/jpa/CustomTypeTestModelRepository.java (89%) rename src/test/java/it/aboutbits/springboot/toolbox/{boot => autoconfiguration}/persistence/impl/jpa/ReferencedTestModel.java (71%) rename src/test/java/it/aboutbits/springboot/toolbox/{boot => autoconfiguration}/swagger/SwaggerTest.java (94%) delete mode 100644 src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithEntityId.java diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/persistence/AbstractCustomTypeContributor.java b/src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/AbstractCustomTypeContributor.java similarity index 67% rename from src/main/java/it/aboutbits/springboot/toolbox/boot/persistence/AbstractCustomTypeContributor.java rename to src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/AbstractCustomTypeContributor.java index 5b6690a..3f0f02e 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/boot/persistence/AbstractCustomTypeContributor.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/AbstractCustomTypeContributor.java @@ -1,4 +1,4 @@ -package it.aboutbits.springboot.toolbox.boot.persistence; +package it.aboutbits.springboot.toolbox.autoconfiguration.persistence; import it.aboutbits.springboot.toolbox.reflection.util.ClassScannerUtil; import lombok.SneakyThrows; @@ -11,28 +11,27 @@ import java.util.Set; public abstract class AbstractCustomTypeContributor implements TypeContributor { - private final ClassScannerUtil.ClassScanner classScanner; + @SuppressWarnings("rawtypes") + private final Set> relevantTypes; protected AbstractCustomTypeContributor(String... packageNames) { - classScanner = ClassScannerUtil.getScannerForPackages(packageNames); + var classScanner = ClassScannerUtil.getScannerForPackages(packageNames); + + this.relevantTypes = findAllRelevantTypes(classScanner); } @SneakyThrows({InstantiationException.class, IllegalAccessException.class, InvocationTargetException.class}) @Override public void contribute(TypeContributions typeContributions, ServiceRegistry serviceRegistry) { - var types = findAllRelevantTypes(); - - for (var type : types) { + for (var type : relevantTypes) { typeContributions.contributeJavaType( (JavaType) type.getConstructors()[0].newInstance() ); } - - classScanner.close(); } @SuppressWarnings("rawtypes") - private Set> findAllRelevantTypes() { + private static Set> findAllRelevantTypes(ClassScannerUtil.ClassScanner classScanner) { return classScanner.getSubTypesOf(AutoRegisteredJavaType.class); } } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/persistence/AutoRegisteredJavaType.java b/src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/AutoRegisteredJavaType.java similarity index 63% rename from src/main/java/it/aboutbits/springboot/toolbox/boot/persistence/AutoRegisteredJavaType.java rename to src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/AutoRegisteredJavaType.java index 393520c..881a05b 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/boot/persistence/AutoRegisteredJavaType.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/AutoRegisteredJavaType.java @@ -1,4 +1,4 @@ -package it.aboutbits.springboot.toolbox.boot.persistence; +package it.aboutbits.springboot.toolbox.autoconfiguration.persistence; import org.hibernate.type.descriptor.java.JavaType; diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/persistence/CustomTypeContributor.java b/src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/CustomTypeContributor.java similarity index 71% rename from src/main/java/it/aboutbits/springboot/toolbox/boot/persistence/CustomTypeContributor.java rename to src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/CustomTypeContributor.java index c7c4208..8bd8407 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/boot/persistence/CustomTypeContributor.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/CustomTypeContributor.java @@ -1,4 +1,4 @@ -package it.aboutbits.springboot.toolbox.boot.persistence; +package it.aboutbits.springboot.toolbox.autoconfiguration.persistence; public final class CustomTypeContributor extends AbstractCustomTypeContributor { public CustomTypeContributor() { diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/RegisterCustomSwaggerTypes.java b/src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/swagger/RegisterCustomTypesWithSwagger.java similarity index 52% rename from src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/RegisterCustomSwaggerTypes.java rename to src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/swagger/RegisterCustomTypesWithSwagger.java index 38c0b43..ed99c70 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/RegisterCustomSwaggerTypes.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/swagger/RegisterCustomTypesWithSwagger.java @@ -1,5 +1,7 @@ -package it.aboutbits.springboot.toolbox.boot.type.swagger; +package it.aboutbits.springboot.toolbox.autoconfiguration.swagger; +import it.aboutbits.springboot.toolbox.swagger.CustomTypeModelConverter; +import it.aboutbits.springboot.toolbox.swagger.CustomTypePropertyCustomizer; import org.springframework.context.annotation.Import; import java.lang.annotation.ElementType; @@ -11,9 +13,7 @@ @Retention(RetentionPolicy.RUNTIME) @Import({ CustomTypeModelConverter.class, - CustomTypePropertyCustomizer.class, - EntityIdModelConverter.class, - EntityIdPropertyCustomizer.class + CustomTypePropertyCustomizer.class }) -public @interface RegisterCustomSwaggerTypes { +public @interface RegisterCustomTypesWithSwagger { } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeConfiguration.java b/src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/web/CustomTypeConfiguration.java similarity index 78% rename from src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeConfiguration.java rename to src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/web/CustomTypeConfiguration.java index 09d4f48..231739a 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeConfiguration.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/web/CustomTypeConfiguration.java @@ -1,9 +1,9 @@ -package it.aboutbits.springboot.toolbox.boot.type; +package it.aboutbits.springboot.toolbox.autoconfiguration.web; +import it.aboutbits.springboot.toolbox.jackson.CustomTypeDeserializer; +import it.aboutbits.springboot.toolbox.jackson.CustomTypeSerializer; +import it.aboutbits.springboot.toolbox.mvc.CustomTypePropertyEditor; import it.aboutbits.springboot.toolbox.type.CustomType; -import it.aboutbits.springboot.toolbox.type.jackson.CustomTypeDeserializer; -import it.aboutbits.springboot.toolbox.type.jackson.CustomTypeSerializer; -import it.aboutbits.springboot.toolbox.type.mvc.CustomTypePropertyEditor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; import org.springframework.context.annotation.Bean; @@ -23,7 +23,7 @@ public static class CustomTypePropertyBinder { private final Set> types; public CustomTypePropertyBinder(CustomTypeScanner configuration) { - this.types = configuration.findAllCustomTypeRecords(); + this.types = configuration.getRelevantTypes(); } @InitBinder @@ -36,7 +36,7 @@ public void initBinder(WebDataBinder binder) { @Bean public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer(CustomTypeScanner configuration) { - var types = configuration.findAllCustomTypeRecords(); + var types = configuration.getRelevantTypes(); var deserializers = types.stream() .map(CustomTypeDeserializer::new) diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeScanner.java b/src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/web/CustomTypeScanner.java similarity index 56% rename from src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeScanner.java rename to src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/web/CustomTypeScanner.java index 9d3327f..ca98f35 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeScanner.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/web/CustomTypeScanner.java @@ -1,21 +1,23 @@ -package it.aboutbits.springboot.toolbox.boot.type; +package it.aboutbits.springboot.toolbox.autoconfiguration.web; import it.aboutbits.springboot.toolbox.reflection.util.ClassScannerUtil; import it.aboutbits.springboot.toolbox.type.CustomType; -import jakarta.annotation.PostConstruct; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.Set; import java.util.stream.Collectors; @Slf4j +@Getter public class CustomTypeScanner { - public static final String LIBRARY_BASE_PACKAGE_NAME = "it.aboutbits.springboot.toolbox"; + private static final String LIBRARY_BASE_PACKAGE_NAME = "it.aboutbits.springboot.toolbox"; - private String[] packageNamesToScan; - private ClassScannerUtil.ClassScanner classScanner; + @SuppressWarnings("rawtypes") + private Set> relevantTypes = new HashSet<>(); public void setAdditionalTypePackages(String[] additionalTypePackages) { var tmp = new ArrayList(); @@ -24,20 +26,18 @@ public void setAdditionalTypePackages(String[] additionalTypePackages) { .filter(item -> !item.isBlank()) .collect(Collectors.toSet())); - this.packageNamesToScan = tmp.toArray(new String[0]); - - classScanner = ClassScannerUtil.getScannerForPackages(packageNamesToScan); - } + var packageNamesToScan = tmp.toArray(new String[0]); - @PostConstruct - public void init() { log.info("CustomTypeConfiguration enabled. Scanning: {}", Arrays.toString(packageNamesToScan)); + var classScanner = ClassScannerUtil.getScannerForPackages(packageNamesToScan); + + this.relevantTypes = findAllCustomTypeRecords(classScanner); } @SuppressWarnings("rawtypes") - public Set> findAllCustomTypeRecords() { + public static Set> findAllCustomTypeRecords(ClassScannerUtil.ClassScanner classScanner) { return classScanner.getSubTypesOf(CustomType.class).stream() .filter(Record.class::isAssignableFrom) - .collect(Collectors.toSet()); + .collect(Collectors.toSet()); //TODO: ttp:// } } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeConfigurationRegistrar.java b/src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/web/CustomTypeScannerRegistrar.java similarity index 67% rename from src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeConfigurationRegistrar.java rename to src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/web/CustomTypeScannerRegistrar.java index b2750bb..a77f0b7 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/CustomTypeConfigurationRegistrar.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/web/CustomTypeScannerRegistrar.java @@ -1,4 +1,4 @@ -package it.aboutbits.springboot.toolbox.boot.type; +package it.aboutbits.springboot.toolbox.autoconfiguration.web; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; @@ -8,14 +8,19 @@ import java.util.Objects; -public class CustomTypeConfigurationRegistrar implements ImportBeanDefinitionRegistrar { +public class CustomTypeScannerRegistrar implements ImportBeanDefinitionRegistrar { + /** + * We use this to register a new bean with actual configuration parameters coming from an annotation. + * That way we can use @RegisterCustomTypesWithJacksonAndMvc and take the parameter "additionalTypePackages" to + * scan packages. + */ @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { var attributes = new AnnotationAttributes( Objects.requireNonNull( metadata.getAnnotationAttributes( - RegisterCustomTypes.class.getName() + RegisterCustomTypesWithJacksonAndMvc.class.getName() ) ) ); diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/RegisterCustomTypes.java b/src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/web/RegisterCustomTypesWithJacksonAndMvc.java similarity index 63% rename from src/main/java/it/aboutbits/springboot/toolbox/boot/type/RegisterCustomTypes.java rename to src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/web/RegisterCustomTypesWithJacksonAndMvc.java index 4a6d307..c4d6ffe 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/RegisterCustomTypes.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/web/RegisterCustomTypesWithJacksonAndMvc.java @@ -1,4 +1,4 @@ -package it.aboutbits.springboot.toolbox.boot.type; +package it.aboutbits.springboot.toolbox.autoconfiguration.web; import org.springframework.context.annotation.Import; @@ -9,7 +9,7 @@ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) -@Import({CustomTypeConfigurationRegistrar.class, CustomTypeConfiguration.class}) -public @interface RegisterCustomTypes { +@Import({CustomTypeScannerRegistrar.class, CustomTypeConfiguration.class}) +public @interface RegisterCustomTypesWithJacksonAndMvc { String[] additionalTypePackages() default ""; } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/EntityIdModelConverter.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/EntityIdModelConverter.java deleted file mode 100644 index 8a0cfc5..0000000 --- a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/EntityIdModelConverter.java +++ /dev/null @@ -1,64 +0,0 @@ -package it.aboutbits.springboot.toolbox.boot.type.swagger; - -import io.swagger.v3.core.converter.AnnotatedType; -import io.swagger.v3.core.converter.ModelConverter; -import io.swagger.v3.core.converter.ModelConverterContext; -import io.swagger.v3.oas.models.media.Schema; -import it.aboutbits.springboot.toolbox.persistence.identity.EntityId; -import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil; -import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; -import org.springframework.stereotype.Component; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.Iterator; - -@Component -public class EntityIdModelConverter implements ModelConverter { - - @Override - public Schema resolve( - AnnotatedType annotatedType, - ModelConverterContext context, - Iterator chain - ) { - - var type = annotatedType.getType(); - - if (type instanceof Class clazz && EntityId.class.isAssignableFrom(clazz)) { - var constructor = RecordReflectionUtil.getCanonicalConstructor(clazz); - - var wrappedType = constructor.getParameters()[0].getType(); - - if (Short.class.isAssignableFrom(wrappedType)) { - return context.resolve(new AnnotatedType(Short.TYPE)); - } - if (Integer.class.isAssignableFrom(wrappedType)) { - return context.resolve(new AnnotatedType(Integer.TYPE)); - } - if (Long.class.isAssignableFrom(wrappedType)) { - return context.resolve(new AnnotatedType(Long.TYPE)); - } - if (BigInteger.class.isAssignableFrom(wrappedType)) { - return context.resolve(new AnnotatedType(Long.TYPE)); - } - if (Float.class.isAssignableFrom(wrappedType)) { - return context.resolve(new AnnotatedType(Float.TYPE)); - } - if (Double.class.isAssignableFrom(wrappedType)) { - return context.resolve(new AnnotatedType(Double.TYPE)); - } - if (BigDecimal.class.isAssignableFrom(wrappedType)) { - return context.resolve(new AnnotatedType(Double.TYPE)); - } - if (ScaledBigDecimal.class.isAssignableFrom(wrappedType)) { - return context.resolve(new AnnotatedType(Double.TYPE)); - } - if (String.class.isAssignableFrom(wrappedType)) { - return context.resolve(new AnnotatedType(String.class)); - } - } - - return chain.hasNext() ? chain.next().resolve(annotatedType, context, chain) : null; - } -} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/EntityIdPropertyCustomizer.java b/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/EntityIdPropertyCustomizer.java deleted file mode 100644 index 968ee9f..0000000 --- a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/EntityIdPropertyCustomizer.java +++ /dev/null @@ -1,126 +0,0 @@ -package it.aboutbits.springboot.toolbox.boot.type.swagger; - -import com.fasterxml.jackson.databind.type.SimpleType; -import io.swagger.v3.core.converter.AnnotatedType; -import io.swagger.v3.oas.models.media.Schema; -import it.aboutbits.springboot.toolbox.persistence.identity.EntityId; -import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil; -import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; -import lombok.NonNull; -import lombok.extern.slf4j.Slf4j; -import org.springdoc.core.customizers.PropertyCustomizer; -import org.springframework.stereotype.Component; - -import java.math.BigDecimal; -import java.math.BigInteger; - -@Slf4j -@Component -public class EntityIdPropertyCustomizer implements PropertyCustomizer { - @Override - public Schema customize(Schema property, AnnotatedType annotatedType) { - var type = annotatedType.getType(); - - if (type instanceof SimpleType simpleType && EntityId.class.isAssignableFrom(simpleType.getRawClass())) { - String displayName = null; - - var bindings = simpleType.getBindings(); - var boundType = bindings.getBoundType(0); - - if (boundType == null) { - var rawClass = simpleType.getRawClass(); - displayName = resolveDisplayName(rawClass); - } - - var constructor = RecordReflectionUtil.getCanonicalConstructor(simpleType.getRawClass()); - - var wrappedType = constructor.getParameters()[0].getType(); - - - if (Short.class.isAssignableFrom(wrappedType)) { - property.type("integer"); - property.format(""); - property.setDescription(displayName); - property.setProperties(null); - property.set$ref(null); - return property; - } - if (Integer.class.isAssignableFrom(wrappedType)) { - property.type("integer"); - property.format("int32"); - property.setDescription(displayName); - property.setProperties(null); - property.set$ref(null); - return property; - } - if (Long.class.isAssignableFrom(wrappedType)) { - property.type("integer"); - property.format("int64"); - property.setDescription(displayName); - property.setProperties(null); - property.set$ref(null); - return property; - } - if (BigInteger.class.isAssignableFrom(wrappedType)) { - property.type("integer"); - property.format("int64"); - property.setDescription(displayName); - property.setProperties(null); - property.set$ref(null); - return property; - } - if (Float.class.isAssignableFrom(wrappedType)) { - property.type("number"); - property.format("float"); - property.setDescription(displayName); - property.setProperties(null); - property.set$ref(null); - return property; - } - if (Double.class.isAssignableFrom(wrappedType)) { - property.type("number"); - property.format("double"); - property.setDescription(displayName); - property.setProperties(null); - property.set$ref(null); - return property; - } - if (BigDecimal.class.isAssignableFrom(wrappedType)) { - property.type("number"); - property.format(""); - property.setDescription(displayName); - property.setProperties(null); - property.set$ref(null); - return property; - } - if (ScaledBigDecimal.class.isAssignableFrom(wrappedType)) { - property.type("number"); - property.format(""); - property.setDescription(displayName); - property.setProperties(null); - property.set$ref(null); - return property; - } - if (String.class.isAssignableFrom(wrappedType)) { - property.type("string"); - property.format(null); - property.setDescription(displayName); - property.setProperties(null); - property.set$ref(null); - return property; - } - log.warn("Property {} of type EntityId: Can not resolve parameter type!", property.getName()); - } - - return property; - } - - @NonNull - private static String resolveDisplayName(Class rawClass) { - var parent = rawClass.getEnclosingClass(); - if (parent != null) { - return parent.getSimpleName() + "." + rawClass.getSimpleName(); - } - return rawClass.getSimpleName(); - } -} diff --git a/src/main/java/it/aboutbits/springboot/toolbox/type/jackson/CustomTypeDeserializer.java b/src/main/java/it/aboutbits/springboot/toolbox/jackson/CustomTypeDeserializer.java similarity index 99% rename from src/main/java/it/aboutbits/springboot/toolbox/type/jackson/CustomTypeDeserializer.java rename to src/main/java/it/aboutbits/springboot/toolbox/jackson/CustomTypeDeserializer.java index 1137753..7d39bc0 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/type/jackson/CustomTypeDeserializer.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/jackson/CustomTypeDeserializer.java @@ -1,4 +1,4 @@ -package it.aboutbits.springboot.toolbox.type.jackson; +package it.aboutbits.springboot.toolbox.jackson; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; diff --git a/src/main/java/it/aboutbits/springboot/toolbox/type/jackson/CustomTypeSerializer.java b/src/main/java/it/aboutbits/springboot/toolbox/jackson/CustomTypeSerializer.java similarity index 95% rename from src/main/java/it/aboutbits/springboot/toolbox/type/jackson/CustomTypeSerializer.java rename to src/main/java/it/aboutbits/springboot/toolbox/jackson/CustomTypeSerializer.java index 4101800..1b8909d 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/type/jackson/CustomTypeSerializer.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/jackson/CustomTypeSerializer.java @@ -1,4 +1,4 @@ -package it.aboutbits.springboot.toolbox.type.jackson; +package it.aboutbits.springboot.toolbox.jackson; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; diff --git a/src/main/java/it/aboutbits/springboot/toolbox/type/mvc/CustomTypePropertyEditor.java b/src/main/java/it/aboutbits/springboot/toolbox/mvc/CustomTypePropertyEditor.java similarity index 98% rename from src/main/java/it/aboutbits/springboot/toolbox/type/mvc/CustomTypePropertyEditor.java rename to src/main/java/it/aboutbits/springboot/toolbox/mvc/CustomTypePropertyEditor.java index 1b9af95..2b06cfd 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/type/mvc/CustomTypePropertyEditor.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/mvc/CustomTypePropertyEditor.java @@ -1,4 +1,4 @@ -package it.aboutbits.springboot.toolbox.type.mvc; +package it.aboutbits.springboot.toolbox.mvc; import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil; import it.aboutbits.springboot.toolbox.type.CustomType; diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/EmailAddressJavaType.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/EmailAddressJavaType.java index 496c029..4ee5961 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/EmailAddressJavaType.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/EmailAddressJavaType.java @@ -1,6 +1,6 @@ package it.aboutbits.springboot.toolbox.persistence.javatype; -import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.AutoRegisteredJavaType; import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedStringJavaType; import it.aboutbits.springboot.toolbox.type.EmailAddress; diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/IbanJavaType.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/IbanJavaType.java index 7347b03..020c787 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/IbanJavaType.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/IbanJavaType.java @@ -1,6 +1,6 @@ package it.aboutbits.springboot.toolbox.persistence.javatype; -import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.AutoRegisteredJavaType; import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedStringJavaType; import it.aboutbits.springboot.toolbox.type.Iban; diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/ScaledBigDecimalJavaType.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/ScaledBigDecimalJavaType.java index 5819f43..7b659db 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/ScaledBigDecimalJavaType.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/javatype/ScaledBigDecimalJavaType.java @@ -1,6 +1,6 @@ package it.aboutbits.springboot.toolbox.persistence.javatype; -import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.AutoRegisteredJavaType; import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedBigDecimalJavaType; import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; diff --git a/src/main/java/it/aboutbits/springboot/toolbox/reflection/util/ClassScannerUtil.java b/src/main/java/it/aboutbits/springboot/toolbox/reflection/util/ClassScannerUtil.java index 13016ca..653dd52 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/reflection/util/ClassScannerUtil.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/reflection/util/ClassScannerUtil.java @@ -15,7 +15,7 @@ public static ClassScanner getScannerForPackages(String... packages) { return new ClassScanner(packages); } - public static final class ClassScanner { + public static final class ClassScanner implements AutoCloseable { private final ScanResult scanResult; private ClassScanner(String... packages) { @@ -34,6 +34,7 @@ public Set> getSubTypesOf(@NonNull Class clazz) { .collect(Collectors.toSet()); } + @Override public void close() { scanResult.close(); } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/CustomTypeModelConverter.java b/src/main/java/it/aboutbits/springboot/toolbox/swagger/CustomTypeModelConverter.java similarity index 87% rename from src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/CustomTypeModelConverter.java rename to src/main/java/it/aboutbits/springboot/toolbox/swagger/CustomTypeModelConverter.java index f99009a..803937f 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/CustomTypeModelConverter.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/swagger/CustomTypeModelConverter.java @@ -1,19 +1,17 @@ -package it.aboutbits.springboot.toolbox.boot.type.swagger; +package it.aboutbits.springboot.toolbox.swagger; import io.swagger.v3.core.converter.AnnotatedType; import io.swagger.v3.core.converter.ModelConverter; import io.swagger.v3.core.converter.ModelConverterContext; import io.swagger.v3.oas.models.media.Schema; -import it.aboutbits.springboot.toolbox.persistence.identity.EntityId; import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil; import it.aboutbits.springboot.toolbox.type.CustomType; import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; -import org.springframework.stereotype.Component; import java.math.BigDecimal; +import java.math.BigInteger; import java.util.Iterator; -@Component public class CustomTypeModelConverter implements ModelConverter { @Override @@ -25,8 +23,7 @@ public Schema resolve( var type = annotatedType.getType(); - if (type instanceof Class clazz && CustomType.class.isAssignableFrom(clazz) && !EntityId.class.isAssignableFrom( - clazz)) { + if (type instanceof Class clazz && CustomType.class.isAssignableFrom(clazz)) { var constructor = RecordReflectionUtil.getCanonicalConstructor(clazz); var wrappedType = constructor.getParameters()[0].getType(); @@ -39,7 +36,7 @@ public Schema resolve( if (Long.class.isAssignableFrom(wrappedType)) { return context.resolve(new AnnotatedType(Long.TYPE)); } - if (BigDecimal.class.isAssignableFrom(wrappedType)) { + if (BigInteger.class.isAssignableFrom(wrappedType)) { return context.resolve(new AnnotatedType(Long.TYPE)); } if (Float.class.isAssignableFrom(wrappedType)) { diff --git a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/CustomTypePropertyCustomizer.java b/src/main/java/it/aboutbits/springboot/toolbox/swagger/CustomTypePropertyCustomizer.java similarity index 86% rename from src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/CustomTypePropertyCustomizer.java rename to src/main/java/it/aboutbits/springboot/toolbox/swagger/CustomTypePropertyCustomizer.java index b2bd284..fd3f81e 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/boot/type/swagger/CustomTypePropertyCustomizer.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/swagger/CustomTypePropertyCustomizer.java @@ -1,32 +1,34 @@ -package it.aboutbits.springboot.toolbox.boot.type.swagger; +package it.aboutbits.springboot.toolbox.swagger; import com.fasterxml.jackson.databind.type.SimpleType; import io.swagger.v3.core.converter.AnnotatedType; import io.swagger.v3.oas.models.media.Schema; -import it.aboutbits.springboot.toolbox.persistence.identity.EntityId; import it.aboutbits.springboot.toolbox.reflection.util.RecordReflectionUtil; import it.aboutbits.springboot.toolbox.type.CustomType; import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; +import it.aboutbits.springboot.toolbox.type.identity.EntityId; +import lombok.NonNull; import lombok.extern.slf4j.Slf4j; import org.springdoc.core.customizers.PropertyCustomizer; -import org.springframework.stereotype.Component; import java.math.BigDecimal; import java.math.BigInteger; @Slf4j -@Component public class CustomTypePropertyCustomizer implements PropertyCustomizer { @Override public Schema customize(Schema property, AnnotatedType annotatedType) { var type = annotatedType.getType(); - if (type instanceof SimpleType simpleType && CustomType.class.isAssignableFrom(simpleType.getRawClass()) && !EntityId.class.isAssignableFrom( - simpleType.getRawClass())) { + if (type instanceof SimpleType simpleType && CustomType.class.isAssignableFrom(simpleType.getRawClass())) { var rawClass = simpleType.getRawClass(); var displayName = rawClass.getSimpleName(); + if (EntityId.class.isAssignableFrom(rawClass)) { + displayName = resolveEntityIdDisplayName(rawClass); + } + var constructor = RecordReflectionUtil.getCanonicalConstructor(rawClass); var wrappedType = constructor.getParameters()[0].getType(); @@ -109,4 +111,13 @@ public Schema customize(Schema property, AnnotatedType annotatedType) { return property; } + + @NonNull + private static String resolveEntityIdDisplayName(Class rawClass) { + var parent = rawClass.getEnclosingClass(); + if (parent != null) { + return parent.getSimpleName() + "." + rawClass.getSimpleName(); + } + return rawClass.getSimpleName(); + } } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/EntityId.java b/src/main/java/it/aboutbits/springboot/toolbox/type/identity/EntityId.java similarity index 86% rename from src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/EntityId.java rename to src/main/java/it/aboutbits/springboot/toolbox/type/identity/EntityId.java index 1d89385..4f8ad59 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/EntityId.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/type/identity/EntityId.java @@ -1,4 +1,4 @@ -package it.aboutbits.springboot.toolbox.persistence.identity; +package it.aboutbits.springboot.toolbox.type.identity; import it.aboutbits.springboot.toolbox.type.CustomType; diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/Identified.java b/src/main/java/it/aboutbits/springboot/toolbox/type/identity/Identified.java similarity index 54% rename from src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/Identified.java rename to src/main/java/it/aboutbits/springboot/toolbox/type/identity/Identified.java index caeb5d9..6d0613b 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/persistence/identity/Identified.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/type/identity/Identified.java @@ -1,4 +1,4 @@ -package it.aboutbits.springboot.toolbox.persistence.identity; +package it.aboutbits.springboot.toolbox.type.identity; public interface Identified> { ID getId(); diff --git a/src/test/java/it/aboutbits/springboot/toolbox/TestApp.java b/src/test/java/it/aboutbits/springboot/toolbox/TestApp.java index 68f1947..51be45a 100644 --- a/src/test/java/it/aboutbits/springboot/toolbox/TestApp.java +++ b/src/test/java/it/aboutbits/springboot/toolbox/TestApp.java @@ -1,14 +1,14 @@ package it.aboutbits.springboot.toolbox; -import it.aboutbits.springboot.toolbox.boot.type.RegisterCustomTypes; -import it.aboutbits.springboot.toolbox.boot.type.swagger.RegisterCustomSwaggerTypes; +import it.aboutbits.springboot.toolbox.autoconfiguration.swagger.RegisterCustomTypesWithSwagger; +import it.aboutbits.springboot.toolbox.autoconfiguration.web.RegisterCustomTypesWithJacksonAndMvc; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SuppressWarnings("checkstyle:HideUtilityClassConstructor") @SpringBootApplication -@RegisterCustomTypes -@RegisterCustomSwaggerTypes +@RegisterCustomTypesWithJacksonAndMvc +@RegisterCustomTypesWithSwagger public class TestApp { public static void main(String[] args) { diff --git a/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/CustomTypeBindingsForControllerTest.java b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/CustomTypeBindingsForControllerTest.java similarity index 95% rename from src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/CustomTypeBindingsForControllerTest.java rename to src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/CustomTypeBindingsForControllerTest.java index 36fcf00..0017b0a 100644 --- a/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/CustomTypeBindingsForControllerTest.java +++ b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/CustomTypeBindingsForControllerTest.java @@ -1,9 +1,9 @@ -package it.aboutbits.springboot.toolbox.boot.mvc; +package it.aboutbits.springboot.toolbox.autoconfiguration.mvc; import com.fasterxml.jackson.databind.ObjectMapper; -import it.aboutbits.springboot.toolbox.boot.mvc.body.BodyWithEmailAddress; -import it.aboutbits.springboot.toolbox.boot.mvc.body.BodyWithIban; -import it.aboutbits.springboot.toolbox.boot.mvc.body.BodyWithScaledBigDecimal; +import it.aboutbits.springboot.toolbox.autoconfiguration.mvc.body.BodyWithEmailAddress; +import it.aboutbits.springboot.toolbox.autoconfiguration.mvc.body.BodyWithIban; +import it.aboutbits.springboot.toolbox.autoconfiguration.mvc.body.BodyWithScaledBigDecimal; import it.aboutbits.springboot.toolbox.support.HttpTest; import it.aboutbits.springboot.toolbox.type.EmailAddress; import it.aboutbits.springboot.toolbox.type.Iban; diff --git a/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/EntityIdBindingsForControllerTest.java b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/EntityIdBindingsForControllerTest.java similarity index 92% rename from src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/EntityIdBindingsForControllerTest.java rename to src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/EntityIdBindingsForControllerTest.java index 65b414d..b6c2a6c 100644 --- a/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/EntityIdBindingsForControllerTest.java +++ b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/EntityIdBindingsForControllerTest.java @@ -1,8 +1,8 @@ -package it.aboutbits.springboot.toolbox.boot.mvc; +package it.aboutbits.springboot.toolbox.autoconfiguration.mvc; import com.fasterxml.jackson.databind.ObjectMapper; -import it.aboutbits.springboot.toolbox.boot.mvc.body.BodyWithEntityId; -import it.aboutbits.springboot.toolbox.boot.persistence.impl.jpa.CustomTypeTestModel; +import it.aboutbits.springboot.toolbox.autoconfiguration.mvc.body.BodyWithEntityId; +import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.impl.jpa.CustomTypeTestModel; import it.aboutbits.springboot.toolbox.support.HttpTest; import lombok.NonNull; import org.junit.jupiter.api.Nested; diff --git a/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithEmailAddress.java b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/body/BodyWithEmailAddress.java similarity index 66% rename from src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithEmailAddress.java rename to src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/body/BodyWithEmailAddress.java index 49d7c44..6feb6ff 100644 --- a/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithEmailAddress.java +++ b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/body/BodyWithEmailAddress.java @@ -1,4 +1,4 @@ -package it.aboutbits.springboot.toolbox.boot.mvc.body; +package it.aboutbits.springboot.toolbox.autoconfiguration.mvc.body; import it.aboutbits.springboot.toolbox.type.EmailAddress; diff --git a/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/body/BodyWithEntityId.java b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/body/BodyWithEntityId.java new file mode 100644 index 0000000..5a75d33 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/body/BodyWithEntityId.java @@ -0,0 +1,8 @@ +package it.aboutbits.springboot.toolbox.autoconfiguration.mvc.body; + +import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.impl.jpa.CustomTypeTestModel; + +public record BodyWithEntityId( + CustomTypeTestModel.ID entityId +) { +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithIban.java b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/body/BodyWithIban.java similarity index 60% rename from src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithIban.java rename to src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/body/BodyWithIban.java index 2d24b5d..607e29f 100644 --- a/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithIban.java +++ b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/body/BodyWithIban.java @@ -1,4 +1,4 @@ -package it.aboutbits.springboot.toolbox.boot.mvc.body; +package it.aboutbits.springboot.toolbox.autoconfiguration.mvc.body; import it.aboutbits.springboot.toolbox.type.Iban; diff --git a/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithScaledBigDecimal.java b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/body/BodyWithScaledBigDecimal.java similarity index 69% rename from src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithScaledBigDecimal.java rename to src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/body/BodyWithScaledBigDecimal.java index ea2d5e7..e2c9cea 100644 --- a/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithScaledBigDecimal.java +++ b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/body/BodyWithScaledBigDecimal.java @@ -1,4 +1,4 @@ -package it.aboutbits.springboot.toolbox.boot.mvc.body; +package it.aboutbits.springboot.toolbox.autoconfiguration.mvc.body; import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; diff --git a/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/controller/CustomTypeTestController.java b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/controller/CustomTypeTestController.java similarity index 86% rename from src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/controller/CustomTypeTestController.java rename to src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/controller/CustomTypeTestController.java index dd98121..7c4d827 100644 --- a/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/controller/CustomTypeTestController.java +++ b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/controller/CustomTypeTestController.java @@ -1,8 +1,8 @@ -package it.aboutbits.springboot.toolbox.boot.mvc.controller; +package it.aboutbits.springboot.toolbox.autoconfiguration.mvc.controller; -import it.aboutbits.springboot.toolbox.boot.mvc.body.BodyWithEmailAddress; -import it.aboutbits.springboot.toolbox.boot.mvc.body.BodyWithIban; -import it.aboutbits.springboot.toolbox.boot.mvc.body.BodyWithScaledBigDecimal; +import it.aboutbits.springboot.toolbox.autoconfiguration.mvc.body.BodyWithEmailAddress; +import it.aboutbits.springboot.toolbox.autoconfiguration.mvc.body.BodyWithIban; +import it.aboutbits.springboot.toolbox.autoconfiguration.mvc.body.BodyWithScaledBigDecimal; import it.aboutbits.springboot.toolbox.type.EmailAddress; import it.aboutbits.springboot.toolbox.type.Iban; import it.aboutbits.springboot.toolbox.type.ScaledBigDecimal; diff --git a/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/controller/EntityIdTestController.java b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/controller/EntityIdTestController.java similarity index 81% rename from src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/controller/EntityIdTestController.java rename to src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/controller/EntityIdTestController.java index 6cf40bc..abd6ccc 100644 --- a/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/controller/EntityIdTestController.java +++ b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/mvc/controller/EntityIdTestController.java @@ -1,7 +1,7 @@ -package it.aboutbits.springboot.toolbox.boot.mvc.controller; +package it.aboutbits.springboot.toolbox.autoconfiguration.mvc.controller; -import it.aboutbits.springboot.toolbox.boot.mvc.body.BodyWithEntityId; -import it.aboutbits.springboot.toolbox.boot.persistence.impl.jpa.CustomTypeTestModel; +import it.aboutbits.springboot.toolbox.autoconfiguration.mvc.body.BodyWithEntityId; +import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.impl.jpa.CustomTypeTestModel; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; diff --git a/src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/CustomTypeJpaTest.java b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/CustomTypeJpaTest.java similarity index 89% rename from src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/CustomTypeJpaTest.java rename to src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/CustomTypeJpaTest.java index bd045bf..0cca224 100644 --- a/src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/CustomTypeJpaTest.java +++ b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/CustomTypeJpaTest.java @@ -1,7 +1,7 @@ -package it.aboutbits.springboot.toolbox.boot.persistence; +package it.aboutbits.springboot.toolbox.autoconfiguration.persistence; -import it.aboutbits.springboot.toolbox.boot.persistence.impl.jpa.CustomTypeTestModel; -import it.aboutbits.springboot.toolbox.boot.persistence.impl.jpa.CustomTypeTestModelRepository; +import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.impl.jpa.CustomTypeTestModel; +import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.impl.jpa.CustomTypeTestModelRepository; import it.aboutbits.springboot.toolbox.support.ApplicationTest; import it.aboutbits.springboot.toolbox.type.EmailAddress; import it.aboutbits.springboot.toolbox.type.Iban; diff --git a/src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/EntityIdJpaTest.java b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/EntityIdJpaTest.java similarity index 79% rename from src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/EntityIdJpaTest.java rename to src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/EntityIdJpaTest.java index 59110c1..eafb95d 100644 --- a/src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/EntityIdJpaTest.java +++ b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/EntityIdJpaTest.java @@ -1,8 +1,8 @@ -package it.aboutbits.springboot.toolbox.boot.persistence; +package it.aboutbits.springboot.toolbox.autoconfiguration.persistence; -import it.aboutbits.springboot.toolbox.boot.persistence.impl.jpa.CustomTypeTestModel; -import it.aboutbits.springboot.toolbox.boot.persistence.impl.jpa.CustomTypeTestModelRepository; -import it.aboutbits.springboot.toolbox.boot.persistence.impl.jpa.ReferencedTestModel; +import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.impl.jpa.CustomTypeTestModel; +import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.impl.jpa.CustomTypeTestModelRepository; +import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.impl.jpa.ReferencedTestModel; import it.aboutbits.springboot.toolbox.support.ApplicationTest; import it.aboutbits.springboot.toolbox.support.WithPersistence; import org.junit.jupiter.api.Nested; diff --git a/src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/impl/jpa/CustomTypeTestModel.java b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/impl/jpa/CustomTypeTestModel.java similarity index 86% rename from src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/impl/jpa/CustomTypeTestModel.java rename to src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/impl/jpa/CustomTypeTestModel.java index ed6075c..73e2d15 100644 --- a/src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/impl/jpa/CustomTypeTestModel.java +++ b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/impl/jpa/CustomTypeTestModel.java @@ -1,8 +1,8 @@ -package it.aboutbits.springboot.toolbox.boot.persistence.impl.jpa; +package it.aboutbits.springboot.toolbox.autoconfiguration.persistence.impl.jpa; -import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; -import it.aboutbits.springboot.toolbox.persistence.identity.EntityId; -import it.aboutbits.springboot.toolbox.persistence.identity.Identified; +import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.type.identity.EntityId; +import it.aboutbits.springboot.toolbox.type.identity.Identified; import it.aboutbits.springboot.toolbox.persistence.javatype.EmailAddressJavaType; import it.aboutbits.springboot.toolbox.persistence.javatype.IbanJavaType; import it.aboutbits.springboot.toolbox.persistence.javatype.ScaledBigDecimalJavaType; diff --git a/src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/impl/jpa/CustomTypeTestModelRepository.java b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/impl/jpa/CustomTypeTestModelRepository.java similarity index 89% rename from src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/impl/jpa/CustomTypeTestModelRepository.java rename to src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/impl/jpa/CustomTypeTestModelRepository.java index 647ea69..6616575 100644 --- a/src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/impl/jpa/CustomTypeTestModelRepository.java +++ b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/impl/jpa/CustomTypeTestModelRepository.java @@ -1,4 +1,4 @@ -package it.aboutbits.springboot.toolbox.boot.persistence.impl.jpa; +package it.aboutbits.springboot.toolbox.autoconfiguration.persistence.impl.jpa; import it.aboutbits.springboot.toolbox.type.EmailAddress; import it.aboutbits.springboot.toolbox.type.Iban; diff --git a/src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/impl/jpa/ReferencedTestModel.java b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/impl/jpa/ReferencedTestModel.java similarity index 71% rename from src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/impl/jpa/ReferencedTestModel.java rename to src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/impl/jpa/ReferencedTestModel.java index 349bac8..38ca9a7 100644 --- a/src/test/java/it/aboutbits/springboot/toolbox/boot/persistence/impl/jpa/ReferencedTestModel.java +++ b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/persistence/impl/jpa/ReferencedTestModel.java @@ -1,7 +1,7 @@ -package it.aboutbits.springboot.toolbox.boot.persistence.impl.jpa; +package it.aboutbits.springboot.toolbox.autoconfiguration.persistence.impl.jpa; -import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; -import it.aboutbits.springboot.toolbox.persistence.identity.EntityId; +import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.type.identity.EntityId; import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedLongJavaType; public class ReferencedTestModel { diff --git a/src/test/java/it/aboutbits/springboot/toolbox/boot/swagger/SwaggerTest.java b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/swagger/SwaggerTest.java similarity index 94% rename from src/test/java/it/aboutbits/springboot/toolbox/boot/swagger/SwaggerTest.java rename to src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/swagger/SwaggerTest.java index 7e1e12f..0e57c3e 100644 --- a/src/test/java/it/aboutbits/springboot/toolbox/boot/swagger/SwaggerTest.java +++ b/src/test/java/it/aboutbits/springboot/toolbox/autoconfiguration/swagger/SwaggerTest.java @@ -1,4 +1,4 @@ -package it.aboutbits.springboot.toolbox.boot.swagger; +package it.aboutbits.springboot.toolbox.autoconfiguration.swagger; import it.aboutbits.springboot.toolbox.support.HttpTest; import lombok.NonNull; diff --git a/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithEntityId.java b/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithEntityId.java deleted file mode 100644 index 06c4a13..0000000 --- a/src/test/java/it/aboutbits/springboot/toolbox/boot/mvc/body/BodyWithEntityId.java +++ /dev/null @@ -1,8 +0,0 @@ -package it.aboutbits.springboot.toolbox.boot.mvc.body; - -import it.aboutbits.springboot.toolbox.boot.persistence.impl.jpa.CustomTypeTestModel; - -public record BodyWithEntityId( - CustomTypeTestModel.ID entityId -) { -} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapBigDecimalJavaType.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapBigDecimalJavaType.java index 5374d1a..f754b8c 100644 --- a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapBigDecimalJavaType.java +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapBigDecimalJavaType.java @@ -1,6 +1,6 @@ package it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype; -import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.AutoRegisteredJavaType; import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedBigDecimalJavaType; import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapBigDecimal; diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapBigIntegerJavaType.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapBigIntegerJavaType.java index 99b796c..71d2a5d 100644 --- a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapBigIntegerJavaType.java +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapBigIntegerJavaType.java @@ -1,6 +1,6 @@ package it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype; -import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.AutoRegisteredJavaType; import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedBigIntegerJavaType; import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapBigInteger; diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapDoubleJavaType.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapDoubleJavaType.java index 932d68d..2e69f31 100644 --- a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapDoubleJavaType.java +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapDoubleJavaType.java @@ -1,6 +1,6 @@ package it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype; -import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.AutoRegisteredJavaType; import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedDoubleJavaType; import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapDouble; diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapFloatJavaType.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapFloatJavaType.java index 908aab2..81a1815 100644 --- a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapFloatJavaType.java +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapFloatJavaType.java @@ -1,6 +1,6 @@ package it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype; -import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.AutoRegisteredJavaType; import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedFloatJavaType; import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapFloat; diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapIntegerJavaType.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapIntegerJavaType.java index 6d5e350..d20de96 100644 --- a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapIntegerJavaType.java +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapIntegerJavaType.java @@ -1,6 +1,6 @@ package it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype; -import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.AutoRegisteredJavaType; import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedIntegerJavaType; import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapInteger; diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapLongJavaType.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapLongJavaType.java index c014d7e..9375633 100644 --- a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapLongJavaType.java +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapLongJavaType.java @@ -1,6 +1,6 @@ package it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype; -import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.AutoRegisteredJavaType; import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedLongJavaType; import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapLong; diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapScaledBigDecimalJavaType.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapScaledBigDecimalJavaType.java index f508865..0959545 100644 --- a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapScaledBigDecimalJavaType.java +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapScaledBigDecimalJavaType.java @@ -1,6 +1,6 @@ package it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype; -import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.AutoRegisteredJavaType; import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedScaledBigDecimalJavaType; import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapScaledBigDecimal; diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapShortJavaType.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapShortJavaType.java index 8a3440e..78d3a36 100644 --- a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapShortJavaType.java +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapShortJavaType.java @@ -1,6 +1,6 @@ package it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype; -import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.AutoRegisteredJavaType; import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedShortJavaType; import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapShort; diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapStringJavaType.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapStringJavaType.java index fb9e910..43466e3 100644 --- a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapStringJavaType.java +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/javatype/WrapStringJavaType.java @@ -1,6 +1,6 @@ package it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype; -import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.AutoRegisteredJavaType; import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedStringJavaType; import it.aboutbits.springboot.toolbox.persistence.javatype.impl.type.WrapString; diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/jpa/WrapperTypesModel.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/jpa/WrapperTypesModel.java index b778cf9..f2b5d84 100644 --- a/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/jpa/WrapperTypesModel.java +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/javatype/impl/jpa/WrapperTypesModel.java @@ -1,8 +1,8 @@ package it.aboutbits.springboot.toolbox.persistence.javatype.impl.jpa; -import it.aboutbits.springboot.toolbox.boot.persistence.AutoRegisteredJavaType; -import it.aboutbits.springboot.toolbox.persistence.identity.EntityId; -import it.aboutbits.springboot.toolbox.persistence.identity.Identified; +import it.aboutbits.springboot.toolbox.autoconfiguration.persistence.AutoRegisteredJavaType; +import it.aboutbits.springboot.toolbox.type.identity.EntityId; +import it.aboutbits.springboot.toolbox.type.identity.Identified; import it.aboutbits.springboot.toolbox.persistence.javatype.base.WrappedLongJavaType; import it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype.WrapBigDecimalJavaType; import it.aboutbits.springboot.toolbox.persistence.javatype.impl.javatype.WrapBigIntegerJavaType; From 84702e81148ab9e1b1a2f6bb615d6645c44fcb4e Mon Sep 17 00:00:00 2001 From: Andreas Hufler Date: Mon, 9 Sep 2024 12:05:15 +0200 Subject: [PATCH 9/9] remove comment --- .../toolbox/autoconfiguration/web/CustomTypeScanner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/web/CustomTypeScanner.java b/src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/web/CustomTypeScanner.java index ca98f35..d677d4b 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/web/CustomTypeScanner.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/autoconfiguration/web/CustomTypeScanner.java @@ -38,6 +38,6 @@ public void setAdditionalTypePackages(String[] additionalTypePackages) { public static Set> findAllCustomTypeRecords(ClassScannerUtil.ClassScanner classScanner) { return classScanner.getSubTypesOf(CustomType.class).stream() .filter(Record.class::isAssignableFrom) - .collect(Collectors.toSet()); //TODO: ttp:// + .collect(Collectors.toSet()); } }