From 76c6b9b6b7ef65a5a09c8e465a845ed67512d70f Mon Sep 17 00:00:00 2001 From: Thomas Sapelza Date: Fri, 20 Feb 2026 18:32:35 +0100 Subject: [PATCH 1/5] put `@NullMarked` closest to the target --- .../ConstraintViolationExceptionBuilder.java | 16 ++++++++-------- .../springboot/toolbox/swagger/SwaggerMeta.java | 10 +++++----- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/java/it/aboutbits/springboot/toolbox/exception/ConstraintViolationExceptionBuilder.java b/src/main/java/it/aboutbits/springboot/toolbox/exception/ConstraintViolationExceptionBuilder.java index 22c2cc9..13ba8c3 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/exception/ConstraintViolationExceptionBuilder.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/exception/ConstraintViolationExceptionBuilder.java @@ -61,26 +61,26 @@ public String getMessage() { return message; } - @Nullable @Override + @Nullable public String getMessageTemplate() { return null; } - @Nullable @Override + @Nullable public Object getRootBean() { return null; } - @Nullable @Override + @Nullable public Class getRootBeanClass() { return null; } - @Nullable @Override + @Nullable public Object getLeafBean() { return null; } @@ -90,8 +90,8 @@ public Object[] getExecutableParameters() { return new Object[0]; } - @Nullable @Override + @Nullable public Object getExecutableReturnValue() { return null; } @@ -101,20 +101,20 @@ public Path getPropertyPath() { return propertyPath; } - @Nullable @Override + @Nullable public Object getInvalidValue() { return null; } - @Nullable @Override + @Nullable public ConstraintDescriptor getConstraintDescriptor() { return null; } - @Nullable @Override + @Nullable public U unwrap(Class type) { return null; } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/swagger/SwaggerMeta.java b/src/main/java/it/aboutbits/springboot/toolbox/swagger/SwaggerMeta.java index 82f70a5..4b49b60 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/swagger/SwaggerMeta.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/swagger/SwaggerMeta.java @@ -16,26 +16,26 @@ public class SwaggerMeta { @Nullable private String originalTypeFqn = null; - @Nullable @JsonProperty("isIdentity") + @Nullable private Boolean isIdentity = null; - @Nullable @JsonProperty("isCustomType") + @Nullable private Boolean isCustomType = null; - @Nullable @JsonProperty("isNestedStructure") + @Nullable private Boolean isNestedStructure = null; - @Nullable @JsonProperty("isMap") + @Nullable private Boolean isMap = null; @Nullable private String mapKeyTypeFqn = null; - @Nullable @JsonProperty("isNullable") + @Nullable private Boolean isNullable = null; } From cecc45e16085294034252ca3a732ef70340ff4b6 Mon Sep 17 00:00:00 2001 From: Thomas Sapelza Date: Fri, 20 Feb 2026 18:33:05 +0100 Subject: [PATCH 2/5] add jOOQ dependency and support jOOQ Field in SortMappings --- pom.xml | 9 +++++++++ .../springboot/toolbox/persistence/SortMappings.java | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/pom.xml b/pom.xml index beab2c3..a37ac94 100644 --- a/pom.xml +++ b/pom.xml @@ -18,6 +18,7 @@ 25 2.46.0 + 3.20.11 0.12.15 @@ -52,6 +53,14 @@ spring-security-core + + + org.jooq + jooq + ${jooq.version} + provided + + org.projectlombok diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/SortMappings.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/SortMappings.java index f8615e8..fa8ca6d 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/persistence/SortMappings.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/SortMappings.java @@ -1,6 +1,7 @@ package it.aboutbits.springboot.toolbox.persistence; import it.aboutbits.springboot.toolbox.parameter.SortParameter; +import org.jooq.Field; import org.jspecify.annotations.NullMarked; import java.util.HashMap; @@ -18,6 +19,13 @@ public static & SortParameter.Definition> Mapping map( return new Mapping<>(property, column); } + public static & SortParameter.Definition> Mapping map( + T property, + Field column + ) { + return new Mapping<>(property, column.getName()); + } + @SafeVarargs public static & SortParameter.Definition> SortMappings of( Mapping... mappings From fd061b61ead17a15b6620ed44003803832d42a47 Mon Sep 17 00:00:00 2001 From: Thomas Sapelza Date: Fri, 20 Feb 2026 19:31:20 +0100 Subject: [PATCH 3/5] add jOOQ as an optional dependency and support jOOQ Field in SortMappings in new class SortMappingsForJooq --- pom.xml | 3 +- .../toolbox/parameter/SortParameter.java | 66 +++++++++++++++++-- .../toolbox/persistence/SortMappings.java | 14 +--- .../persistence/SortMappingsForJooq.java | 33 ++++++++++ .../persistence/SortMappingsForJooqTest.java | 53 +++++++++++++++ 5 files changed, 151 insertions(+), 18 deletions(-) create mode 100644 src/main/java/it/aboutbits/springboot/toolbox/persistence/SortMappingsForJooq.java create mode 100644 src/test/java/it/aboutbits/springboot/toolbox/persistence/SortMappingsForJooqTest.java diff --git a/pom.xml b/pom.xml index a37ac94..cf5030d 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ it.aboutbits spring-boot-toolbox - 2.2.0 + 2.2.1-SNAPSHOT Utility library for Spring Boot projects. jar @@ -59,6 +59,7 @@ jooq ${jooq.version} provided + true diff --git a/src/main/java/it/aboutbits/springboot/toolbox/parameter/SortParameter.java b/src/main/java/it/aboutbits/springboot/toolbox/parameter/SortParameter.java index 8872cf4..9159eb1 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/parameter/SortParameter.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/parameter/SortParameter.java @@ -7,6 +7,8 @@ import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Sort; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -24,7 +26,7 @@ @NullMarked public final class SortParameter & SortParameter.Definition> { private static final String DEFAULT_SORT_PROPERTY = "id"; - private static final Sort.Direction DEFAULT_SORT_DIRETION = Sort.Direction.ASC; + private static final Sort.Direction DEFAULT_SORT_DIRECTION = Sort.Direction.ASC; @Accessors(fluent = true) @Getter @@ -226,11 +228,11 @@ public SortParameter or(SortParameter fallback) { * @return an instance of {@link Sort} created using the transformed key-value mapping, * excluding default sorting behavior. */ - public Sort buildSortWithoutDefault(Map mapping) { + public Sort buildSortWithoutDefault(Map mapping) { var stringMapping = mapping.entrySet().stream() .collect(Collectors.toMap( entry -> entry.getKey().name(), - Map.Entry::getValue + entry -> convertToString(entry.getValue()) )); return buildSort(stringMapping, false); @@ -246,16 +248,68 @@ public Sort buildSortWithoutDefault(Map mapping) { * The enumeration keys must implement the `name()` method to retrieve their string representation. * @return an instance of {@link Sort} created using the transformed key-value mapping with default sorting behavior. */ - public Sort buildSort(Map mapping) { + public Sort buildSort(Map mapping) { var stringMapping = mapping.entrySet().stream() .collect(Collectors.toMap( entry -> entry.getKey().name(), - Map.Entry::getValue + entry -> convertToString(entry.getValue()) )); return buildSort(stringMapping, true); } + private static String convertToString(Object value) { + if (value instanceof String string) { + return string; + } + + // Use reflection to get the column name from a mapped jOOQ Field using the Field.getName() method + // This allows us to have jOOQ as an optional dependency and not break existing projects that use Hibernate + try { + var getName = findAccessibleGetNameMethod(value.getClass()); + if (getName == null) { + getName = value.getClass().getMethod("getName"); + getName.setAccessible(true); + } + + return (String) getName.invoke(value); + } catch (Exception e) { + throw new IllegalArgumentException( + "Cannot get the jOOQ Field name from the mapped SortMappings$Mapping column value [value.class.name=%s]".formatted( + value.getClass().getName() + ), + e + ); + } + } + + private static @Nullable Method findAccessibleGetNameMethod(Class clazz) { + if (Modifier.isPublic(clazz.getModifiers())) { + try { + var method = clazz.getDeclaredMethod("getName"); + if (Modifier.isPublic(method.getModifiers())) { + return method; + } + } catch (NoSuchMethodException _) { + // ignored, let's search in the interfaces or superclass + } + } + + for (var interFace : clazz.getInterfaces()) { + var method = findAccessibleGetNameMethod(interFace); + if (method != null) { + return method; + } + } + + var superclass = clazz.getSuperclass(); + if (superclass != null) { + return findAccessibleGetNameMethod(superclass); + } + + return null; + } + private Sort buildSort(Map mapping, boolean withDefault) { if (sortFields.isEmpty()) { return withDefault ? getMappedDefaultSort(mapping) : Sort.unsorted(); @@ -289,7 +343,7 @@ private Sort buildSort(Map mapping, boolean withDefault) { } private static Sort getMappedDefaultSort(Map mapping) { - return Sort.by(DEFAULT_SORT_DIRETION, mapping.getOrDefault(DEFAULT_SORT_PROPERTY, DEFAULT_SORT_PROPERTY)); + return Sort.by(DEFAULT_SORT_DIRECTION, mapping.getOrDefault(DEFAULT_SORT_PROPERTY, DEFAULT_SORT_PROPERTY)); } public record SortField( diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/SortMappings.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/SortMappings.java index fa8ca6d..3a2f971 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/persistence/SortMappings.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/SortMappings.java @@ -1,14 +1,13 @@ package it.aboutbits.springboot.toolbox.persistence; import it.aboutbits.springboot.toolbox.parameter.SortParameter; -import org.jooq.Field; import org.jspecify.annotations.NullMarked; import java.util.HashMap; @NullMarked -public final class SortMappings & SortParameter.Definition> extends HashMap { - private SortMappings() { +public class SortMappings & SortParameter.Definition> extends HashMap { + SortMappings() { super(); } @@ -19,13 +18,6 @@ public static & SortParameter.Definition> Mapping map( return new Mapping<>(property, column); } - public static & SortParameter.Definition> Mapping map( - T property, - Field column - ) { - return new Mapping<>(property, column.getName()); - } - @SafeVarargs public static & SortParameter.Definition> SortMappings of( Mapping... mappings @@ -39,6 +31,6 @@ public static & SortParameter.Definition> SortMappings of( return sortMappings; } - public record Mapping & SortParameter.Definition>(T property, String column) { + public record Mapping & SortParameter.Definition>(T property, Object column) { } } diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/SortMappingsForJooq.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/SortMappingsForJooq.java new file mode 100644 index 0000000..1970e93 --- /dev/null +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/SortMappingsForJooq.java @@ -0,0 +1,33 @@ +package it.aboutbits.springboot.toolbox.persistence; + +import it.aboutbits.springboot.toolbox.parameter.SortParameter; +import it.aboutbits.springboot.toolbox.persistence.SortMappings.Mapping; +import org.jooq.Field; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public final class SortMappingsForJooq & SortParameter.Definition> extends SortMappings { + private SortMappingsForJooq() { + super(); + } + + public static & SortParameter.Definition> Mapping map( + T property, + Field column + ) { + return new Mapping<>(property, column); + } + + @SafeVarargs + public static & SortParameter.Definition> SortMappingsForJooq of( + Mapping... mappings + ) { + var sortMappings = new SortMappingsForJooq(); + + for (var mapping : mappings) { + sortMappings.put(mapping.property(), mapping.column()); + } + + return sortMappings; + } +} diff --git a/src/test/java/it/aboutbits/springboot/toolbox/persistence/SortMappingsForJooqTest.java b/src/test/java/it/aboutbits/springboot/toolbox/persistence/SortMappingsForJooqTest.java new file mode 100644 index 0000000..3d1ca11 --- /dev/null +++ b/src/test/java/it/aboutbits/springboot/toolbox/persistence/SortMappingsForJooqTest.java @@ -0,0 +1,53 @@ +package it.aboutbits.springboot.toolbox.persistence; + +import it.aboutbits.springboot.toolbox.parameter.SortParameter; +import org.jooq.impl.DSL; +import org.jspecify.annotations.NullMarked; +import org.junit.jupiter.api.Test; +import org.springframework.data.domain.Sort; + +import java.util.Objects; + +import static org.assertj.core.api.Assertions.assertThat; + +@NullMarked +class SortMappingsForJooqTest { + private enum ESort implements SortParameter.Definition { + Property1, + Property2 + } + + @Test + void map_shouldReturnMappingWithFieldObject() { + var field = DSL.field("my_column"); + var mapping = SortMappingsForJooq.map(ESort.Property1, field); + + assertThat(mapping.property()).isEqualTo(ESort.Property1); + assertThat(mapping.column()).isEqualTo(field); + } + + @Test + void of_shouldCreateSortMappingsForJooq() { + var field1 = DSL.field("col1"); + var mappings = SortMappingsForJooq.of( + SortMappingsForJooq.map(ESort.Property1, field1) + ); + + assertThat(mappings).isInstanceOf(SortMappingsForJooq.class); + assertThat(mappings.get(ESort.Property1)).isEqualTo(field1); + } + + @Test + void buildSpringSortWithJooqFields() { + var mappings = SortMappingsForJooq.of( + SortMappingsForJooq.map(ESort.Property1, DSL.field("my_col")) + ); + var sortParameter = SortParameter.by(ESort.Property1, Sort.Direction.DESC); + + var result = sortParameter.buildSort(mappings); + + var order = result.getOrderFor("my_col"); + assertThat(order).isNotNull(); + assertThat(Objects.requireNonNull(order).getDirection()).isEqualTo(Sort.Direction.DESC); + } +} From c0c648145792336e09bf2ef99e4de19841848537 Mon Sep 17 00:00:00 2001 From: Thomas Sapelza Date: Fri, 20 Feb 2026 19:31:43 +0100 Subject: [PATCH 4/5] add jOOQ as an optional dependency and support jOOQ Field in SortMappings in new class SortMappingsForJooq --- .../springboot/toolbox/persistence/SortMappingsForJooq.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/it/aboutbits/springboot/toolbox/persistence/SortMappingsForJooq.java b/src/main/java/it/aboutbits/springboot/toolbox/persistence/SortMappingsForJooq.java index 1970e93..f1e87f9 100644 --- a/src/main/java/it/aboutbits/springboot/toolbox/persistence/SortMappingsForJooq.java +++ b/src/main/java/it/aboutbits/springboot/toolbox/persistence/SortMappingsForJooq.java @@ -1,7 +1,6 @@ package it.aboutbits.springboot.toolbox.persistence; import it.aboutbits.springboot.toolbox.parameter.SortParameter; -import it.aboutbits.springboot.toolbox.persistence.SortMappings.Mapping; import org.jooq.Field; import org.jspecify.annotations.NullMarked; From 55596f8df6afa68895c0e44a53dc6d5588909dba Mon Sep 17 00:00:00 2001 From: Thomas Sapelza Date: Fri, 20 Feb 2026 22:13:04 +0100 Subject: [PATCH 5/5] accidentally committed local version 2.2.1-SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cf5030d..5002c5a 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ it.aboutbits spring-boot-toolbox - 2.2.1-SNAPSHOT + 2.2.0 Utility library for Spring Boot projects. jar