From a7a990283a6fe15bf4b15a0c4789d37dcfcb7c24 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Mon, 18 Aug 2025 10:07:17 +0200 Subject: [PATCH 1/5] 1980-jspecify - Prepare branch --- pom.xml | 2 +- spring-data-jdbc-distribution/pom.xml | 2 +- spring-data-jdbc/pom.xml | 4 ++-- spring-data-r2dbc/pom.xml | 4 ++-- spring-data-relational/pom.xml | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 98f945a6b2..296c68636a 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-relational-parent - 4.0.0-SNAPSHOT + 4.0.0-1980-jspecify-SNAPSHOT pom Spring Data Relational Parent diff --git a/spring-data-jdbc-distribution/pom.xml b/spring-data-jdbc-distribution/pom.xml index b3c39e64c3..9a73f9bd9a 100644 --- a/spring-data-jdbc-distribution/pom.xml +++ b/spring-data-jdbc-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-relational-parent - 4.0.0-SNAPSHOT + 4.0.0-1980-jspecify-SNAPSHOT ../pom.xml diff --git a/spring-data-jdbc/pom.xml b/spring-data-jdbc/pom.xml index e61fd64020..1db4d455ef 100644 --- a/spring-data-jdbc/pom.xml +++ b/spring-data-jdbc/pom.xml @@ -6,7 +6,7 @@ 4.0.0 spring-data-jdbc - 4.0.0-SNAPSHOT + 4.0.0-1980-jspecify-SNAPSHOT Spring Data JDBC Spring Data module for JDBC repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-relational-parent - 4.0.0-SNAPSHOT + 4.0.0-1980-jspecify-SNAPSHOT diff --git a/spring-data-r2dbc/pom.xml b/spring-data-r2dbc/pom.xml index 64ff1ebcb3..07274a3649 100644 --- a/spring-data-r2dbc/pom.xml +++ b/spring-data-r2dbc/pom.xml @@ -6,7 +6,7 @@ 4.0.0 spring-data-r2dbc - 4.0.0-SNAPSHOT + 4.0.0-1980-jspecify-SNAPSHOT Spring Data R2DBC Spring Data module for R2DBC @@ -15,7 +15,7 @@ org.springframework.data spring-data-relational-parent - 4.0.0-SNAPSHOT + 4.0.0-1980-jspecify-SNAPSHOT diff --git a/spring-data-relational/pom.xml b/spring-data-relational/pom.xml index 8fd6d7a6f0..9c80f47d9f 100644 --- a/spring-data-relational/pom.xml +++ b/spring-data-relational/pom.xml @@ -6,7 +6,7 @@ 4.0.0 spring-data-relational - 4.0.0-SNAPSHOT + 4.0.0-1980-jspecify-SNAPSHOT Spring Data Relational Spring Data Relational support @@ -14,7 +14,7 @@ org.springframework.data spring-data-relational-parent - 4.0.0-SNAPSHOT + 4.0.0-1980-jspecify-SNAPSHOT From 8b4c0349630a9bbf15a005b3a51e82778f3c442f Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Mon, 18 Aug 2025 10:28:04 +0200 Subject: [PATCH 2/5] Migrate to JSpecify annotations for nullability constraints. Replace nullability annotations with their JSpecify equivalents. Enable checking this annotations at compile time using Errorprone and NullAway. Closes #1980 --- .gitignore | 1 + .../data/jdbc/aot/JdbcRuntimeHints.java | 2 +- .../JdbcAggregateChangeExecutionContext.java | 97 +-- .../jdbc/core/JdbcAggregateOperations.java | 6 +- .../data/jdbc/core/JdbcAggregateTemplate.java | 4 +- .../jdbc/core/convert/AggregateReader.java | 3 +- .../jdbc/core/convert/CachingResultSet.java | 124 --- .../jdbc/core/convert/DataAccessStrategy.java | 6 +- .../convert/DefaultDataAccessStrategy.java | 16 +- .../convert/DelegatingDataAccessStrategy.java | 8 +- .../jdbc/core/convert/FunctionCollector.java | 2 +- .../IdGeneratingBatchInsertStrategy.java | 4 +- .../convert/IdGeneratingInsertStrategy.java | 6 +- .../data/jdbc/core/convert/Identifier.java | 4 +- .../jdbc/core/convert/InsertStrategy.java | 2 +- .../core/convert/InsertStrategyFactory.java | 35 +- .../data/jdbc/core/convert/InsertSubject.java | 2 +- .../IterableOfEntryToMapConverter.java | 2 +- .../data/jdbc/core/convert/JdbcConverter.java | 2 +- .../core/convert/JdbcIdentifierBuilder.java | 3 +- .../convert/JdbcPropertyValueProvider.java | 3 +- .../jdbc/core/convert/MapEntityRowMapper.java | 4 + .../core/convert/MappingJdbcConverter.java | 25 +- .../data/jdbc/core/convert/QueryMapper.java | 66 +- .../convert/QueryMappingConfiguration.java | 4 +- .../convert/ReadingDataAccessStrategy.java | 6 +- .../jdbc/core/convert/ResultSetAccessor.java | 2 +- .../ResultSetAccessorPropertyAccessor.java | 2 +- .../convert/RowDocumentExtractorSupport.java | 30 +- .../RowDocumentResultSetExtractor.java | 17 +- .../SequenceEntityCallbackDelegate.java | 9 +- .../SingleQueryDataAccessStrategy.java | 3 +- ...SingleQueryFallbackDataAccessStrategy.java | 3 +- .../data/jdbc/core/convert/SqlGenerator.java | 43 +- .../convert/SqlIdentifierParameterSource.java | 5 +- .../core/convert/SqlParametersFactory.java | 14 +- .../data/jdbc/core/convert/package-info.java | 4 +- .../jdbc/core/dialect/DialectResolver.java | 3 +- .../data/jdbc/core/dialect/package-info.java | 4 +- .../data/jdbc/core/mapping/JdbcValue.java | 10 +- .../data/jdbc/core/mapping/package-info.java | 2 +- .../mapping/schema/DefaultSqlTypeMapping.java | 5 +- .../schema/LiquibaseChangeSetWriter.java | 2 +- .../jdbc/core/mapping/schema/SchemaDiff.java | 4 + .../core/mapping/schema/SqlTypeMapping.java | 3 +- .../data/jdbc/core/mapping/schema/Table.java | 4 +- .../data/jdbc/core/mapping/schema/Tables.java | 39 +- .../core/mapping/schema/package-info.java | 4 +- .../data/jdbc/core/package-info.java | 4 +- .../data/jdbc/mybatis/MyBatisContext.java | 2 +- .../mybatis/MyBatisDataAccessStrategy.java | 5 +- .../data/jdbc/mybatis/package-info.java | 4 +- .../config/AbstractJdbcConfiguration.java | 3 +- .../DefaultQueryMappingConfiguration.java | 4 +- .../jdbc/repository/config/package-info.java | 4 +- .../query/AbstractDelegatingRowMapper.java | 9 +- .../repository/query/AbstractJdbcQuery.java | 2 +- .../query/CallbackCapableRowMapper.java | 2 +- .../repository/query/ConvertingRowMapper.java | 2 +- .../query/DefaultRowMapperFactory.java | 5 +- .../query/EscapingParameterSource.java | 13 +- .../query/JdbcDeleteQueryCreator.java | 4 +- .../jdbc/repository/query/JdbcParameters.java | 10 +- .../repository/query/JdbcQueryCreator.java | 9 +- .../repository/query/JdbcQueryExecution.java | 2 +- .../repository/query/JdbcQueryMethod.java | 4 +- .../repository/query/PartTreeJdbcQuery.java | 8 +- .../query/StringBasedJdbcQuery.java | 11 +- .../jdbc/repository/query/package-info.java | 4 +- .../BeanFactoryAwareRowMapperFactory.java | 5 +- .../support/JdbcQueryLookupStrategy.java | 49 +- .../support/JdbcRepositoryFactory.java | 4 +- .../support/JdbcRepositoryFactoryBean.java | 10 +- .../jdbc/repository/support/package-info.java | 4 +- .../data/jdbc/support/package-info.java | 4 +- .../core/JdbcAggregateOperationsExtensions.kt | 56 +- ...eChangeIdGenerationImmutableUnitTests.java | 762 ++++++++---------- .../AggregateChangeIdGenerationUnitTests.java | 4 +- ...angeExecutorContextImmutableUnitTests.java | 229 +++--- ...gregateChangeExecutorContextUnitTests.java | 2 +- .../SqlGeneratorEmbeddedUnitTests.java | 13 +- .../core/convert/SqlGeneratorUnitTests.java | 9 +- .../DeclaredQueryRepositoryUnitTests.java | 5 +- .../JdbcRepositoryIntegrationTests.java | 348 ++++---- .../SimpleJdbcRepositoryEventsUnitTests.java | 56 +- .../QueryAnnotationHsqlIntegrationTests.java | 2 +- .../config/AbstractR2dbcConfiguration.java | 6 +- .../data/r2dbc/config/package-info.java | 3 +- .../r2dbc/convert/MappingR2dbcConverter.java | 20 +- .../data/r2dbc/convert/R2dbcConverters.java | 3 +- .../r2dbc/convert/RowPropertyAccessor.java | 2 +- .../SequenceEntityCallbackDelegate.java | 5 +- .../data/r2dbc/convert/package-info.java | 3 +- .../data/r2dbc/core/BindParameterSource.java | 2 +- .../DefaultReactiveDataAccessStrategy.java | 34 +- .../r2dbc/core/DefaultStatementMapper.java | 7 +- .../r2dbc/core/MapBindParameterSource.java | 9 +- .../data/r2dbc/core/NamedParameterUtils.java | 206 ++--- .../data/r2dbc/core/R2dbcEntityTemplate.java | 51 +- .../core/ReactiveDataAccessStrategy.java | 2 +- .../core/ReactiveDeleteOperationSupport.java | 2 +- .../core/ReactiveInsertOperationSupport.java | 2 +- .../core/ReactiveSelectOperationSupport.java | 2 +- .../core/ReactiveUpdateOperationSupport.java | 2 +- .../data/r2dbc/core/StatementMapper.java | 18 +- .../data/r2dbc/core/package-info.java | 3 +- .../data/r2dbc/dialect/BindTargetBinder.java | 2 +- .../data/r2dbc/dialect/MySqlDialect.java | 3 +- .../data/r2dbc/dialect/package-info.java | 4 +- .../data/r2dbc/mapping/OutboundRow.java | 4 +- .../r2dbc/mapping/event/package-info.java | 2 +- .../data/r2dbc/mapping/package-info.java | 4 +- .../data/r2dbc/query/QueryMapper.java | 51 +- .../data/r2dbc/query/UpdateMapper.java | 12 +- .../data/r2dbc/query/package-info.java | 3 +- .../r2dbc/repository/config/package-info.java | 3 +- .../data/r2dbc/repository/package-info.java | 3 +- .../query/R2dbcParameterAccessor.java | 4 +- .../repository/query/R2dbcQueryCreator.java | 11 +- .../repository/query/R2dbcQueryExecution.java | 8 +- .../repository/query/R2dbcQueryMethod.java | 7 +- .../r2dbc/repository/query/package-info.java | 6 +- .../support/R2dbcRepositoryFactory.java | 2 +- .../support/R2dbcRepositoryFactoryBean.java | 12 +- .../support/SimpleR2dbcRepository.java | 16 +- .../repository/support/package-info.java | 3 +- .../data/r2dbc/support/package-info.java | 2 +- .../core/R2dbcEntityTemplateUnitTests.java | 2 +- ...oryWithMixedCaseNamesIntegrationTests.java | 34 +- .../ProjectingRepositoryIntegrationTests.java | 2 +- spring-data-relational/pom.xml | 284 ++++--- ...agedTypesBeanRegistrationAotProcessor.java | 2 +- .../data/relational/aot/package-info.java | 4 +- .../core/EntityLifecycleEventDelegate.java | 2 +- .../core/conversion/BatchedActions.java | 6 +- .../relational/core/conversion/DbAction.java | 214 ++--- .../conversion/DbActionExecutionResult.java | 9 +- .../DefaultRootAggregateChange.java | 10 +- .../conversion/DeleteAggregateChange.java | 2 +- .../DeleteBatchingAggregateChange.java | 2 +- .../conversion/DocumentPropertyAccessor.java | 2 +- .../MappingRelationalConverter.java | 88 +- .../conversion/MutableAggregateChange.java | 2 +- .../core/conversion/ObjectPath.java | 2 +- .../relational/core/conversion/PathNode.java | 45 +- .../core/conversion/RelationalConverter.java | 4 +- .../RelationalEntityDeleteWriter.java | 2 +- .../RelationalEntityVersionUtils.java | 2 +- .../core/conversion/RowDocumentAccessor.java | 2 +- .../SaveBatchingAggregateChange.java | 2 +- .../core/conversion/WritingContext.java | 28 +- .../core/conversion/package-info.java | 4 +- .../data/relational/core/dialect/Escaper.java | 2 +- .../relational/core/dialect/package-info.java | 4 +- .../core/mapping/AggregatePath.java | 20 +- .../BasicRelationalPersistentEntity.java | 4 +- .../BasicRelationalPersistentProperty.java | 13 +- .../core/mapping/DefaultAggregatePath.java | 91 ++- .../core/mapping/DerivedSqlIdentifier.java | 2 +- .../core/mapping/EmbeddedContext.java | 4 - .../EmbeddedRelationalPersistentEntity.java | 45 +- .../EmbeddedRelationalPersistentProperty.java | 3 +- .../mapping/PersistentPropertyTranslator.java | 2 +- .../mapping/RelationalMappingContext.java | 6 +- .../mapping/RelationalPersistentProperty.java | 7 +- .../core/mapping/event/AfterDeleteEvent.java | 2 +- .../core/mapping/event/BeforeDeleteEvent.java | 2 +- .../core/mapping/event/Identifier.java | 2 +- .../mapping/event/RelationalDeleteEvent.java | 2 +- .../core/mapping/event/RelationalEvent.java | 2 +- .../core/mapping/event/package-info.java | 4 +- .../relational/core/mapping/package-info.java | 4 +- .../data/relational/core/query/Criteria.java | 27 +- .../core/query/CriteriaDefinition.java | 4 +- .../data/relational/core/query/Query.java | 2 +- .../data/relational/core/query/Update.java | 2 +- .../relational/core/query/ValueFunction.java | 2 +- .../relational/core/query/package-info.java | 3 +- .../core/sql/AbstractImportValidator.java | 2 +- .../relational/core/sql/AbstractSegment.java | 2 +- .../relational/core/sql/BooleanLiteral.java | 9 +- .../relational/core/sql/CaseExpression.java | 11 +- .../data/relational/core/sql/Column.java | 2 +- .../core/sql/CompositeSqlIdentifier.java | 2 +- .../relational/core/sql/DefaultDelete.java | 2 +- .../core/sql/DefaultDeleteBuilder.java | 10 +- .../relational/core/sql/DefaultInsert.java | 6 +- .../core/sql/DefaultInsertBuilder.java | 6 +- .../relational/core/sql/DefaultSelect.java | 2 +- .../core/sql/DefaultSelectBuilder.java | 14 +- .../core/sql/DefaultSqlIdentifier.java | 2 +- .../relational/core/sql/DefaultUpdate.java | 2 +- .../core/sql/DefaultUpdateBuilder.java | 9 +- .../data/relational/core/sql/Literal.java | 4 +- .../relational/core/sql/NumericLiteral.java | 2 +- .../relational/core/sql/OrderByField.java | 4 +- .../data/relational/core/sql/SQL.java | 2 +- .../data/relational/core/sql/Select.java | 2 +- .../relational/core/sql/StringLiteral.java | 2 +- .../data/relational/core/sql/Table.java | 2 +- .../relational/core/sql/package-info.java | 6 +- .../sql/render/AnalyticFunctionVisitor.java | 2 +- .../core/sql/render/BetweenVisitor.java | 2 +- .../core/sql/render/CastVisitor.java | 2 +- .../core/sql/render/ColumnVisitor.java | 2 +- .../core/sql/render/ComparisonVisitor.java | 2 +- .../core/sql/render/ConditionVisitor.java | 2 +- .../core/sql/render/DelegatingVisitor.java | 11 +- .../core/sql/render/ExpressionVisitor.java | 2 +- .../FilteredSingleConditionRenderSupport.java | 6 +- .../sql/render/FilteredSubtreeVisitor.java | 2 +- .../core/sql/render/FromTableVisitor.java | 2 +- .../core/sql/render/LikeVisitor.java | 2 +- .../core/sql/render/NameRenderer.java | 16 +- .../sql/render/NestedConditionVisitor.java | 2 +- .../core/sql/render/NotConditionVisitor.java | 2 +- .../core/sql/render/OrderByClauseVisitor.java | 6 +- .../TypedSingleConditionRenderSupport.java | 5 +- .../core/sql/render/TypedSubtreeVisitor.java | 2 +- .../core/sql/render/ValuesVisitor.java | 2 +- .../core/sql/render/package-info.java | 6 +- .../SingleQuerySqlGenerator.java | 2 +- .../core/sqlgeneration/SqlGenerator.java | 2 +- .../data/relational/domain/RowDocument.java | 2 +- .../data/relational/domain/SqlSort.java | 6 +- .../repository/query/CriteriaFactory.java | 68 +- .../repository/query/ParameterMetadata.java | 24 +- .../query/ParameterMetadataProvider.java | 9 +- .../repository/query/package-info.java | 4 +- .../MappingRelationalEntityInformation.java | 4 +- .../support/TableNameQueryPreprocessor.java | 6 +- .../repository/support/package-info.java | 4 +- .../core/conversion/DbActionTestSupport.java | 8 +- .../DeleteBatchingAggregateChangeTest.java | 6 +- ...RelationalEntityDeleteWriterUnitTests.java | 12 +- ...RelationalEntityInsertWriterUnitTests.java | 4 +- ...RelationalEntityUpdateWriterUnitTests.java | 2 +- .../RelationalEntityWriterUnitTests.java | 159 ++-- .../SaveBatchingAggregateChangeTest.java | 28 +- ...RelationalPersistentPropertyUnitTests.java | 122 ++- .../ParameterMetadataProviderUnitTests.java | 10 +- .../query/RelationalExampleMapperTests.java | 2 +- 242 files changed, 2275 insertions(+), 2275 deletions(-) delete mode 100644 spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/CachingResultSet.java diff --git a/.gitignore b/.gitignore index d9642d2c66..37d0ff8eee 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ target/ .idea/ +.vscode/ .settings/ *.iml .flattened-pom.xml diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/aot/JdbcRuntimeHints.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/aot/JdbcRuntimeHints.java index 3a5eb3a73e..a788e59f6b 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/aot/JdbcRuntimeHints.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/aot/JdbcRuntimeHints.java @@ -17,6 +17,7 @@ import java.util.Arrays; +import org.jspecify.annotations.Nullable; import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsRegistrar; @@ -30,7 +31,6 @@ import org.springframework.data.relational.core.mapping.event.BeforeConvertCallback; import org.springframework.data.relational.core.mapping.event.BeforeDeleteCallback; import org.springframework.data.relational.core.mapping.event.BeforeSaveCallback; -import org.springframework.lang.Nullable; /** * {@link RuntimeHintsRegistrar} for JDBC. diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutionContext.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutionContext.java index 75579d83a4..11611bdda7 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutionContext.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutionContext.java @@ -15,20 +15,12 @@ */ package org.springframework.data.jdbc.core; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.stream.Collectors; +import org.jspecify.annotations.Nullable; import org.springframework.dao.IncorrectUpdateSemanticsDataAccessException; import org.springframework.dao.OptimisticLockingFailureException; import org.springframework.data.jdbc.core.convert.DataAccessStrategy; @@ -48,7 +40,6 @@ import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; import org.springframework.data.relational.core.sql.LockMode; import org.springframework.data.util.Pair; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -82,8 +73,8 @@ class JdbcAggregateChangeExecutionContext { void executeInsertRoot(DbAction.InsertRoot insert) { - Object id = accessStrategy.insert(insert.getEntity(), insert.getEntityType(), Identifier.empty(), - insert.getIdValueSource()); + Object id = accessStrategy.insert(insert.entity(), insert.entityType(), Identifier.empty(), + insert.idValueSource()); add(new DbActionExecutionResult(insert, id)); } @@ -91,9 +82,9 @@ void executeBatchInsertRoot(DbAction.BatchInsertRoot batchInsertRoot) { List> inserts = batchInsertRoot.getActions(); List> insertSubjects = inserts.stream() - .map(insert -> InsertSubject.describedBy(insert.getEntity(), Identifier.empty())).collect(Collectors.toList()); + .map(insert -> InsertSubject.describedBy(insert.entity(), Identifier.empty())).collect(Collectors.toList()); - Object[] ids = accessStrategy.insert(insertSubjects, batchInsertRoot.getEntityType(), + Object[] ids = accessStrategy.insert(insertSubjects, batchInsertRoot.entityType(), batchInsertRoot.getBatchValue()); for (int i = 0; i < inserts.size(); i++) { @@ -104,8 +95,8 @@ void executeBatchInsertRoot(DbAction.BatchInsertRoot batchInsertRoot) { void executeInsert(DbAction.Insert insert) { Identifier parentKeys = getParentKeys(insert, converter); - Object id = accessStrategy.insert(insert.getEntity(), insert.getEntityType(), parentKeys, - insert.getIdValueSource()); + Object id = accessStrategy.insert(insert.entity(), insert.entityType(), parentKeys, + insert.idValueSource()); add(new DbActionExecutionResult(insert, id)); } @@ -113,10 +104,10 @@ void executeBatchInsert(DbAction.BatchInsert batchInsert) { List> inserts = batchInsert.getActions(); List> insertSubjects = inserts.stream() - .map(insert -> InsertSubject.describedBy(insert.getEntity(), getParentKeys(insert, converter))) + .map(insert -> InsertSubject.describedBy(insert.entity(), getParentKeys(insert, converter))) .collect(Collectors.toList()); - Object[] ids = accessStrategy.insert(insertSubjects, batchInsert.getEntityType(), batchInsert.getBatchValue()); + Object[] ids = accessStrategy.insert(insertSubjects, batchInsert.entityType(), batchInsert.getBatchValue()); for (int i = 0; i < inserts.size(); i++) { add(new DbActionExecutionResult(inserts.get(i), ids.length > 0 ? ids[i] : null)); @@ -135,46 +126,46 @@ void executeUpdateRoot(DbAction.UpdateRoot update) { void executeDeleteRoot(DbAction.DeleteRoot delete) { - if (delete.getPreviousVersion() != null) { - accessStrategy.deleteWithVersion(delete.getId(), delete.getEntityType(), delete.getPreviousVersion()); + if (delete.previousVersion() != null) { + accessStrategy.deleteWithVersion(delete.id(), delete.entityType(), delete.previousVersion()); } else { - accessStrategy.delete(delete.getId(), delete.getEntityType()); + accessStrategy.delete(delete.id(), delete.entityType()); } } void executeBatchDeleteRoot(DbAction.BatchDeleteRoot batchDelete) { - List rootIds = batchDelete.getActions().stream().map(DbAction.DeleteRoot::getId).toList(); - accessStrategy.delete(rootIds, batchDelete.getEntityType()); + List rootIds = batchDelete.getActions().stream().map(DbAction.DeleteRoot::id).toList(); + accessStrategy.delete(rootIds, batchDelete.entityType()); } void executeDelete(DbAction.Delete delete) { - accessStrategy.delete(delete.getRootId(), delete.getPropertyPath()); + accessStrategy.delete(delete.rootId(), delete.propertyPath()); } void executeBatchDelete(DbAction.BatchDelete batchDelete) { - List rootIds = batchDelete.getActions().stream().map(DbAction.Delete::getRootId).toList(); + List rootIds = batchDelete.getActions().stream().map(DbAction.Delete::rootId).toList(); accessStrategy.delete(rootIds, batchDelete.getBatchValue()); } void executeDeleteAllRoot(DbAction.DeleteAllRoot deleteAllRoot) { - accessStrategy.deleteAll(deleteAllRoot.getEntityType()); + accessStrategy.deleteAll(deleteAllRoot.entityType()); } void executeDeleteAll(DbAction.DeleteAll delete) { - accessStrategy.deleteAll(delete.getPropertyPath()); + accessStrategy.deleteAll(delete.propertyPath()); } void executeAcquireLock(DbAction.AcquireLockRoot acquireLock) { - accessStrategy.acquireLockById(acquireLock.getId(), LockMode.PESSIMISTIC_WRITE, acquireLock.getEntityType()); + accessStrategy.acquireLockById(acquireLock.getId(), LockMode.PESSIMISTIC_WRITE, acquireLock.entityType()); } void executeAcquireLockAllRoot(DbAction.AcquireLockAllRoot acquireLock) { - accessStrategy.acquireLockAll(LockMode.PESSIMISTIC_WRITE, acquireLock.getEntityType()); + accessStrategy.acquireLockAll(LockMode.PESSIMISTIC_WRITE, acquireLock.entityType()); } private void add(DbActionExecutionResult result) { @@ -185,11 +176,11 @@ private Identifier getParentKeys(DbAction.WithDependingOn action, JdbcConvert Object id = getParentId(action); - AggregatePath aggregatePath = context.getAggregatePath(action.getPropertyPath()); + AggregatePath aggregatePath = context.getAggregatePath(action.propertyPath()); JdbcIdentifierBuilder identifier = JdbcIdentifierBuilder // .forBackReferences(converter, aggregatePath, getIdMapper(id, aggregatePath, converter)); - for (Map.Entry, Object> qualifier : action.getQualifiers() + for (Map.Entry, Object> qualifier : action.qualifiers() .entrySet()) { identifier = identifier.withQualifier(context.getAggregatePath(qualifier.getKey()), qualifier.getValue()); } @@ -200,21 +191,21 @@ private Identifier getParentKeys(DbAction.WithDependingOn action, JdbcConvert static Function getIdMapper(Object idValue, AggregatePath path, JdbcConverter converter) { RelationalPersistentProperty idProperty = path.getIdDefiningParentPath().getRequiredIdProperty(); - RelationalPersistentEntity entity = converter.getMappingContext() - .getPersistentEntity(idProperty); + RelationalPersistentEntity entity = converter.getMappingContext().getPersistentEntity(idProperty); if (entity == null) { return aggregatePath -> idValue; } PersistentPropertyPathAccessor propertyPathAccessor = entity.getPropertyPathAccessor(idValue); - return aggregatePath -> propertyPathAccessor.getProperty(aggregatePath.getSubPathBasedOn(idProperty.getActualType()).getRequiredPersistentPropertyPath()); + return aggregatePath -> propertyPathAccessor + .getProperty(aggregatePath.getSubPathBasedOn(idProperty.getActualType()).getRequiredPersistentPropertyPath()); } private Object getParentId(DbAction.WithDependingOn action) { DbAction.WithEntity idOwningAction = getIdOwningAction(action, - context.getAggregatePath(action.getPropertyPath()).getIdDefiningParentPath()); + context.getAggregatePath(action.propertyPath()).getIdDefiningParentPath()); return getPotentialGeneratedIdFrom(idOwningAction); } @@ -229,16 +220,16 @@ private DbAction.WithEntity getIdOwningAction(DbAction.WithEntity action, return action; } - if (idPath.equals(context.getAggregatePath(withDependingOn.getPropertyPath()))) { + if (idPath.equals(context.getAggregatePath(withDependingOn.propertyPath()))) { return action; } - return getIdOwningAction(withDependingOn.getDependingOn(), idPath); + return getIdOwningAction(withDependingOn.dependingOn(), idPath); } private Object getPotentialGeneratedIdFrom(DbAction.WithEntity idOwningAction) { - if (IdValueSource.GENERATED.equals(idOwningAction.getIdValueSource())) { + if (IdValueSource.GENERATED.equals(idOwningAction.idValueSource())) { DbActionExecutionResult dbActionExecutionResult = results.get(idOwningAction); Object generatedId = Optional.ofNullable(dbActionExecutionResult) // @@ -255,8 +246,8 @@ private Object getPotentialGeneratedIdFrom(DbAction.WithEntity idOwningAction private Object getIdFrom(DbAction.WithEntity idOwningAction) { - RelationalPersistentEntity persistentEntity = getRequiredPersistentEntity(idOwningAction.getEntityType()); - Object identifier = persistentEntity.getIdentifierAccessor(idOwningAction.getEntity()).getIdentifier(); + RelationalPersistentEntity persistentEntity = getRequiredPersistentEntity(idOwningAction.entityType()); + Object identifier = persistentEntity.getIdentifierAccessor(idOwningAction.entity()).getIdentifier(); Assert.state(identifier != null, () -> "Couldn't obtain a required id value for " + persistentEntity); @@ -290,12 +281,12 @@ List populateIdsIfNecessary() { Pair qualifier = insert.getQualifier(); Object qualifierValue = qualifier == null ? null : qualifier.getSecond(); - if (newEntity != action.getEntity()) { + if (newEntity != action.entity()) { - cascadingValues.stage(insert.getDependingOn(), insert.getPropertyPath(), qualifierValue, newEntity); - } else if (insert.getPropertyPath().getLeafProperty().isCollectionLike()) { + cascadingValues.stage(insert.dependingOn(), insert.propertyPath(), qualifierValue, newEntity); + } else if (insert.propertyPath().getLeafProperty().isCollectionLike()) { - cascadingValues.gather(insert.getDependingOn(), insert.getPropertyPath(), qualifierValue, newEntity); + cascadingValues.gather(insert.dependingOn(), insert.propertyPath(), qualifierValue, newEntity); } } } @@ -315,14 +306,14 @@ List populateIdsIfNecessary() { private Object setIdAndCascadingProperties(DbAction.WithEntity action, @Nullable Object generatedId, StagedValues cascadingValues) { - S originalEntity = action.getEntity(); + S originalEntity = action.entity(); RelationalPersistentEntity persistentEntity = (RelationalPersistentEntity) context - .getRequiredPersistentEntity(action.getEntityType()); + .getRequiredPersistentEntity(action.entityType()); PersistentPropertyPathAccessor propertyAccessor = converter.getPropertyAccessor(persistentEntity, originalEntity); - if (IdValueSource.GENERATED.equals(action.getIdValueSource())) { + if (IdValueSource.GENERATED.equals(action.idValueSource())) { propertyAccessor.setProperty(persistentEntity.getRequiredIdProperty(), generatedId); } @@ -337,7 +328,7 @@ private Object setIdAndCascadingProperties(DbAction.WithEntity action, @N private PersistentPropertyPath getRelativePath(DbAction action, PersistentPropertyPath pathToValue) { if (action instanceof DbAction.Insert insert) { - return pathToValue.getExtensionForBaseOf(insert.getPropertyPath()); + return pathToValue.getExtensionForBaseOf(insert.propertyPath()); } if (action instanceof DbAction.InsertRoot) { @@ -358,10 +349,10 @@ private RelationalPersistentEntity getRequiredPersistentEntity(Class t private void updateWithoutVersion(DbAction.UpdateRoot update) { - if (!accessStrategy.update(update.getEntity(), update.getEntityType())) { + if (!accessStrategy.update(update.entity(), update.entityType())) { throw new IncorrectUpdateSemanticsDataAccessException( - String.format(UPDATE_FAILED, update.getEntity(), getIdFrom(update))); + String.format(UPDATE_FAILED, update.entity(), getIdFrom(update))); } } @@ -370,9 +361,9 @@ private void updateWithVersion(DbAction.UpdateRoot update) { Number previousVersion = update.getPreviousVersion(); Assert.notNull(previousVersion, "The root aggregate cannot be updated because the version property is null"); - if (!accessStrategy.updateWithVersion(update.getEntity(), update.getEntityType(), previousVersion)) { + if (!accessStrategy.updateWithVersion(update.entity(), update.entityType(), previousVersion)) { - throw new OptimisticLockingFailureException(String.format(UPDATE_FAILED_OPTIMISTIC_LOCKING, update.getEntity())); + throw new OptimisticLockingFailureException(String.format(UPDATE_FAILED_OPTIMISTIC_LOCKING, update.entity())); } } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateOperations.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateOperations.java index 8b9dbd6f33..f98aad06c0 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateOperations.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateOperations.java @@ -19,6 +19,7 @@ import java.util.Optional; import java.util.stream.Stream; +import org.jspecify.annotations.Nullable; import org.springframework.dao.IncorrectUpdateSemanticsDataAccessException; import org.springframework.data.domain.Example; import org.springframework.data.domain.Page; @@ -27,7 +28,6 @@ import org.springframework.data.jdbc.core.convert.DataAccessStrategy; import org.springframework.data.jdbc.core.convert.JdbcConverter; import org.springframework.data.relational.core.query.Query; -import org.springframework.lang.Nullable; /** * Specifies operations one can perform on a database, based on an Domain Type. @@ -171,8 +171,8 @@ public interface JdbcAggregateOperations { List findAllById(Iterable ids, Class domainType); /** - * Loads all entities that match one of the ids passed as an argument to a {@link Stream}. - * It is not guaranteed that the number of ids passed in matches the number of entities returned. + * Loads all entities that match one of the ids passed as an argument to a {@link Stream}. It is not guaranteed that + * the number of ids passed in matches the number of entities returned. * * @param ids the Ids of the entities to load. Must not be {@code null}. * @param domainType the type of entities to load. Must not be {@code null}. diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateTemplate.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateTemplate.java index 9f5dd08d6b..b1c6e8b2da 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateTemplate.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateTemplate.java @@ -28,6 +28,7 @@ import java.util.stream.Stream; import java.util.stream.StreamSupport; +import org.jspecify.annotations.Nullable; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -55,7 +56,6 @@ import org.springframework.data.relational.core.mapping.event.*; import org.springframework.data.relational.core.query.Query; import org.springframework.data.support.PageableExecutionUtils; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -305,7 +305,7 @@ public boolean existsById(Object id, Class domainType) { } @Override - public T findById(Object id, Class domainType) { + public @Nullable T findById(Object id, Class domainType) { Assert.notNull(id, "Id must not be null"); Assert.notNull(domainType, "Domain type must not be null"); diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/AggregateReader.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/AggregateReader.java index be3629f9d7..bf4526e267 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/AggregateReader.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/AggregateReader.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.Optional; +import org.jspecify.annotations.Nullable; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.data.relational.core.dialect.Dialect; import org.springframework.data.relational.core.mapping.AggregatePath; @@ -40,7 +41,6 @@ import org.springframework.jdbc.core.ResultSetExtractor; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; -import org.springframework.lang.Nullable; /** * Reads complete Aggregates from the database, by generating appropriate SQL using a {@link SingleQuerySqlGenerator} @@ -110,6 +110,7 @@ public T findById(Object id, RelationalPersistentEntity entity) { * @return the found aggregate root, or {@literal null} if not found. * @param aggregator type. */ + @SuppressWarnings("NullAway") // NullAway complains about the ResultSetExtractor returning null, which should be ok. @Nullable public T findOne(Query query, RelationalPersistentEntity entity) { return doFind(query, entity, rs -> extractZeroOrOne(rs, entity)); diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/CachingResultSet.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/CachingResultSet.java deleted file mode 100644 index d98fb9f5b6..0000000000 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/CachingResultSet.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2023-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.jdbc.core.convert; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.HashMap; -import java.util.Map; - -import org.springframework.lang.Nullable; - -/** - * Despite its name not really a {@link ResultSet}, but it offers the part of the {@literal ResultSet} API that is used - * by {@link AggregateReader}. It allows peeking in the next row of a ResultSet by caching one row of the ResultSet. - * - * @author Jens Schauder - * @since 3.2 - */ -class CachingResultSet { - - private final ResultSetAccessor accessor; - private final ResultSet resultSet; - private Cache cache; - - CachingResultSet(ResultSet resultSet) { - - this.accessor = new ResultSetAccessor(resultSet); - this.resultSet = resultSet; - } - - public boolean next() { - - if (isPeeking()) { - - final boolean next = cache.next; - cache = null; - return next; - } - - try { - return resultSet.next(); - } catch (SQLException e) { - throw new RuntimeException("Failed to advance CachingResultSet", e); - } - } - - @Nullable - public Object getObject(String columnLabel) { - - Object returnValue; - if (isPeeking()) { - returnValue = cache.values.get(columnLabel); - } else { - returnValue = safeGetFromDelegate(columnLabel); - } - - return returnValue; - } - - @Nullable - Object peek(String columnLabel) { - - if (!isPeeking()) { - createCache(); - } - - if (!cache.next) { - return null; - } - - return safeGetFromDelegate(columnLabel); - } - - @Nullable - private Object safeGetFromDelegate(String columnLabel) { - return accessor.getObject(columnLabel); - } - - private void createCache() { - cache = new Cache(); - - try { - int columnCount = resultSet.getMetaData().getColumnCount(); - for (int i = 1; i <= columnCount; i++) { - // at least some databases return lower case labels although rs.getObject(UPPERCASE_LABEL) returns the expected - // value. The aliases we use happen to be uppercase. So we transform everything to upper case. - cache.add(resultSet.getMetaData().getColumnLabel(i).toLowerCase(), - accessor.getObject(resultSet.getMetaData().getColumnLabel(i))); - } - - cache.next = resultSet.next(); - } catch (SQLException se) { - throw new RuntimeException("Can't cache result set data", se); - } - - } - - private boolean isPeeking() { - return cache != null; - } - - private static class Cache { - - boolean next; - Map values = new HashMap<>(); - - void add(String columnName, Object value) { - values.put(columnName, value); - } - } -} diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DataAccessStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DataAccessStrategy.java index 1e8fea9a8c..4d08c00f36 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DataAccessStrategy.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DataAccessStrategy.java @@ -20,6 +20,7 @@ import java.util.Optional; import java.util.stream.Stream; +import org.jspecify.annotations.Nullable; import org.springframework.dao.OptimisticLockingFailureException; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; @@ -31,7 +32,6 @@ import org.springframework.data.relational.core.query.Query; import org.springframework.data.relational.core.sql.LockMode; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; -import org.springframework.lang.Nullable; /** * Abstraction for accesses to the database that should be implementable with a single SQL statement per method and @@ -291,8 +291,8 @@ public interface DataAccessStrategy extends ReadingDataAccessStrategy, RelationR Iterable findAllById(Iterable ids, Class domainType); /** - * Loads all entities that match one of the ids passed as an argument to a {@link Stream}. - * It is not guaranteed that the number of ids passed in matches the number of entities returned. + * Loads all entities that match one of the ids passed as an argument to a {@link Stream}. It is not guaranteed that + * the number of ids passed in matches the number of entities returned. * * @param ids the Ids of the entities to load. Must not be {@code null}. * @param domainType the type of entities to load. Must not be {@code null}. diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java index fdcf95aa3e..59445dc1f9 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java @@ -24,6 +24,7 @@ import java.util.Optional; import java.util.stream.Stream; +import org.jspecify.annotations.Nullable; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.dao.OptimisticLockingFailureException; import org.springframework.data.domain.Pageable; @@ -43,7 +44,6 @@ import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.jdbc.core.namedparam.SqlParameterSource; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -117,7 +117,8 @@ public NamedParameterJdbcOperations getJdbcOperations() { } @Override - public Object insert(T instance, Class domainType, Identifier identifier, IdValueSource idValueSource) { + public @Nullable Object insert(T instance, Class domainType, Identifier identifier, + IdValueSource idValueSource) { SqlIdentifierParameterSource parameterSource = sqlParametersFactory.forInsert(instance, domainType, identifier, idValueSource); @@ -281,7 +282,7 @@ public long count(Class domainType) { } @Override - public T findById(Object id, Class domainType) { + public @Nullable T findById(Object id, Class domainType) { String findOneSql = sql(domainType).getFindOne(); SqlIdentifierParameterSource parameter = sqlParametersFactory.forQueryById(id, domainType); @@ -338,7 +339,11 @@ public List findAllByPath(Identifier identifier, Assert.notNull(propertyPath, "propertyPath must not be null"); AggregatePath path = context.getAggregatePath(propertyPath); - Class actualType = path.getLeafEntity().getType(); + RelationalPersistentEntity leafEntity = path.getLeafEntity(); + + Assert.state(leafEntity != null, "leafEntity must not be null"); + + Class actualType = leafEntity.getType(); String findAllByProperty = sql(actualType) // .getFindAllByProperty(identifier, propertyPath); @@ -358,7 +363,8 @@ public Object mapRow(ResultSet rs, int rowNum) throws SQLException { if (!path.hasIdProperty() && path.isQualified()) { TableInfo tableInfo = path.getTableInfo(); - identifierToUse = identifierToUse.withPart(tableInfo.qualifierColumnInfo().name(), rowNum, Object.class); + identifierToUse = identifierToUse.withPart(tableInfo.requiredQualifierColumnInfo().name(), rowNum, + Object.class); } return getEntityRowMapper(path, identifierToUse).mapRow(rs, rowNum); diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DelegatingDataAccessStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DelegatingDataAccessStrategy.java index ad6404f873..fad57b737c 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DelegatingDataAccessStrategy.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DelegatingDataAccessStrategy.java @@ -19,6 +19,7 @@ import java.util.Optional; import java.util.stream.Stream; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.mapping.PersistentPropertyPath; @@ -47,11 +48,13 @@ public class DelegatingDataAccessStrategy implements DataAccessStrategy { private DataAccessStrategy delegate; + @SuppressWarnings("NullAway.Init") public DelegatingDataAccessStrategy() {} public DelegatingDataAccessStrategy(DataAccessStrategy delegate) { Assert.notNull(delegate, "DataAccessStrategy must not be null"); + this.delegate = delegate; } @@ -66,7 +69,8 @@ public NamedParameterJdbcOperations getJdbcOperations() { } @Override - public Object insert(T instance, Class domainType, Identifier identifier, IdValueSource idValueSource) { + public @Nullable Object insert(T instance, Class domainType, Identifier identifier, + IdValueSource idValueSource) { return delegate.insert(instance, domainType, identifier, idValueSource); } @@ -137,7 +141,7 @@ public long count(Class domainType) { } @Override - public T findById(Object id, Class domainType) { + public @Nullable T findById(Object id, Class domainType) { Assert.notNull(delegate, "Delegate is null"); diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/FunctionCollector.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/FunctionCollector.java index d218e666c1..8456aa21fc 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/FunctionCollector.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/FunctionCollector.java @@ -96,7 +96,7 @@ public Set characteristics() { */ static class ResultOrException { - private T result; + @SuppressWarnings("NullAway.Init") private T result; private final List exceptions = new LinkedList<>(); private boolean hasResult = false; diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/IdGeneratingBatchInsertStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/IdGeneratingBatchInsertStrategy.java index 78337bf52c..4185efacb8 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/IdGeneratingBatchInsertStrategy.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/IdGeneratingBatchInsertStrategy.java @@ -20,13 +20,13 @@ import java.util.Map; import java.util.Optional; +import org.jspecify.annotations.Nullable; import org.springframework.data.relational.core.dialect.Dialect; import org.springframework.data.relational.core.dialect.IdGeneration; import org.springframework.data.relational.core.sql.SqlIdentifier; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.jdbc.core.namedparam.SqlParameterSource; import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.lang.Nullable; /** * A {@link BatchInsertStrategy} that expects ids to be generated from the batch insert. When the {@link Dialect} does @@ -42,7 +42,7 @@ class IdGeneratingBatchInsertStrategy implements BatchInsertStrategy { private final InsertStrategy insertStrategy; private final Dialect dialect; private final NamedParameterJdbcOperations jdbcOperations; - private final SqlIdentifier idColumn; + private final @Nullable SqlIdentifier idColumn; IdGeneratingBatchInsertStrategy(InsertStrategy insertStrategy, Dialect dialect, NamedParameterJdbcOperations jdbcOperations, @Nullable SqlIdentifier idColumn) { diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/IdGeneratingInsertStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/IdGeneratingInsertStrategy.java index 47b2f9d084..32149c5ba1 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/IdGeneratingInsertStrategy.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/IdGeneratingInsertStrategy.java @@ -18,6 +18,7 @@ import java.util.Map; import java.util.Optional; +import org.jspecify.annotations.Nullable; import org.springframework.dao.DataRetrievalFailureException; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.relational.core.dialect.Dialect; @@ -27,7 +28,6 @@ import org.springframework.jdbc.core.namedparam.SqlParameterSource; import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.jdbc.support.KeyHolder; -import org.springframework.lang.Nullable; /** * An {@link InsertStrategy} that expects an id to be generated from the insert. @@ -40,7 +40,7 @@ class IdGeneratingInsertStrategy implements InsertStrategy { private final Dialect dialect; private final NamedParameterJdbcOperations jdbcOperations; - private final SqlIdentifier idColumn; + private final @Nullable SqlIdentifier idColumn; IdGeneratingInsertStrategy(Dialect dialect, NamedParameterJdbcOperations jdbcOperations, @Nullable SqlIdentifier idColumn) { @@ -50,7 +50,7 @@ class IdGeneratingInsertStrategy implements InsertStrategy { } @Override - public Object execute(String sql, SqlParameterSource sqlParameterSource) { + public @Nullable Object execute(String sql, SqlParameterSource sqlParameterSource) { KeyHolder holder = new GeneratedKeyHolder(); diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/Identifier.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/Identifier.java index 711ba330c8..cf3fb292ac 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/Identifier.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/Identifier.java @@ -23,8 +23,8 @@ import java.util.Map; import java.util.Objects; +import org.jspecify.annotations.Nullable; import org.springframework.data.relational.core.sql.SqlIdentifier; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -291,7 +291,7 @@ public StringKeyedLinkedHashMap(int initialCapacity) { } @Override - public V get(Object key) { + public @Nullable V get(Object key) { if (key instanceof String) { diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/InsertStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/InsertStrategy.java index 0c618e2466..0484e773cb 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/InsertStrategy.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/InsertStrategy.java @@ -15,8 +15,8 @@ */ package org.springframework.data.jdbc.core.convert; +import org.jspecify.annotations.Nullable; import org.springframework.jdbc.core.namedparam.SqlParameterSource; -import org.springframework.lang.Nullable; /** * Strategy for executing an insert. diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/InsertStrategyFactory.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/InsertStrategyFactory.java index a245e5235f..3f47d5a700 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/InsertStrategyFactory.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/InsertStrategyFactory.java @@ -15,12 +15,12 @@ */ package org.springframework.data.jdbc.core.convert; +import org.jspecify.annotations.Nullable; import org.springframework.data.relational.core.conversion.IdValueSource; import org.springframework.data.relational.core.dialect.Dialect; import org.springframework.data.relational.core.sql.SqlIdentifier; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.jdbc.core.namedparam.SqlParameterSource; -import org.springframework.lang.Nullable; /** * Factory which selects and builds the appropriate {@link InsertStrategy} or {@link BatchInsertStrategy} based on @@ -70,36 +70,25 @@ BatchInsertStrategy batchInsertStrategy(IdValueSource idValueSource, @Nullable S return new DefaultBatchInsertStrategy(jdbcOperations); } - private static class DefaultInsertStrategy implements InsertStrategy { - - private final NamedParameterJdbcOperations jdbcOperations; - - DefaultInsertStrategy(NamedParameterJdbcOperations jdbcOperations) { - this.jdbcOperations = jdbcOperations; - } + private record DefaultInsertStrategy(NamedParameterJdbcOperations jdbcOperations) implements InsertStrategy { @Override - public Object execute(String sql, SqlParameterSource sqlParameterSource) { + public @Nullable Object execute(String sql, SqlParameterSource sqlParameterSource) { - jdbcOperations.update(sql, sqlParameterSource); - return null; + jdbcOperations.update(sql, sqlParameterSource); + return null; + } } - } - private static class DefaultBatchInsertStrategy implements BatchInsertStrategy { - - private final NamedParameterJdbcOperations jdbcOperations; - - DefaultBatchInsertStrategy(NamedParameterJdbcOperations jdbcOperations) { - this.jdbcOperations = jdbcOperations; - } + private record DefaultBatchInsertStrategy( + NamedParameterJdbcOperations jdbcOperations) implements BatchInsertStrategy { @Override - public Object[] execute(String sql, SqlParameterSource[] sqlParameterSources) { + public Object[] execute(String sql, SqlParameterSource[] sqlParameterSources) { - jdbcOperations.batchUpdate(sql, sqlParameterSources); - return new Object[sqlParameterSources.length]; + jdbcOperations.batchUpdate(sql, sqlParameterSources); + return new Object[sqlParameterSources.length]; + } } - } } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/InsertSubject.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/InsertSubject.java index 6db9fcf7fd..0c9cb3ef07 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/InsertSubject.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/InsertSubject.java @@ -17,7 +17,7 @@ import java.util.Objects; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * The subject of an insert, described by the entity instance and its {@link Identifier}, where identifier contains diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/IterableOfEntryToMapConverter.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/IterableOfEntryToMapConverter.java index f5203db269..5d7d6b0a05 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/IterableOfEntryToMapConverter.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/IterableOfEntryToMapConverter.java @@ -19,10 +19,10 @@ import java.util.Map; import java.util.Map.Entry; +import org.jspecify.annotations.Nullable; import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.converter.ConditionalConverter; import org.springframework.core.convert.converter.Converter; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcConverter.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcConverter.java index 3e73a73cf7..56dbbc0917 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcConverter.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcConverter.java @@ -17,13 +17,13 @@ import java.sql.SQLType; +import org.jspecify.annotations.Nullable; import org.springframework.data.jdbc.core.mapping.JdbcValue; import org.springframework.data.relational.core.conversion.RelationalConverter; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; import org.springframework.data.relational.domain.RowDocument; import org.springframework.data.util.TypeInformation; -import org.springframework.lang.Nullable; /** * A {@link JdbcConverter} is responsible for converting for values to the native relational representation and vice diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcIdentifierBuilder.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcIdentifierBuilder.java index 24213662ff..b9eba04ba8 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcIdentifierBuilder.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcIdentifierBuilder.java @@ -93,7 +93,8 @@ public JdbcIdentifierBuilder withQualifier(AggregatePath path, Object value) { Assert.notNull(value, "Value must not be null"); AggregatePath.TableInfo tableInfo = path.getTableInfo(); - identifier = identifier.withPart(tableInfo.qualifierColumnInfo().name(), value, tableInfo.qualifierColumnType()); + identifier = identifier.withPart(tableInfo.requiredQualifierColumnInfo().name(), value, + tableInfo.requiredQualifierColumnType()); return this; } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcPropertyValueProvider.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcPropertyValueProvider.java index 4485ef28bc..b40f4cd5ad 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcPropertyValueProvider.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcPropertyValueProvider.java @@ -15,6 +15,7 @@ */ package org.springframework.data.jdbc.core.convert; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.model.PropertyValueProvider; import org.springframework.data.relational.core.mapping.AggregatePath; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; @@ -42,7 +43,7 @@ class JdbcPropertyValueProvider implements PropertyValueProvider T getPropertyValue(RelationalPersistentProperty property) { + public @Nullable T getPropertyValue(RelationalPersistentProperty property) { return (T) resultSet.getObject(getColumnName(property)); } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MapEntityRowMapper.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MapEntityRowMapper.java index ca38e9604d..935b65a355 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MapEntityRowMapper.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MapEntityRowMapper.java @@ -25,6 +25,7 @@ import org.springframework.data.relational.domain.RowDocument; import org.springframework.data.util.TypeInformation; import org.springframework.jdbc.core.RowMapper; +import org.springframework.util.Assert; /** * A {@link RowMapper} that maps a row to a {@link Map.Entry} so an {@link Iterable} of those can be converted to a @@ -55,6 +56,9 @@ public Map.Entry mapRow(ResultSet rs, int rowNum) throws SQLException RowDocument document = RowDocumentResultSetExtractor.toRowDocument(rs); Object key = document.get(keyColumn.getReference()); + + Assert.notNull(key, "Key must not be null"); + Class qualifierColumnType = path.getRequiredLeafProperty().getQualifierColumnType(); Object convertedKey = converter.readValue(key, TypeInformation.of(qualifierColumnType)); diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MappingJdbcConverter.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MappingJdbcConverter.java index 31baf0eabe..22f3309909 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MappingJdbcConverter.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MappingJdbcConverter.java @@ -25,6 +25,7 @@ import java.util.Optional; import java.util.function.Function; +import org.jspecify.annotations.Nullable; import org.springframework.context.ApplicationContextAware; import org.springframework.core.convert.converter.Converter; import org.springframework.dao.DataAccessException; @@ -49,7 +50,6 @@ import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator; import org.springframework.jdbc.support.SQLExceptionSubclassTranslator; import org.springframework.jdbc.support.SQLExceptionTranslator; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -165,7 +165,12 @@ private Class doGetColumnType(RelationalPersistentProperty property) { } if (property.isEntity()) { - Class columnType = getEntityColumnType(property.getTypeInformation().getActualType()); + + TypeInformation actualType = property.getTypeInformation().getActualType(); + + Assert.state(actualType != null, "actualType must not be null"); + + Class columnType = getEntityColumnType(actualType); if (columnType != null) { return columnType; @@ -207,11 +212,17 @@ public Object readValue(@Nullable Object value, TypeInformation targetType) { List> types = targetType.getTypeArguments(); TypeInformation idType = types.get(1); + + Object referencedId; if (value instanceof AggregateReference ref) { - return AggregateReference.to(readValue(ref.getId(), idType)); + referencedId = readValue(ref.getId(), idType); } else { - return AggregateReference.to(readValue(value, idType)); + referencedId = readValue(value, idType); } + + Assert.state(referencedId != null, "Reference must not be null"); + + return AggregateReference.to(referencedId); } return getPotentiallyConvertedSimpleRead(value, targetType); @@ -545,7 +556,11 @@ private static AggregatePath smartAppend(AggregatePath base, AggregatePath suffi if (owner.equals(base.getRequiredLeafEntity())) { return base.append(suffix); } else { - return smartAppend(base, suffix.getTail()); + AggregatePath tail = suffix.getTail(); + + Assert.state(tail != null, "Tail must not be null"); + + return smartAppend(base, tail); } } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/QueryMapper.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/QueryMapper.java index 1d3ce3095e..56b7b8651b 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/QueryMapper.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/QueryMapper.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.Map; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Sort; import org.springframework.data.jdbc.core.mapping.JdbcValue; import org.springframework.data.jdbc.support.JdbcUtil; @@ -42,7 +43,6 @@ import org.springframework.data.util.Pair; import org.springframework.data.util.TypeInformation; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -98,7 +98,8 @@ public List getMappedSort(Table table, Sort sort, @Nullable Relati } - private OrderByField createSimpleOrderByField(Table table, RelationalPersistentEntity entity, Sort.Order order) { + private OrderByField createSimpleOrderByField(Table table, @Nullable RelationalPersistentEntity entity, + Sort.Order order) { if (order instanceof SqlSort.SqlOrder sqlOrder && sqlOrder.isUnsafe()) { return OrderByField.from(Expressions.just(sqlOrder.getProperty())); @@ -182,8 +183,13 @@ private Condition unroll(CriteriaDefinition criteria, Table table, @Nullable Rel Map forwardChain = new HashMap<>(); while (current.hasPrevious()) { - forwardChain.put(current.getPrevious(), current); - current = current.getPrevious(); + + CriteriaDefinition previous = current.getPrevious(); + + Assert.state(previous != null, "Previous must not be null"); + + forwardChain.put(previous, current); + current = previous; } // perform the actual mapping @@ -270,12 +276,19 @@ private Condition combine(@Nullable Condition currentCondition, CriteriaDefiniti private Condition mapCondition(CriteriaDefinition criteria, MapSqlParameterSource parameterSource, Table table, @Nullable RelationalPersistentEntity entity) { - Field propertyField = createPropertyField(entity, criteria.getColumn(), this.mappingContext); + SqlIdentifier criteriaColumn = criteria.getColumn(); + + Assert.notNull(criteriaColumn, "Column must not be null"); + + Field propertyField = createPropertyField(entity, criteriaColumn, this.mappingContext); // Single embedded entity if (propertyField.isEmbedded()) { - return mapEmbeddedObjectCondition(criteria, parameterSource, table, - ((MetadataBackedField) propertyField).getPath().getLeafProperty()); + PersistentPropertyPath path = ((MetadataBackedField) propertyField).getPath(); + + Assert.state(path != null, "Path must not be null"); + + return mapEmbeddedObjectCondition(criteria, parameterSource, table, path.getLeafProperty()); } TypeInformation actualType = propertyField.getTypeHint().getRequiredActualType(); @@ -284,6 +297,8 @@ private Condition mapCondition(CriteriaDefinition criteria, MapSqlParameterSourc SQLType sqlType; Comparator comparator = criteria.getComparator(); + Assert.notNull(comparator, "Comparator must not be null"); + if (criteria.getValue() instanceof JdbcValue settableValue) { mappedValue = convertValue(comparator, settableValue.getValue(), propertyField.getTypeHint()); @@ -329,7 +344,14 @@ private JdbcValue convertToJdbcValue(RelationalPersistentProperty property, @Nul JdbcValue first = getWriteValue(property, ((Pair) value).getFirst()); JdbcValue second = getWriteValue(property, ((Pair) value).getSecond()); - return JdbcValue.of(Pair.of(first.getValue(), second.getValue()), first.getJdbcType()); + Object firstValue = first.getValue(); + Object secondValue = second.getValue(); + + Assert.state(firstValue != null, "First value must not be null"); + + Assert.state(secondValue != null, "Second value must not be null"); + + return JdbcValue.of(Pair.of(firstValue, secondValue), first.getJdbcType()); } if (value instanceof Iterable) { @@ -407,8 +429,12 @@ private Condition mapEmbeddedObjectCondition(CriteriaDefinition criteria, MapSql nestedProperty.getTypeInformation()); SQLType sqlType = converter.getTargetSqlType(nestedProperty); + Comparator criteriaComparator = criteria.getComparator(); + + Assert.notNull(criteriaComparator, "Criteria comparator must not be null"); + Condition mappedCondition = createCondition(table.column(sqlIdentifier), mappedNestedValue, sqlType, - parameterSource, criteria.getComparator(), criteria.isIgnoreCase()); + parameterSource, criteriaComparator, criteria.isIgnoreCase()); if (condition != null) { condition = condition.and(mappedCondition); @@ -417,6 +443,8 @@ private Condition mapEmbeddedObjectCondition(CriteriaDefinition criteria, MapSql } } + Assert.state(condition != null, "Condition must not be null"); + return Conditions.nest(condition); } @@ -457,6 +485,10 @@ protected Object convertValue(@Nullable Object value, TypeInformation typeInf ? typeInformation.getRequiredActualType() : TypeInformation.OBJECT); + Assert.state(first != null, "First value must not be null"); + + Assert.state(second != null, "Second value must not be null"); + return Pair.of(first, second); } @@ -534,6 +566,8 @@ private Condition createCondition(Column column, @Nullable Object mappedValue, S if (comparator == Comparator.BETWEEN || comparator == Comparator.NOT_BETWEEN) { + Assert.state(mappedValue != null, "Mapped value must not be null"); + Pair pair = (Pair) mappedValue; Expression begin = bind(pair.getFirst(), sqlType, parameterSource, column.getName().getReference(), ignoreCase); @@ -603,11 +637,19 @@ SQLType getTypeHint(@Nullable Object mappedValue, Class propertyType, JdbcVal return JdbcUtil.TYPE_UNKNOWN; } - if (mappedValue.getClass().equals(settableValue.getValue().getClass())) { + Object settableValueValue = settableValue.getValue(); + + Assert.state(settableValueValue != null, "Settable value must not be null"); + + if (mappedValue.getClass().equals(settableValueValue.getClass())) { return JdbcUtil.TYPE_UNKNOWN; } - return settableValue.getJdbcType(); + SQLType jdbcType = settableValue.getJdbcType(); + + Assert.state(jdbcType != null, "JDBC type must not be null"); + + return jdbcType; } private Expression bind(@Nullable Object mappedValue, SQLType sqlType, MapSqlParameterSource parameterSource, @@ -690,7 +732,7 @@ protected static class MetadataBackedField extends Field { private final RelationalPersistentEntity entity; private final MappingContext, RelationalPersistentProperty> mappingContext; - private final RelationalPersistentProperty property; + private final @Nullable RelationalPersistentProperty property; private final @Nullable PersistentPropertyPath path; private final boolean embedded; private final SQLType sqlType; diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/QueryMappingConfiguration.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/QueryMappingConfiguration.java index 91becb7fb7..42174aa834 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/QueryMappingConfiguration.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/QueryMappingConfiguration.java @@ -15,8 +15,8 @@ */ package org.springframework.data.jdbc.core.convert; +import org.jspecify.annotations.Nullable; import org.springframework.jdbc.core.RowMapper; -import org.springframework.lang.Nullable; /** * Configures a {@link org.springframework.jdbc.core.RowMapper} for each type to be used for extracting entities of that @@ -39,7 +39,7 @@ default RowMapper getRowMapper(Class type) { QueryMappingConfiguration EMPTY = new QueryMappingConfiguration() { @Override - public RowMapper getRowMapper(Class type) { + public @Nullable RowMapper getRowMapper(Class type) { return null; } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/ReadingDataAccessStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/ReadingDataAccessStrategy.java index 5b00f99dd5..c7d01f6e58 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/ReadingDataAccessStrategy.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/ReadingDataAccessStrategy.java @@ -19,10 +19,10 @@ import java.util.Optional; import java.util.stream.Stream; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.relational.core.query.Query; -import org.springframework.lang.Nullable; /** * The finding methods of a {@link DataAccessStrategy}. @@ -74,8 +74,8 @@ interface ReadingDataAccessStrategy { Iterable findAllById(Iterable ids, Class domainType); /** - * Loads all entities that match one of the ids passed as an argument to a {@link Stream}. - * It is not guaranteed that the number of ids passed in matches the number of entities returned. + * Loads all entities that match one of the ids passed as an argument to a {@link Stream}. It is not guaranteed that + * the number of ids passed in matches the number of entities returned. * * @param ids the Ids of the entities to load. Must not be {@code null}. * @param domainType the type of entities to load. Must not be {@code null}. diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/ResultSetAccessor.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/ResultSetAccessor.java index 5cb10291fb..532a7e6255 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/ResultSetAccessor.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/ResultSetAccessor.java @@ -22,9 +22,9 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.MappingException; import org.springframework.jdbc.support.JdbcUtils; -import org.springframework.lang.Nullable; import org.springframework.util.LinkedCaseInsensitiveMap; /** diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/ResultSetAccessorPropertyAccessor.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/ResultSetAccessorPropertyAccessor.java index dc5576a11e..1d9c4c0b56 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/ResultSetAccessorPropertyAccessor.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/ResultSetAccessorPropertyAccessor.java @@ -15,10 +15,10 @@ */ package org.springframework.data.jdbc.core.convert; +import org.jspecify.annotations.Nullable; import org.springframework.expression.EvaluationContext; import org.springframework.expression.PropertyAccessor; import org.springframework.expression.TypedValue; -import org.springframework.lang.Nullable; /** * {@link PropertyAccessor} to access a column from a {@link ResultSetAccessor}. diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/RowDocumentExtractorSupport.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/RowDocumentExtractorSupport.java index 5509157847..6e60e0bf47 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/RowDocumentExtractorSupport.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/RowDocumentExtractorSupport.java @@ -22,6 +22,7 @@ import java.util.Map; import java.util.TreeMap; +import org.jspecify.annotations.Nullable; import org.springframework.data.relational.core.mapping.AggregatePath; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; @@ -29,7 +30,7 @@ import org.springframework.data.relational.core.mapping.RelationalPredicates; import org.springframework.data.relational.core.sql.SqlIdentifier; import org.springframework.data.relational.domain.RowDocument; -import org.springframework.lang.Nullable; +import org.springframework.util.Assert; /** * Support class for {@code ResultSet}-driven extractor implementations extracting {@link RowDocument documents} from @@ -105,7 +106,12 @@ public boolean containsColumn(String columnName) { @Nullable public Object getObject(RS row, String columnName) { - return adapter.getObject(row, columnMap.get(columnName)); + + Integer index = columnMap.get(columnName); + + Assert.state(index != null, "Column not found"); + + return adapter.getObject(row, index); } /** @@ -176,9 +182,9 @@ protected static class RowDocumentSink extends TabularSink { private final AggregateContext aggregateContext; private final RelationalPersistentEntity entity; private final AggregatePath basePath; - private RowDocument result; + private @Nullable RowDocument result; - private String keyColumnName; + private final String keyColumnName; private @Nullable Object key; private final Map> readerState = new LinkedHashMap<>(); @@ -297,6 +303,8 @@ boolean hasResult() { @Override RowDocument getResult() { + Assert.state(result != null, "Result must not be null"); + readerState.forEach((property, reader) -> { if (reader.hasResult()) { @@ -350,7 +358,12 @@ boolean hasResult() { @Override Object getResult() { - return getValue(); + + Object result = getValue(); + + Assert.state(result != null, "Result must not be null"); + + return result; } @Nullable @@ -375,7 +388,7 @@ private static class ContainerSink extends TabularSink { private final String keyColumn; private final AggregateContext aggregateContext; - private Object key; + private @Nullable Object key; private boolean hasResult = false; private final TabularSink componentReader; @@ -413,6 +426,9 @@ void accept(RS row) { if (keyChange) { if (componentReader.hasResult()) { + + Assert.state(this.key != null, "Key must not be null"); + container.add(this.key, componentReader.getResult()); componentReader.reset(); } @@ -435,6 +451,8 @@ public Object getResult() { if (componentReader.hasResult()) { + Assert.state(this.key != null, "Key must not be null"); + container.add(this.key, componentReader.getResult()); componentReader.reset(); } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/RowDocumentResultSetExtractor.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/RowDocumentResultSetExtractor.java index f4e52538a8..05535c246a 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/RowDocumentResultSetExtractor.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/RowDocumentResultSetExtractor.java @@ -24,6 +24,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import org.springframework.dao.DataRetrievalFailureException; import org.springframework.data.jdbc.core.convert.RowDocumentExtractorSupport.AggregateContext; import org.springframework.data.jdbc.core.convert.RowDocumentExtractorSupport.RowDocumentSink; @@ -33,7 +34,7 @@ import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; import org.springframework.data.relational.domain.RowDocument; import org.springframework.jdbc.support.JdbcUtils; -import org.springframework.lang.Nullable; +import org.springframework.util.Assert; import org.springframework.util.LinkedCaseInsensitiveMap; /** @@ -73,7 +74,7 @@ static RowDocument toRowDocument(ResultSet resultSet) throws SQLException { for (int i = 0; i < columnCount; i++) { Object rsv = JdbcUtils.getResultSetValue(resultSet, i + 1); - String columnName = JdbcUtils.lookupColumnName(md, i+1); + String columnName = JdbcUtils.lookupColumnName(md, i + 1); Object old = document.putIfAbsent(columnName, rsv instanceof Array a ? a.getArray() : rsv); if (old != null) { log.warn(DUPLICATE_COLUMN_WARNING.formatted(columnName, i)); @@ -91,7 +92,7 @@ enum ResultSetAdapter implements TabularResultAdapter { INSTANCE; @Override - public Object getObject(ResultSet row, int index) { + public @Nullable Object getObject(ResultSet row, int index) { try { @@ -120,7 +121,7 @@ public Map getColumnMap(ResultSet result) { String columnLabel = metaData.getColumnLabel(i + 1); Object old = columns.put(columnLabel, i + 1); if (old != null) { - log.warn(DUPLICATE_COLUMN_WARNING.formatted( columnLabel, i)); + log.warn(DUPLICATE_COLUMN_WARNING.formatted(columnLabel, i)); } } return columns; @@ -207,8 +208,14 @@ private class RowDocumentIterator implements Iterator { this.aggregateContext = new AggregateContext<>(adapter, context, propertyToColumn, columns); this.resultSet = resultSet; - this.identifierIndex = columns.get(idColumn); + + Integer index = columns.get(idColumn); + + Assert.state(index != null, "Identifier index must not be null"); + + this.identifierIndex = index; this.hasNext = hasRow(resultSet); + } private static boolean hasRow(ResultSet resultSet) { diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SequenceEntityCallbackDelegate.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SequenceEntityCallbackDelegate.java index 00efd7fcff..9f5150d4d1 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SequenceEntityCallbackDelegate.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SequenceEntityCallbackDelegate.java @@ -17,7 +17,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.relational.core.dialect.Dialect; @@ -26,7 +26,6 @@ import org.springframework.data.util.ReflectionUtils; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; -import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; import org.springframework.util.NumberUtils; @@ -87,7 +86,11 @@ protected boolean hasValue(PersistentProperty property, PersistentPropertyAcc SqlIdentifier sequence = property.getSequence(); - if (sequence != null && !dialect.getIdGeneration().sequencesSupported()) { + if (sequence == null) { + return null; + } + + if (!dialect.getIdGeneration().sequencesSupported()) { LOG.warn(""" Aggregate type '%s' is marked for sequence usage but configured dialect '%s' does not support sequences. Falling back to identity columns. diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SingleQueryDataAccessStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SingleQueryDataAccessStrategy.java index d367c9c0c0..840a2cee19 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SingleQueryDataAccessStrategy.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SingleQueryDataAccessStrategy.java @@ -20,6 +20,7 @@ import java.util.Optional; import java.util.stream.Stream; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.relational.core.dialect.Dialect; @@ -49,7 +50,7 @@ public SingleQueryDataAccessStrategy(Dialect dialect, JdbcConverter converter, } @Override - public T findById(Object id, Class domainType) { + public @Nullable T findById(Object id, Class domainType) { return aggregateReader.findById(id, getPersistentEntity(domainType)); } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SingleQueryFallbackDataAccessStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SingleQueryFallbackDataAccessStrategy.java index 962e19831c..778c8a66e1 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SingleQueryFallbackDataAccessStrategy.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SingleQueryFallbackDataAccessStrategy.java @@ -18,6 +18,7 @@ import java.util.Collections; import java.util.Optional; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.PersistentPropertyPath; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; import org.springframework.data.relational.core.query.Query; @@ -55,7 +56,7 @@ public SingleQueryFallbackDataAccessStrategy(SqlGeneratorSource sqlGeneratorSour } @Override - public T findById(Object id, Class domainType) { + public @Nullable T findById(Object id, Class domainType) { if (isSingleSelectQuerySupported(domainType)) { return singleSelectDelegate.findById(id, domainType); diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGenerator.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGenerator.java index 82f2db5158..0b4d12abb9 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGenerator.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGenerator.java @@ -15,21 +15,13 @@ */ package org.springframework.data.jdbc.core.convert; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; +import java.util.*; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jdbc.repository.support.SimpleJdbcRepository; @@ -49,7 +41,6 @@ import org.springframework.data.util.Lazy; import org.springframework.data.util.Predicates; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -298,7 +289,7 @@ String getFindAllByProperty(Identifier parentIdentifier, * keyColumn must not be {@code null}. * @return a SQL String. */ - String getFindAllByProperty(Identifier parentIdentifier, @Nullable AggregatePath.ColumnInfo keyColumn, + String getFindAllByProperty(Identifier parentIdentifier, AggregatePath.@Nullable ColumnInfo keyColumn, boolean ordered) { Assert.isTrue(keyColumn != null || !ordered, @@ -315,9 +306,15 @@ String getFindAllByProperty(Identifier parentIdentifier, @Nullable AggregatePath Condition condition = buildConditionForBackReference(parentIdentifier, table); SelectBuilder.SelectWhereAndOr withWhereClause = builder.where(condition); - Select select = ordered // - ? withWhereClause.orderBy(table.column(keyColumn.name()).as(keyColumn.alias())).build() // - : withWhereClause.build(); + Select select; + if (ordered) { + + Assert.isTrue(keyColumn != null, "KeyColumn must not be null"); + + select = withWhereClause.orderBy(table.column(keyColumn.name()).as(keyColumn.alias())).build(); + } else { + select = withWhereClause.build(); + } return render(select); } @@ -514,7 +511,12 @@ private Condition equalityCondition(Map columnMap) { AggregatePath.ColumnInfos idColumnInfos = mappingContext.getAggregatePath(entity).getTableInfo().idColumnInfos(); return createPredicate(columnMap, (aggregatePath, column) -> { - return column.isEqualTo(getBindMarker(idColumnInfos.get(aggregatePath).name())); + + AggregatePath.ColumnInfo columnInfo = idColumnInfos.get(aggregatePath); + + Assert.notNull(columnInfo, "ColumnInfo must not be null"); + + return column.isEqualTo(getBindMarker(columnInfo.name())); }); } @@ -813,8 +815,11 @@ Join getJoin(AggregatePath path) { Condition joinCondition = backRefColumnInfos.reduce(Conditions.unrestricted(), (aggregatePath, columnInfo) -> { - return currentTable.column(columnInfo.name()) - .isEqualTo(parentTable.column(idColumnInfos.get(aggregatePath).name())); + AggregatePath.ColumnInfo idColumnInfo = idColumnInfos.get(aggregatePath); + + Assert.notNull(idColumnInfo, "IdColumnInfo must not be null"); + + return currentTable.column(columnInfo.name()).isEqualTo(parentTable.column(idColumnInfo.name())); }, Condition::and); return new Join(currentTable, joinCondition); @@ -883,6 +888,8 @@ private String createInsertSql(Set additionalColumns) { insertWithValues = (insertWithValues == null ? insert : insertWithValues).values(getBindMarker(cn)); } + Assert.state(insertWithValues != null, "InsertWithValues must not be null"); + return render(insertWithValues.build()); } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlIdentifierParameterSource.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlIdentifierParameterSource.java index 209aa7108d..530260d6c3 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlIdentifierParameterSource.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlIdentifierParameterSource.java @@ -21,6 +21,7 @@ import java.util.Map; import java.util.Set; +import org.jspecify.annotations.Nullable; import org.springframework.data.relational.core.sql.SqlIdentifier; import org.springframework.jdbc.core.namedparam.AbstractSqlParameterSource; @@ -44,7 +45,7 @@ public boolean hasValue(String paramName) { } @Override - public Object getValue(String paramName) throws IllegalArgumentException { + public @Nullable Object getValue(String paramName) throws IllegalArgumentException { return namesToValues.get(paramName); } @@ -61,7 +62,7 @@ void addValue(SqlIdentifier name, Object value) { addValue(name, value, Integer.MIN_VALUE); } - void addValue(SqlIdentifier identifier, Object value, int sqlType) { + void addValue(SqlIdentifier identifier, @Nullable Object value, int sqlType) { identifiers.add(identifier); String name = BindParameterNameSanitizer.sanitize(identifier.getReference()); diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlParametersFactory.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlParametersFactory.java index 370b087f52..32a04d8ee8 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlParametersFactory.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlParametersFactory.java @@ -23,6 +23,7 @@ import java.util.function.BiFunction; import java.util.function.Predicate; +import org.jspecify.annotations.Nullable; import org.springframework.data.jdbc.core.mapping.JdbcValue; import org.springframework.data.jdbc.support.JdbcUtil; import org.springframework.data.mapping.PersistentProperty; @@ -35,7 +36,6 @@ import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; import org.springframework.data.relational.core.mapping.RelationalPredicates; import org.springframework.data.relational.core.sql.SqlIdentifier; -import org.springframework.lang.Nullable; /** * Creates the {@link SqlIdentifierParameterSource} for various SQL operations, dialect identifier processing rules and @@ -94,7 +94,7 @@ SqlIdentifierParameterSource forInsert(T instance, Class domainType, Iden AggregatePath.ColumnInfos columnInfos = context.getAggregatePath(persistentEntity).getTableInfo().idColumnInfos(); - // fullPath: because we use the result with a PropertyPathAccessor + // fullPath: because we use the result with a PropertyPathAccessor columnInfos.forEach((ap, __) -> { Object idValue = propertyPathAccessor.getProperty(ap.getRequiredPersistentPropertyPath()); RelationalPersistentProperty idProperty = ap.getRequiredLeafProperty(); @@ -192,7 +192,7 @@ private T doWithIdentifiers(Class domainType, IdentifierCallback callb interface IdentifierCallback { T doWithIdentifiers(AggregatePath.ColumnInfos columns, RelationalPersistentProperty idProperty, - RelationalPersistentEntity complexId); + @Nullable RelationalPersistentEntity complexId); } /** @@ -267,7 +267,6 @@ private SqlIdentifierParameterSource getParameterSource(@Nullable S insta PersistentPropertyAccessor propertyAccessor = instance != null ? persistentEntity.getPropertyAccessor(instance) : NoValuePropertyAccessor.instance(); - persistentEntity.doWithAll(property -> { if (skipProperty.test(property) || !property.isWritable()) { @@ -281,7 +280,8 @@ private SqlIdentifierParameterSource getParameterSource(@Nullable S insta if (property.isEmbedded()) { Object value = propertyAccessor.getProperty(property); - RelationalPersistentEntity embeddedEntity = context.getPersistentEntity(property.getTypeInformation()); + RelationalPersistentEntity embeddedEntity = context + .getRequiredPersistentEntity(property.getTypeInformation()); SqlIdentifierParameterSource additionalParameters = getParameterSource((T) value, (RelationalPersistentEntity) embeddedEntity, prefix + property.getEmbeddedPrefix(), skipProperty); parameters.addAll(additionalParameters); @@ -316,13 +316,13 @@ public void setProperty(PersistentProperty property, @Nullable Object value) } @Override - public Object getProperty(PersistentProperty property) { + public @Nullable Object getProperty(PersistentProperty property) { return null; } @Override public T getBean() { - return null; + throw new UnsupportedOperationException("Cannot get bean of NoValuePropertyAccessor"); } } } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/package-info.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/package-info.java index 43ca52cb9d..e991b443e4 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/package-info.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/package-info.java @@ -1,7 +1,7 @@ /** * JDBC-specific conversion classes. */ -@NonNullApi +@NullMarked package org.springframework.data.jdbc.core.convert; -import org.springframework.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/DialectResolver.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/DialectResolver.java index 3ec2c9b107..fda3d5ddd1 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/DialectResolver.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/DialectResolver.java @@ -29,6 +29,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jspecify.annotations.Nullable; import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.dao.NonTransientDataAccessException; import org.springframework.data.relational.core.dialect.Dialect; @@ -44,7 +45,6 @@ import org.springframework.data.util.Optionals; import org.springframework.jdbc.core.ConnectionCallback; import org.springframework.jdbc.core.JdbcOperations; -import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; /** @@ -112,6 +112,7 @@ public interface JdbcDialectProvider { public static class DefaultDialectProvider implements JdbcDialectProvider { @Override + @SuppressWarnings("NullAway") // Nullaway compalins, while IntelliJ seems to befine. public Optional getDialect(JdbcOperations operations) { return Optional.ofNullable(operations.execute((ConnectionCallback) DefaultDialectProvider::getDialect)); } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/package-info.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/package-info.java index 645c30d7c6..bf63b66b33 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/package-info.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/package-info.java @@ -1,7 +1,7 @@ /** * JDBC-specific Dialect implementations. */ -@NonNullApi +@NullMarked package org.springframework.data.jdbc.core.dialect; -import org.springframework.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/JdbcValue.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/JdbcValue.java index 33d409d79f..a6c402d3f5 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/JdbcValue.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/JdbcValue.java @@ -19,7 +19,7 @@ import java.sql.SQLType; import java.util.Objects; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * Wraps a value with the JDBCType that should be used to pass it as a bind parameter to a @@ -31,16 +31,19 @@ */ public class JdbcValue { - private final Object value; + private final @Nullable Object value; private final SQLType jdbcType; - protected JdbcValue(@Nullable Object value, @Nullable SQLType jdbcType) { + protected JdbcValue(@Nullable Object value, SQLType jdbcType) { this.value = value; this.jdbcType = jdbcType; } public static JdbcValue of(@Nullable Object value, @Nullable SQLType jdbcType) { + if (jdbcType == null) { + jdbcType = value == null ? JDBCType.NULL : JDBCType.OTHER; + } return new JdbcValue(value, jdbcType); } @@ -49,7 +52,6 @@ public Object getValue() { return this.value; } - @Nullable public SQLType getJdbcType() { return this.jdbcType; } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/package-info.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/package-info.java index e9fddca812..bd3a520cc8 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/package-info.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/package-info.java @@ -1,2 +1,2 @@ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.jdbc.core.mapping; diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/DefaultSqlTypeMapping.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/DefaultSqlTypeMapping.java index 7ab07a0a00..2b49d1230f 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/DefaultSqlTypeMapping.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/DefaultSqlTypeMapping.java @@ -24,6 +24,7 @@ import java.util.HashMap; import java.util.UUID; +import org.jspecify.annotations.Nullable; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; import org.springframework.util.ClassUtils; @@ -62,12 +63,12 @@ public DefaultSqlTypeMapping() { } @Override - public String getColumnType(RelationalPersistentProperty property) { + public @Nullable String getColumnType(RelationalPersistentProperty property) { return getColumnType(property.getActualType()); } @Override - public String getColumnType(Class type) { + public @Nullable String getColumnType(Class type) { return typeMap.get(ClassUtils.resolvePrimitiveIfNecessary(type)); } } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/LiquibaseChangeSetWriter.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/LiquibaseChangeSetWriter.java index 675eb89b70..877c054d10 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/LiquibaseChangeSetWriter.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/LiquibaseChangeSetWriter.java @@ -57,12 +57,12 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import org.jspecify.annotations.Nullable; import org.springframework.core.io.Resource; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; import org.springframework.data.util.Predicates; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/SchemaDiff.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/SchemaDiff.java index 73e468a1a3..23f862031a 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/SchemaDiff.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/SchemaDiff.java @@ -25,6 +25,8 @@ import java.util.function.Function; import java.util.function.Predicate; +import org.springframework.util.Assert; + /** * This class is created to return the difference between a source and target {@link Tables} The difference consists of * Table Additions, Deletions, and Modified Tables (i.e. table exists in both source and target - but has columns to add @@ -90,6 +92,8 @@ private static List diffTable(Tables mappedEntities, Map mappedColumns = createMapping(mappedEntity.columns(), Column::name, nameComparator); diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/SqlTypeMapping.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/SqlTypeMapping.java index 5a7da71aed..229c0b9ba1 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/SqlTypeMapping.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/SqlTypeMapping.java @@ -15,8 +15,8 @@ */ package org.springframework.data.jdbc.core.mapping.schema; +import org.jspecify.annotations.Nullable; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -48,7 +48,6 @@ public interface SqlTypeMapping { * @param type class for which the type should be determined. * @return the SQL type to use, such as {@code VARCHAR} or {@code NUMERIC}. Can be {@literal null} if the strategy * cannot provide a column type. - * * @since 3.3 */ @Nullable diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/Table.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/Table.java index d72d820df8..4c20f6bef9 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/Table.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/Table.java @@ -17,9 +17,9 @@ import java.util.ArrayList; import java.util.List; - import java.util.stream.Collectors; -import org.springframework.lang.Nullable; + +import org.jspecify.annotations.Nullable; import org.springframework.util.ObjectUtils; /** diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/Tables.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/Tables.java index e77cdd6884..1ad91a0e8c 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/Tables.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/Tables.java @@ -27,6 +27,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import org.jspecify.annotations.Nullable; import org.springframework.data.annotation.Id; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.relational.core.mapping.MappedCollection; @@ -35,7 +36,6 @@ import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; import org.springframework.data.relational.core.mapping.RelationalPredicates; import org.springframework.data.relational.core.sql.SqlIdentifier; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -74,7 +74,11 @@ public static Tables from(Stream> persis continue; } - Column column = new Column(property.getColumnName().getReference(), sqlTypeMapping.getColumnType(property), + String columnType = sqlTypeMapping.getColumnType(property); + + Assert.state(columnType != null, "Column type must not be null"); + + Column column = new Column(property.getColumnName().getReference(), columnType, sqlTypeMapping.isNullable(property), identifierColumns.contains(property)); table.columns().add(column); } @@ -103,13 +107,17 @@ private static void applyForeignKeyMetadata(List tables, List parentIdColumnNames = parentIdColumns.stream().map(Column::name).toList(); String foreignKeyName = getForeignKeyName(foreignKeyMetadata.parentTableName, parentIdColumnNames); + String keyColumnType = foreignKeyMetadata.keyColumnType(); + if (parentIdColumnNames.size() == 1) { addIfAbsent(table.columns(), new Column(foreignKeyMetadata.referencingColumnName(), parentIdColumns.get(0).type(), false, table.getIdColumns().isEmpty())); if (foreignKeyMetadata.keyColumnName() != null) { - addIfAbsent(table.columns(), - new Column(foreignKeyMetadata.keyColumnName(), foreignKeyMetadata.keyColumnType(), false, true)); + + Assert.state(keyColumnType != null, "Key column type must not be null"); + + addIfAbsent(table.columns(), new Column(foreignKeyMetadata.keyColumnName(), keyColumnType, false, true)); } addIfAbsent(table.foreignKeys(), new ForeignKey(foreignKeyName, foreignKeyMetadata.tableName(), @@ -118,8 +126,12 @@ private static void applyForeignKeyMetadata(List
tables, List collectParentIdentityColumns(ForeignKeyMetadata chil excludeTables.add(child.tableName()); Table parentTable = findTableByName(tables, child.parentTableName()); + + Assert.state(parentTable != null, "Parent table must not be null"); ForeignKeyMetadata parentMetadata = findMetadataByTableName(foreignKeyMetadataList, child.parentTableName(), excludeTables); List parentIdColumns = parentTable.getIdColumns(); @@ -165,8 +179,11 @@ private static List collectParentIdentityColumns(ForeignKeyMetadata chil parentParentIdColumns = new LinkedList<>(List.of(withChangedName)); } if (parentMetadata.keyColumnName() != null) { - parentParentIdColumns - .add(new Column(parentMetadata.keyColumnName(), parentMetadata.keyColumnType(), false, true)); + String keyColumnType = parentMetadata.keyColumnType(); + + Assert.state(keyColumnType != null, "Key column type must not be null"); + + parentParentIdColumns.add(new Column(parentMetadata.keyColumnName(), keyColumnType, false, true)); } return parentParentIdColumns; } @@ -197,7 +214,11 @@ private static ForeignKeyMetadata createForeignKeyMetadata(RelationalPersistentE if (property.getType() == List.class) { referencedKeyColumnType = sqlTypeMapping.getColumnType(Integer.class); } else if (property.getType() == Map.class) { - referencedKeyColumnType = sqlTypeMapping.getColumnType(property.getComponentType()); + Class componentType = property.getComponentType(); + + Assert.state(componentType != null, "Component type must not be null"); + + referencedKeyColumnType = sqlTypeMapping.getColumnType(componentType); } } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/package-info.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/package-info.java index 2173c50d6f..e8d6d1f33b 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/package-info.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/package-info.java @@ -1,7 +1,7 @@ /** * Schema creation and schema update integration with Liquibase. */ -@NonNullApi +@NullMarked package org.springframework.data.jdbc.core.mapping.schema; -import org.springframework.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/package-info.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/package-info.java index 51e8e0fbc1..10d70d6b5a 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/package-info.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/package-info.java @@ -1,7 +1,7 @@ /** * Core JDBC implementation. */ -@NonNullApi +@NullMarked package org.springframework.data.jdbc.core; -import org.springframework.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisContext.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisContext.java index ae43172856..7436f2afda 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisContext.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisContext.java @@ -18,8 +18,8 @@ import java.util.Collections; import java.util.Map; +import org.jspecify.annotations.Nullable; import org.springframework.data.jdbc.core.convert.Identifier; -import org.springframework.lang.Nullable; /** * {@link MyBatisContext} instances get passed to MyBatis mapped statements as arguments, making Ids, instances, diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java index 66ce57e942..46a00dba6e 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java @@ -27,8 +27,8 @@ import org.apache.ibatis.cursor.Cursor; import org.apache.ibatis.session.SqlSession; +import org.jspecify.annotations.Nullable; import org.mybatis.spring.SqlSessionTemplate; - import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; @@ -165,7 +165,8 @@ public void setNamespaceStrategy(NamespaceStrategy namespaceStrategy) { } @Override - public Object insert(T instance, Class domainType, Identifier identifier, IdValueSource idValueSource) { + public @Nullable Object insert(T instance, Class domainType, Identifier identifier, + IdValueSource idValueSource) { MyBatisContext myBatisContext = new MyBatisContext(identifier, instance, domainType); sqlSession().insert(namespace(domainType) + ".insert", myBatisContext); diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/package-info.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/package-info.java index 8c7a6f928c..3acfbcdf23 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/package-info.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/package-info.java @@ -1,4 +1,4 @@ -@NonNullApi +@NullMarked package org.springframework.data.jdbc.mybatis; -import org.springframework.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java index b66fc4a2aa..39047b2e8d 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java @@ -25,7 +25,6 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -80,7 +79,7 @@ public class AbstractJdbcConfiguration implements ApplicationContextAware { private static final Log LOG = LogFactory.getLog(AbstractJdbcConfiguration.class); - private ApplicationContext applicationContext; + @SuppressWarnings("NullAway.Init") private ApplicationContext applicationContext; private QueryMappingConfiguration queryMappingConfiguration = QueryMappingConfiguration.EMPTY; diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DefaultQueryMappingConfiguration.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DefaultQueryMappingConfiguration.java index 65c461d505..6537983199 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DefaultQueryMappingConfiguration.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DefaultQueryMappingConfiguration.java @@ -3,10 +3,10 @@ import java.util.LinkedHashMap; import java.util.Map; +import org.jspecify.annotations.Nullable; import org.springframework.data.jdbc.core.convert.QueryMappingConfiguration; import org.springframework.jdbc.core.ResultSetExtractor; import org.springframework.jdbc.core.RowMapper; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -19,7 +19,7 @@ */ public class DefaultQueryMappingConfiguration implements QueryMappingConfiguration { - private Map, RowMapper> mappers = new LinkedHashMap<>(); + private final Map, RowMapper> mappers = new LinkedHashMap<>(); @Nullable public RowMapper getRowMapper(Class type) { diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/package-info.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/package-info.java index fe1dfb9ec6..d5f5cf7bb5 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/package-info.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/package-info.java @@ -1,4 +1,4 @@ -@NonNullApi +@NullMarked package org.springframework.data.jdbc.repository.config; -import org.springframework.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/AbstractDelegatingRowMapper.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/AbstractDelegatingRowMapper.java index 7bbeb88dcc..07b9e80f63 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/AbstractDelegatingRowMapper.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/AbstractDelegatingRowMapper.java @@ -18,8 +18,8 @@ import java.sql.ResultSet; import java.sql.SQLException; +import org.jspecify.annotations.Nullable; import org.springframework.jdbc.core.RowMapper; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -41,11 +41,14 @@ protected AbstractDelegatingRowMapper(RowMapper delegate) { } @Override - @Nullable public T mapRow(ResultSet rs, int rowNum) throws SQLException { T intermediate = delegate.mapRow(rs, rowNum); - return postProcessMapping(intermediate); + T result = postProcessMapping(intermediate); + + Assert.state(result != null, "Result must not be null"); + + return result; } /** diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/AbstractJdbcQuery.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/AbstractJdbcQuery.java index 0ef27d3f63..2331b3a6dd 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/AbstractJdbcQuery.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/AbstractJdbcQuery.java @@ -19,6 +19,7 @@ import java.util.function.Supplier; import java.util.stream.Stream; +import org.jspecify.annotations.Nullable; import org.springframework.core.convert.converter.Converter; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.data.repository.query.RepositoryQuery; @@ -28,7 +29,6 @@ import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.RowMapperResultSetExtractor; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/CallbackCapableRowMapper.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/CallbackCapableRowMapper.java index 3b43091ff9..3a16b48850 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/CallbackCapableRowMapper.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/CallbackCapableRowMapper.java @@ -17,12 +17,12 @@ import java.sql.ResultSet; +import org.jspecify.annotations.Nullable; import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.mapping.callback.EntityCallbacks; import org.springframework.data.relational.core.mapping.event.AfterConvertCallback; import org.springframework.data.relational.core.mapping.event.AfterConvertEvent; import org.springframework.jdbc.core.RowMapper; -import org.springframework.lang.Nullable; /** * Delegating {@link RowMapper} implementation that applies post-processing logic after the diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/ConvertingRowMapper.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/ConvertingRowMapper.java index 0efc660e0c..cf03d50ce6 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/ConvertingRowMapper.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/ConvertingRowMapper.java @@ -15,9 +15,9 @@ */ package org.springframework.data.jdbc.repository.query; +import org.jspecify.annotations.Nullable; import org.springframework.core.convert.converter.Converter; import org.springframework.jdbc.core.RowMapper; -import org.springframework.lang.Nullable; /** * Delegating {@link RowMapper} that reads a row into {@code T} and converts it afterward into {@code Object}. diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/DefaultRowMapperFactory.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/DefaultRowMapperFactory.java index f1f91ec4bc..bb3fc680af 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/DefaultRowMapperFactory.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/DefaultRowMapperFactory.java @@ -15,6 +15,7 @@ */ package org.springframework.data.jdbc.repository.query; +import org.jspecify.annotations.Nullable; import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.jdbc.core.convert.EntityRowMapper; import org.springframework.data.jdbc.core.convert.JdbcConverter; @@ -42,11 +43,11 @@ public class DefaultRowMapperFactory implements RowMapperFactory { private final RelationalMappingContext context; private final JdbcConverter converter; private final QueryMappingConfiguration queryMappingConfiguration; - private final EntityCallbacks entityCallbacks; + private final @Nullable EntityCallbacks entityCallbacks; private final ApplicationEventPublisher publisher; public DefaultRowMapperFactory(JdbcConverter converter, QueryMappingConfiguration queryMappingConfiguration, - EntityCallbacks entityCallbacks, ApplicationEventPublisher publisher) { + @Nullable EntityCallbacks entityCallbacks, ApplicationEventPublisher publisher) { this.context = converter.getMappingContext(); this.converter = converter; diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/EscapingParameterSource.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/EscapingParameterSource.java index b8f4031556..fc119f6300 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/EscapingParameterSource.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/EscapingParameterSource.java @@ -16,9 +16,11 @@ package org.springframework.data.jdbc.repository.query; +import org.jspecify.annotations.Nullable; import org.springframework.data.relational.core.dialect.Escaper; import org.springframework.data.relational.core.query.ValueFunction; import org.springframework.jdbc.core.namedparam.SqlParameterSource; +import org.springframework.util.Assert; /** * This {@link SqlParameterSource} will apply escaping to its values. @@ -43,7 +45,7 @@ public boolean hasValue(String paramName) { } @Override - public Object getValue(String paramName) throws IllegalArgumentException { + public @Nullable Object getValue(String paramName) throws IllegalArgumentException { Object value = parameterSource.getValue(paramName); if (value instanceof ValueFunction valueFunction) { @@ -52,19 +54,22 @@ public Object getValue(String paramName) throws IllegalArgumentException { return value; } - @Override public int getSqlType(String paramName) { return parameterSource.getSqlType(paramName); } @Override - public String getTypeName(String paramName) { + public @Nullable String getTypeName(String paramName) { return parameterSource.getTypeName(paramName); } @Override public String[] getParameterNames() { - return parameterSource.getParameterNames(); + String[] parameterNames = parameterSource.getParameterNames(); + + Assert.state(parameterNames != null, "Parameter names must not be null"); + + return parameterNames; } } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcDeleteQueryCreator.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcDeleteQueryCreator.java index d3968b18a0..9fb8c4dacb 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcDeleteQueryCreator.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcDeleteQueryCreator.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.function.Consumer; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Sort; import org.springframework.data.jdbc.core.convert.JdbcConverter; import org.springframework.data.jdbc.core.convert.QueryMapper; @@ -48,7 +49,6 @@ import org.springframework.data.repository.query.parser.PartTree; import org.springframework.data.util.Predicates; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -158,7 +158,7 @@ private void deleteRelations(RelationalPersistentEntity entity, Select parent ).from(table) // .where(inCondition) // .build(); - deleteRelations(aggregatePath.getLeafEntity(), select, deleteConsumer); + deleteRelations(aggregatePath.getRequiredLeafEntity(), select, deleteConsumer); deleteConsumer.accept(StatementBuilder.delete(table).where(inCondition).build()); } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcParameters.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcParameters.java index ddfb1e7431..e90d64341f 100755 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcParameters.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcParameters.java @@ -26,6 +26,7 @@ import org.springframework.data.repository.query.ParametersSource; import org.springframework.data.util.Lazy; import org.springframework.data.util.TypeInformation; +import org.springframework.util.Assert; /** * Custom extension of {@link RelationalParameters}. @@ -84,8 +85,13 @@ public static class JdbcParameter extends RelationalParameter { sqlType = JdbcUtil.targetSqlTypeFor(JdbcColumnTypes.INSTANCE.resolvePrimitiveType(typeInformation.getType())); - actualSqlType = Lazy.of(() -> JdbcUtil - .targetSqlTypeFor(JdbcColumnTypes.INSTANCE.resolvePrimitiveType(typeInformation.getActualType().getType()))); + TypeInformation actualType = typeInformation.getActualType(); + actualSqlType = Lazy.of(() -> { + + Assert.state(actualType != null, "ActualType must not be null"); + + return JdbcUtil.targetSqlTypeFor(JdbcColumnTypes.INSTANCE.resolvePrimitiveType(actualType.getType())); + }); } public SQLType getSqlType() { diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcQueryCreator.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcQueryCreator.java index fa7202a4a8..cc8faf9458 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcQueryCreator.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcQueryCreator.java @@ -18,6 +18,7 @@ import java.util.Optional; import java.util.function.Predicate; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jdbc.core.convert.JdbcConverter; @@ -47,7 +48,6 @@ import org.springframework.data.repository.query.parser.Part; import org.springframework.data.repository.query.parser.PartTree; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -233,7 +233,12 @@ SelectBuilder.SelectWhere applyLimitAndOffset(SelectBuilder.SelectLimitOffset li if (tree.isExistsProjection()) { limitOffsetBuilder = limitOffsetBuilder.limit(1); } else if (tree.isLimiting()) { - limitOffsetBuilder = limitOffsetBuilder.limit(tree.getMaxResults()); + + Integer maxResults = tree.getMaxResults(); + + Assert.state(maxResults != null, "Max results must not be null"); + + limitOffsetBuilder = limitOffsetBuilder.limit(maxResults); } Pageable pageable = accessor.getPageable(); diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcQueryExecution.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcQueryExecution.java index bcebc67b5c..dc9381f33a 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcQueryExecution.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcQueryExecution.java @@ -15,6 +15,7 @@ */ package org.springframework.data.jdbc.repository.query; +import org.jspecify.annotations.Nullable; import org.springframework.core.convert.converter.Converter; import org.springframework.data.convert.DtoInstantiatingConverter; import org.springframework.data.mapping.context.MappingContext; @@ -25,7 +26,6 @@ import org.springframework.data.repository.query.ReturnedType; import org.springframework.data.util.Lazy; import org.springframework.jdbc.core.namedparam.SqlParameterSource; -import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; /** diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcQueryMethod.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcQueryMethod.java index 7fc7c114c6..0b1e93b8b4 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcQueryMethod.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcQueryMethod.java @@ -20,6 +20,7 @@ import java.util.Map; import java.util.Optional; +import org.jspecify.annotations.Nullable; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.data.mapping.context.MappingContext; @@ -31,12 +32,9 @@ import org.springframework.data.relational.repository.query.SimpleRelationalEntityMetadata; import org.springframework.data.repository.core.NamedQueries; import org.springframework.data.repository.core.RepositoryMetadata; -import org.springframework.data.repository.query.Parameters; -import org.springframework.data.repository.query.ParametersSource; import org.springframework.data.repository.query.QueryMethod; import org.springframework.jdbc.core.ResultSetExtractor; import org.springframework.jdbc.core.RowMapper; -import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; import org.springframework.util.ConcurrentReferenceHashMap; import org.springframework.util.ObjectUtils; diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQuery.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQuery.java index 6edd6cdc67..aada3772c2 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQuery.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQuery.java @@ -25,6 +25,7 @@ import java.util.function.LongSupplier; import java.util.function.Supplier; +import org.jspecify.annotations.Nullable; import org.springframework.core.convert.converter.Converter; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; @@ -47,7 +48,6 @@ import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.jdbc.core.namedparam.SqlParameterSource; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -195,7 +195,11 @@ private JdbcQueryExecution getQueryExecution(ResultProcessor processor, Object count = singleObjectQuery((rs, i) -> rs.getLong(1)).execute(countQuery.getQuery(), countQuery.getParameterSource(dialect.getLikeEscaper())); - return converter.getConversionService().convert(count, Long.class); + Long converted = converter.getConversionService().convert(count, Long.class); + + Assert.state(converted != null, "Count must not be null"); + + return converted; }); } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java index 814daac7d0..0e02f38609 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java @@ -27,6 +27,7 @@ import java.util.function.Function; import java.util.function.Supplier; +import org.jspecify.annotations.Nullable; import org.springframework.beans.BeanInstantiationException; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.BeanFactory; @@ -50,7 +51,6 @@ import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; @@ -156,7 +156,7 @@ public StringBasedJdbcQuery(String query, JdbcQueryMethod queryMethod, NamedPara } @Override - public Object execute(Object[] objects) { + public @Nullable Object execute(Object[] objects) { RelationalParameterAccessor accessor = new RelationalParametersParameterAccessor(getQueryMethod(), objects); ResultProcessor processor = getQueryMethod().getResultProcessor().withDynamicProjection(accessor); @@ -242,8 +242,7 @@ private MapSqlParameterSource bindParameters(RelationalParameterAccessor accesso JdbcParameters.JdbcParameter parameter = getQueryMethod().getParameters() .getParameter(bindableParameter.getIndex()); - JdbcValue jdbcValue = writeValue(value, parameter.getTypeInformation(), - parameter); + JdbcValue jdbcValue = writeValue(value, parameter.getTypeInformation(), parameter); SQLType jdbcType = jdbcValue.getJdbcType(); if (jdbcType == null) { @@ -410,6 +409,8 @@ public CachedRowMapperFactory(Supplier> defaultMapper) { return defaultMapper.get(); } + Assert.state(constructor != null, "Constructor must not be null"); + return (RowMapper) BeanUtils.instantiateClass(constructor); }); } @@ -465,6 +466,8 @@ public CachedResultSetExtractorFactory(Supplier> resultSetExtractor return BeanUtils.instantiateClass(rowMapperConstructor, rowMapper.get()); } + Assert.state(constructor != null, "Constructor must not be null"); + return BeanUtils.instantiateClass(constructor); }; diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/package-info.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/package-info.java index 96d41b6bb5..5ec7f203d4 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/package-info.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/package-info.java @@ -1,7 +1,7 @@ /** * Query derivation mechanism for JDBC specific repositories. */ -@NonNullApi +@NullMarked package org.springframework.data.jdbc.repository.query; -import org.springframework.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/BeanFactoryAwareRowMapperFactory.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/BeanFactoryAwareRowMapperFactory.java index b5199fe6e3..cd190829af 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/BeanFactoryAwareRowMapperFactory.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/BeanFactoryAwareRowMapperFactory.java @@ -15,6 +15,7 @@ */ package org.springframework.data.jdbc.repository.support; +import org.jspecify.annotations.Nullable; import org.springframework.beans.factory.BeanFactory; import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.jdbc.core.convert.JdbcConverter; @@ -25,7 +26,6 @@ import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.jdbc.core.ResultSetExtractor; import org.springframework.jdbc.core.RowMapper; -import org.springframework.lang.Nullable; /** * This {@link RowMapperFactory} implementation extends the {@link DefaultRowMapperFactory} by adding the capabilities @@ -41,7 +41,8 @@ public class BeanFactoryAwareRowMapperFactory extends DefaultRowMapperFactory { private final @Nullable BeanFactory beanFactory; BeanFactoryAwareRowMapperFactory(JdbcConverter converter, QueryMappingConfiguration queryMappingConfiguration, - EntityCallbacks entityCallbacks, ApplicationEventPublisher publisher, @Nullable BeanFactory beanFactory) { + @Nullable EntityCallbacks entityCallbacks, ApplicationEventPublisher publisher, + @Nullable BeanFactory beanFactory) { super(converter, queryMappingConfiguration, entityCallbacks, publisher); diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java index d5652d8bab..e69930ddb5 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java @@ -19,7 +19,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - +import org.jspecify.annotations.Nullable; import org.springframework.beans.factory.BeanFactory; import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.jdbc.core.convert.JdbcConverter; @@ -39,7 +39,6 @@ import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.ValueExpressionDelegate; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -68,9 +67,8 @@ abstract class JdbcQueryLookupStrategy extends RelationalQueryLookupStrategy { protected final ValueExpressionDelegate delegate; JdbcQueryLookupStrategy(ApplicationEventPublisher publisher, @Nullable EntityCallbacks callbacks, - JdbcConverter converter, Dialect dialect, - QueryMappingConfiguration queryMappingConfiguration, NamedParameterJdbcOperations operations, - ValueExpressionDelegate delegate) { + JdbcConverter converter, Dialect dialect, QueryMappingConfiguration queryMappingConfiguration, + NamedParameterJdbcOperations operations, ValueExpressionDelegate delegate) { super(converter.getMappingContext(), dialect); @@ -99,14 +97,13 @@ static class CreateQueryLookupStrategy extends JdbcQueryLookupStrategy { private final RowMapperFactory rowMapperFactory; CreateQueryLookupStrategy(ApplicationEventPublisher publisher, @Nullable EntityCallbacks callbacks, - JdbcConverter converter, Dialect dialect, - QueryMappingConfiguration queryMappingConfiguration, NamedParameterJdbcOperations operations, - ValueExpressionDelegate delegate) { + JdbcConverter converter, Dialect dialect, QueryMappingConfiguration queryMappingConfiguration, + NamedParameterJdbcOperations operations, ValueExpressionDelegate delegate) { super(publisher, callbacks, converter, dialect, queryMappingConfiguration, operations, delegate); - this.rowMapperFactory = new DefaultRowMapperFactory(getConverter(), - getQueryMappingConfiguration(), getCallbacks(), getPublisher()); + this.rowMapperFactory = new DefaultRowMapperFactory(getConverter(), getQueryMappingConfiguration(), + getCallbacks(), getPublisher()); } @Override @@ -115,8 +112,7 @@ public RepositoryQuery resolveQuery(Method method, RepositoryMetadata repository JdbcQueryMethod queryMethod = getJdbcQueryMethod(method, repositoryMetadata, projectionFactory, namedQueries); - return new PartTreeJdbcQuery(queryMethod, getDialect(), getConverter(), getOperations(), - rowMapperFactory); + return new PartTreeJdbcQuery(queryMethod, getDialect(), getConverter(), getOperations(), rowMapperFactory); } } @@ -132,13 +128,12 @@ static class DeclaredQueryLookupStrategy extends JdbcQueryLookupStrategy { private final RowMapperFactory rowMapperFactory; DeclaredQueryLookupStrategy(ApplicationEventPublisher publisher, @Nullable EntityCallbacks callbacks, - JdbcConverter converter, Dialect dialect, - QueryMappingConfiguration queryMappingConfiguration, NamedParameterJdbcOperations operations, - @Nullable BeanFactory beanfactory, ValueExpressionDelegate delegate) { + JdbcConverter converter, Dialect dialect, QueryMappingConfiguration queryMappingConfiguration, + NamedParameterJdbcOperations operations, @Nullable BeanFactory beanfactory, ValueExpressionDelegate delegate) { super(publisher, callbacks, converter, dialect, queryMappingConfiguration, operations, delegate); - this.rowMapperFactory = new BeanFactoryAwareRowMapperFactory(converter, queryMappingConfiguration, - callbacks, publisher, beanfactory); + this.rowMapperFactory = new BeanFactoryAwareRowMapperFactory(converter, queryMappingConfiguration, callbacks, + publisher, beanfactory); } @Override @@ -185,10 +180,9 @@ static class CreateIfNotFoundQueryLookupStrategy extends JdbcQueryLookupStrategy * @param lookupStrategy must not be {@literal null}. */ CreateIfNotFoundQueryLookupStrategy(ApplicationEventPublisher publisher, @Nullable EntityCallbacks callbacks, - JdbcConverter converter, Dialect dialect, - QueryMappingConfiguration queryMappingConfiguration, NamedParameterJdbcOperations operations, - CreateQueryLookupStrategy createStrategy, DeclaredQueryLookupStrategy lookupStrategy, - ValueExpressionDelegate delegate) { + JdbcConverter converter, Dialect dialect, QueryMappingConfiguration queryMappingConfiguration, + NamedParameterJdbcOperations operations, CreateQueryLookupStrategy createStrategy, + DeclaredQueryLookupStrategy lookupStrategy, ValueExpressionDelegate delegate) { super(publisher, callbacks, converter, dialect, queryMappingConfiguration, operations, delegate); @@ -223,10 +217,9 @@ JdbcQueryMethod getJdbcQueryMethod(Method method, RepositoryMetadata repositoryM * Creates a {@link QueryLookupStrategy} based on the provided * {@link org.springframework.data.repository.query.QueryLookupStrategy.Key}. * - * @param key the key that decides what {@link QueryLookupStrategy} shozld be used. + * @param key the key that decides what {@link QueryLookupStrategy} should be used. May be {@literal null} * @param publisher must not be {@literal null} * @param callbacks may be {@literal null} - * @param context must not be {@literal null} * @param converter must not be {@literal null} * @param dialect must not be {@literal null} * @param queryMappingConfiguration must not be {@literal null} @@ -245,8 +238,8 @@ public static QueryLookupStrategy create(@Nullable Key key, ApplicationEventPubl Assert.notNull(operations, "NamedParameterJdbcOperations must not be null"); Assert.notNull(delegate, "ValueExpressionDelegate must not be null"); - CreateQueryLookupStrategy createQueryLookupStrategy = new CreateQueryLookupStrategy(publisher, callbacks, - converter, dialect, queryMappingConfiguration, operations, delegate); + CreateQueryLookupStrategy createQueryLookupStrategy = new CreateQueryLookupStrategy(publisher, callbacks, converter, + dialect, queryMappingConfiguration, operations, delegate); DeclaredQueryLookupStrategy declaredQueryLookupStrategy = new DeclaredQueryLookupStrategy(publisher, callbacks, converter, dialect, queryMappingConfiguration, operations, beanFactory, delegate); @@ -258,9 +251,8 @@ public static QueryLookupStrategy create(@Nullable Key key, ApplicationEventPubl return switch (keyToUse) { case CREATE -> createQueryLookupStrategy; case USE_DECLARED_QUERY -> declaredQueryLookupStrategy; - case CREATE_IF_NOT_FOUND -> - new CreateIfNotFoundQueryLookupStrategy(publisher, callbacks, converter, dialect, - queryMappingConfiguration, operations, createQueryLookupStrategy, declaredQueryLookupStrategy, delegate); + case CREATE_IF_NOT_FOUND -> new CreateIfNotFoundQueryLookupStrategy(publisher, callbacks, converter, dialect, + queryMappingConfiguration, operations, createQueryLookupStrategy, declaredQueryLookupStrategy, delegate); }; } @@ -276,6 +268,7 @@ QueryMappingConfiguration getQueryMappingConfiguration() { return queryMappingConfiguration; } + @Nullable EntityCallbacks getCallbacks() { return callbacks; } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java index da27a3a16a..28693dd146 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java @@ -17,6 +17,7 @@ import java.util.Optional; +import org.jspecify.annotations.Nullable; import org.springframework.beans.factory.BeanFactory; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; @@ -38,7 +39,6 @@ import org.springframework.data.repository.query.QueryLookupStrategy; import org.springframework.data.repository.query.ValueExpressionDelegate; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -175,7 +175,7 @@ protected Class getRepositoryBaseClass(RepositoryMetadata repositoryMetadata) } @Override - protected Optional getQueryLookupStrategy(@Nullable QueryLookupStrategy.Key key, + protected Optional getQueryLookupStrategy(QueryLookupStrategy.@Nullable Key key, ValueExpressionDelegate valueExpressionDelegate) { DataAccessStrategy strategy = operations.getDataAccessStrategy(); diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java index 3c23a2cdd8..18442fe451 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java @@ -18,7 +18,6 @@ import java.io.Serializable; import org.jspecify.annotations.Nullable; - import org.springframework.beans.factory.BeanFactory; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; @@ -255,9 +254,10 @@ public void afterPropertiesSet() { if (this.dataAccessStrategy == null) { Assert.state(this.dialect != null, "Dialect must not be null"); + Assert.state(this.converter != null, "Converter must not be null"); - DataAccessStrategyFactory factory = getDataAccessStrategyFactory(this.converter, - this.dialect, this.jdbcOperations, this.queryMappingConfiguration); + DataAccessStrategyFactory factory = getDataAccessStrategyFactory(this.converter, this.dialect, + this.jdbcOperations, this.queryMappingConfiguration); this.dataAccessStrategy = factory.create(); } @@ -268,9 +268,7 @@ public void afterPropertiesSet() { } private static DataAccessStrategyFactory getDataAccessStrategyFactory(JdbcConverter converter, Dialect dialect, - NamedParameterJdbcOperations operations, - QueryMappingConfiguration queryMapping) { - + NamedParameterJdbcOperations operations, QueryMappingConfiguration queryMapping) { return new DataAccessStrategyFactory(converter, operations, dialect, queryMapping); } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/package-info.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/package-info.java index d60ca96fde..53160d4fe3 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/package-info.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/package-info.java @@ -1,4 +1,4 @@ -@NonNullApi +@NullMarked package org.springframework.data.jdbc.repository.support; -import org.springframework.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/support/package-info.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/support/package-info.java index 8ff6810baa..825eb43b9b 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/support/package-info.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/support/package-info.java @@ -1,4 +1,4 @@ -@NonNullApi +@NullMarked package org.springframework.data.jdbc.support; -import org.springframework.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; diff --git a/spring-data-jdbc/src/main/kotlin/org/springframework/data/jdbc/core/JdbcAggregateOperationsExtensions.kt b/spring-data-jdbc/src/main/kotlin/org/springframework/data/jdbc/core/JdbcAggregateOperationsExtensions.kt index c0a46a20ea..1400f08fc1 100644 --- a/spring-data-jdbc/src/main/kotlin/org/springframework/data/jdbc/core/JdbcAggregateOperationsExtensions.kt +++ b/spring-data-jdbc/src/main/kotlin/org/springframework/data/jdbc/core/JdbcAggregateOperationsExtensions.kt @@ -33,91 +33,91 @@ import java.util.* * Extension for [JdbcAggregateOperations.count]. */ inline fun JdbcAggregateOperations.count(): Long = - count(T::class.java) + count(T::class.java) /** * Extension for [JdbcAggregateOperations.count] with a query. */ -inline fun JdbcAggregateOperations.count(query: Query): Long = - count(query, T::class.java) +inline fun JdbcAggregateOperations.count(query: Query): Long = + count(query, T::class.java) /** * Extension for [JdbcAggregateOperations.exists]. */ -inline fun JdbcAggregateOperations.exists(query: Query): Boolean = - exists(query, T::class.java) +inline fun JdbcAggregateOperations.exists(query: Query): Boolean = + exists(query, T::class.java) /** * Extension for [JdbcAggregateOperations.existsById]. */ -inline fun JdbcAggregateOperations.existsById(id: Any): Boolean = - existsById(id, T::class.java) +inline fun JdbcAggregateOperations.existsById(id: Any): Boolean = + existsById(id, T::class.java) /** * Extension for [JdbcAggregateOperations.findById]. */ -inline fun JdbcAggregateOperations.findById(id: Any): T? = - findById(id, T::class.java) +inline fun JdbcAggregateOperations.findById(id: Any): T? = + findById(id, T::class.java) /** * Extension for [JdbcAggregateOperations.findAllById]. */ -inline fun JdbcAggregateOperations.findAllById(ids: Iterable<*>): List = - findAllById(ids, T::class.java) +inline fun JdbcAggregateOperations.findAllById(ids: Iterable<*>): List = + findAllById(ids, T::class.java) /** * Extension for [JdbcAggregateOperations.findAll]. */ -inline fun JdbcAggregateOperations.findAll(): List = - findAll(T::class.java) +inline fun JdbcAggregateOperations.findAll(): List = + findAll(T::class.java) /** * Extension for [JdbcAggregateOperations.findAll] with sorting. */ -inline fun JdbcAggregateOperations.findAll(sort: Sort): List = - findAll(T::class.java, sort) +inline fun JdbcAggregateOperations.findAll(sort: Sort): List = + findAll(T::class.java, sort) /** * Extension for [JdbcAggregateOperations.findAll] with pagination. */ inline fun JdbcAggregateOperations.findAll(pageable: Pageable): Page = - findAll(T::class.java, pageable) + findAll(T::class.java, pageable) /** * Extension for [JdbcAggregateOperations.findOne] with a query. */ -inline fun JdbcAggregateOperations.findOne(query: Query): Optional = - findOne(query, T::class.java) +inline fun JdbcAggregateOperations.findOne(query: Query): Optional = + findOne(query, T::class.java) /** * Extension for [JdbcAggregateOperations.findAll] with a query. */ -inline fun JdbcAggregateOperations.findAll(query: Query): List = - findAll(query, T::class.java) +inline fun JdbcAggregateOperations.findAll(query: Query): List = + findAll(query, T::class.java) /** * Extension for [JdbcAggregateOperations.findAll] with query and pagination. */ inline fun JdbcAggregateOperations.findAll( - query: Query, - pageable: Pageable + query: Query, + pageable: Pageable ): Page = - findAll(query, T::class.java, pageable) + findAll(query, T::class.java, pageable) /** * Extension for [JdbcAggregateOperations.deleteById]. */ -inline fun JdbcAggregateOperations.deleteById(id: Any): Unit = - deleteById(id, T::class.java) +inline fun JdbcAggregateOperations.deleteById(id: Any): Unit = + deleteById(id, T::class.java) /** * Extension for [JdbcAggregateOperations.deleteAllById]. */ -inline fun JdbcAggregateOperations.deleteAllById(ids: Iterable<*>): Unit = - deleteAllById(ids, T::class.java) +inline fun JdbcAggregateOperations.deleteAllById(ids: Iterable<*>): Unit = + deleteAllById(ids, T::class.java) /** * Extension for [JdbcAggregateOperations.deleteAll]. */ inline fun JdbcAggregateOperations.deleteAll(): Unit = - deleteAll(T::class.java) + deleteAll(T::class.java) diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AggregateChangeIdGenerationImmutableUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AggregateChangeIdGenerationImmutableUnitTests.java index ba958af4d8..dc5bfbed76 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AggregateChangeIdGenerationImmutableUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AggregateChangeIdGenerationImmutableUnitTests.java @@ -15,6 +15,21 @@ */ package org.springframework.data.jdbc.core; +import static java.util.Arrays.*; +import static java.util.Collections.*; +import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.SoftAssertions.*; +import static org.mockito.Mockito.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.data.annotation.Id; @@ -30,20 +45,6 @@ import org.springframework.data.relational.core.mapping.Embedded; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; -import org.springframework.lang.Nullable; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static java.util.Arrays.*; -import static java.util.Collections.*; -import static org.assertj.core.api.Assertions.*; -import static org.assertj.core.api.SoftAssertions.*; -import static org.mockito.Mockito.*; /** * Unit tests for the {@link MutableAggregateChange} testing the setting of generated ids in aggregates consisting of @@ -216,7 +217,7 @@ public void setIdForDeepElementSetElementSet() { .extracting(c -> c.id) // .containsExactly(2); // softly.assertThat(entity.contentSet.stream() // - .flatMap(c -> c.tagSet.stream())) // + .flatMap(c -> c.tagSet.stream())) // .extracting(t -> t.id) // .containsExactlyInAnyOrder(3, 4); // }); @@ -284,8 +285,8 @@ public void setIdForDeepElementListElementList() { .extracting(c -> c.id) // .containsExactly(2, 3); // softly.assertThat(entity.contentList.stream() // - .flatMap(c -> c.tagList.stream()) // - ).extracting(t -> t.id) // + .flatMap(c -> c.tagList.stream()) // + ).extracting(t -> t.id) // .containsExactly(4, 5, 6); // }); } @@ -320,13 +321,13 @@ public void setIdForDeepElementMapElementMap() { .extracting(Map.Entry::getKey, e -> e.getValue().id) // .containsExactly(tuple("one", 2), tuple("two", 3)); // softly.assertThat(entity.contentMap.values().stream() // - .flatMap(c -> c.tagMap.entrySet().stream())) // + .flatMap(c -> c.tagMap.entrySet().stream())) // .extracting(Map.Entry::getKey, e -> e.getValue().id) // .containsExactly( // tuple("111", 4), // tuple("222", 5), // tuple("333", 6) // - ); // + ); // }); } @@ -402,19 +403,18 @@ private static Map createTagMap(Object... keysAndValues) { DbAction.Insert createInsert(String propertyName, Object value, @Nullable Object key) { - return new DbAction.Insert<>(value, - context.getPersistentPropertyPath(propertyName, DummyEntity.class), rootInsert, + return new DbAction.Insert<>(value, context.getPersistentPropertyPath(propertyName, DummyEntity.class), rootInsert, singletonMap(toPath(propertyName), key), IdValueSource.GENERATED); } DbAction.Insert createDeepInsert(String propertyName, Object value, Object key, - @Nullable DbAction.Insert parentInsert) { + DbAction.@Nullable Insert parentInsert) { PersistentPropertyPath propertyPath = toPath( - parentInsert.getPropertyPath().toDotPath() + "." + propertyName); + parentInsert.propertyPath().toDotPath() + "." + propertyName); - return new DbAction.Insert<>(value, propertyPath, parentInsert, - singletonMap(propertyPath, key), IdValueSource.GENERATED); + return new DbAction.Insert<>(value, propertyPath, parentInsert, singletonMap(propertyPath, key), + IdValueSource.GENERATED); } PersistentPropertyPath toPath(String path) { @@ -426,412 +426,328 @@ PersistentPropertyPath toPath(String path) { .orElseThrow(() -> new IllegalArgumentException("No matching path found")); } - private static final class DummyEntity { - - @Id - private final - Integer rootId; - private final Content single; - private final Set contentSet; - private final List contentList; - private final Map contentMap; - private final List contentNoIdList; - @Embedded(onEmpty = Embedded.OnEmpty.USE_NULL, prefix = "fooBar") - private final - ContentNoId embedded; - - DummyEntity() { - - rootId = null; - single = null; - contentSet = emptySet(); - contentList = emptyList(); - contentMap = emptyMap(); - contentNoIdList = emptyList(); - embedded = new ContentNoId(); - } - - public DummyEntity(Integer rootId, Content single, Set contentSet, List contentList, Map contentMap, List contentNoIdList, ContentNoId embedded) { - this.rootId = rootId; - this.single = single; - this.contentSet = contentSet; - this.contentList = contentList; - this.contentMap = contentMap; - this.contentNoIdList = contentNoIdList; - this.embedded = embedded; - } - - public Integer getRootId() { - return this.rootId; - } - - public Content getSingle() { - return this.single; - } - - public Set getContentSet() { - return this.contentSet; - } - - public List getContentList() { - return this.contentList; - } + private record DummyEntity(@Id Integer rootId, Content single, Set contentSet, List contentList, + Map contentMap, List contentNoIdList, + @Embedded(onEmpty = Embedded.OnEmpty.USE_NULL, prefix = "fooBar") ContentNoId embedded) { + + DummyEntity() { + + this(null, null, emptySet(), emptyList(), emptyMap(), emptyList(), new ContentNoId()); + } + + @Override + public Integer rootId() { + return this.rootId; + } + + + @Override + public ContentNoId embedded() { + return this.embedded; + } + + public boolean equals(final Object o) { + if (o == this) + return true; + if (!(o instanceof DummyEntity other)) + return false; + final Object this$rootId = this.rootId(); + final Object other$rootId = other.rootId(); + if (!Objects.equals(this$rootId, other$rootId)) + return false; + final Object this$single = this.single(); + final Object other$single = other.single(); + if (!Objects.equals(this$single, other$single)) + return false; + final Object this$contentSet = this.contentSet(); + final Object other$contentSet = other.contentSet(); + if (!Objects.equals(this$contentSet, other$contentSet)) + return false; + final Object this$contentList = this.contentList(); + final Object other$contentList = other.contentList(); + if (!Objects.equals(this$contentList, other$contentList)) + return false; + final Object this$contentMap = this.contentMap(); + final Object other$contentMap = other.contentMap(); + if (!Objects.equals(this$contentMap, other$contentMap)) + return false; + final Object this$contentNoIdList = this.contentNoIdList(); + final Object other$contentNoIdList = other.contentNoIdList(); + if (!Objects.equals(this$contentNoIdList, other$contentNoIdList)) + return false; + final Object this$embedded = this.embedded(); + final Object other$embedded = other.embedded(); + return Objects.equals(this$embedded, other$embedded); + } + + public int hashCode() { + final int PRIME = 59; + int result = 1; + final Object $rootId = this.rootId(); + result = result * PRIME + ($rootId == null ? 43 : $rootId.hashCode()); + final Object $single = this.single(); + result = result * PRIME + ($single == null ? 43 : $single.hashCode()); + final Object $contentSet = this.contentSet(); + result = result * PRIME + ($contentSet == null ? 43 : $contentSet.hashCode()); + final Object $contentList = this.contentList(); + result = result * PRIME + ($contentList == null ? 43 : $contentList.hashCode()); + final Object $contentMap = this.contentMap(); + result = result * PRIME + ($contentMap == null ? 43 : $contentMap.hashCode()); + final Object $contentNoIdList = this.contentNoIdList(); + result = result * PRIME + ($contentNoIdList == null ? 43 : $contentNoIdList.hashCode()); + final Object $embedded = this.embedded(); + result = result * PRIME + ($embedded == null ? 43 : $embedded.hashCode()); + return result; + } + + public String toString() { + return "AggregateChangeIdGenerationImmutableUnitTests.DummyEntity(rootId=" + this.rootId() + ", single=" + + this.single() + ", contentSet=" + this.contentSet() + ", contentList=" + this.contentList() + + ", contentMap=" + this.contentMap() + ", contentNoIdList=" + this.contentNoIdList() + ", embedded=" + + this.embedded() + ")"; + } + + public DummyEntity withRootId(Integer rootId) { + return this.rootId == rootId ? this + : new DummyEntity(rootId, this.single, this.contentSet, this.contentList, this.contentMap, + this.contentNoIdList, this.embedded); + } + + public DummyEntity withSingle(Content single) { + return this.single == single ? this + : new DummyEntity(this.rootId, single, this.contentSet, this.contentList, this.contentMap, + this.contentNoIdList, this.embedded); + } + + public DummyEntity withContentSet(Set contentSet) { + return this.contentSet == contentSet ? this + : new DummyEntity(this.rootId, this.single, contentSet, this.contentList, this.contentMap, + this.contentNoIdList, this.embedded); + } + + public DummyEntity withContentList(List contentList) { + return this.contentList == contentList ? this + : new DummyEntity(this.rootId, this.single, this.contentSet, contentList, this.contentMap, + this.contentNoIdList, this.embedded); + } + + public DummyEntity withContentMap(Map contentMap) { + return this.contentMap == contentMap ? this + : new DummyEntity(this.rootId, this.single, this.contentSet, this.contentList, contentMap, + this.contentNoIdList, this.embedded); + } + + public DummyEntity withContentNoIdList(List contentNoIdList) { + return this.contentNoIdList == contentNoIdList ? this + : new DummyEntity(this.rootId, this.single, this.contentSet, this.contentList, this.contentMap, + contentNoIdList, this.embedded); + } + + public DummyEntity withEmbedded(ContentNoId embedded) { + return this.embedded == embedded ? this + : new DummyEntity(this.rootId, this.single, this.contentSet, this.contentList, this.contentMap, + this.contentNoIdList, embedded); + } + } + + private record Content(@Id Integer id, Tag single, Set tagSet, List tagList, Map tagMap) { + + Content() { + + this(null, null, emptySet(), emptyList(), emptyMap()); + } + + @Override + public Integer id() { + return this.id; + } - public Map getContentMap() { - return this.contentMap; - } - - public List getContentNoIdList() { - return this.contentNoIdList; - } - - public ContentNoId getEmbedded() { - return this.embedded; - } public boolean equals(final Object o) { - if (o == this) return true; - if (!(o instanceof DummyEntity other)) return false; - final Object this$rootId = this.getRootId(); - final Object other$rootId = other.getRootId(); - if (this$rootId == null ? other$rootId != null : !this$rootId.equals(other$rootId)) return false; - final Object this$single = this.getSingle(); - final Object other$single = other.getSingle(); - if (this$single == null ? other$single != null : !this$single.equals(other$single)) return false; - final Object this$contentSet = this.getContentSet(); - final Object other$contentSet = other.getContentSet(); - if (this$contentSet == null ? other$contentSet != null : !this$contentSet.equals(other$contentSet)) - return false; - final Object this$contentList = this.getContentList(); - final Object other$contentList = other.getContentList(); - if (this$contentList == null ? other$contentList != null : !this$contentList.equals(other$contentList)) - return false; - final Object this$contentMap = this.getContentMap(); - final Object other$contentMap = other.getContentMap(); - if (this$contentMap == null ? other$contentMap != null : !this$contentMap.equals(other$contentMap)) - return false; - final Object this$contentNoIdList = this.getContentNoIdList(); - final Object other$contentNoIdList = other.getContentNoIdList(); - if (this$contentNoIdList == null ? other$contentNoIdList != null : !this$contentNoIdList.equals(other$contentNoIdList)) - return false; - final Object this$embedded = this.getEmbedded(); - final Object other$embedded = other.getEmbedded(); - if (this$embedded == null ? other$embedded != null : !this$embedded.equals(other$embedded)) return false; - return true; - } - - public int hashCode() { - final int PRIME = 59; - int result = 1; - final Object $rootId = this.getRootId(); - result = result * PRIME + ($rootId == null ? 43 : $rootId.hashCode()); - final Object $single = this.getSingle(); - result = result * PRIME + ($single == null ? 43 : $single.hashCode()); - final Object $contentSet = this.getContentSet(); - result = result * PRIME + ($contentSet == null ? 43 : $contentSet.hashCode()); - final Object $contentList = this.getContentList(); - result = result * PRIME + ($contentList == null ? 43 : $contentList.hashCode()); - final Object $contentMap = this.getContentMap(); - result = result * PRIME + ($contentMap == null ? 43 : $contentMap.hashCode()); - final Object $contentNoIdList = this.getContentNoIdList(); - result = result * PRIME + ($contentNoIdList == null ? 43 : $contentNoIdList.hashCode()); - final Object $embedded = this.getEmbedded(); - result = result * PRIME + ($embedded == null ? 43 : $embedded.hashCode()); - return result; - } - - public String toString() { - return "AggregateChangeIdGenerationImmutableUnitTests.DummyEntity(rootId=" + this.getRootId() + ", single=" + this.getSingle() + ", contentSet=" + this.getContentSet() + ", contentList=" + this.getContentList() + ", contentMap=" + this.getContentMap() + ", contentNoIdList=" + this.getContentNoIdList() + ", embedded=" + this.getEmbedded() + ")"; - } - - public DummyEntity withRootId(Integer rootId) { - return this.rootId == rootId ? this : new DummyEntity(rootId, this.single, this.contentSet, this.contentList, this.contentMap, this.contentNoIdList, this.embedded); - } - - public DummyEntity withSingle(Content single) { - return this.single == single ? this : new DummyEntity(this.rootId, single, this.contentSet, this.contentList, this.contentMap, this.contentNoIdList, this.embedded); - } - - public DummyEntity withContentSet(Set contentSet) { - return this.contentSet == contentSet ? this : new DummyEntity(this.rootId, this.single, contentSet, this.contentList, this.contentMap, this.contentNoIdList, this.embedded); - } - - public DummyEntity withContentList(List contentList) { - return this.contentList == contentList ? this : new DummyEntity(this.rootId, this.single, this.contentSet, contentList, this.contentMap, this.contentNoIdList, this.embedded); - } - - public DummyEntity withContentMap(Map contentMap) { - return this.contentMap == contentMap ? this : new DummyEntity(this.rootId, this.single, this.contentSet, this.contentList, contentMap, this.contentNoIdList, this.embedded); - } - - public DummyEntity withContentNoIdList(List contentNoIdList) { - return this.contentNoIdList == contentNoIdList ? this : new DummyEntity(this.rootId, this.single, this.contentSet, this.contentList, this.contentMap, contentNoIdList, this.embedded); - } - - public DummyEntity withEmbedded(ContentNoId embedded) { - return this.embedded == embedded ? this : new DummyEntity(this.rootId, this.single, this.contentSet, this.contentList, this.contentMap, this.contentNoIdList, embedded); - } - } - - private static final class Content { + if (o == this) + return true; + if (!(o instanceof Content other)) + return false; + final Object this$id = this.id(); + final Object other$id = other.id(); + if (!Objects.equals(this$id, other$id)) + return false; + final Object this$single = this.single(); + final Object other$single = other.single(); + if (!Objects.equals(this$single, other$single)) + return false; + final Object this$tagSet = this.tagSet(); + final Object other$tagSet = other.tagSet(); + if (!Objects.equals(this$tagSet, other$tagSet)) + return false; + final Object this$tagList = this.tagList(); + final Object other$tagList = other.tagList(); + if (!Objects.equals(this$tagList, other$tagList)) + return false; + final Object this$tagMap = this.tagMap(); + final Object other$tagMap = other.tagMap(); + return Objects.equals(this$tagMap, other$tagMap); + } + + public int hashCode() { + final int PRIME = 59; + int result = 1; + final Object $id = this.id(); + result = result * PRIME + ($id == null ? 43 : $id.hashCode()); + final Object $single = this.single(); + result = result * PRIME + ($single == null ? 43 : $single.hashCode()); + final Object $tagSet = this.tagSet(); + result = result * PRIME + ($tagSet == null ? 43 : $tagSet.hashCode()); + final Object $tagList = this.tagList(); + result = result * PRIME + ($tagList == null ? 43 : $tagList.hashCode()); + final Object $tagMap = this.tagMap(); + result = result * PRIME + ($tagMap == null ? 43 : $tagMap.hashCode()); + return result; + } + + public String toString() { + return "AggregateChangeIdGenerationImmutableUnitTests.Content(id=" + this.id() + ", single=" + this.single() + + ", tagSet=" + this.tagSet() + ", tagList=" + this.tagList() + ", tagMap=" + this.tagMap() + ")"; + } + + public Content withId(Integer id) { + return this.id == id ? this : new Content(id, this.single, this.tagSet, this.tagList, this.tagMap); + } + + public Content withSingle(Tag single) { + return this.single == single ? this : new Content(this.id, single, this.tagSet, this.tagList, this.tagMap); + } + + public Content withTagSet(Set tagSet) { + return this.tagSet == tagSet ? this : new Content(this.id, this.single, tagSet, this.tagList, this.tagMap); + } + + public Content withTagList(List tagList) { + return this.tagList == tagList ? this : new Content(this.id, this.single, this.tagSet, tagList, this.tagMap); + } + + public Content withTagMap(Map tagMap) { + return this.tagMap == tagMap ? this : new Content(this.id, this.single, this.tagSet, this.tagList, tagMap); + } + } + + private record ContentNoId(@Column("single") Tag single, Set tagSet, List tagList, + Map tagMap) { + ContentNoId() { + + this(null, emptySet(), emptyList(), emptyMap()); + } + + @Override + public Tag single() { + return this.single; + } - @Id - private final - Integer id; - private final Tag single; - private final Set tagSet; - private final List tagList; - private final Map tagMap; - - Content() { - - id = null; - single = null; - tagSet = emptySet(); - tagList = emptyList(); - tagMap = emptyMap(); - } - - public Content(Integer id, Tag single, Set tagSet, List tagList, Map tagMap) { - this.id = id; - this.single = single; - this.tagSet = tagSet; - this.tagList = tagList; - this.tagMap = tagMap; - } - - public Integer getId() { - return this.id; - } - - public Tag getSingle() { - return this.single; - } - - public Set getTagSet() { - return this.tagSet; - } - - public List getTagList() { - return this.tagList; - } - - public Map getTagMap() { - return this.tagMap; - } public boolean equals(final Object o) { - if (o == this) return true; - if (!(o instanceof Content other)) return false; - final Object this$id = this.getId(); - final Object other$id = other.getId(); - if (this$id == null ? other$id != null : !this$id.equals(other$id)) return false; - final Object this$single = this.getSingle(); - final Object other$single = other.getSingle(); - if (this$single == null ? other$single != null : !this$single.equals(other$single)) return false; - final Object this$tagSet = this.getTagSet(); - final Object other$tagSet = other.getTagSet(); - if (this$tagSet == null ? other$tagSet != null : !this$tagSet.equals(other$tagSet)) return false; - final Object this$tagList = this.getTagList(); - final Object other$tagList = other.getTagList(); - if (this$tagList == null ? other$tagList != null : !this$tagList.equals(other$tagList)) return false; - final Object this$tagMap = this.getTagMap(); - final Object other$tagMap = other.getTagMap(); - if (this$tagMap == null ? other$tagMap != null : !this$tagMap.equals(other$tagMap)) return false; - return true; - } - - public int hashCode() { - final int PRIME = 59; - int result = 1; - final Object $id = this.getId(); - result = result * PRIME + ($id == null ? 43 : $id.hashCode()); - final Object $single = this.getSingle(); - result = result * PRIME + ($single == null ? 43 : $single.hashCode()); - final Object $tagSet = this.getTagSet(); - result = result * PRIME + ($tagSet == null ? 43 : $tagSet.hashCode()); - final Object $tagList = this.getTagList(); - result = result * PRIME + ($tagList == null ? 43 : $tagList.hashCode()); - final Object $tagMap = this.getTagMap(); - result = result * PRIME + ($tagMap == null ? 43 : $tagMap.hashCode()); - return result; - } - - public String toString() { - return "AggregateChangeIdGenerationImmutableUnitTests.Content(id=" + this.getId() + ", single=" + this.getSingle() + ", tagSet=" + this.getTagSet() + ", tagList=" + this.getTagList() + ", tagMap=" + this.getTagMap() + ")"; - } - - public Content withId(Integer id) { - return this.id == id ? this : new Content(id, this.single, this.tagSet, this.tagList, this.tagMap); - } - - public Content withSingle(Tag single) { - return this.single == single ? this : new Content(this.id, single, this.tagSet, this.tagList, this.tagMap); - } - - public Content withTagSet(Set tagSet) { - return this.tagSet == tagSet ? this : new Content(this.id, this.single, tagSet, this.tagList, this.tagMap); - } - - public Content withTagList(List tagList) { - return this.tagList == tagList ? this : new Content(this.id, this.single, this.tagSet, tagList, this.tagMap); - } - - public Content withTagMap(Map tagMap) { - return this.tagMap == tagMap ? this : new Content(this.id, this.single, this.tagSet, this.tagList, tagMap); - } - } - - private static final class ContentNoId { - @Column("single") - private final - Tag single; - private final Set tagSet; - private final List tagList; - private final Map tagMap; - - ContentNoId() { - - single = null; - tagSet = emptySet(); - tagList = emptyList(); - tagMap = emptyMap(); - } - - public ContentNoId(Tag single, Set tagSet, List tagList, Map tagMap) { - this.single = single; - this.tagSet = tagSet; - this.tagList = tagList; - this.tagMap = tagMap; - } + if (o == this) + return true; + if (!(o instanceof ContentNoId other)) + return false; + final Object this$single = this.single(); + final Object other$single = other.single(); + if (!Objects.equals(this$single, other$single)) + return false; + final Object this$tagSet = this.tagSet(); + final Object other$tagSet = other.tagSet(); + if (!Objects.equals(this$tagSet, other$tagSet)) + return false; + final Object this$tagList = this.tagList(); + final Object other$tagList = other.tagList(); + if (!Objects.equals(this$tagList, other$tagList)) + return false; + final Object this$tagMap = this.tagMap(); + final Object other$tagMap = other.tagMap(); + return Objects.equals(this$tagMap, other$tagMap); + } + + public int hashCode() { + final int PRIME = 59; + int result = 1; + final Object $single = this.single(); + result = result * PRIME + ($single == null ? 43 : $single.hashCode()); + final Object $tagSet = this.tagSet(); + result = result * PRIME + ($tagSet == null ? 43 : $tagSet.hashCode()); + final Object $tagList = this.tagList(); + result = result * PRIME + ($tagList == null ? 43 : $tagList.hashCode()); + final Object $tagMap = this.tagMap(); + result = result * PRIME + ($tagMap == null ? 43 : $tagMap.hashCode()); + return result; + } + + public String toString() { + return "AggregateChangeIdGenerationImmutableUnitTests.ContentNoId(single=" + this.single() + ", tagSet=" + + this.tagSet() + ", tagList=" + this.tagList() + ", tagMap=" + this.tagMap() + ")"; + } + + public ContentNoId withSingle(Tag single) { + return this.single == single ? this : new ContentNoId(single, this.tagSet, this.tagList, this.tagMap); + } + + public ContentNoId withTagSet(Set tagSet) { + return this.tagSet == tagSet ? this : new ContentNoId(this.single, tagSet, this.tagList, this.tagMap); + } + + public ContentNoId withTagList(List tagList) { + return this.tagList == tagList ? this : new ContentNoId(this.single, this.tagSet, tagList, this.tagMap); + } + + public ContentNoId withTagMap(Map tagMap) { + return this.tagMap == tagMap ? this : new ContentNoId(this.single, this.tagSet, this.tagList, tagMap); + } + } + + private record Tag(@Id Integer id, String name) { + + Tag(String name) { + this(null, name); + } + + @Override + public Integer id() { + return this.id; + } - public Tag getSingle() { - return this.single; - } - - public Set getTagSet() { - return this.tagSet; - } - - public List getTagList() { - return this.tagList; - } - - public Map getTagMap() { - return this.tagMap; - } public boolean equals(final Object o) { - if (o == this) return true; - if (!(o instanceof ContentNoId other)) return false; - final Object this$single = this.getSingle(); - final Object other$single = other.getSingle(); - if (this$single == null ? other$single != null : !this$single.equals(other$single)) return false; - final Object this$tagSet = this.getTagSet(); - final Object other$tagSet = other.getTagSet(); - if (this$tagSet == null ? other$tagSet != null : !this$tagSet.equals(other$tagSet)) return false; - final Object this$tagList = this.getTagList(); - final Object other$tagList = other.getTagList(); - if (this$tagList == null ? other$tagList != null : !this$tagList.equals(other$tagList)) return false; - final Object this$tagMap = this.getTagMap(); - final Object other$tagMap = other.getTagMap(); - if (this$tagMap == null ? other$tagMap != null : !this$tagMap.equals(other$tagMap)) return false; - return true; + if (o == this) + return true; + if (!(o instanceof Tag other)) + return false; + final Object this$id = this.id(); + final Object other$id = other.id(); + if (!Objects.equals(this$id, other$id)) + return false; + final Object this$name = this.name(); + final Object other$name = other.name(); + return Objects.equals(this$name, other$name); + } + + public int hashCode() { + final int PRIME = 59; + int result = 1; + final Object $id = this.id(); + result = result * PRIME + ($id == null ? 43 : $id.hashCode()); + final Object $name = this.name(); + result = result * PRIME + ($name == null ? 43 : $name.hashCode()); + return result; + } + + public String toString() { + return "AggregateChangeIdGenerationImmutableUnitTests.Tag(id=" + this.id() + ", name=" + this.name() + ")"; + } + + public Tag withId(Integer id) { + return this.id == id ? this : new Tag(id, this.name); + } + + public Tag withName(String name) { + return this.name == name ? this : new Tag(this.id, name); + } } - - public int hashCode() { - final int PRIME = 59; - int result = 1; - final Object $single = this.getSingle(); - result = result * PRIME + ($single == null ? 43 : $single.hashCode()); - final Object $tagSet = this.getTagSet(); - result = result * PRIME + ($tagSet == null ? 43 : $tagSet.hashCode()); - final Object $tagList = this.getTagList(); - result = result * PRIME + ($tagList == null ? 43 : $tagList.hashCode()); - final Object $tagMap = this.getTagMap(); - result = result * PRIME + ($tagMap == null ? 43 : $tagMap.hashCode()); - return result; - } - - public String toString() { - return "AggregateChangeIdGenerationImmutableUnitTests.ContentNoId(single=" + this.getSingle() + ", tagSet=" + this.getTagSet() + ", tagList=" + this.getTagList() + ", tagMap=" + this.getTagMap() + ")"; - } - - public ContentNoId withSingle(Tag single) { - return this.single == single ? this : new ContentNoId(single, this.tagSet, this.tagList, this.tagMap); - } - - public ContentNoId withTagSet(Set tagSet) { - return this.tagSet == tagSet ? this : new ContentNoId(this.single, tagSet, this.tagList, this.tagMap); - } - - public ContentNoId withTagList(List tagList) { - return this.tagList == tagList ? this : new ContentNoId(this.single, this.tagSet, tagList, this.tagMap); - } - - public ContentNoId withTagMap(Map tagMap) { - return this.tagMap == tagMap ? this : new ContentNoId(this.single, this.tagSet, this.tagList, tagMap); - } - } - - private static final class Tag { - - @Id - private final - Integer id; - - private final String name; - - Tag(String name) { - id = null; - this.name = name; - } - - public Tag(Integer id, String name) { - this.id = id; - this.name = name; - } - - public Integer getId() { - return this.id; - } - - public String getName() { - return this.name; - } - - public boolean equals(final Object o) { - if (o == this) return true; - if (!(o instanceof Tag other)) return false; - final Object this$id = this.getId(); - final Object other$id = other.getId(); - if (this$id == null ? other$id != null : !this$id.equals(other$id)) return false; - final Object this$name = this.getName(); - final Object other$name = other.getName(); - if (this$name == null ? other$name != null : !this$name.equals(other$name)) return false; - return true; - } - - public int hashCode() { - final int PRIME = 59; - int result = 1; - final Object $id = this.getId(); - result = result * PRIME + ($id == null ? 43 : $id.hashCode()); - final Object $name = this.getName(); - result = result * PRIME + ($name == null ? 43 : $name.hashCode()); - return result; - } - - public String toString() { - return "AggregateChangeIdGenerationImmutableUnitTests.Tag(id=" + this.getId() + ", name=" + this.getName() + ")"; - } - - public Tag withId(Integer id) { - return this.id == id ? this : new Tag(id, this.name); - } - - public Tag withName(String name) { - return this.name == name ? this : new Tag(this.id, name); - } - } } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AggregateChangeIdGenerationUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AggregateChangeIdGenerationUnitTests.java index ce695532c1..927414957b 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AggregateChangeIdGenerationUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AggregateChangeIdGenerationUnitTests.java @@ -27,6 +27,7 @@ import java.util.Map; import java.util.Set; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -42,7 +43,6 @@ import org.springframework.data.relational.core.conversion.RootAggregateChange; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; -import org.springframework.lang.Nullable; /** * Unit tests for the {@link MutableAggregateChange}. @@ -335,7 +335,7 @@ DbAction.Insert createDeepInsert(String propertyName, Object value, @Nullable DbAction.Insert parentInsert) { PersistentPropertyPath propertyPath = toPath( - parentInsert.getPropertyPath().toDotPath() + "." + propertyName); + parentInsert.propertyPath().toDotPath() + "." + propertyName); return new DbAction.Insert<>(value, propertyPath, parentInsert, key == null ? emptyMap() : singletonMap(propertyPath, key), IdValueSource.GENERATED); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutorContextImmutableUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutorContextImmutableUnitTests.java index bf0b41a044..c432ab9b84 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutorContextImmutableUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutorContextImmutableUnitTests.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Objects; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Version; @@ -37,7 +38,6 @@ import org.springframework.data.relational.core.mapping.AggregatePath; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; -import org.springframework.lang.Nullable; /** * Test for the {@link JdbcAggregateChangeExecutionContext} when operating on immutable classes. @@ -180,142 +180,115 @@ PersistentPropertyPath toPath(String path) { .orElseThrow(() -> new IllegalArgumentException("No matching path found")); } - private static final class DummyEntity { + private record DummyEntity(@Id Long id, @Version long version, Content content, List list) { - @Id private final Long id; - @Version private final long version; + DummyEntity() { - private final Content content; + this(null, 0, null, null); + } - private final List list; + @Override + public Long id() { + return this.id; + } - DummyEntity() { + @Override + public long version() { + return this.version; + } - id = null; - version = 0; - content = null; - list = null; - } - - public DummyEntity(Long id, long version, Content content, List list) { - this.id = id; - this.version = version; - this.content = content; - this.list = list; - } - - public Long getId() { - return this.id; - } - - public long getVersion() { - return this.version; - } - - public Content getContent() { - return this.content; - } - - public List getList() { - return this.list; - } public boolean equals(final Object o) { - if (o == this) - return true; - if (!(o instanceof final DummyEntity other)) - return false; - final Object this$id = this.getId(); - final Object other$id = other.getId(); - if (!Objects.equals(this$id, other$id)) - return false; - if (this.getVersion() != other.getVersion()) - return false; - final Object this$content = this.getContent(); - final Object other$content = other.getContent(); - if (!Objects.equals(this$content, other$content)) - return false; - final Object this$list = this.getList(); - final Object other$list = other.getList(); - return Objects.equals(this$list, other$list); - } - - public int hashCode() { - final int PRIME = 59; - int result = 1; - final Object $id = this.getId(); - result = result * PRIME + ($id == null ? 43 : $id.hashCode()); - final long $version = this.getVersion(); - result = result * PRIME + (int) ($version >>> 32 ^ $version); - final Object $content = this.getContent(); - result = result * PRIME + ($content == null ? 43 : $content.hashCode()); - final Object $list = this.getList(); - result = result * PRIME + ($list == null ? 43 : $list.hashCode()); - return result; - } - - public String toString() { - return "JdbcAggregateChangeExecutorContextImmutableUnitTests.DummyEntity(id=" + this.getId() + ", version=" - + this.getVersion() + ", content=" + this.getContent() + ", list=" + this.getList() + ")"; + if (o == this) + return true; + if (!(o instanceof final DummyEntity other)) + return false; + final Object this$id = this.id(); + final Object other$id = other.id(); + if (!Objects.equals(this$id, other$id)) + return false; + if (this.version() != other.version()) + return false; + final Object this$content = this.content(); + final Object other$content = other.content(); + if (!Objects.equals(this$content, other$content)) + return false; + final Object this$list = this.list(); + final Object other$list = other.list(); + return Objects.equals(this$list, other$list); + } + + public int hashCode() { + final int PRIME = 59; + int result = 1; + final Object $id = this.id(); + result = result * PRIME + ($id == null ? 43 : $id.hashCode()); + final long $version = this.version(); + result = result * PRIME + (int) ($version >>> 32 ^ $version); + final Object $content = this.content(); + result = result * PRIME + ($content == null ? 43 : $content.hashCode()); + final Object $list = this.list(); + result = result * PRIME + ($list == null ? 43 : $list.hashCode()); + return result; + } + + public String toString() { + return "JdbcAggregateChangeExecutorContextImmutableUnitTests.DummyEntity(id=" + this.id() + ", version=" + + this.version() + ", content=" + this.content() + ", list=" + this.list() + ")"; + } + + public DummyEntity withId(Long id) { + return this.id == id ? this : new DummyEntity(id, this.version, this.content, this.list); + } + + public DummyEntity withVersion(long version) { + return this.version == version ? this : new DummyEntity(this.id, version, this.content, this.list); + } + + public DummyEntity withContent(Content content) { + return this.content == content ? this : new DummyEntity(this.id, this.version, content, this.list); + } + + public DummyEntity withList(List list) { + return this.list == list ? this : new DummyEntity(this.id, this.version, this.content, list); + } } - public DummyEntity withId(Long id) { - return this.id == id ? this : new DummyEntity(id, this.version, this.content, this.list); + private record Content(@Id Long id) { + Content() { + this(null); + } + + @Override + public Long id() { + return this.id; + } + + public boolean equals(final Object o) { + if (o == this) + return true; + if (!(o instanceof final Content other)) + return false; + final Object this$id = this.id(); + final Object other$id = other.id(); + return Objects.equals(this$id, other$id); + } + + public int hashCode() { + final int PRIME = 59; + int result = 1; + final Object $id = this.id(); + result = result * PRIME + ($id == null ? 43 : $id.hashCode()); + return result; + } + + public String toString() { + return "JdbcAggregateChangeExecutorContextImmutableUnitTests.Content(id=" + this.id() + ")"; + } + + public Content withId(Long id) { + return this.id == id ? this : new Content(id); + } } - public DummyEntity withVersion(long version) { - return this.version == version ? this : new DummyEntity(this.id, version, this.content, this.list); - } - - public DummyEntity withContent(Content content) { - return this.content == content ? this : new DummyEntity(this.id, this.version, content, this.list); - } - - public DummyEntity withList(List list) { - return this.list == list ? this : new DummyEntity(this.id, this.version, this.content, list); - } - } - - private static final class Content { - @Id private final Long id; - - Content() { - id = null; - } - - public Content(Long id) { - this.id = id; - } - - public Long getId() { - return this.id; - } - - public boolean equals(final Object o) { - if (o == this) - return true; - if (!(o instanceof final Content other)) - return false; - final Object this$id = this.getId(); - final Object other$id = other.getId(); - return Objects.equals(this$id, other$id); - } - - public int hashCode() { - final int PRIME = 59; - int result = 1; - final Object $id = this.getId(); - result = result * PRIME + ($id == null ? 43 : $id.hashCode()); - return result; - } - - public String toString() { - return "JdbcAggregateChangeExecutorContextImmutableUnitTests.Content(id=" + this.getId() + ")"; - } - - public Content withId(Long id) { - return this.id == id ? this : new Content(id); - } - } - } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutorContextUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutorContextUnitTests.java index afb0f224c0..5319224401 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutorContextUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateChangeExecutorContextUnitTests.java @@ -24,6 +24,7 @@ import java.util.ArrayList; import java.util.List; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import org.springframework.data.annotation.Id; import org.springframework.data.jdbc.core.convert.DataAccessStrategy; @@ -39,7 +40,6 @@ import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; import org.springframework.data.relational.core.sql.SqlIdentifier; -import org.springframework.lang.Nullable; /** * Unit tests for {@link JdbcAggregateChangeExecutionContext}. diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorEmbeddedUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorEmbeddedUnitTests.java index 80283e4caa..2272e2824c 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorEmbeddedUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorEmbeddedUnitTests.java @@ -18,9 +18,9 @@ import static java.util.Collections.*; import static org.assertj.core.api.Assertions.*; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; - import org.springframework.data.annotation.Id; import org.springframework.data.jdbc.core.PersistentPropertyPathTestUtils; import org.springframework.data.jdbc.core.mapping.AggregateReference; @@ -35,7 +35,6 @@ import org.springframework.data.relational.core.mapping.Table; import org.springframework.data.relational.core.sql.Aliased; import org.springframework.data.relational.core.sql.SqlIdentifier; -import org.springframework.lang.Nullable; /** * Unit tests for the {@link SqlGenerator} in a context of the {@link Embedded} annotation. @@ -46,8 +45,8 @@ */ class SqlGeneratorEmbeddedUnitTests { - private RelationalMappingContext context = JdbcMappingContext.forPlainIdentifiers(); - private JdbcConverter converter = new MappingJdbcConverter(context, (identifier, path) -> { + private final RelationalMappingContext context = JdbcMappingContext.forPlainIdentifiers(); + private final JdbcConverter converter = new MappingJdbcConverter(context, (identifier, path) -> { throw new UnsupportedOperationException(); }); private SqlGenerator sqlGenerator; @@ -416,8 +415,7 @@ void columnForEmbeddedWithReferenceProperty() { SqlIdentifier.unquoted("prefix_other_value")); } - @Nullable - private SqlGenerator.Join generateJoin(String path, Class type) { + private SqlGenerator.@Nullable Join generateJoin(String path, Class type) { return createSqlGenerator(type) .getJoin(context.getAggregatePath(PersistentPropertyPathTestUtils.getPath(path, type, context))); } @@ -431,8 +429,7 @@ private SqlIdentifier getAlias(Object maybeAliased) { return null; } - @Nullable - private org.springframework.data.relational.core.sql.Column generatedColumn(String path, Class type) { + private org.springframework.data.relational.core.sql.@Nullable Column generatedColumn(String path, Class type) { return createSqlGenerator(type) .getColumn(context.getAggregatePath(PersistentPropertyPathTestUtils.getPath(path, type, context))); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorUnitTests.java index 08831d801a..659b008035 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorUnitTests.java @@ -24,9 +24,9 @@ import java.util.Map; import java.util.Set; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; - import org.springframework.data.annotation.Id; import org.springframework.data.annotation.ReadOnlyProperty; import org.springframework.data.annotation.Version; @@ -56,7 +56,6 @@ import org.springframework.data.relational.core.sql.SqlIdentifier; import org.springframework.data.relational.core.sql.Table; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; -import org.springframework.lang.Nullable; /** * Unit tests for the {@link SqlGenerator}. @@ -827,8 +826,7 @@ void joinForOneToOneWithoutId() { }); } - @Nullable - private SqlGenerator.Join generateJoin(String path, Class type) { + private SqlGenerator.@Nullable Join generateJoin(String path, Class type) { return createSqlGenerator(type, AnsiDialect.INSTANCE) .getJoin(context.getAggregatePath(PersistentPropertyPathTestUtils.getPath(path, type, context))); } @@ -1035,8 +1033,7 @@ private SqlIdentifier getAlias(Object maybeAliased) { return null; } - @Nullable - private org.springframework.data.relational.core.sql.Column generatedColumn(String path, Class type) { + private org.springframework.data.relational.core.sql.@Nullable Column generatedColumn(String path, Class type) { return createSqlGenerator(type, AnsiDialect.INSTANCE) .getColumn(context.getAggregatePath(PersistentPropertyPathTestUtils.getPath(path, type, context))); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/DeclaredQueryRepositoryUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/DeclaredQueryRepositoryUnitTests.java index 31722d9b50..4c8fb4e83d 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/DeclaredQueryRepositoryUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/DeclaredQueryRepositoryUnitTests.java @@ -20,9 +20,9 @@ import static org.mockito.Mockito.*; import org.jetbrains.annotations.NotNull; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; - import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.annotation.Id; import org.springframework.data.jdbc.core.convert.DataAccessStrategy; @@ -42,7 +42,6 @@ import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.jdbc.core.namedparam.SqlParameterSource; -import org.springframework.lang.Nullable; /** * Extracts the SQL statement that results from declared queries of a repository and perform assertions on it. @@ -51,7 +50,7 @@ */ public class DeclaredQueryRepositoryUnitTests { - private NamedParameterJdbcOperations operations = mock(NamedParameterJdbcOperations.class, RETURNS_DEEP_STUBS); + private final NamedParameterJdbcOperations operations = mock(NamedParameterJdbcOperations.class, RETURNS_DEEP_STUBS); @Test // GH-1856 void plainSql() { diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java index 10bd3eced8..00015aed4d 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java @@ -37,6 +37,7 @@ import java.util.function.Consumer; import java.util.stream.Stream; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -89,7 +90,6 @@ import org.springframework.data.util.Streamable; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; -import org.springframework.lang.Nullable; import org.springframework.test.jdbc.JdbcTestUtils; /** @@ -621,7 +621,7 @@ public void stringQueryProjectionShouldReturnDtoProjectedEntities() { List result = repository.findProjectedWithSql(DtoProjection.class); assertThat(result).hasSize(1); - assertThat(result.get(0).getName()).isEqualTo("Entity Name"); + assertThat(result.get(0).name()).isEqualTo("Entity Name"); } @Test // GH-971 @@ -1697,77 +1697,61 @@ public EvaluationContextExtension evaluationContextExtension() { } - static final class Root { - - @Id private final Long id; - private final String name; - private final Intermediate intermediate; - @MappedCollection(idColumn = "ROOT_ID", keyColumn = "ROOT_KEY") private final List intermediates; - - public Root(Long id, String name, Intermediate intermediate, List intermediates) { - this.id = id; - this.name = name; - this.intermediate = intermediate; - this.intermediates = intermediates; - } + record Root(@Id Long id, String name, Intermediate intermediate, + @MappedCollection(idColumn = "ROOT_ID", keyColumn = "ROOT_KEY") List intermediates) { - public Long getId() { - return this.id; - } - - public String getName() { - return this.name; - } + @Override + public Long id() { + return this.id; + } - public Intermediate getIntermediate() { - return this.intermediate; - } - public List getIntermediates() { - return this.intermediates; - } + @Override + public List intermediates() { + return this.intermediates; + } - public boolean equals(final Object o) { - if (o == this) - return true; - if (!(o instanceof final Root other)) - return false; - final Object this$id = this.getId(); - final Object other$id = other.getId(); - if (!Objects.equals(this$id, other$id)) - return false; - final Object this$name = this.getName(); - final Object other$name = other.getName(); - if (!Objects.equals(this$name, other$name)) - return false; - final Object this$intermediate = this.getIntermediate(); - final Object other$intermediate = other.getIntermediate(); - if (!Objects.equals(this$intermediate, other$intermediate)) - return false; - final Object this$intermediates = this.getIntermediates(); - final Object other$intermediates = other.getIntermediates(); - return Objects.equals(this$intermediates, other$intermediates); - } + public boolean equals(final Object o) { + if (o == this) + return true; + if (!(o instanceof final Root other)) + return false; + final Object this$id = this.id(); + final Object other$id = other.id(); + if (!Objects.equals(this$id, other$id)) + return false; + final Object this$name = this.name(); + final Object other$name = other.name(); + if (!Objects.equals(this$name, other$name)) + return false; + final Object this$intermediate = this.intermediate(); + final Object other$intermediate = other.intermediate(); + if (!Objects.equals(this$intermediate, other$intermediate)) + return false; + final Object this$intermediates = this.intermediates(); + final Object other$intermediates = other.intermediates(); + return Objects.equals(this$intermediates, other$intermediates); + } - public int hashCode() { - final int PRIME = 59; - int result = 1; - final Object $id = this.getId(); - result = result * PRIME + ($id == null ? 43 : $id.hashCode()); - final Object $name = this.getName(); - result = result * PRIME + ($name == null ? 43 : $name.hashCode()); - final Object $intermediate = this.getIntermediate(); - result = result * PRIME + ($intermediate == null ? 43 : $intermediate.hashCode()); - final Object $intermediates = this.getIntermediates(); - result = result * PRIME + ($intermediates == null ? 43 : $intermediates.hashCode()); - return result; - } + public int hashCode() { + final int PRIME = 59; + int result = 1; + final Object $id = this.id(); + result = result * PRIME + ($id == null ? 43 : $id.hashCode()); + final Object $name = this.name(); + result = result * PRIME + ($name == null ? 43 : $name.hashCode()); + final Object $intermediate = this.intermediate(); + result = result * PRIME + ($intermediate == null ? 43 : $intermediate.hashCode()); + final Object $intermediates = this.intermediates(); + result = result * PRIME + ($intermediates == null ? 43 : $intermediates.hashCode()); + return result; + } - public String toString() { - return "JdbcRepositoryIntegrationTests.Root(id=" + this.getId() + ", name=" + this.getName() + ", intermediate=" - + this.getIntermediate() + ", intermediates=" + this.getIntermediates() + ")"; + public String toString() { + return "JdbcRepositoryIntegrationTests.Root(id=" + this.id() + ", name=" + this.name() + ", intermediate=" + + this.intermediate() + ", intermediates=" + this.intermediates() + ")"; + } } - } @Table("WITH_DELIMITED_COLUMN") static class WithDelimitedColumn { @@ -1800,124 +1784,98 @@ public void setType(String type) { } } - static final class Intermediate { - - @Id private final Long id; - private final String name; - private final Leaf leaf; - @MappedCollection(idColumn = "INTERMEDIATE_ID", keyColumn = "INTERMEDIATE_KEY") private final List leaves; - - public Intermediate(Long id, String name, Leaf leaf, List leaves) { - this.id = id; - this.name = name; - this.leaf = leaf; - this.leaves = leaves; - } - - public Long getId() { - return this.id; - } + record Intermediate(@Id Long id, String name, Leaf leaf, + @MappedCollection(idColumn = "INTERMEDIATE_ID", keyColumn = "INTERMEDIATE_KEY") List leaves) { - public String getName() { - return this.name; - } + @Override + public Long id() { + return this.id; + } - public Leaf getLeaf() { - return this.leaf; - } - public List getLeaves() { - return this.leaves; - } + @Override + public List leaves() { + return this.leaves; + } - public boolean equals(final Object o) { - if (o == this) - return true; - if (!(o instanceof final Intermediate other)) - return false; - final Object this$id = this.getId(); - final Object other$id = other.getId(); - if (!Objects.equals(this$id, other$id)) - return false; - final Object this$name = this.getName(); - final Object other$name = other.getName(); - if (!Objects.equals(this$name, other$name)) - return false; - final Object this$leaf = this.getLeaf(); - final Object other$leaf = other.getLeaf(); - if (!Objects.equals(this$leaf, other$leaf)) - return false; - final Object this$leaves = this.getLeaves(); - final Object other$leaves = other.getLeaves(); - return Objects.equals(this$leaves, other$leaves); - } + public boolean equals(final Object o) { + if (o == this) + return true; + if (!(o instanceof final Intermediate other)) + return false; + final Object this$id = this.id(); + final Object other$id = other.id(); + if (!Objects.equals(this$id, other$id)) + return false; + final Object this$name = this.name(); + final Object other$name = other.name(); + if (!Objects.equals(this$name, other$name)) + return false; + final Object this$leaf = this.leaf(); + final Object other$leaf = other.leaf(); + if (!Objects.equals(this$leaf, other$leaf)) + return false; + final Object this$leaves = this.leaves(); + final Object other$leaves = other.leaves(); + return Objects.equals(this$leaves, other$leaves); + } - public int hashCode() { - final int PRIME = 59; - int result = 1; - final Object $id = this.getId(); - result = result * PRIME + ($id == null ? 43 : $id.hashCode()); - final Object $name = this.getName(); - result = result * PRIME + ($name == null ? 43 : $name.hashCode()); - final Object $leaf = this.getLeaf(); - result = result * PRIME + ($leaf == null ? 43 : $leaf.hashCode()); - final Object $leaves = this.getLeaves(); - result = result * PRIME + ($leaves == null ? 43 : $leaves.hashCode()); - return result; - } + public int hashCode() { + final int PRIME = 59; + int result = 1; + final Object $id = this.id(); + result = result * PRIME + ($id == null ? 43 : $id.hashCode()); + final Object $name = this.name(); + result = result * PRIME + ($name == null ? 43 : $name.hashCode()); + final Object $leaf = this.leaf(); + result = result * PRIME + ($leaf == null ? 43 : $leaf.hashCode()); + final Object $leaves = this.leaves(); + result = result * PRIME + ($leaves == null ? 43 : $leaves.hashCode()); + return result; + } - public String toString() { - return "JdbcRepositoryIntegrationTests.Intermediate(id=" + this.getId() + ", name=" + this.getName() + ", leaf=" - + this.getLeaf() + ", leaves=" + this.getLeaves() + ")"; + public String toString() { + return "JdbcRepositoryIntegrationTests.Intermediate(id=" + this.id() + ", name=" + this.name() + ", leaf=" + + this.leaf() + ", leaves=" + this.leaves() + ")"; + } } - } - static final class Leaf { - - @Id private final Long id; - private final String name; + record Leaf(@Id Long id, String name) { - public Leaf(Long id, String name) { - this.id = id; - this.name = name; - } - - public Long getId() { - return this.id; - } + @Override + public Long id() { + return this.id; + } - public String getName() { - return this.name; - } public boolean equals(final Object o) { - if (o == this) - return true; - if (!(o instanceof final Leaf other)) - return false; - final Object this$id = this.getId(); - final Object other$id = other.getId(); - if (!Objects.equals(this$id, other$id)) - return false; - final Object this$name = this.getName(); - final Object other$name = other.getName(); - return Objects.equals(this$name, other$name); - } + if (o == this) + return true; + if (!(o instanceof final Leaf other)) + return false; + final Object this$id = this.id(); + final Object other$id = other.id(); + if (!Objects.equals(this$id, other$id)) + return false; + final Object this$name = this.name(); + final Object other$name = other.name(); + return Objects.equals(this$name, other$name); + } - public int hashCode() { - final int PRIME = 59; - int result = 1; - final Object $id = this.getId(); - result = result * PRIME + ($id == null ? 43 : $id.hashCode()); - final Object $name = this.getName(); - result = result * PRIME + ($name == null ? 43 : $name.hashCode()); - return result; - } + public int hashCode() { + final int PRIME = 59; + int result = 1; + final Object $id = this.id(); + result = result * PRIME + ($id == null ? 43 : $id.hashCode()); + final Object $name = this.name(); + result = result * PRIME + ($name == null ? 43 : $name.hashCode()); + return result; + } - public String toString() { - return "JdbcRepositoryIntegrationTests.Leaf(id=" + this.getId() + ", name=" + this.getName() + ")"; + public String toString() { + return "JdbcRepositoryIntegrationTests.Leaf(id=" + this.id() + ", name=" + this.name() + ")"; + } } - } static class MyEventListener implements ApplicationListener> { @@ -1980,7 +1938,7 @@ static class EntityWithSequence { @Id @Sequence(sequence = "ENTITY_SEQUENCE") private Long id; - private String name; + private final String name; public EntityWithSequence(Long id, String name) { this.id = id; @@ -2004,9 +1962,9 @@ static class ProvidedIdEntity implements Persistable { @Id private final Long id; - private String name; + private final String name; - @Transient private boolean isNew; + @Transient private final boolean isNew; private ProvidedIdEntity(Long id, String name, boolean isNew) { this.id = id; @@ -2169,39 +2127,31 @@ public AggregateReference getRef() { } } - static final class DtoProjection { - private final String name; - - public DtoProjection(String name) { - this.name = name; - } + record DtoProjection(String name) { - public String getName() { - return this.name; - } public boolean equals(final Object o) { - if (o == this) - return true; - if (!(o instanceof final DtoProjection other)) - return false; - final Object this$name = this.getName(); - final Object other$name = other.getName(); - return Objects.equals(this$name, other$name); - } + if (o == this) + return true; + if (!(o instanceof final DtoProjection other)) + return false; + final Object this$name = this.name(); + final Object other$name = other.name(); + return Objects.equals(this$name, other$name); + } - public int hashCode() { - final int PRIME = 59; - int result = 1; - final Object $name = this.getName(); - result = result * PRIME + ($name == null ? 43 : $name.hashCode()); - return result; - } + public int hashCode() { + final int PRIME = 59; + int result = 1; + final Object $name = this.name(); + result = result * PRIME + ($name == null ? 43 : $name.hashCode()); + return result; + } - public String toString() { - return "JdbcRepositoryIntegrationTests.DtoProjection(name=" + this.getName() + ")"; + public String toString() { + return "JdbcRepositoryIntegrationTests.DtoProjection(name=" + this.name() + ")"; + } } - } static class CustomRowMapper implements RowMapper { diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java index be5b194ffa..827d4559a8 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java @@ -25,6 +25,7 @@ import java.util.HashMap; import java.util.List; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.stubbing.Answer; @@ -56,7 +57,6 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.jdbc.core.namedparam.SqlParameterSource; import org.springframework.jdbc.support.KeyHolder; -import org.springframework.lang.Nullable; import org.springframework.util.ObjectUtils; /** @@ -132,7 +132,7 @@ void publishesEventsOnSaveMany() { repository.saveAll(asList(entity1, entity2)); assertThat(publisher.events) // - .extracting(RelationalEvent::getClass, e -> ((DummyEntity) e.getEntity()).getId()) // + .extracting(RelationalEvent::getClass, e -> ((DummyEntity) e.getEntity()).id()) // .containsExactly( // tuple(BeforeConvertEvent.class, null), // tuple(BeforeSaveEvent.class, null), // @@ -299,44 +299,40 @@ private static NamedParameterJdbcOperations createIdGeneratingOperations() { interface DummyEntityRepository extends CrudRepository, PagingAndSortingRepository {} - static final class DummyEntity { - private final @Id Long id; + record DummyEntity(@Id Long id) { - public DummyEntity(Long id) { - this.id = id; - } + @Override + public Long id() { + return this.id; + } - public Long getId() { - return this.id; - } + public DummyEntity withId(Long id) { + return this.id == id ? this : new DummyEntity(id); + } - public DummyEntity withId(Long id) { - return this.id == id ? this : new DummyEntity(id); - } + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; + DummyEntity that = (DummyEntity) o; - DummyEntity that = (DummyEntity) o; + return ObjectUtils.nullSafeEquals(id, that.id); + } - return ObjectUtils.nullSafeEquals(id, that.id); - } + @Override + public int hashCode() { + return ObjectUtils.nullSafeHashCode(id); + } - @Override - public int hashCode() { - return ObjectUtils.nullSafeHashCode(id); - } + public String toString() { + return "SimpleJdbcRepositoryEventsUnitTests.DummyEntity(id=" + this.id() + ")"; + } - public String toString() { - return "SimpleJdbcRepositoryEventsUnitTests.DummyEntity(id=" + this.getId() + ")"; } - } - static class CollectingEventPublisher implements ApplicationEventPublisher { List events = new ArrayList<>(); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/QueryAnnotationHsqlIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/QueryAnnotationHsqlIntegrationTests.java index bd322ee8aa..da76983ed0 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/QueryAnnotationHsqlIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/QueryAnnotationHsqlIntegrationTests.java @@ -24,6 +24,7 @@ import java.util.Optional; import java.util.stream.Stream; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.ComponentScan; @@ -39,7 +40,6 @@ import org.springframework.data.jdbc.testing.TestConfiguration; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; -import org.springframework.lang.Nullable; /** * Tests the execution of queries from {@link Query} annotations on repository methods. diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/config/AbstractR2dbcConfiguration.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/config/AbstractR2dbcConfiguration.java index 1ca3a89227..f5b741902b 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/config/AbstractR2dbcConfiguration.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/config/AbstractR2dbcConfiguration.java @@ -25,6 +25,7 @@ import java.util.Optional; import java.util.Set; +import org.jspecify.annotations.Nullable; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -49,7 +50,6 @@ import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.relational.core.mapping.Table; import org.springframework.data.util.TypeScanner; -import org.springframework.lang.Nullable; import org.springframework.r2dbc.core.DatabaseClient; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -193,8 +193,8 @@ public R2dbcMappingContext r2dbcMappingContext(Optional namingSt * @since 3.5 */ @Bean - public IdGeneratingEntityCallback idGeneratingBeforeSaveCallback( - RelationalMappingContext relationalMappingContext, DatabaseClient databaseClient) { + public IdGeneratingEntityCallback idGeneratingBeforeSaveCallback(RelationalMappingContext relationalMappingContext, + DatabaseClient databaseClient) { return new IdGeneratingEntityCallback(relationalMappingContext, getDialect(lookupConnectionFactory()), databaseClient); } diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/config/package-info.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/config/package-info.java index e4e7fb58ab..40c5db6d04 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/config/package-info.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/config/package-info.java @@ -1,6 +1,5 @@ /** * Configuration classes for Spring Data R2DBC. */ -@org.springframework.lang.NonNullApi -@org.springframework.lang.NonNullFields +@org.jspecify.annotations.NullMarked package org.springframework.data.r2dbc.config; diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/MappingR2dbcConverter.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/MappingR2dbcConverter.java index a0198bbee0..3270d9fbc6 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/MappingR2dbcConverter.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/MappingR2dbcConverter.java @@ -31,6 +31,7 @@ import java.util.Optional; import java.util.function.BiFunction; +import org.jspecify.annotations.Nullable; import org.springframework.core.convert.ConversionService; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.convert.CustomConversions; @@ -46,7 +47,6 @@ import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; import org.springframework.data.relational.domain.RowDocument; import org.springframework.data.util.TypeInformation; -import org.springframework.lang.Nullable; import org.springframework.r2dbc.core.Parameter; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -104,7 +104,12 @@ public R read(Class type, Row row, @Nullable RowMetadata metadata) { if (getConversions().hasCustomReadTarget(Row.class, rawType) && getConversionService().canConvert(Row.class, rawType)) { - return getConversionService().convert(row, rawType); + + R converted = getConversionService().convert(row, rawType); + + Assert.notNull(converted, "Converted must not be null"); + + return converted; } RowDocument document = toRowDocument(type, row, metadata != null ? metadata.getColumnMetadatas() : null); @@ -175,6 +180,9 @@ public void write(Object source, OutboundRow sink) { if (customTarget.isPresent()) { OutboundRow result = getConversionService().convert(source, OutboundRow.class); + + Assert.notNull(result, "Result must not be null"); + sink.putAll(result); return; } @@ -376,6 +384,8 @@ private Object getPotentiallyConvertedSimpleWrite(@Nullable Object value, Class< } } + Assert.state(value != null, "Value must not be null"); + Optional> customTarget = getConversions().getCustomWriteTarget(value.getClass()); if (customTarget.isPresent()) { @@ -407,7 +417,11 @@ public Object getArrayValue(ArrayColumns arrayColumns, RelationalPersistentPrope int depth = value.getClass().isArray() ? ArrayUtils.getDimensionDepth(value.getClass()) : 1; Class targetArrayType = ArrayUtils.getArrayClass(targetType, depth); - return getConversionService().convert(value, targetArrayType); + Object converted = getConversionService().convert(value, targetArrayType); + + Assert.state(converted != null, "Value must not be null"); + + return converted; } return value; diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/R2dbcConverters.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/R2dbcConverters.java index 7be1e328ee..b7cbc6c175 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/R2dbcConverters.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/R2dbcConverters.java @@ -28,6 +28,7 @@ import java.util.List; import java.util.UUID; +import org.jspecify.annotations.Nullable; import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.ConverterFactory; import org.springframework.util.Assert; @@ -159,7 +160,7 @@ static class RowToNumber implements Converter { } @Override - public T convert(Row source) { + public @Nullable T convert(Row source) { Object object = source.get(0); diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/RowPropertyAccessor.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/RowPropertyAccessor.java index 520a8d9927..7141a4ad37 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/RowPropertyAccessor.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/RowPropertyAccessor.java @@ -18,10 +18,10 @@ import io.r2dbc.spi.Row; import io.r2dbc.spi.RowMetadata; +import org.jspecify.annotations.Nullable; import org.springframework.expression.EvaluationContext; import org.springframework.expression.PropertyAccessor; import org.springframework.expression.TypedValue; -import org.springframework.lang.Nullable; /** * {@link PropertyAccessor} to read values from a {@link Row}. diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/SequenceEntityCallbackDelegate.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/SequenceEntityCallbackDelegate.java index 5c3f452d87..2af3e78c3d 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/SequenceEntityCallbackDelegate.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/SequenceEntityCallbackDelegate.java @@ -19,7 +19,6 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.r2dbc.mapping.OutboundRow; @@ -90,6 +89,10 @@ private Mono getSequenceValue(RelationalPersistentProperty property) { SqlIdentifier sequence = property.getSequence(); + if (sequence == null) { + return Mono.empty(); + } + if (sequence != null && !dialect.getIdGeneration().sequencesSupported()) { LOG.warn(""" Entity type '%s' is marked for sequence usage but configured dialect '%s' diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/package-info.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/package-info.java index cb313f6a8a..b6f4aca395 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/package-info.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/package-info.java @@ -1,6 +1,5 @@ /** * R2DBC-specific conversion and converter implementations. */ -@org.springframework.lang.NonNullApi -@org.springframework.lang.NonNullFields +@org.jspecify.annotations.NullMarked package org.springframework.data.r2dbc.convert; diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/BindParameterSource.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/BindParameterSource.java index 3049eeb81f..22341103ca 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/BindParameterSource.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/BindParameterSource.java @@ -15,8 +15,8 @@ */ package org.springframework.data.r2dbc.core; +import org.jspecify.annotations.Nullable; import org.springframework.data.util.Streamable; -import org.springframework.lang.Nullable; /** * Interface that defines common functionality for objects that can offer parameter values for named bind parameters, diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/DefaultReactiveDataAccessStrategy.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/DefaultReactiveDataAccessStrategy.java index c1f918f23e..148dba73a8 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/DefaultReactiveDataAccessStrategy.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/DefaultReactiveDataAccessStrategy.java @@ -28,6 +28,7 @@ import java.util.Map; import java.util.function.BiFunction; +import org.jspecify.annotations.Nullable; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.dao.InvalidDataAccessResourceUsageException; import org.springframework.data.mapping.context.MappingContext; @@ -48,7 +49,6 @@ import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; import org.springframework.data.relational.core.sql.SqlIdentifier; import org.springframework.data.relational.domain.RowDocument; -import org.springframework.lang.Nullable; import org.springframework.r2dbc.core.Parameter; import org.springframework.r2dbc.core.PreparedOperation; import org.springframework.util.Assert; @@ -202,21 +202,19 @@ private boolean shouldConvertArrayValue(RelationalPersistentProperty property, P return false; } - if (value.hasValue() && (value.getValue() instanceof Collection || value.getValue().getClass().isArray())) { + if (value.hasValue() && value.getValue() instanceof Collection + // use != null, because NullAway doesn't understand the semantics of hasValue + || (value.getValue() != null && value.getValue().getClass().isArray())) { return true; } - if (Collection.class.isAssignableFrom(value.getType()) || value.getType().isArray()) { - return true; - } - - return false; + return Collection.class.isAssignableFrom(value.getType()) || value.getType().isArray(); } - private Parameter getArrayValue(Parameter value, RelationalPersistentProperty property) { + private Parameter getArrayValue(Parameter parameter, RelationalPersistentProperty property) { - if (value.getType().equals(byte[].class)) { - return value; + if (parameter.getType().equals(byte[].class)) { + return parameter; } ArrayColumns arrayColumns = this.dialect.getArraySupport(); @@ -227,10 +225,12 @@ private Parameter getArrayValue(Parameter value, RelationalPersistentProperty pr } Class actualType = null; - if (value.getValue() instanceof Collection) { - actualType = CollectionUtils.findCommonElementType((Collection) value.getValue()); - } else if (!value.isEmpty() && value.getValue().getClass().isArray()) { - actualType = value.getValue().getClass().getComponentType(); + + Object value = parameter.getValue(); + if (value instanceof Collection) { + actualType = CollectionUtils.findCommonElementType((Collection) value); + } else if (value != null && value.getClass().isArray()) { + actualType = value.getClass().getComponentType(); } if (actualType == null) { @@ -239,7 +239,7 @@ private Parameter getArrayValue(Parameter value, RelationalPersistentProperty pr actualType = converter.getTargetType(actualType); - if (value.isEmpty()) { + if (parameter.isEmpty()) { Class targetType = arrayColumns.getArrayType(actualType); int depth = actualType.isArray() ? ArrayUtils.getDimensionDepth(actualType) : 1; @@ -247,7 +247,9 @@ private Parameter getArrayValue(Parameter value, RelationalPersistentProperty pr return Parameter.empty(targetArrayType); } - return Parameter.fromOrEmpty(this.converter.getArrayValue(arrayColumns, property, value.getValue()), actualType); + Assert.state(value != null, "value must not be null"); + + return Parameter.fromOrEmpty(this.converter.getArrayValue(arrayColumns, property, value), actualType); } @Override diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/DefaultStatementMapper.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/DefaultStatementMapper.java index 24f7f46a5b..195aa10859 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/DefaultStatementMapper.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/DefaultStatementMapper.java @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.List; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.r2dbc.convert.R2dbcConverter; import org.springframework.data.r2dbc.dialect.R2dbcDialect; @@ -32,7 +33,6 @@ import org.springframework.data.relational.core.sql.InsertBuilder.InsertValuesWithBuild; import org.springframework.data.relational.core.sql.render.RenderContext; import org.springframework.data.relational.core.sql.render.SqlRenderer; -import org.springframework.lang.Nullable; import org.springframework.r2dbc.core.PreparedOperation; import org.springframework.r2dbc.core.binding.BindMarkers; import org.springframework.r2dbc.core.binding.BindTarget; @@ -247,8 +247,9 @@ private PreparedOperation getMappedObject(DeleteSpec deleteSpec, if (criteria != null && !criteria.isEmpty()) { - BoundCondition boundCondition = this.updateMapper.getMappedObject(bindMarkers, deleteSpec.getCriteria(), table, - entity); + Assert.state(criteria != null, "DeleteSpec must have a criteria"); + + BoundCondition boundCondition = this.updateMapper.getMappedObject(bindMarkers, criteria, table, entity); bindings = boundCondition.getBindings(); delete = deleteBuilder.where(boundCondition.getCondition()).build(); diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/MapBindParameterSource.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/MapBindParameterSource.java index 01a4d5e227..c4b5637dad 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/MapBindParameterSource.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/MapBindParameterSource.java @@ -18,6 +18,7 @@ import java.util.LinkedHashMap; import java.util.Map; +import org.jspecify.annotations.Nullable; import org.springframework.data.util.Streamable; import org.springframework.r2dbc.core.Parameter; import org.springframework.util.Assert; @@ -93,13 +94,17 @@ public Class getType(String paramName) { } @Override - public Object getValue(String paramName) throws IllegalArgumentException { + public @Nullable Object getValue(String paramName) throws IllegalArgumentException { if (!hasValue(paramName)) { throw new IllegalArgumentException("No value registered for key '" + paramName + "'"); } - return this.values.get(paramName).getValue(); + Parameter parameter = this.values.get(paramName); + + Assert.notNull(parameter, "Parameter must not be null"); + + return parameter.getValue(); } @Override diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/NamedParameterUtils.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/NamedParameterUtils.java index 812011d2dc..12bd73ff70 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/NamedParameterUtils.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/NamedParameterUtils.java @@ -25,8 +25,8 @@ import java.util.Set; import java.util.TreeMap; +import org.jspecify.annotations.Nullable; import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.lang.Nullable; import org.springframework.r2dbc.core.PreparedOperation; import org.springframework.r2dbc.core.binding.BindMarker; import org.springframework.r2dbc.core.binding.BindMarkers; @@ -173,7 +173,7 @@ public static ParsedSql parseSqlStatement(String sql) { } ParsedSql parsedSql = new ParsedSql(sqlToUse); for (ParameterHolder ph : parameterList) { - parsedSql.addNamedParameter(ph.getParameterName(), ph.getStartIndex(), ph.getEndIndex()); + parsedSql.addNamedParameter(ph.parameterName(), ph.startIndex(), ph.endIndex()); } parsedSql.setNamedParameterCount(namedParameterCount); parsedSql.setUnnamedParameterCount(unnamedParameterCount); @@ -354,47 +354,20 @@ public static PreparedOperation substituteNamedParameters(String sql, Bi return substituteNamedParameters(parsedSql, bindMarkersFactory, paramSource); } - private static final class ParameterHolder { + private record ParameterHolder(String parameterName, int startIndex, int endIndex) { - private final String parameterName; - - private final int startIndex; - - private final int endIndex; - - ParameterHolder(String parameterName, int startIndex, int endIndex) { - this.parameterName = parameterName; - this.startIndex = startIndex; - this.endIndex = endIndex; - } - - String getParameterName() { - return this.parameterName; - } - - int getStartIndex() { - return this.startIndex; - } - - int getEndIndex() { - return this.endIndex; - } @Override - public boolean equals(@Nullable Object o) { - if (this == o) - return true; - if (o instanceof ParameterHolder that) { - return this.startIndex == that.startIndex && this.endIndex == that.endIndex - && Objects.equals(this.parameterName, that.parameterName); + public boolean equals(@Nullable Object o) { + if (this == o) + return true; + if (o instanceof ParameterHolder that) { + return this.startIndex == that.startIndex && this.endIndex == that.endIndex + && Objects.equals(this.parameterName, that.parameterName); + } + return false; } - return false; - } - @Override - public int hashCode() { - return Objects.hash(this.parameterName, this.startIndex, this.endIndex); - } } /** @@ -483,127 +456,116 @@ String getPlaceholder(int counter) { * Expanded query that allows binding of parameters using parameter names that were used to expand the query. Binding * unrolls {@link Collection}s and nested arrays. */ - private static class ExpandedQuery implements PreparedOperation { - - private final String expandedSql; - - private final NamedParameters parameters; - - private final BindParameterSource parameterSource; - - ExpandedQuery(String expandedSql, NamedParameters parameters, BindParameterSource parameterSource) { - this.expandedSql = expandedSql; - this.parameters = parameters; - this.parameterSource = parameterSource; - } + private record ExpandedQuery(String expandedSql, NamedParameters parameters, + BindParameterSource parameterSource) implements PreparedOperation { @SuppressWarnings("unchecked") - public void bind(org.springframework.r2dbc.core.binding.BindTarget target, String identifier, Object value) { + public void bind(BindTarget target, String identifier, Object value) { - List> bindMarkers = getBindMarkers(identifier); + List> bindMarkers = getBindMarkers(identifier); - if (bindMarkers == null) { + if (bindMarkers == null) { - target.bind(identifier, value); - return; - } + target.bind(identifier, value); + return; + } - for (List outer : bindMarkers) { - if (value instanceof Collection) { - Collection collection = (Collection) value; + for (List outer : bindMarkers) { + if (value instanceof Collection) { + Collection collection = (Collection) value; - Iterator iterator = collection.iterator(); - Iterator markers = outer.iterator(); + Iterator iterator = collection.iterator(); + Iterator markers = outer.iterator(); - while (iterator.hasNext()) { + while (iterator.hasNext()) { - Object valueToBind = iterator.next(); + Object valueToBind = iterator.next(); - if (valueToBind instanceof Object[] objects) { - for (Object object : objects) { - bind(target, markers, object); + if (valueToBind instanceof Object[] objects) { + for (Object object : objects) { + bind(target, markers, object); + } + } else { + bind(target, markers, valueToBind); } - } else { - bind(target, markers, valueToBind); } - } - } else { - for (BindMarker bindMarker : outer) { - bindMarker.bind(target, value); + } else { + for (BindMarker bindMarker : outer) { + bindMarker.bind(target, value); + } } } } - } - private void bind(org.springframework.r2dbc.core.binding.BindTarget target, Iterator markers, - Object valueToBind) { + private void bind(BindTarget target, Iterator markers, + Object valueToBind) { - Assert.isTrue(markers.hasNext(), - () -> String.format( - "No bind marker for value [%s] in SQL [%s]; Check that the query was expanded using the same arguments", - valueToBind, toQuery())); + Assert.isTrue(markers.hasNext(), + () -> String.format( + "No bind marker for value [%s] in SQL [%s]; Check that the query was expanded using the same arguments", + valueToBind, toQuery())); - markers.next().bind(target, valueToBind); - } + markers.next().bind(target, valueToBind); + } - public void bindNull(org.springframework.r2dbc.core.binding.BindTarget target, String identifier, - Class valueType) { + public void bindNull(BindTarget target, String identifier, + Class valueType) { - List> bindMarkers = getBindMarkers(identifier); + List> bindMarkers = getBindMarkers(identifier); - if (bindMarkers == null) { + if (bindMarkers == null) { - target.bindNull(identifier, valueType); - return; - } + target.bindNull(identifier, valueType); + return; + } - for (List outer : bindMarkers) { - for (BindMarker bindMarker : outer) { - bindMarker.bindNull(target, valueType); + for (List outer : bindMarkers) { + for (BindMarker bindMarker : outer) { + bindMarker.bindNull(target, valueType); + } } } - } - @Nullable - List> getBindMarkers(String identifier) { + @Nullable + List> getBindMarkers(String identifier) { - List parameters = this.parameters.getMarker(identifier); + List parameters = this.parameters.getMarker(identifier); - if (parameters == null) { - return null; - } + if (parameters == null) { + return null; + } - List> markers = new ArrayList<>(); - for (NamedParameters.NamedParameter parameter : parameters) { - markers.add(new ArrayList<>(parameter.placeholders)); - } + List> markers = new ArrayList<>(); + for (NamedParameters.NamedParameter parameter : parameters) { + markers.add(new ArrayList<>(parameter.placeholders)); + } - return markers; - } + return markers; + } - @Override - public String getSource() { - return this.expandedSql; - } + @Override + public String getSource() { + return this.expandedSql; + } - @Override - public void bindTo(BindTarget target) { + @Override + public void bindTo(BindTarget target) { - for (String namedParameter : this.parameterSource.getParameterNames()) { + for (String namedParameter : this.parameterSource.getParameterNames()) { - Object value = this.parameterSource.getValue(namedParameter); + Object value = this.parameterSource.getValue(namedParameter); - if (value == null) { - bindNull(target, namedParameter, this.parameterSource.getType(namedParameter)); - } else { - bind(target, namedParameter, value); + if (value == null) { + bindNull(target, namedParameter, this.parameterSource.getType(namedParameter)); + } else { + bind(target, namedParameter, value); + } } } - } - @Override - public String toQuery() { - return this.expandedSql; + @Override + public String toQuery() { + return this.expandedSql; + } } - } } diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java index 353c550fdb..d2af66384a 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java @@ -33,8 +33,8 @@ import java.util.function.Function; import java.util.stream.Collectors; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; - import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; @@ -75,7 +75,6 @@ import org.springframework.data.relational.domain.RowDocument; import org.springframework.data.util.Predicates; import org.springframework.data.util.ProxyUtils; -import org.springframework.lang.Nullable; import org.springframework.r2dbc.core.DatabaseClient; import org.springframework.r2dbc.core.Parameter; import org.springframework.r2dbc.core.PreparedOperation; @@ -218,7 +217,11 @@ public void setApplicationContext(ApplicationContext applicationContext) throws } projectionFactory.setBeanFactory(applicationContext); - projectionFactory.setBeanClassLoader(applicationContext.getClassLoader()); + ClassLoader classLoader = applicationContext.getClassLoader(); + + Assert.notNull(classLoader, "ClassLoader must not be null"); + + projectionFactory.setBeanClassLoader(classLoader); } /** @@ -644,6 +647,8 @@ private Mono doUpdate(T entity, SqlIdentifier tableName) { } } + Assert.state(criteria != null, "Criteria must not be null"); + if (matchingVersionCriteria != null) { criteria = criteria.and(matchingVersionCriteria); } @@ -702,11 +707,17 @@ private T incrementVersion(RelationalPersistentEntity persistentEntity, T PersistentPropertyAccessor propertyAccessor = persistentEntity.getPropertyAccessor(entity); RelationalPersistentProperty versionProperty = persistentEntity.getVersionProperty(); + Assert.state(versionProperty != null, "Version property must not be null"); + ConversionService conversionService = this.dataAccessStrategy.getConverter().getConversionService(); Object currentVersionValue = propertyAccessor.getProperty(versionProperty); long newVersionValue = 1L; if (currentVersionValue != null) { - newVersionValue = conversionService.convert(currentVersionValue, Long.class) + 1; + Long converted = conversionService.convert(currentVersionValue, Long.class); + + Assert.state(converted != null, "Current version value must not be null"); + + newVersionValue = converted + 1; } Class versionPropertyType = versionProperty.getType(); propertyAccessor.setProperty(versionProperty, conversionService.convert(newVersionValue, versionPropertyType)); @@ -719,6 +730,8 @@ private Criteria createMatchingVersionCriteria(T entity, RelationalPersisten PersistentPropertyAccessor propertyAccessor = persistentEntity.getPropertyAccessor(entity); RelationalPersistentProperty versionProperty = persistentEntity.getVersionProperty(); + Assert.state(versionProperty != null, "Version property must not be null"); + Object version = propertyAccessor.getProperty(versionProperty); Criteria.CriteriaStep versionColumn = Criteria.where(dataAccessStrategy.toSql(versionProperty.getColumnName())); if (version == null) { @@ -896,29 +909,23 @@ public RowsFetchSpec getRowsFetchSpec(DatabaseClient.GenericExecuteSpec e * * @param */ - private static class UnwrapOptionalFetchSpecAdapter implements RowsFetchSpec { - - private final RowsFetchSpec> delegate; - - private UnwrapOptionalFetchSpecAdapter(RowsFetchSpec> delegate) { - this.delegate = delegate; - } + private record UnwrapOptionalFetchSpecAdapter(RowsFetchSpec> delegate) implements RowsFetchSpec { @Override - public Mono one() { - return delegate.one().handle((optional, sink) -> optional.ifPresent(sink::next)); - } + public Mono one() { + return delegate.one().handle((optional, sink) -> optional.ifPresent(sink::next)); + } - @Override - public Mono first() { - return delegate.first().handle((optional, sink) -> optional.ifPresent(sink::next)); - } + @Override + public Mono first() { + return delegate.first().handle((optional, sink) -> optional.ifPresent(sink::next)); + } - @Override - public Flux all() { - return delegate.all().handle((optional, sink) -> optional.ifPresent(sink::next)); + @Override + public Flux all() { + return delegate.all().handle((optional, sink) -> optional.ifPresent(sink::next)); + } } - } /** * {@link RowsFetchSpec} adapter applying {@link #maybeCallAfterConvert(Object, SqlIdentifier)} to each emitted diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveDataAccessStrategy.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveDataAccessStrategy.java index dbecbef1a6..b12696e67a 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveDataAccessStrategy.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveDataAccessStrategy.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.function.BiFunction; +import org.jspecify.annotations.Nullable; import org.springframework.data.r2dbc.convert.R2dbcConverter; import org.springframework.data.r2dbc.mapping.OutboundRow; import org.springframework.data.relational.core.dialect.AnsiDialect; @@ -30,7 +31,6 @@ import org.springframework.data.relational.core.sql.IdentifierProcessing; import org.springframework.data.relational.core.sql.SqlIdentifier; import org.springframework.data.relational.domain.RowDocument; -import org.springframework.lang.Nullable; import org.springframework.r2dbc.core.Parameter; import org.springframework.r2dbc.core.PreparedOperation; import org.springframework.util.Assert; diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveDeleteOperationSupport.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveDeleteOperationSupport.java index 4c43d7ad69..732ffb8698 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveDeleteOperationSupport.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveDeleteOperationSupport.java @@ -17,9 +17,9 @@ import reactor.core.publisher.Mono; +import org.jspecify.annotations.Nullable; import org.springframework.data.relational.core.query.Query; import org.springframework.data.relational.core.sql.SqlIdentifier; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveInsertOperationSupport.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveInsertOperationSupport.java index 9f2135d058..4930dde7ce 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveInsertOperationSupport.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveInsertOperationSupport.java @@ -17,8 +17,8 @@ import reactor.core.publisher.Mono; +import org.jspecify.annotations.Nullable; import org.springframework.data.relational.core.sql.SqlIdentifier; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveSelectOperationSupport.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveSelectOperationSupport.java index 4210e31242..2bf8ad008c 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveSelectOperationSupport.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveSelectOperationSupport.java @@ -18,9 +18,9 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import org.jspecify.annotations.Nullable; import org.springframework.data.relational.core.query.Query; import org.springframework.data.relational.core.sql.SqlIdentifier; -import org.springframework.lang.Nullable; import org.springframework.r2dbc.core.RowsFetchSpec; import org.springframework.util.Assert; diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveUpdateOperationSupport.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveUpdateOperationSupport.java index 883ccbb6fb..a90d91d8cd 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveUpdateOperationSupport.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveUpdateOperationSupport.java @@ -17,10 +17,10 @@ import reactor.core.publisher.Mono; +import org.jspecify.annotations.Nullable; import org.springframework.data.relational.core.query.Query; import org.springframework.data.relational.core.query.Update; import org.springframework.data.relational.core.sql.SqlIdentifier; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/StatementMapper.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/StatementMapper.java index 88729b49cc..0b7d50c850 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/StatementMapper.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/StatementMapper.java @@ -26,6 +26,7 @@ import java.util.function.Supplier; import java.util.stream.Collectors; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.r2dbc.convert.R2dbcConverter; @@ -37,7 +38,6 @@ import org.springframework.data.relational.core.sql.SqlIdentifier; import org.springframework.data.relational.core.sql.Table; import org.springframework.data.relational.core.sql.render.RenderContext; -import org.springframework.lang.Nullable; import org.springframework.r2dbc.core.Parameter; import org.springframework.r2dbc.core.PreparedOperation; import org.springframework.util.Assert; @@ -229,10 +229,11 @@ class SelectSpec { private final long offset; private final int limit; private final boolean distinct; - private final LockMode lockMode; + private final @Nullable LockMode lockMode; protected SelectSpec(Table table, List projectedFields, List selectList, - @Nullable CriteriaDefinition criteria, Sort sort, int limit, long offset, boolean distinct, LockMode lockMode) { + @Nullable CriteriaDefinition criteria, Sort sort, int limit, long offset, boolean distinct, + @Nullable LockMode lockMode) { this.table = table; this.projectedFields = projectedFields; this.selectList = selectList; @@ -536,10 +537,10 @@ public Map getAssignments() { class UpdateSpec { private final SqlIdentifier table; - private final @Nullable org.springframework.data.relational.core.query.Update update; + private final org.springframework.data.relational.core.query.@Nullable Update update; private final @Nullable CriteriaDefinition criteria; - protected UpdateSpec(SqlIdentifier table, @Nullable org.springframework.data.relational.core.query.Update update, + protected UpdateSpec(SqlIdentifier table, org.springframework.data.relational.core.query.@Nullable Update update, @Nullable CriteriaDefinition criteria) { this.table = table; @@ -582,8 +583,7 @@ public SqlIdentifier getTable() { return this.table; } - @Nullable - public org.springframework.data.relational.core.query.Update getUpdate() { + public org.springframework.data.relational.core.query.@Nullable Update getUpdate() { return this.update; } @@ -601,7 +601,7 @@ class DeleteSpec { private final SqlIdentifier table; private final @Nullable CriteriaDefinition criteria; - protected DeleteSpec(SqlIdentifier table, CriteriaDefinition criteria) { + protected DeleteSpec(SqlIdentifier table, @Nullable CriteriaDefinition criteria) { this.table = table; this.criteria = criteria; } @@ -633,7 +633,7 @@ public static DeleteSpec create(SqlIdentifier table) { * @param criteria * @return the {@link DeleteSpec}. */ - public DeleteSpec withCriteria(CriteriaDefinition criteria) { + public DeleteSpec withCriteria(@Nullable CriteriaDefinition criteria) { return new DeleteSpec(this.table, criteria); } diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/package-info.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/package-info.java index 42634076b2..54d58766c7 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/package-info.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/package-info.java @@ -1,6 +1,5 @@ /** * Core domain types around DatabaseClient. */ -@org.springframework.lang.NonNullApi -@org.springframework.lang.NonNullFields +@org.jspecify.annotations.NullMarked package org.springframework.data.r2dbc.core; diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/BindTargetBinder.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/BindTargetBinder.java index eba27ec96a..1509ad2467 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/BindTargetBinder.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/BindTargetBinder.java @@ -58,7 +58,7 @@ public void bind(int index, Parameter parameter) { if (value == null) { target.bindNull(index, parameter.getType()); } else { - target.bind(index, parameter.getValue()); + target.bind(index, value); } } } diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/MySqlDialect.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/MySqlDialect.java index bd0f27912c..b006812223 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/MySqlDialect.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/MySqlDialect.java @@ -25,6 +25,7 @@ import java.util.Set; import java.util.UUID; +import org.jspecify.annotations.Nullable; import org.springframework.core.convert.converter.Converter; import org.springframework.data.convert.ReadingConverter; import org.springframework.data.convert.WritingConverter; @@ -84,7 +85,7 @@ public enum ByteToBooleanConverter implements Converter { INSTANCE; @Override - public Boolean convert(Byte s) { + public @Nullable Boolean convert(@Nullable Byte s) { if (s == null) { return null; diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/package-info.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/package-info.java index 9643ac2aad..f1d472ceec 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/package-info.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/package-info.java @@ -1,7 +1,7 @@ /** * Dialects abstract the SQL dialect of the underlying database. */ -@NonNullApi +@NullMarked package org.springframework.data.r2dbc.dialect; -import org.springframework.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/mapping/OutboundRow.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/mapping/OutboundRow.java index 523babe811..368e164e1e 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/mapping/OutboundRow.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/mapping/OutboundRow.java @@ -23,8 +23,8 @@ import java.util.Set; import java.util.function.BiConsumer; +import org.jspecify.annotations.Nullable; import org.springframework.data.relational.core.sql.SqlIdentifier; -import org.springframework.lang.Nullable; import org.springframework.r2dbc.core.Parameter; import org.springframework.util.Assert; @@ -152,7 +152,7 @@ public boolean containsValue(Object value) { } @Override - public Parameter get(Object key) { + public @Nullable Parameter get(Object key) { return this.rowAsMap.get(convertKeyIfNecessary(key)); } diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/mapping/event/package-info.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/mapping/event/package-info.java index 9412744223..f475aacd2d 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/mapping/event/package-info.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/mapping/event/package-info.java @@ -1,5 +1,5 @@ /** * Mapping event callback infrastructure for the R2DBC row-to-object mapping subsystem. */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.r2dbc.mapping.event; diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/mapping/package-info.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/mapping/package-info.java index b3712b5e7a..9050656f43 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/mapping/package-info.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/mapping/package-info.java @@ -1,7 +1,7 @@ /** * Domain objects for R2DBC. */ -@NonNullApi +@NullMarked package org.springframework.data.r2dbc.mapping; -import org.springframework.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/query/QueryMapper.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/query/QueryMapper.java index 7b9b7d89a5..a7cb5eb31b 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/query/QueryMapper.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/query/QueryMapper.java @@ -22,6 +22,7 @@ import java.util.Map; import java.util.regex.Pattern; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Sort; import org.springframework.data.mapping.MappingException; import org.springframework.data.mapping.PersistentProperty; @@ -42,7 +43,6 @@ import org.springframework.data.relational.domain.SqlSort; import org.springframework.data.util.Pair; import org.springframework.data.util.TypeInformation; -import org.springframework.lang.Nullable; import org.springframework.r2dbc.core.Parameter; import org.springframework.r2dbc.core.binding.BindMarker; import org.springframework.r2dbc.core.binding.BindMarkers; @@ -203,12 +203,17 @@ public List getMappedObjects(Expression expression, @Nullable Relati List expressions = new ArrayList<>(); for (RelationalPersistentProperty embeddedProperty : embeddedEntity) { + + Assert.state(table != null, "Embedded table must not be null"); + expressions.addAll(getMappedObjects(Column.create(embeddedProperty.getName(), table), embeddedEntity)); } return expressions; } + Assert.state(table != null, "Table must not be null"); + Column columnFromTable = table.column(field.getMappedColumnName()); return List.of(column instanceof Aliased ? columnFromTable.as(((Aliased) column).getAlias()) : columnFromTable); } @@ -267,8 +272,13 @@ private Condition unroll(CriteriaDefinition criteria, Table table, @Nullable Rel Map forwardChain = new HashMap<>(); while (current.hasPrevious()) { - forwardChain.put(current.getPrevious(), current); - current = current.getPrevious(); + + CriteriaDefinition previous = current.getPrevious(); + + Assert.state(previous != null, "Previous criteria must not be null"); + + forwardChain.put(previous, current); + current = previous; } // perform the actual mapping @@ -355,7 +365,11 @@ private Condition combine(CriteriaDefinition criteria, @Nullable Condition curre private Condition mapCondition(CriteriaDefinition criteria, MutableBindings bindings, Table table, @Nullable RelationalPersistentEntity entity) { - Field propertyField = createPropertyField(entity, criteria.getColumn(), this.mappingContext); + SqlIdentifier criteriaColumn = criteria.getColumn(); + + Assert.notNull(criteriaColumn, "CriteriaColumn must not be null"); + + Field propertyField = createPropertyField(entity, criteriaColumn, this.mappingContext); if (propertyField.isEmbedded() && entity != null) { @@ -400,6 +414,9 @@ public Object getValue() { Class typeHint; Comparator comparator = criteria.getComparator(); + + Assert.state(comparator != null, "CriteriaComparator must not be null"); + if (criteria.getValue() instanceof Parameter parameter) { mappedValue = convertValue(comparator, parameter.getValue(), propertyField.getTypeHint()); @@ -447,7 +464,7 @@ public void setProperty(PersistentProperty property, @org.jspecify.annotation @Override public Object getBean() { - return null; + throw new UnsupportedOperationException("Can't get bean for null valued embedded property"); } }; } @@ -474,7 +491,11 @@ public Parameter getBindValue(Parameter value) { return Parameter.empty(converter.getTargetType(value.getType())); } - return Parameter.from(convertValue(value.getValue(), TypeInformation.OBJECT)); + Object convertedValue = convertValue(value.getValue(), TypeInformation.OBJECT); + + Assert.state(convertedValue != null, "Value must not be null"); + + return Parameter.from(convertedValue); } @Nullable @@ -508,10 +529,12 @@ protected Object convertValue(@Nullable Object value, TypeInformation typeInf Object first = convertValue(pair.getFirst(), typeInformation.getActualType() != null ? typeInformation.getRequiredActualType() : TypeInformation.OBJECT); - Object second = convertValue(pair.getSecond(), typeInformation.getActualType() != null ? typeInformation.getRequiredActualType() : TypeInformation.OBJECT); + Assert.state(first != null, "First value must not be null"); + Assert.state(second != null, "Second value must not be null"); + return Pair.of(first, second); } @@ -586,6 +609,8 @@ private Condition createCondition(Column column, @Nullable Object mappedValue, C Pair pair = (Pair) mappedValue; + Assert.state(pair != null, "Pair must not be null"); + Expression begin = bind(pair.getFirst(), valueType, bindings, bindings.nextMarker(column.getName().getReference()), ignoreCase); Expression end = bind(pair.getSecond(), valueType, bindings, bindings.nextMarker(column.getName().getReference()), @@ -654,7 +679,11 @@ Class getTypeHint(@Nullable Object mappedValue, Class propertyType, Parame return parameter.getType(); } - if (mappedValue.getClass().equals(parameter.getValue().getClass())) { + Object value = parameter.getValue(); + + Assert.state(value != null, "Value must not be null"); + + if (mappedValue.getClass().equals(value.getClass())) { return parameter.getType(); } @@ -679,8 +708,8 @@ private Expression bind(@Nullable Object mappedValue, Class valueType, Mutabl : SQL.bindMarker(bindMarker.getPlaceholder()); } - private Expression booleanBind(Column column, Object mappedValue, Class valueType, MutableBindings bindings, - boolean ignoreCase) { + private Expression booleanBind(Column column, @Nullable Object mappedValue, Class valueType, + MutableBindings bindings, boolean ignoreCase) { BindMarker bindMarker = bindings.nextMarker(column.getName().getReference()); return bind(mappedValue, valueType, bindings, bindMarker, ignoreCase); @@ -953,7 +982,7 @@ public boolean isEmpty() { @Override public Combinator getCombinator() { - return null; + throw new UnsupportedOperationException("No combinator for AbstractCriteria"); } } } diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/query/UpdateMapper.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/query/UpdateMapper.java index 4070959e2c..16f50393a4 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/query/UpdateMapper.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/query/UpdateMapper.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Map; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.r2dbc.convert.R2dbcConverter; import org.springframework.data.r2dbc.dialect.R2dbcDialect; @@ -36,7 +37,6 @@ import org.springframework.data.relational.core.sql.SqlIdentifier; import org.springframework.data.relational.core.sql.Table; import org.springframework.data.util.TypeInformation; -import org.springframework.lang.Nullable; import org.springframework.r2dbc.core.Parameter; import org.springframework.r2dbc.core.binding.BindMarker; import org.springframework.r2dbc.core.binding.BindMarkers; @@ -86,8 +86,8 @@ public BoundAssignments getMappedObject(BindMarkers markers, Update update, Tabl * @param entity related {@link RelationalPersistentEntity}, can be {@literal null}. * @return the mapped {@link BoundAssignments}. */ - public BoundAssignments getMappedObject(BindMarkers markers, Map assignments, - Table table, @Nullable RelationalPersistentEntity entity) { + public BoundAssignments getMappedObject(BindMarkers markers, Map assignments, Table table, + @Nullable RelationalPersistentEntity entity) { Assert.notNull(markers, "BindMarkers must not be null"); Assert.notNull(assignments, "Assignments must not be null"); @@ -103,8 +103,8 @@ public BoundAssignments getMappedObject(BindMarkers markers, Map getAssignments(SqlIdentifier columnName, Object value, MutableBindings bindings, - Table table, @Nullable RelationalPersistentEntity entity) { + private Collection getAssignments(SqlIdentifier columnName, @Nullable Object value, + MutableBindings bindings, Table table, @Nullable RelationalPersistentEntity entity) { Field propertyField = createPropertyField(entity, columnName, getMappingContext()); @@ -161,7 +161,7 @@ private Collection getAssignments(SqlIdentifier columnName, Object v return List.of(createAssignment(column, mappedValue, typeHint, bindings)); } - private Assignment createAssignment(Column column, Object value, Class type, MutableBindings bindings) { + private Assignment createAssignment(Column column, @Nullable Object value, Class type, MutableBindings bindings) { BindMarker bindMarker = bindings.nextMarker(column.getName().getReference()); AssignValue assignValue = Assignments.value(column, SQL.bindMarker(bindMarker.getPlaceholder())); diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/query/package-info.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/query/package-info.java index 572bc04240..bb628cc86c 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/query/package-info.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/query/package-info.java @@ -1,6 +1,5 @@ /** * Query and update support. */ -@org.springframework.lang.NonNullApi -@org.springframework.lang.NonNullFields +@org.jspecify.annotations.NullMarked package org.springframework.data.r2dbc.query; diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/config/package-info.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/config/package-info.java index 4cacf80ec4..cf4aa2877b 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/config/package-info.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/config/package-info.java @@ -1,6 +1,5 @@ /** * Support infrastructure for the configuration of R2DBC-specific repositories. */ -@org.springframework.lang.NonNullApi -@org.springframework.lang.NonNullFields +@org.jspecify.annotations.NullMarked package org.springframework.data.r2dbc.repository.config; diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/package-info.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/package-info.java index 7082b8cac9..654e6e397d 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/package-info.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/package-info.java @@ -1,6 +1,5 @@ /** * R2DBC-specific repository implementation. */ -@org.springframework.lang.NonNullApi -@org.springframework.lang.NonNullFields +@org.jspecify.annotations.NullMarked package org.springframework.data.r2dbc.repository; diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcParameterAccessor.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcParameterAccessor.java index 95ecebc615..90e3e8d58a 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcParameterAccessor.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcParameterAccessor.java @@ -24,8 +24,8 @@ import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; +import org.jspecify.annotations.Nullable; import org.reactivestreams.Publisher; - import org.springframework.data.relational.repository.query.RelationalParametersParameterAccessor; import org.springframework.data.repository.util.ReactiveWrapperConverters; import org.springframework.data.util.ReactiveWrappers; @@ -69,7 +69,7 @@ public Object[] getValues() { /* (non-Javadoc) * @see org.springframework.data.repository.query.ParametersParameterAccessor#getBindableValue(int) */ - public Object getBindableValue(int index) { + public @Nullable Object getBindableValue(int index) { return getValue(getParameters().getBindableParameter(index).getIndex()); } diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcQueryCreator.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcQueryCreator.java index a4b6b04f5a..f2b68d6f6c 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcQueryCreator.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcQueryCreator.java @@ -21,6 +21,7 @@ import java.util.Optional; import java.util.stream.Collectors; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.r2dbc.core.ReactiveDataAccessStrategy; @@ -40,8 +41,8 @@ import org.springframework.data.relational.repository.query.RelationalQueryCreator; import org.springframework.data.repository.query.parser.AbstractQueryCreator; import org.springframework.data.repository.query.parser.PartTree; -import org.springframework.lang.Nullable; import org.springframework.r2dbc.core.PreparedOperation; +import org.springframework.util.Assert; /** * Implementation of {@link AbstractQueryCreator} that creates {@link PreparedOperation} from a {@link PartTree}. @@ -76,6 +77,7 @@ class R2dbcQueryCreator extends RelationalQueryCreator> { public R2dbcQueryCreator(PartTree tree, ReactiveDataAccessStrategy dataAccessStrategy, RelationalEntityMetadata entityMetadata, RelationalParameterAccessor accessor, List projectedProperties, Optional lock) { + super(tree, accessor); this.tree = tree; @@ -123,7 +125,12 @@ private PreparedOperation select(@Nullable Criteria criteria, Sort sort, Stat if (tree.isExistsProjection()) { selectSpec = selectSpec.limit(1); } else if (tree.isLimiting()) { - selectSpec = selectSpec.limit(tree.getMaxResults()); + + Integer maxResults = tree.getMaxResults(); + + Assert.state(maxResults != null, "Max results must be specified when limit is set"); + + selectSpec = selectSpec.limit(maxResults); } Pageable pageable = accessor.getPageable(); diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcQueryExecution.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcQueryExecution.java index d68bc97fe5..40f38e9ffb 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcQueryExecution.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcQueryExecution.java @@ -30,6 +30,7 @@ import org.springframework.data.util.Lazy; import org.springframework.data.util.ReflectionUtils; import org.springframework.r2dbc.core.RowsFetchSpec; +import org.springframework.util.Assert; import org.springframework.util.ClassUtils; /** @@ -60,7 +61,12 @@ final class ResultProcessingExecution implements R2dbcQueryExecution { */ @Override public Publisher execute(RowsFetchSpec fetchSpec) { - return (Publisher) this.converter.convert(this.delegate.execute(fetchSpec)); + + Publisher publisher = (Publisher) this.converter.convert(this.delegate.execute(fetchSpec)); + + Assert.state(publisher != null, "Publisher must not be null"); + + return publisher; } } diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcQueryMethod.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcQueryMethod.java index 5e34272d43..33e5d60c0a 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcQueryMethod.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcQueryMethod.java @@ -18,6 +18,7 @@ import java.lang.reflect.Method; import java.util.Optional; +import org.jspecify.annotations.Nullable; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Page; @@ -36,14 +37,12 @@ import org.springframework.data.relational.repository.query.SimpleRelationalEntityMetadata; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.query.Parameter; -import org.springframework.data.repository.query.ParametersSource; import org.springframework.data.repository.query.QueryMethod; import org.springframework.data.repository.util.ReactiveWrapperConverters; import org.springframework.data.util.Lazy; import org.springframework.data.util.ReactiveWrappers; import org.springframework.data.util.ReflectionUtils; import org.springframework.data.util.TypeInformation; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -106,12 +105,12 @@ public R2dbcQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFa if (!multiWrapper) { throw new IllegalStateException(String.format( "Method has to use a either multi-item reactive wrapper return type or a wrapped Page/Slice type; Offending method: %s", - method.toString())); + method)); } if (ReflectionUtils.hasParameterOfType(method, Sort.class)) { throw new IllegalStateException(String.format("Method must not have Pageable *and* Sort parameter; " - + "Use sorting capabilities on Pageable instead; Offending method: %s", method.toString())); + + "Use sorting capabilities on Pageable instead; Offending method: %s", method)); } } diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/package-info.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/package-info.java index 0ab85fa3f4..ec67ff63d3 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/package-info.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/package-info.java @@ -1,9 +1,7 @@ /** * Query support for R2DBC repositories. */ -@NonNullApi -@NonNullFields +@NullMarked package org.springframework.data.r2dbc.repository.query; -import org.springframework.lang.NonNullApi; -import org.springframework.lang.NonNullFields; +import org.jspecify.annotations.NullMarked; diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/support/R2dbcRepositoryFactory.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/support/R2dbcRepositoryFactory.java index a18dd815af..223bc0fdbf 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/support/R2dbcRepositoryFactory.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/support/R2dbcRepositoryFactory.java @@ -18,6 +18,7 @@ import java.lang.reflect.Method; import java.util.Optional; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.r2dbc.convert.R2dbcConverter; @@ -42,7 +43,6 @@ import org.springframework.data.repository.query.QueryLookupStrategy.Key; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.ValueExpressionDelegate; -import org.springframework.lang.Nullable; import org.springframework.r2dbc.core.DatabaseClient; import org.springframework.util.Assert; diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/support/R2dbcRepositoryFactoryBean.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/support/R2dbcRepositoryFactoryBean.java index 9f02fba47f..4bfe7795a3 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/support/R2dbcRepositoryFactoryBean.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/support/R2dbcRepositoryFactoryBean.java @@ -17,6 +17,7 @@ import java.io.Serializable; +import org.jspecify.annotations.Nullable; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -27,7 +28,6 @@ import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; import org.springframework.data.repository.core.support.RepositoryFactorySupport; -import org.springframework.lang.Nullable; import org.springframework.r2dbc.core.DatabaseClient; import org.springframework.util.Assert; @@ -90,8 +90,14 @@ protected void setMappingContext(MappingContext mappingContext) { @Override protected final RepositoryFactorySupport createRepositoryFactory() { - return this.operations != null ? getFactoryInstance(this.operations) - : getFactoryInstance(this.client, this.dataAccessStrategy); + if (this.operations != null) { + return getFactoryInstance(this.operations); + } + + Assert.state(this.client != null, "DatabaseClient must not be null"); + Assert.state(this.dataAccessStrategy != null, "DataAccessStrategy must not be null"); + + return getFactoryInstance(this.client, this.dataAccessStrategy); } /** diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/support/SimpleR2dbcRepository.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/support/SimpleR2dbcRepository.java index 1fecd89e8a..43733350ff 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/support/SimpleR2dbcRepository.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/support/SimpleR2dbcRepository.java @@ -71,7 +71,7 @@ public class SimpleR2dbcRepository implements R2dbcRepository { private final R2dbcEntityOperations entityOperations; private final Lazy idProperty; private final RelationalExampleMapper exampleMapper; - private MappingContext, ? extends RelationalPersistentProperty> mappingContext; + private final MappingContext, ? extends RelationalPersistentProperty> mappingContext; /** * Create a new {@link SimpleR2dbcRepository}. @@ -107,11 +107,11 @@ public SimpleR2dbcRepository(RelationalEntityInformation entity, Database this.entity = entity; this.entityOperations = new R2dbcEntityTemplate(databaseClient, accessStrategy); - this.idProperty = Lazy.of(() -> converter // - .getMappingContext() // + this.mappingContext = converter.getMappingContext(); + this.idProperty = Lazy.of(() -> mappingContext // .getRequiredPersistentEntity(this.entity.getJavaType()) // .getRequiredIdProperty()); - this.exampleMapper = new RelationalExampleMapper(converter.getMappingContext()); + this.exampleMapper = new RelationalExampleMapper(mappingContext); } // ------------------------------------------------------------------------- @@ -377,8 +377,12 @@ private Query getIdQuery(Object id) { idEntity.doWithProperties(new PropertyHandler() { @Override public void doWithPersistentProperty(RelationalPersistentProperty persistentProperty) { - criteriaHolder[0] = criteriaHolder[0].and(persistentProperty.getName()) - .is(accessor.getProperty(persistentProperty)); + + Object property = accessor.getProperty(persistentProperty); + + Assert.state(property != null, "Property must not be null"); + + criteriaHolder[0] = criteriaHolder[0].and(persistentProperty.getName()).is(property); } }); criteria = criteriaHolder[0]; diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/support/package-info.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/support/package-info.java index 8b4a962640..0709966f42 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/support/package-info.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/support/package-info.java @@ -1,6 +1,5 @@ /** * Support infrastructure for query derivation of R2DBC-specific repositories. */ -@org.springframework.lang.NonNullApi -@org.springframework.lang.NonNullFields +@org.jspecify.annotations.NullMarked package org.springframework.data.r2dbc.repository.support; diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/support/package-info.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/support/package-info.java index fe6a259cd5..6431fd9674 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/support/package-info.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/support/package-info.java @@ -1,5 +1,5 @@ /** * Support infrastructure for the configuration of R2DBC-specific repositories. */ -@org.springframework.lang.NonNullApi +@org.jspecify.annotations.NullMarked package org.springframework.data.r2dbc.support; diff --git a/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplateUnitTests.java b/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplateUnitTests.java index a596fd0384..875bcdb321 100644 --- a/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplateUnitTests.java +++ b/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplateUnitTests.java @@ -35,6 +35,7 @@ import java.util.Collections; import java.util.List; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.ObjectFactory; @@ -66,7 +67,6 @@ import org.springframework.data.relational.core.query.Update; import org.springframework.data.relational.core.sql.SqlIdentifier; import org.springframework.data.relational.domain.RowDocument; -import org.springframework.lang.Nullable; import org.springframework.r2dbc.core.DatabaseClient; import org.springframework.r2dbc.core.Parameter; import org.springframework.util.CollectionUtils; diff --git a/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/repository/AbstractR2dbcRepositoryWithMixedCaseNamesIntegrationTests.java b/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/repository/AbstractR2dbcRepositoryWithMixedCaseNamesIntegrationTests.java index 7cc44f4dac..7f208b4dde 100644 --- a/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/repository/AbstractR2dbcRepositoryWithMixedCaseNamesIntegrationTests.java +++ b/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/repository/AbstractR2dbcRepositoryWithMixedCaseNamesIntegrationTests.java @@ -15,7 +15,19 @@ */ package org.springframework.data.r2dbc.repository; +import static org.assertj.core.api.Assertions.*; + import io.r2dbc.spi.ConnectionFactory; +import reactor.test.StepVerifier; + +import java.time.Duration; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +import javax.sql.DataSource; + +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -27,16 +39,6 @@ import org.springframework.data.relational.core.mapping.Table; import org.springframework.data.repository.reactive.ReactiveCrudRepository; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.lang.Nullable; -import reactor.test.StepVerifier; - -import javax.sql.DataSource; -import java.time.Duration; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; - -import static org.assertj.core.api.Assertions.*; /** * Abstract base class for integration tests for {@link LegoSetRepository} with table and column names that contain @@ -120,14 +122,11 @@ public static class LegoSet { @Nullable @Column("Id") - @Id - Integer id; + @Id Integer id; - @Column("Name") - String name; + @Column("Name") String name; - @Column("Manual") - Integer manual; + @Column("Manual") Integer manual; @PersistenceCreator LegoSet(@Nullable Integer id, String name, Integer manual) { @@ -136,8 +135,7 @@ public static class LegoSet { this.manual = manual; } - public LegoSet() { - } + public LegoSet() {} @Override public boolean equals(@Nullable Object o) { diff --git a/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/repository/ProjectingRepositoryIntegrationTests.java b/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/repository/ProjectingRepositoryIntegrationTests.java index 4f75bad392..546ef65277 100644 --- a/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/repository/ProjectingRepositoryIntegrationTests.java +++ b/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/repository/ProjectingRepositoryIntegrationTests.java @@ -26,6 +26,7 @@ import javax.sql.DataSource; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -45,7 +46,6 @@ import org.springframework.data.relational.core.mapping.Table; import org.springframework.data.repository.reactive.ReactiveCrudRepository; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.lang.Nullable; import org.springframework.test.context.junit.jupiter.SpringExtension; /** diff --git a/spring-data-relational/pom.xml b/spring-data-relational/pom.xml index 9c80f47d9f..ac7e28ad4b 100644 --- a/spring-data-relational/pom.xml +++ b/spring-data-relational/pom.xml @@ -1,109 +1,181 @@ - - 4.0.0 - - spring-data-relational - 4.0.0-1980-jspecify-SNAPSHOT - - Spring Data Relational - Spring Data Relational support - - - org.springframework.data - spring-data-relational-parent - 4.0.0-1980-jspecify-SNAPSHOT - - - - spring.data.relational - ${basedir}/.. - 5.0 - - - - - - ${project.groupId} - spring-data-commons - ${springdata.commons} - - - - org.springframework - spring-tx - - - - org.springframework - spring-context - - - - org.springframework - spring-beans - - - - org.springframework - spring-core - - - - com.google.code.findbugs - jsr305 - ${jsr305.version} - true - - - - org.assertj - assertj-core - ${assertj} - test - - - net.bytebuddy - byte-buddy - - - - - - com.tngtech.archunit - archunit - ${archunit.version} - test - - - - org.jetbrains.kotlin - kotlin-stdlib - true - - - - io.mockk - mockk-jvm - ${mockk} - test - - - - org.testcontainers - testcontainers - ${testcontainers} - test - - - - com.github.jsqlparser - jsqlparser - ${jsqlparser} - test - - - + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + + 4.0.0 + + spring-data-relational + 4.0.0-1980-jspecify-SNAPSHOT + + Spring Data Relational + Spring Data Relational support + + + org.springframework.data + spring-data-relational-parent + 4.0.0-1980-jspecify-SNAPSHOT + + + + spring.data.relational + ${basedir}/.. + 5.0 + + + + + + ${project.groupId} + spring-data-commons + ${springdata.commons} + + + + org.springframework + spring-tx + + + + org.springframework + spring-context + + + + org.springframework + spring-beans + + + + org.springframework + spring-core + + + + com.google.code.findbugs + jsr305 + ${jsr305.version} + true + + + + org.assertj + assertj-core + ${assertj} + test + + + net.bytebuddy + byte-buddy + + + + + + com.tngtech.archunit + archunit + ${archunit.version} + test + + + + org.jetbrains.kotlin + kotlin-stdlib + true + + + + io.mockk + mockk-jvm + ${mockk} + test + + + + org.testcontainers + testcontainers + ${testcontainers} + test + + + + com.github.jsqlparser + jsqlparser + ${jsqlparser} + test + + + + + + nullaway + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + com.querydsl + querydsl-apt + ${querydsl} + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh} + + + com.google.errorprone + error_prone_core + ${errorprone} + + + com.uber.nullaway + nullaway + ${nullaway} + + + + + + default-compile + none + + + default-testCompile + none + + + java-compile + compile + + compile + + + + -XDcompilePolicy=simple + --should-stop=ifError=FLOW + -Xplugin:ErrorProne -XepDisableAllChecks -Xep:NullAway:ERROR + -XepOpt:NullAway:OnlyNullMarked=true + -XepOpt:NullAway:TreatGeneratedAsUnannotated=true + -XepOpt:NullAway:CustomContractAnnotations=org.springframework.lang.Contract + + + + + + java-test-compile + test-compile + + testCompile + + + + + + + + diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/aot/RelationalManagedTypesBeanRegistrationAotProcessor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/aot/RelationalManagedTypesBeanRegistrationAotProcessor.java index 97ef3bfdc4..dce6c784a0 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/aot/RelationalManagedTypesBeanRegistrationAotProcessor.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/aot/RelationalManagedTypesBeanRegistrationAotProcessor.java @@ -15,9 +15,9 @@ */ package org.springframework.data.relational.aot; +import org.jspecify.annotations.Nullable; import org.springframework.data.aot.ManagedTypesBeanRegistrationAotProcessor; import org.springframework.data.relational.RelationalManagedTypes; -import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; /** diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/aot/package-info.java b/spring-data-relational/src/main/java/org/springframework/data/relational/aot/package-info.java index 32b576617e..8b67ec252d 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/aot/package-info.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/aot/package-info.java @@ -1,7 +1,7 @@ /** * Ahead of Time processing utilities for Spring Data Relational. */ -@NonNullApi +@NullMarked package org.springframework.data.relational.aot; -import org.springframework.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/EntityLifecycleEventDelegate.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/EntityLifecycleEventDelegate.java index 3d48dc123d..ef2e13703c 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/EntityLifecycleEventDelegate.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/EntityLifecycleEventDelegate.java @@ -17,8 +17,8 @@ import java.util.function.Supplier; +import org.jspecify.annotations.Nullable; import org.springframework.context.ApplicationEventPublisher; -import org.springframework.lang.Nullable; /** * Delegate class to encapsulate lifecycle event configuration and publishing. Event creation is deferred within an diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/BatchedActions.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/BatchedActions.java index a55d853bc6..7712ac6299 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/BatchedActions.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/BatchedActions.java @@ -71,7 +71,7 @@ private BatchedActions(Combiner combiner, * @param action the action to combine with other actions. */ void add(S action) { - combiner.merge(actionMap, action.getPropertyPath(), action); + combiner.merge(actionMap, action.propertyPath(), action); } void forEach(Consumer consumer) { @@ -150,9 +150,9 @@ public void merge( actionMap.merge( // propertyPath, // - new HashMap<>(singletonMap(action.getIdValueSource(), new ArrayList<>(singletonList(action)))), // + new HashMap<>(singletonMap(action.idValueSource(), new ArrayList<>(singletonList(action)))), // (map, mapDefaultValue) -> { - map.merge(action.getIdValueSource(), new ArrayList<>(singletonList(action)), + map.merge(action.idValueSource(), new ArrayList<>(singletonList(action)), (actions, listDefaultValue) -> { actions.add(action); return actions; diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DbAction.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DbAction.java index ec5ab5106a..daad66457e 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DbAction.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DbAction.java @@ -23,10 +23,10 @@ import java.util.Set; import java.util.function.Function; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.PersistentPropertyPath; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; import org.springframework.data.util.Pair; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -42,64 +42,41 @@ */ public interface DbAction { - Class getEntityType(); + Class entityType(); /** * Represents an insert statement for a single entity that is not the root of an aggregate. * * @param type of the entity for which this represents a database interaction. */ - class Insert implements WithDependingOn { - - private final T entity; - private final PersistentPropertyPath propertyPath; - private final WithEntity dependingOn; - private final IdValueSource idValueSource; - - final Map, Object> qualifiers; - - public Insert(T entity, PersistentPropertyPath propertyPath, - WithEntity dependingOn, Map, Object> qualifiers, - IdValueSource idValueSource) { - - this.entity = entity; - this.propertyPath = propertyPath; - this.dependingOn = dependingOn; - this.qualifiers = Map.copyOf(qualifiers); - this.idValueSource = idValueSource; - } - - @Override - public Class getEntityType() { - return WithDependingOn.super.getEntityType(); - } - - public T getEntity() { - return this.entity; - } - - public PersistentPropertyPath getPropertyPath() { - return this.propertyPath; - } - - public DbAction.WithEntity getDependingOn() { - return this.dependingOn; - } + record Insert(T entity, PersistentPropertyPath propertyPath, + WithEntity dependingOn, + Map, Object> qualifiers, + IdValueSource idValueSource) implements WithDependingOn { + + public Insert(T entity, PersistentPropertyPath propertyPath, + WithEntity dependingOn, Map, Object> qualifiers, + IdValueSource idValueSource) { + + this.entity = entity; + this.propertyPath = propertyPath; + this.dependingOn = dependingOn; + this.qualifiers = Map.copyOf(qualifiers); + this.idValueSource = idValueSource; + } - public Map, Object> getQualifiers() { - return this.qualifiers; - } + @Override + public Class entityType() { + return WithDependingOn.super.entityType(); + } - public IdValueSource getIdValueSource() { - return idValueSource; - } @Override - public String toString() { - return "Insert{" + "entity=" + entity + ", propertyPath=" + propertyPath + ", dependingOn=" + dependingOn - + ", idValueSource=" + idValueSource + ", qualifiers=" + qualifiers + '}'; + public String toString() { + return "Insert{" + "entity=" + entity + ", propertyPath=" + propertyPath + ", dependingOn=" + dependingOn + + ", idValueSource=" + idValueSource + ", qualifiers=" + qualifiers + '}'; + } } - } /** * Represents an insert statement for the root of an aggregate. Upon a successful insert, the initial version and @@ -118,7 +95,7 @@ public InsertRoot(T entity, IdValueSource idValueSource) { this.idValueSource = idValueSource; } - public T getEntity() { + public T entity() { return this.entity; } @@ -127,7 +104,7 @@ public void setEntity(T entity) { this.entity = entity; } - public IdValueSource getIdValueSource() { + public IdValueSource idValueSource() { return idValueSource; } @@ -153,7 +130,7 @@ public UpdateRoot(T entity, @Nullable Number previousVersion) { this.previousVersion = previousVersion; } - public T getEntity() { + public T entity() { return this.entity; } @@ -163,7 +140,7 @@ public void setEntity(T entity) { } @Override - public IdValueSource getIdValueSource() { + public IdValueSource idValueSource() { return IdValueSource.PROVIDED; } @@ -173,7 +150,7 @@ public Number getPreviousVersion() { } public String toString() { - return "DbAction.UpdateRoot(entity=" + this.getEntity() + ")"; + return "DbAction.UpdateRoot(entity=" + this.entity() + ")"; } } @@ -182,29 +159,14 @@ public String toString() { * * @param type of the entity for which this represents a database interaction. */ - final class Delete implements WithPropertyPath { - - private final Object rootId; + record Delete(Object rootId, + PersistentPropertyPath propertyPath) implements WithPropertyPath { - private final PersistentPropertyPath propertyPath; - - public Delete(Object rootId, PersistentPropertyPath propertyPath) { - this.rootId = rootId; - this.propertyPath = propertyPath; - } - - public Object getRootId() { - return this.rootId; - } - - public PersistentPropertyPath getPropertyPath() { - return this.propertyPath; - } public String toString() { - return "DbAction.Delete(rootId=" + this.getRootId() + ", propertyPath=" + this.getPropertyPath() + ")"; + return "DbAction.Delete(rootId=" + this.rootId() + ", propertyPath=" + this.propertyPath() + ")"; + } } - } /** * Represents a delete statement for a aggregate root when only the ID is known. @@ -215,38 +177,15 @@ public String toString() { * * @param type of the entity for which this represents a database interaction. */ - final class DeleteRoot implements DbAction { - - private final Object id; - private final Class entityType; - @Nullable private final Number previousVersion; - - public DeleteRoot(Object id, Class entityType, @Nullable Number previousVersion) { - - this.id = id; - this.entityType = entityType; - this.previousVersion = previousVersion; - } + record DeleteRoot(Object id, Class entityType, @Nullable Number previousVersion) implements DbAction { - public Object getId() { - return this.id; - } - - public Class getEntityType() { - return this.entityType; - } - - @Nullable - public Number getPreviousVersion() { - return this.previousVersion; - } public String toString() { - return "DbAction.DeleteRoot(id=" + this.getId() + ", entityType=" + this.getEntityType() + ", previousVersion=" - + this.getPreviousVersion() + ")"; + return "DbAction.DeleteRoot(id=" + this.id() + ", entityType=" + this.entityType() + ", previousVersion=" + + this.previousVersion() + ")"; + } } - } /** * Represents a delete statement for all entities that are reachable via a given path from any aggregate root of a @@ -254,22 +193,14 @@ public String toString() { * * @param type of the entity for which this represents a database interaction. */ - final class DeleteAll implements WithPropertyPath { + record DeleteAll( + PersistentPropertyPath propertyPath) implements WithPropertyPath { - private final PersistentPropertyPath propertyPath; - - public DeleteAll(PersistentPropertyPath propertyPath) { - this.propertyPath = propertyPath; - } - - public PersistentPropertyPath getPropertyPath() { - return this.propertyPath; - } public String toString() { - return "DbAction.DeleteAll(propertyPath=" + this.getPropertyPath() + ")"; + return "DbAction.DeleteAll(propertyPath=" + this.propertyPath() + ")"; + } } - } /** * Represents a delete statement for all aggregate roots of a given type. @@ -280,22 +211,13 @@ public String toString() { * * @param type of the entity for which this represents a database interaction. */ - final class DeleteAllRoot implements DbAction { + record DeleteAllRoot(Class entityType) implements DbAction { - private final Class entityType; - - public DeleteAllRoot(Class entityType) { - this.entityType = entityType; - } - - public Class getEntityType() { - return this.entityType; - } public String toString() { - return "DbAction.DeleteAllRoot(entityType=" + this.getEntityType() + ")"; + return "DbAction.DeleteAllRoot(entityType=" + this.entityType() + ")"; + } } - } /** * Represents an acquire lock statement for a aggregate root when only the ID is known. @@ -316,12 +238,12 @@ public Object getId() { return this.id; } - public Class getEntityType() { + public Class entityType() { return this.entityType; } public String toString() { - return "DbAction.AcquireLockRoot(id=" + this.getId() + ", entityType=" + this.getEntityType() + ")"; + return "DbAction.AcquireLockRoot(id=" + this.getId() + ", entityType=" + this.entityType() + ")"; } } @@ -338,12 +260,12 @@ final class AcquireLockAllRoot implements DbAction { this.entityType = entityType; } - public Class getEntityType() { + public Class entityType() { return this.entityType; } public String toString() { - return "DbAction.AcquireLockAllRoot(entityType=" + this.getEntityType() + ")"; + return "DbAction.AcquireLockAllRoot(entityType=" + this.entityType() + ")"; } } @@ -379,8 +301,8 @@ abstract class BatchWithValue, B> implements DbAction getEntityType() { - return actions.get(0).getEntityType(); + public Class entityType() { + return actions.get(0).entityType(); } public List getActions() { @@ -405,7 +327,7 @@ public String toString() { */ final class BatchInsert extends BatchWithValue, IdValueSource> { public BatchInsert(List> actions) { - super(actions, Insert::getIdValueSource); + super(actions, Insert::idValueSource); } } @@ -417,7 +339,7 @@ public BatchInsert(List> actions) { */ final class BatchInsertRoot extends BatchWithValue, IdValueSource> { public BatchInsertRoot(List> actions) { - super(actions, InsertRoot::getIdValueSource); + super(actions, InsertRoot::idValueSource); } } @@ -431,7 +353,7 @@ public BatchInsertRoot(List> actions) { final class BatchDelete extends BatchWithValue, PersistentPropertyPath> { public BatchDelete(List> actions) { - super(actions, Delete::getPropertyPath); + super(actions, Delete::propertyPath); } } @@ -444,7 +366,7 @@ public BatchDelete(List> actions) { final class BatchDeleteRoot extends BatchWithValue, Class> { BatchDeleteRoot(List> actions) { - super(actions, DeleteRoot::getEntityType); + super(actions, DeleteRoot::entityType); } } @@ -461,9 +383,9 @@ interface WithDependingOn extends WithPropertyPath, WithEntity { * become available once the parent entity got persisted. * * @return guaranteed to be not {@code null}. - * @see #getQualifiers() + * @see #qualifiers() */ - WithEntity getDependingOn(); + WithEntity dependingOn(); /** * Additional values to be set during insert or update statements. @@ -473,7 +395,7 @@ interface WithDependingOn extends WithPropertyPath, WithEntity { * * @return guaranteed to be not {@code null}. */ - Map, Object> getQualifiers(); + Map, Object> qualifiers(); // TODO: Encapsulate propertyPath and qualifier in object: PropertyPathWithListIndex, // PropertyPathWithMapIndex, PropertyPathInSet, PropertyPathWithoutQualifier @@ -481,7 +403,7 @@ interface WithDependingOn extends WithPropertyPath, WithEntity { @Nullable default Pair, Object> getQualifier() { - Map, Object> qualifiers = getQualifiers(); + Map, Object> qualifiers = qualifiers(); if (qualifiers.isEmpty()) { return null; @@ -501,8 +423,8 @@ default Pair, Object> getQu } @Override - default Class getEntityType() { - return WithEntity.super.getEntityType(); + default Class entityType() { + return WithEntity.super.entityType(); } } @@ -517,18 +439,18 @@ interface WithEntity extends DbAction { /** * @return the entity to persist. Guaranteed to be not {@code null}. */ - T getEntity(); + T entity(); @SuppressWarnings("unchecked") @Override - default Class getEntityType() { - return (Class) getEntity().getClass(); + default Class entityType() { + return (Class) entity().getClass(); } /** * @return the {@link IdValueSource} for the entity to persist. Guaranteed to be not {@code null}. */ - IdValueSource getIdValueSource(); + IdValueSource idValueSource(); } /** @@ -553,12 +475,12 @@ interface WithPropertyPath extends DbAction { /** * @return the path from the aggregate root to the affected entity */ - PersistentPropertyPath getPropertyPath(); + PersistentPropertyPath propertyPath(); @SuppressWarnings("unchecked") @Override - default Class getEntityType() { - return (Class) getPropertyPath().getLeafProperty().getActualType(); + default Class entityType() { + return (Class) propertyPath().getLeafProperty().getActualType(); } } } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DbActionExecutionResult.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DbActionExecutionResult.java index e80465cf6d..7b99e6fb88 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DbActionExecutionResult.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DbActionExecutionResult.java @@ -15,7 +15,7 @@ */ package org.springframework.data.relational.core.conversion; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * @author Jens Schauder @@ -24,7 +24,7 @@ */ public class DbActionExecutionResult { - private final Object generatedId; + private final @Nullable Object generatedId; private final DbAction.WithEntity action; public DbActionExecutionResult(DbAction.WithEntity action) { @@ -39,6 +39,11 @@ public DbActionExecutionResult(DbAction.WithEntity action, @Nullable Object g this.generatedId = generatedId; } + /** + * @deprecated Use one of the other constructors. + */ + @SuppressWarnings("NullAway") + @Deprecated(since = "4.0", forRemoval = true) public DbActionExecutionResult() { action = null; diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DefaultRootAggregateChange.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DefaultRootAggregateChange.java index 056bede333..dbdca7fb93 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DefaultRootAggregateChange.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DefaultRootAggregateChange.java @@ -19,7 +19,7 @@ import java.util.List; import java.util.function.Consumer; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.Assert; /** @@ -37,7 +37,7 @@ class DefaultRootAggregateChange implements RootAggregateChange { private final List> actions = new ArrayList<>(); - private DbAction.WithRoot rootAction; + private DbAction.@Nullable WithRoot rootAction; /** The previous version assigned to the instance being changed, if available */ @Nullable private final Number previousVersion; @@ -77,6 +77,7 @@ public void setRoot(T aggregateRoot) { Assert.isInstanceOf(this.entityType, aggregateRoot, String.format("AggregateRoot must be of type %s", entityType.getName())); + Assert.state(rootAction != null, "rootAction must not be null"); rootAction.setEntity(aggregateRoot); } @@ -97,7 +98,10 @@ public Number getPreviousVersion() { @Override public T getRoot() { - return this.rootAction.getEntity(); + + Assert.state(rootAction != null, "rootAction must not be null"); + + return this.rootAction.entity(); } @Override diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DeleteAggregateChange.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DeleteAggregateChange.java index e600291813..5596e739b6 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DeleteAggregateChange.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DeleteAggregateChange.java @@ -19,7 +19,7 @@ import java.util.List; import java.util.function.Consumer; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.Assert; /** diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DeleteBatchingAggregateChange.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DeleteBatchingAggregateChange.java index b916dfe9d7..7a27f10fa0 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DeleteBatchingAggregateChange.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DeleteBatchingAggregateChange.java @@ -65,7 +65,7 @@ public void add(DeleteAggregateChange aggregateChange) { private void addDeleteRoot(DbAction.DeleteRoot action) { - if (action.getPreviousVersion() == null) { + if (action.previousVersion() == null) { rootActionsWithoutVersion.add(action); } else { rootActionsWithVersion.add(action); diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DocumentPropertyAccessor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DocumentPropertyAccessor.java index 6075fa9a10..4e5b2814d9 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DocumentPropertyAccessor.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DocumentPropertyAccessor.java @@ -17,11 +17,11 @@ import java.util.Map; +import org.jspecify.annotations.Nullable; import org.springframework.context.expression.MapAccessor; import org.springframework.data.relational.domain.RowDocument; import org.springframework.expression.EvaluationContext; import org.springframework.expression.TypedValue; -import org.springframework.lang.Nullable; /** * {@link org.springframework.expression.PropertyAccessor} to allow entity based field access to diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java index 63f3bf5f8f..bbb9e28132 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java @@ -25,6 +25,7 @@ import java.util.function.Function; import java.util.function.Predicate; +import org.jspecify.annotations.Nullable; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -44,16 +45,7 @@ import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.mapping.PersistentPropertyPathAccessor; import org.springframework.data.mapping.context.MappingContext; -import org.springframework.data.mapping.model.CachingValueExpressionEvaluatorFactory; -import org.springframework.data.mapping.model.ConvertingPropertyAccessor; -import org.springframework.data.mapping.model.EntityInstantiator; -import org.springframework.data.mapping.model.ParameterValueProvider; -import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider; -import org.springframework.data.mapping.model.PropertyValueProvider; -import org.springframework.data.mapping.model.SimpleTypeHolder; -import org.springframework.data.mapping.model.SpELContext; -import org.springframework.data.mapping.model.ValueExpressionEvaluator; -import org.springframework.data.mapping.model.ValueExpressionParameterValueProvider; +import org.springframework.data.mapping.model.*; import org.springframework.data.projection.EntityProjection; import org.springframework.data.projection.EntityProjectionIntrospector; import org.springframework.data.projection.EntityProjectionIntrospector.ProjectionPredicate; @@ -72,7 +64,6 @@ import org.springframework.data.util.TypeInformation; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; @@ -152,7 +143,11 @@ public void setApplicationContext(ApplicationContext applicationContext) throws this.spELContext = new SpELContext(this.spELContext, applicationContext); this.environment = applicationContext.getEnvironment(); this.projectionFactory.setBeanFactory(applicationContext); - this.projectionFactory.setBeanClassLoader(applicationContext.getClassLoader()); + ClassLoader classLoader = applicationContext.getClassLoader(); + + Assert.notNull(classLoader, "ClassLoader must not be null"); + + this.projectionFactory.setBeanClassLoader(classLoader); } @Override @@ -269,7 +264,7 @@ String getColumnName(RelationalPersistentProperty prop) { return populateProperties(context, mappedEntity, documentAccessor, evaluator, instance); } - private Object doReadOrProject(ConversionContext context, RowDocument source, TypeInformation typeHint, + private @Nullable Object doReadOrProject(ConversionContext context, RowDocument source, TypeInformation typeHint, EntityProjection typeDescriptor) { if (typeDescriptor.isProjection()) { @@ -284,12 +279,12 @@ static class MapPersistentPropertyAccessor implements PersistentPropertyAccessor Map map = new LinkedHashMap<>(); @Override - public void setProperty(PersistentProperty persistentProperty, Object o) { + public void setProperty(PersistentProperty persistentProperty, @Nullable Object o) { map.put(persistentProperty.getName(), o); } @Override - public Object getProperty(PersistentProperty persistentProperty) { + public @Nullable Object getProperty(PersistentProperty persistentProperty) { return map.get(persistentProperty.getName()); } @@ -344,7 +339,12 @@ protected S readAggregate(ConversionContext context, RowDocumentAccessor doc Class rawType = typeHint.getType(); if (getConversions().hasCustomReadTarget(RowDocument.class, rawType)) { - return doConvert(documentAccessor.getDocument(), rawType, typeHint.getType()); + + S converted = doConvert(documentAccessor.getDocument(), rawType, typeHint.getType()); + + Assert.state(converted != null, "Converted must not be null"); + + return converted; } if (RowDocument.class.isAssignableFrom(rawType)) { @@ -389,7 +389,7 @@ protected Map readMap(ConversionContext context, Map sourc Class rawKeyType = keyType != null ? keyType.getType() : Object.class; Map map = CollectionFactory.createMap(mapType, rawKeyType, - ((Map) source).keySet().size()); + ((Map) source).size()); source.forEach((k, v) -> { @@ -433,7 +433,12 @@ protected Object readCollectionOrArray(ConversionContext context, Collection : CollectionFactory.createCollection(collectionType, rawComponentType, source.size()); if (source.isEmpty()) { - return getPotentiallyConvertedSimpleRead(items, targetType); + + Object converted = getPotentiallyConvertedSimpleRead(items, targetType); + + Assert.state(converted != null, "Converted must not be null"); + + return converted; } for (Object element : source) { @@ -443,12 +448,11 @@ protected Object readCollectionOrArray(ConversionContext context, Collection return getPotentiallyConvertedSimpleRead(items, targetType); } - private T doConvert(Object value, Class target) { + private @Nullable T doConvert(Object value, Class target) { return doConvert(value, target, null); } - @SuppressWarnings("ConstantConditions") - private T doConvert(Object value, Class target, @Nullable Class fallback) { + private @Nullable T doConvert(Object value, Class target, @Nullable Class fallback) { if (getConversionService().canConvert(value.getClass(), target) || fallback == null) { return getConversionService().convert(value, target); @@ -654,7 +658,12 @@ protected Object getPotentiallyConvertedSimpleRead(Object value, TypeInformation Class target = type.getType(); if (getConversions().hasCustomReadTarget(value.getClass(), target)) { - return getConversionService().convert(value, TypeDescriptor.forObject(value), createTypeDescriptor(type)); + Object converted = getConversionService().convert(value, TypeDescriptor.forObject(value), + createTypeDescriptor(type)); + + Assert.state(converted != null, "Converted must not be null"); + + return converted; } if (ClassUtils.isAssignableValue(target, value)) { @@ -665,7 +674,12 @@ protected Object getPotentiallyConvertedSimpleRead(Object value, TypeInformation return Enum.valueOf((Class) target, value.toString()); } - return getConversionService().convert(value, TypeDescriptor.forObject(value), createTypeDescriptor(type)); + Object converted = getConversionService().convert(value, TypeDescriptor.forObject(value), + createTypeDescriptor(type)); + + Assert.state(converted != null, "Converted must not be null"); + + return converted; } private static TypeDescriptor createTypeDescriptor(TypeInformation type) { @@ -794,7 +808,11 @@ private Object writeCollection(Iterable value, TypeInformation type) { targetType = Array.newInstance(targetComponentType, 0).getClass(); } - return getConversionService().convert(mapped, targetType); + Object converted = getConversionService().convert(mapped, targetType); + + Assert.state(converted != null, "Converted must not be null"); + + return converted; } /** @@ -947,8 +965,14 @@ protected ProjectingConversionContext(RelationalConverter sourceConverter, Custo ObjectPath path, ContainerValueConverter> collectionConverter, ContainerValueConverter> mapConverter, ValueConverter elementConverter, EntityProjection projection) { - super(sourceConverter, customConversions, path, - (context, source, typeHint) -> doReadOrProject(context, source, typeHint, projection), + super(sourceConverter, customConversions, path, (context, source, typeHint) -> { + + Object result = doReadOrProject(context, source, typeHint, projection); + + Assert.state(result != null, "Result must not be null"); + + return result; + }, collectionConverter, mapConverter, elementConverter); this.returnedTypeDescriptor = projection; @@ -1053,7 +1077,7 @@ enum NoOpParameterValueProvider implements ParameterValueProvider T getParameterValue(Parameter parameter) { + public @Nullable T getParameterValue(Parameter parameter) { return null; } } @@ -1194,10 +1218,6 @@ public Object getValue(AggregatePath path) { Object value = document.get(path.getColumnInfo().alias().getReference()); - if (value == null) { - return null; - } - return value; } @@ -1226,6 +1246,8 @@ public boolean hasNonEmptyValue(AggregatePath path) { Object value = document.get(path.getColumnInfo().alias().getReference()); + Assert.state(value != null, "Value must not be null"); + if (value instanceof Collection || value.getClass().isArray()) { return !ObjectUtils.isEmpty(value); } @@ -1264,7 +1286,7 @@ class ConvertingParameterValueProvider

> implemen @Override @SuppressWarnings("unchecked") - public T getParameterValue(Parameter parameter) { + public @Nullable T getParameterValue(Parameter parameter) { return (T) readValue(delegate.apply(parameter), parameter.getType()); } } @@ -1318,7 +1340,7 @@ public void setProperty(PersistentProperty property, @Nullable Object value) } @Override - public Object getProperty(PersistentProperty property) { + public @Nullable Object getProperty(PersistentProperty property) { return delegate.getProperty(translate(property)); } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MutableAggregateChange.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MutableAggregateChange.java index b872e0dbf6..cc640b928c 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MutableAggregateChange.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MutableAggregateChange.java @@ -15,7 +15,7 @@ */ package org.springframework.data.relational.core.conversion; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/ObjectPath.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/ObjectPath.java index 90c0ae68c6..b387df8be6 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/ObjectPath.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/ObjectPath.java @@ -18,8 +18,8 @@ import java.util.ArrayList; import java.util.List; +import org.jspecify.annotations.Nullable; import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/PathNode.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/PathNode.java index 321d6ab73f..d06fd7b949 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/PathNode.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/PathNode.java @@ -15,33 +15,24 @@ */ package org.springframework.data.relational.core.conversion; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.PersistentPropertyPath; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; import org.springframework.data.util.Pair; -import org.springframework.lang.Nullable; + +import java.util.List; +import java.util.Map; /** * Represents a single entity in an aggregate along with its property path from the root entity and the chain of objects * to traverse a long this path. * + * @param path The path to this entity + * @param parent The parent {@link PathNode}. This is {@code null} if this is the root entity. + * @param value The value of the entity. * @author Jens Schauder */ -final class PathNode { - - /** - * The path to this entity - */ - private final PersistentPropertyPath path; - - /** - * The parent {@link PathNode}. This is {@code null} if this is the root entity. - */ - @Nullable private final PathNode parent; - - /** - * The value of the entity. - */ - private final Object value; +record PathNode(PersistentPropertyPath path, @Nullable PathNode parent, Object value) { PathNode(PersistentPropertyPath path, @Nullable PathNode parent, Object value) { @@ -51,29 +42,17 @@ final class PathNode { } /** - * If the node represents a qualified property (i.e. a {@link java.util.List} or {@link java.util.Map}) the actual + * If the node represents a qualified property (i.e. a {@link List} or {@link Map}) the actual * value is an element of the {@literal List} or a value of the {@literal Map}, while the {@link #value} is actually a * {@link Pair} with the index or key as the first element and the actual value as second element. */ Object getActualValue() { - return getPath().getLeafProperty().isQualified() // - ? ((Pair) getValue()).getSecond() // - : getValue(); - } - - public PersistentPropertyPath getPath() { - return this.path; + return path().getLeafProperty().isQualified() // + ? ((Pair) value()).getSecond() // + : value(); } - @Nullable - public PathNode getParent() { - return this.parent; - } - - public Object getValue() { - return this.value; - } @Override public String toString() { diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/RelationalConverter.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/RelationalConverter.java index 2a68dcfc66..ab196d77e1 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/RelationalConverter.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/RelationalConverter.java @@ -15,6 +15,7 @@ */ package org.springframework.data.relational.core.conversion; +import org.jspecify.annotations.Nullable; import org.springframework.core.convert.ConversionService; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentPropertyAccessor; @@ -27,7 +28,6 @@ import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; import org.springframework.data.relational.domain.RowDocument; import org.springframework.data.util.TypeInformation; -import org.springframework.lang.Nullable; /** * A {@link RelationalConverter} is responsible for converting for values to the native relational representation and @@ -101,7 +101,7 @@ public interface RelationalConverter { * @param aggregate type. * @since 3.2 */ - R read(Class type, RowDocument source); + @Nullable R read(Class type, RowDocument source); /** * Read a relational value into the desired {@link TypeInformation destination type}. diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/RelationalEntityDeleteWriter.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/RelationalEntityDeleteWriter.java index cc706f7cb5..d5ad10cd75 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/RelationalEntityDeleteWriter.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/RelationalEntityDeleteWriter.java @@ -20,12 +20,12 @@ import java.util.List; import java.util.function.Consumer; +import org.jspecify.annotations.Nullable; import org.springframework.data.convert.EntityWriter; import org.springframework.data.mapping.PersistentPropertyPath; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; import org.springframework.data.relational.core.mapping.RelationalPredicates; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/RelationalEntityVersionUtils.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/RelationalEntityVersionUtils.java index 00ea3657aa..63fdcac0f5 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/RelationalEntityVersionUtils.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/RelationalEntityVersionUtils.java @@ -15,11 +15,11 @@ */ package org.springframework.data.relational.core.conversion; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.mapping.model.ConvertingPropertyAccessor; import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; -import org.springframework.lang.Nullable; /** * Utilities commonly used to set/get properties for instances of RelationalPersistentEntities. diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/RowDocumentAccessor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/RowDocumentAccessor.java index 0945cdfd5c..7194012522 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/RowDocumentAccessor.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/RowDocumentAccessor.java @@ -17,9 +17,9 @@ import java.util.Objects; +import org.jspecify.annotations.Nullable; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; import org.springframework.data.relational.domain.RowDocument; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/SaveBatchingAggregateChange.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/SaveBatchingAggregateChange.java index 0e256a0ab3..10ffbde0d4 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/SaveBatchingAggregateChange.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/SaveBatchingAggregateChange.java @@ -83,7 +83,7 @@ public void add(RootAggregateChange aggregateChange) { } else if (action instanceof DbAction.InsertRoot rootAction) { if (!insertRootBatchCandidates.isEmpty() - && !insertRootBatchCandidates.get(0).getIdValueSource().equals(rootAction.getIdValueSource())) { + && !insertRootBatchCandidates.get(0).idValueSource().equals(rootAction.idValueSource())) { combineBatchCandidatesIntoSingleBatchRootAction(); } // noinspection unchecked diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/WritingContext.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/WritingContext.java index 6602e72841..6e41093044 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/WritingContext.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/WritingContext.java @@ -23,13 +23,13 @@ import java.util.List; import java.util.Map; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.PersistentPropertyPath; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; import org.springframework.data.relational.core.mapping.RelationalPredicates; import org.springframework.data.util.Pair; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -122,28 +122,31 @@ private List> insertReferenced() { @SuppressWarnings("unchecked") private List> insertAll(PersistentPropertyPath path) { - RelationalPersistentEntity persistentEntity = context - .getRequiredPersistentEntity(path.getLeafProperty()); + RelationalPersistentEntity persistentEntity = context.getRequiredPersistentEntity(path.getLeafProperty()); List> inserts = new ArrayList<>(); from(path).forEach(node -> { - DbAction.WithEntity parentAction = getAction(node.getParent()); + DbAction.WithEntity parentAction = getAction(node.parent()); + + Assert.state(parentAction != null, "parentAction must not be null"); + Map, Object> qualifiers = new HashMap<>(); Object instance; - if (node.getPath().getLeafProperty().isQualified()) { + if (node.path().getLeafProperty().isQualified()) { - Pair value = (Pair) node.getValue(); - qualifiers.put(node.getPath(), value.getFirst()); + Pair value = (Pair) node.value(); + qualifiers.put(node.path(), value.getFirst()); - RelationalPersistentEntity parentEntity = context.getRequiredPersistentEntity(parentAction.getEntityType()); + RelationalPersistentEntity parentEntity = context.getRequiredPersistentEntity(parentAction.entityType()); if (!parentEntity.hasIdProperty() && parentAction instanceof DbAction.Insert) { - qualifiers.putAll(((DbAction.Insert) parentAction).getQualifiers()); + qualifiers.putAll(((DbAction.Insert) parentAction).qualifiers()); } instance = value.getSecond(); } else { - instance = node.getValue(); + instance = node.value(); } + IdValueSource idValueSource = IdValueSource.forInstance(instance, persistentEntity); DbAction.Insert insert = new DbAction.Insert<>(instance, path, parentAction, qualifiers, idValueSource); inserts.add(insert); @@ -166,6 +169,8 @@ private DbAction.Delete deleteReferenced(PersistentPropertyPath(id, path); } @@ -176,8 +181,7 @@ private void setRootAction(DbAction.WithRoot dbAction) { previousActions.put(null, dbAction); } - @Nullable - private DbAction.WithEntity getAction(@Nullable PathNode parent) { + private DbAction.@Nullable WithEntity getAction(@Nullable PathNode parent) { DbAction action = previousActions.get(parent); diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/package-info.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/package-info.java index a54e73fa4f..11e4e5dc81 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/package-info.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/package-info.java @@ -1,4 +1,4 @@ -@NonNullApi +@NullMarked package org.springframework.data.relational.core.conversion; -import org.springframework.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Escaper.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Escaper.java index e8f863189e..931db6c675 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Escaper.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Escaper.java @@ -19,7 +19,7 @@ import java.util.Arrays; import java.util.List; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * Helper class encapsulating an escape character for LIKE queries and the actually usage of it in escaping diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/package-info.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/package-info.java index 00c26658f7..866051986a 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/package-info.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/package-info.java @@ -1,7 +1,7 @@ /** * Dialects abstract the SQL dialect of the underlying database. */ -@NonNullApi +@NullMarked package org.springframework.data.relational.core.dialect; -import org.springframework.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/AggregatePath.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/AggregatePath.java index f5befcb930..5df80219ff 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/AggregatePath.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/AggregatePath.java @@ -29,13 +29,13 @@ import java.util.stream.Stream; import java.util.stream.StreamSupport; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.PersistentPropertyPath; import org.springframework.data.mapping.PropertyHandler; import org.springframework.data.relational.core.sql.Column; import org.springframework.data.relational.core.sql.SqlIdentifier; import org.springframework.data.relational.core.sql.Table; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -342,6 +342,13 @@ static TableInfo of(AggregatePath path) { } + public ColumnInfo requiredQualifierColumnInfo() { + + Assert.notNull(qualifierColumnInfo, "ColumnInfo for qualifier columns must not be null"); + + return qualifierColumnInfo; + } + private static ColumnInfos computeIdColumnInfos(AggregatePath tableOwner, RelationalPersistentEntity leafEntity) { @@ -443,6 +450,15 @@ public ColumnInfo reverseColumnInfo() { public ColumnInfos effectiveIdColumnInfos() { return backReferenceColumnInfos.isEmpty() ? idColumnInfos : backReferenceColumnInfos; } + + public Class requiredQualifierColumnType() { + + Class type = qualifierColumnType(); + + Assert.notNull(type, "ColumnInfo for qualifier columns must not be null"); + + return type; + } } /** @@ -640,7 +656,7 @@ public T any(BiFunction mapper) { * @param path for which to return the {@literal ColumnInfo} * @return {@literal ColumnInfo} for the given path. */ - public ColumnInfo get(AggregatePath path) { + public @Nullable ColumnInfo get(AggregatePath path) { return columnInfos.get(path); } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentEntity.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentEntity.java index 93a6d79199..16de527310 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentEntity.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentEntity.java @@ -17,6 +17,7 @@ import java.util.Optional; +import org.jspecify.annotations.Nullable; import org.springframework.data.expression.ValueExpression; import org.springframework.data.expression.ValueExpressionParser; import org.springframework.data.mapping.model.BasicPersistentEntity; @@ -25,7 +26,6 @@ import org.springframework.data.util.TypeInformation; import org.springframework.expression.Expression; import org.springframework.expression.common.LiteralExpression; -import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; /** @@ -58,7 +58,7 @@ class BasicRelationalPersistentEntity extends BasicPersistentEntity information, NamingStrategy namingStrategy, - SqlIdentifierExpressionEvaluator sqlIdentifierExpressionEvaluator) { + SqlIdentifierExpressionEvaluator sqlIdentifierExpressionEvaluator) { super(information); diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentProperty.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentProperty.java index b6824fa0e0..da48e9f7bc 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentProperty.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentProperty.java @@ -18,6 +18,7 @@ import java.util.Optional; import java.util.Set; +import org.jspecify.annotations.Nullable; import org.springframework.data.expression.ValueExpression; import org.springframework.data.expression.ValueExpressionParser; import org.springframework.data.mapping.Association; @@ -31,7 +32,6 @@ import org.springframework.data.util.Lazy; import org.springframework.expression.Expression; import org.springframework.expression.common.LiteralExpression; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -54,7 +54,7 @@ public class BasicRelationalPersistentProperty extends AnnotationBasedPersistent private final Lazy columnName; private final boolean hasExplicitColumnName; private final @Nullable ValueExpression columnNameExpression; - private final SqlIdentifier sequence; + private final @Nullable SqlIdentifier sequence; private final Lazy> collectionIdColumnName; private final @Nullable ValueExpression collectionIdColumnNameExpression; private final Lazy collectionKeyColumnName; @@ -65,8 +65,8 @@ public class BasicRelationalPersistentProperty extends AnnotationBasedPersistent private final NamingStrategy namingStrategy; private boolean forceQuote = true; - private SqlIdentifierExpressionEvaluator sqlIdentifierExpressionEvaluator = - new SqlIdentifierExpressionEvaluator(EvaluationContextProvider.DEFAULT); + private SqlIdentifierExpressionEvaluator sqlIdentifierExpressionEvaluator = new SqlIdentifierExpressionEvaluator( + EvaluationContextProvider.DEFAULT); /** * Creates a new {@link BasicRelationalPersistentProperty}. @@ -78,7 +78,7 @@ public class BasicRelationalPersistentProperty extends AnnotationBasedPersistent * @since 2.0 */ public BasicRelationalPersistentProperty(Property property, PersistentEntity owner, - SimpleTypeHolder simpleTypeHolder, NamingStrategy namingStrategy) { + SimpleTypeHolder simpleTypeHolder, NamingStrategy namingStrategy) { super(property, owner, simpleTypeHolder); this.namingStrategy = namingStrategy; @@ -224,6 +224,7 @@ public SqlIdentifier getReverseColumnName(RelationalPersistentEntity owner) { } @Override + @Nullable public SqlIdentifier getKeyColumn() { if (!isQualified()) { @@ -267,7 +268,7 @@ public boolean isEmbedded() { @Override public String getEmbeddedPrefix() { - return isEmbedded() ? embeddedPrefix : null; + return isEmbedded() ? embeddedPrefix : ""; } @Override diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/DefaultAggregatePath.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/DefaultAggregatePath.java index 42f25a2475..7a54d1336d 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/DefaultAggregatePath.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/DefaultAggregatePath.java @@ -19,9 +19,10 @@ import java.util.NoSuchElementException; import java.util.Objects; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.PersistentPropertyPath; import org.springframework.data.util.Lazy; -import org.springframework.lang.Nullable; +import org.springframework.data.util.TypeInformation; import org.springframework.util.Assert; import org.springframework.util.ConcurrentLruCache; @@ -89,7 +90,11 @@ public AggregatePath getParentPath() { return context.getAggregatePath(path.getLeafProperty().getOwner()); } - return context.getAggregatePath(path.getParentPath()); + PersistentPropertyPath parentPath = path.getParentPath(); + + Assert.state(parentPath != null, "Parent path must not be null"); + + return context.getAggregatePath(parentPath); } @Override @@ -113,10 +118,20 @@ public AggregatePath append(AggregatePath path) { private AggregatePath doGetAggegatePath(RelationalPersistentProperty property) { - PersistentPropertyPath newPath = isRoot() // - ? context.getPersistentPropertyPath(property.getName(), rootType.getTypeInformation()) // - : context.getPersistentPropertyPath(path.toDotPath() + "." + property.getName(), - path.getBaseProperty().getOwner().getTypeInformation()); + PersistentPropertyPath newPath; + + if (isRoot()) { + + Assert.state(rootType != null, "Root type must not be null"); + + newPath = context.getPersistentPropertyPath(property.getName(), rootType.getTypeInformation()); + } else { + + Assert.state(path != null, "Path must not be null"); + + newPath = context.getPersistentPropertyPath(path.toDotPath() + "." + property.getName(), + path.getBaseProperty().getOwner().getTypeInformation()); + } return context.getAggregatePath(newPath); } @@ -182,7 +197,13 @@ public boolean hasIdProperty() { @Override public RelationalPersistentProperty getRequiredIdProperty() { - return isRoot() ? rootType.getRequiredIdProperty() : getRequiredLeafEntity().getRequiredIdProperty(); + if (isRoot()) { + + Assert.state(rootType != null, "Root type must not be null"); + + return rootType.getRequiredIdProperty(); + } + return getRequiredLeafEntity().getRequiredIdProperty(); } @Override @@ -193,9 +214,16 @@ public PersistentPropertyPath getRequiredPersisten } @Override - public RelationalPersistentEntity getLeafEntity() { - return isRoot() ? rootType - : context.getPersistentEntity(getRequiredLeafProperty().getTypeInformation().getActualType()); + public @Nullable RelationalPersistentEntity getLeafEntity() { + if (isRoot()) { + return rootType; + } + + TypeInformation actualType = getRequiredLeafProperty().getTypeInformation().getActualType(); + + Assert.state(actualType != null, "Actual type must not be null"); + + return context.getPersistentEntity(actualType); } @Override @@ -216,10 +244,16 @@ public AggregatePath getTail() { return null; } + Assert.state(this.path != null, "Path must not be null"); + AggregatePath tail = null; for (RelationalPersistentProperty prop : this.path) { if (tail == null) { - tail = context.getAggregatePath(context.getPersistentEntity(prop)); + RelationalPersistentEntity entity = context.getPersistentEntity(prop); + + Assert.state(entity != null, "Entity must not be null"); + + tail = context.getAggregatePath(entity); } else { tail = tail.append(prop); } @@ -254,6 +288,9 @@ public AggregatePath subtract(@Nullable AggregatePath basePath) { public AggregatePath getSubPathBasedOn(Class baseType) { if (isRoot()) { + + Assert.state(rootType != null, "Root type must not be null"); + if (rootType.getType() != baseType) { throw new IllegalStateException("No matching path found for [%s]".formatted(baseType)); } @@ -264,8 +301,12 @@ public AggregatePath getSubPathBasedOn(Class baseType) { if (owner.getType() == baseType) { return this; } - - return getTail().getSubPathBasedOn(baseType); + + AggregatePath tail = getTail(); + + Assert.state(tail != null, "Tail must not be null"); + + return tail.getSubPathBasedOn(baseType); } /** @@ -306,9 +347,27 @@ public int hashCode() { @Override public String toString() { - return "AggregatePath[" - + (rootType == null ? path.getBaseProperty().getOwner().getType().getName() : rootType.getName()) + "]" - + ((isRoot()) ? "/" : path.toDotPath()); + String typeName; + if (rootType == null) { + + Assert.state(path != null, "Path must not be null"); + + typeName = path.getBaseProperty().getOwner().getType().getName(); + } else { + typeName = rootType.getName(); + } + + String pathPart; + if (isRoot()) { + pathPart = "/"; + } else { + + Assert.state(path != null, "Path must not be null"); + + pathPart = path.toDotPath(); + } + + return "AggregatePath[" + typeName + "]" + pathPart; } private static class AggregatePathIterator implements Iterator { diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/DerivedSqlIdentifier.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/DerivedSqlIdentifier.java index c3bd07e1dd..3d0f83b41d 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/DerivedSqlIdentifier.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/DerivedSqlIdentifier.java @@ -19,9 +19,9 @@ import java.util.Iterator; import java.util.function.UnaryOperator; +import org.jspecify.annotations.Nullable; import org.springframework.data.relational.core.sql.IdentifierProcessing; import org.springframework.data.relational.core.sql.SqlIdentifier; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/EmbeddedContext.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/EmbeddedContext.java index 984d94cf5e..d3157a2cb0 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/EmbeddedContext.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/EmbeddedContext.java @@ -23,10 +23,6 @@ */ record EmbeddedContext(RelationalPersistentProperty ownerProperty) { - public String getEmbeddedPrefix() { - return ownerProperty.getEmbeddedPrefix(); - } - public String withEmbeddedPrefix(String name) { if (!ownerProperty.isEmbedded()) { diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/EmbeddedRelationalPersistentEntity.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/EmbeddedRelationalPersistentEntity.java index 6a3befcdf8..28aadc2ec6 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/EmbeddedRelationalPersistentEntity.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/EmbeddedRelationalPersistentEntity.java @@ -18,6 +18,7 @@ import java.lang.annotation.Annotation; import java.util.Iterator; +import org.jspecify.annotations.Nullable; import org.springframework.core.env.Environment; import org.springframework.data.mapping.*; import org.springframework.data.mapping.model.PersistentPropertyAccessorFactory; @@ -25,7 +26,8 @@ import org.springframework.data.spel.EvaluationContextProvider; import org.springframework.data.util.Streamable; import org.springframework.data.util.TypeInformation; -import org.springframework.lang.Nullable; +import org.springframework.lang.Contract; +import org.springframework.util.Assert; /** * Embedded entity extension for a {@link Embedded entity}. @@ -82,7 +84,13 @@ public boolean hasNext() { @Override public RelationalPersistentProperty next() { - return wrap(iterator.next()); + + RelationalPersistentProperty property = wrap(iterator.next()); + + // NullAway doesn't understand contracts + Assert.state(property != null, "Property must not be null"); + + return property; } }; } @@ -178,28 +186,50 @@ public TypeInformation getTypeInformation() { @Override public void doWithProperties(PropertyHandler handler) { + delegate.doWithProperties((PropertyHandler) persistentProperty -> { - handler.doWithPersistentProperty(wrap(persistentProperty)); + + RelationalPersistentProperty wrapped = wrap(persistentProperty); + + Assert.state(wrapped != null, "Property must not be null"); + + handler.doWithPersistentProperty(wrapped); }); } @Override public void doWithProperties(SimplePropertyHandler handler) { - delegate.doWithProperties((SimplePropertyHandler) property -> handler - .doWithPersistentProperty(wrap((RelationalPersistentProperty) property))); + delegate.doWithProperties((SimplePropertyHandler) property -> { + + RelationalPersistentProperty wrapped = wrap((RelationalPersistentProperty) property); + + Assert.state(wrapped != null, "Property must not be null"); + + handler.doWithPersistentProperty(wrapped); + }); } @Override public void doWithAssociations(AssociationHandler handler) { delegate.doWithAssociations((AssociationHandler) association -> { - handler.doWithAssociation(new Association<>(wrap(association.getInverse()), wrap(association.getObverse()))); + + RelationalPersistentProperty wrapped = wrap(association.getInverse()); + + Assert.state(wrapped != null, "Association must not be null"); + + handler.doWithAssociation(new Association<>(wrapped, wrap(association.getObverse()))); }); } @Override public void doWithAssociations(SimpleAssociationHandler handler) { delegate.doWithAssociations((AssociationHandler) association -> { - handler.doWithAssociation(new Association<>(wrap(association.getInverse()), wrap(association.getObverse()))); + + RelationalPersistentProperty wrapped = wrap(association.getInverse()); + + Assert.state(wrapped != null, "Association must not be null"); + + handler.doWithAssociation(new Association<>(wrapped, wrap(association.getObverse()))); }); } @@ -245,6 +275,7 @@ public boolean requiresPropertyPopulation() { } @Nullable + @Contract("null -> null; !null -> new") private RelationalPersistentProperty wrap(@Nullable RelationalPersistentProperty source) { if (source == null) { diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/EmbeddedRelationalPersistentProperty.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/EmbeddedRelationalPersistentProperty.java index bcfc2ddb5e..dffd811143 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/EmbeddedRelationalPersistentProperty.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/EmbeddedRelationalPersistentProperty.java @@ -19,10 +19,10 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.Association; import org.springframework.data.relational.core.sql.SqlIdentifier; import org.springframework.data.util.TypeInformation; -import org.springframework.lang.Nullable; import org.springframework.util.ObjectUtils; /** @@ -48,7 +48,6 @@ public boolean isEmbedded() { return delegate.isEmbedded(); } - @Nullable @Override public String getEmbeddedPrefix() { return context.withEmbeddedPrefix(delegate.getEmbeddedPrefix()); diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/PersistentPropertyTranslator.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/PersistentPropertyTranslator.java index b3da570085..08ed1fa20b 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/PersistentPropertyTranslator.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/PersistentPropertyTranslator.java @@ -17,8 +17,8 @@ import java.util.function.Predicate; +import org.jspecify.annotations.Nullable; import org.springframework.data.util.Predicates; -import org.springframework.lang.Nullable; /** * Utility to translate a {@link RelationalPersistentProperty} into a corresponding property from a different diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalMappingContext.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalMappingContext.java index 88176a16f1..e7de7468d1 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalMappingContext.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalMappingContext.java @@ -18,6 +18,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.jspecify.annotations.Nullable; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.core.env.Environment; @@ -29,7 +30,6 @@ import org.springframework.data.spel.EvaluationContextProvider; import org.springframework.data.spel.ExtensionAwareEvaluationContextProvider; import org.springframework.data.util.TypeInformation; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -49,8 +49,8 @@ public class RelationalMappingContext private boolean forceQuote = true; - private final SqlIdentifierExpressionEvaluator sqlIdentifierExpressionEvaluator = - new SqlIdentifierExpressionEvaluator(EvaluationContextProvider.DEFAULT); + private final SqlIdentifierExpressionEvaluator sqlIdentifierExpressionEvaluator = new SqlIdentifierExpressionEvaluator( + EvaluationContextProvider.DEFAULT); private boolean singleQueryLoadingEnabled = false; /** diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentProperty.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentProperty.java index 6d05df9239..d75c8d979d 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentProperty.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentProperty.java @@ -15,9 +15,9 @@ */ package org.springframework.data.relational.core.mapping; +import org.jspecify.annotations.Nullable; import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.relational.core.sql.SqlIdentifier; -import org.springframework.lang.Nullable; /** * A {@link PersistentProperty} with methods for additional RDBMS related metadata based on columns. @@ -77,11 +77,10 @@ default boolean isEmbedded() { } /** - * @return Prefix for embedded columns. If the column is not embedded the return value is null. + * @return Prefix for embedded columns. If the column is not embedded the return value is empty. */ - @Nullable default String getEmbeddedPrefix() { - return null; + return ""; } /** diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterDeleteEvent.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterDeleteEvent.java index 3da6a8c0e3..8b0afd302d 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterDeleteEvent.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterDeleteEvent.java @@ -15,8 +15,8 @@ */ package org.springframework.data.relational.core.mapping.event; +import org.jspecify.annotations.Nullable; import org.springframework.data.relational.core.conversion.AggregateChange; -import org.springframework.lang.Nullable; /** * Gets published after deletion of an entity. It will have a {@link Identifier} identifier. If the entity is diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/BeforeDeleteEvent.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/BeforeDeleteEvent.java index 6de588bff4..c4ac1c1c3b 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/BeforeDeleteEvent.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/BeforeDeleteEvent.java @@ -15,8 +15,8 @@ */ package org.springframework.data.relational.core.mapping.event; +import org.jspecify.annotations.Nullable; import org.springframework.data.relational.core.conversion.AggregateChange; -import org.springframework.lang.Nullable; /** * Gets published when an entity is about to get deleted. diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/Identifier.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/Identifier.java index 3b64d0eb49..91d4c708c5 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/Identifier.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/Identifier.java @@ -17,7 +17,7 @@ import java.util.Objects; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.Assert; /** diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalDeleteEvent.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalDeleteEvent.java index 459650c524..7e148ad775 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalDeleteEvent.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalDeleteEvent.java @@ -15,8 +15,8 @@ */ package org.springframework.data.relational.core.mapping.event; +import org.jspecify.annotations.Nullable; import org.springframework.data.relational.core.conversion.AggregateChange; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalEvent.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalEvent.java index 81fb0d0e61..9145f52e68 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalEvent.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalEvent.java @@ -15,9 +15,9 @@ */ package org.springframework.data.relational.core.mapping.event; +import org.jspecify.annotations.Nullable; import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableTypeProvider; -import org.springframework.lang.Nullable; /** * an event signalling JDBC processing. diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/package-info.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/package-info.java index 8ab2a2524f..4de3b377e0 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/package-info.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/package-info.java @@ -1,4 +1,4 @@ -@NonNullApi +@NullMarked package org.springframework.data.relational.core.mapping.event; -import org.springframework.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/package-info.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/package-info.java index c026872132..0f42569e8a 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/package-info.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/package-info.java @@ -1,4 +1,4 @@ -@NonNullApi +@NullMarked package org.springframework.data.relational.core.mapping; -import org.springframework.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Criteria.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Criteria.java index 2b2deff2f2..f8c54724f5 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Criteria.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Criteria.java @@ -24,11 +24,11 @@ import java.util.Objects; import java.util.StringJoiner; +import org.jspecify.annotations.Nullable; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.relational.core.sql.IdentifierProcessing; import org.springframework.data.relational.core.sql.SqlIdentifier; import org.springframework.data.util.Pair; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -421,8 +421,13 @@ private void unroll(CriteriaDefinition criteria, StringBuilder stringBuilder) { Map forwardChain = new HashMap<>(); while (current.hasPrevious()) { - forwardChain.put(current.getPrevious(), current); - current = current.getPrevious(); + + CriteriaDefinition previous = current.getPrevious(); + + Assert.state(previous != null, "Previous criteria must not be null"); + + forwardChain.put(previous, current); + current = previous; } // perform the actual mapping @@ -476,13 +481,23 @@ private void render(CriteriaDefinition criteria, StringBuilder stringBuilder) { return; } - stringBuilder.append(criteria.getColumn().toSql(IdentifierProcessing.NONE)).append(' ') - .append(criteria.getComparator().getComparator()); + SqlIdentifier column = criteria.getColumn(); + + Assert.state(column != null, "Column must not be null"); + + Comparator comparator = criteria.getComparator(); - switch (criteria.getComparator()) { + Assert.state(comparator != null, "Comparator must not be null"); + + stringBuilder.append(column.toSql(IdentifierProcessing.NONE)).append(' ').append(comparator.getComparator()); + + switch (comparator) { case BETWEEN: case NOT_BETWEEN: Pair pair = (Pair) criteria.getValue(); + + Assert.state(pair != null, "Pair must not be null"); + stringBuilder.append(' ').append(pair.getFirst()).append(" AND ").append(pair.getSecond()); break; diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/CriteriaDefinition.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/CriteriaDefinition.java index c09129a1b6..3fcb52170b 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/CriteriaDefinition.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/CriteriaDefinition.java @@ -18,8 +18,8 @@ import java.util.Arrays; import java.util.List; +import org.jspecify.annotations.Nullable; import org.springframework.data.relational.core.sql.SqlIdentifier; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -134,7 +134,7 @@ static CriteriaDefinition from(List criteria) { Combinator getCombinator(); enum Combinator { - INITIAL, AND, OR; + INITIAL, AND, OR } enum Comparator { diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Query.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Query.java index 6d1ed69d4f..5bc6b80cb2 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Query.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Query.java @@ -23,10 +23,10 @@ import java.util.Optional; import java.util.stream.Collectors; +import org.jspecify.annotations.Nullable; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.relational.core.sql.SqlIdentifier; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Update.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Update.java index ceaabf69b9..fc2bde1366 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Update.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Update.java @@ -20,9 +20,9 @@ import java.util.Map; import java.util.StringJoiner; +import org.jspecify.annotations.Nullable; import org.springframework.data.relational.core.sql.IdentifierProcessing; import org.springframework.data.relational.core.sql.SqlIdentifier; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/ValueFunction.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/ValueFunction.java index 7fcd79129a..80cc6e7802 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/ValueFunction.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/ValueFunction.java @@ -18,8 +18,8 @@ import java.util.function.Function; import java.util.function.Supplier; +import org.jspecify.annotations.Nullable; import org.springframework.data.relational.core.dialect.Escaper; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/package-info.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/package-info.java index 34fedea567..7243fc2d7f 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/package-info.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/package-info.java @@ -1,6 +1,5 @@ /** * Query and update support. */ -@org.springframework.lang.NonNullApi -@org.springframework.lang.NonNullFields +@org.jspecify.annotations.NullMarked package org.springframework.data.relational.core.query; diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/AbstractImportValidator.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/AbstractImportValidator.java index 110659620a..81323976cc 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/AbstractImportValidator.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/AbstractImportValidator.java @@ -18,7 +18,7 @@ import java.util.HashSet; import java.util.Set; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; /** * Validator for statements to import columns. diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/AbstractSegment.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/AbstractSegment.java index 19846935c0..969f2ea4f4 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/AbstractSegment.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/AbstractSegment.java @@ -15,7 +15,7 @@ */ package org.springframework.data.relational.core.sql; -import org.springframework.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/BooleanLiteral.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/BooleanLiteral.java index 626f1c5213..97a5c1d508 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/BooleanLiteral.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/BooleanLiteral.java @@ -15,6 +15,8 @@ */ package org.springframework.data.relational.core.sql; +import org.springframework.util.Assert; + /** * Represents a {@link Boolean} literal. * @@ -29,7 +31,12 @@ public class BooleanLiteral extends Literal { @Override public Boolean getContent() { - return super.getContent(); + + Boolean content = super.getContent(); + + Assert.state(content != null, "Content must not be null"); + + return content; } @Override diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/CaseExpression.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/CaseExpression.java index fb7b13374e..b95aa800a4 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/CaseExpression.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/CaseExpression.java @@ -1,16 +1,17 @@ package org.springframework.data.relational.core.sql; -import org.springframework.lang.Nullable; +import static java.util.stream.Collectors.*; import java.util.ArrayList; import java.util.List; -import static java.util.stream.Collectors.*; +import org.jspecify.annotations.Nullable; /** * Case with one or more conditions expression. *

* Results in a rendered condition: + * *

  *   CASE
  *     WHEN condition1 THEN result1
@@ -25,8 +26,7 @@
 public class CaseExpression extends AbstractSegment implements Expression {
 
 	private final List whenList;
-	@Nullable
-	private final Expression elseExpression;
+	@Nullable private final Expression elseExpression;
 
 	private static Segment[] children(List whenList, @Nullable Expression elseExpression) {
 
@@ -81,6 +81,7 @@ public CaseExpression elseExpression(Expression elseExpression) {
 
 	@Override
 	public String toString() {
-		return "CASE " + whenList.stream().map(When::toString).collect(joining(" ")) + (elseExpression != null ? " ELSE " + elseExpression : "") + " END";
+		return "CASE " + whenList.stream().map(When::toString).collect(joining(" "))
+				+ (elseExpression != null ? " ELSE " + elseExpression : "") + " END";
 	}
 }
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Column.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Column.java
index 5f1660ff1b..69abbe0690 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Column.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Column.java
@@ -17,7 +17,7 @@
 
 import java.util.Objects;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 
 /**
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/CompositeSqlIdentifier.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/CompositeSqlIdentifier.java
index 0b232b256c..5bee08e26b 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/CompositeSqlIdentifier.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/CompositeSqlIdentifier.java
@@ -20,7 +20,7 @@
 import java.util.StringJoiner;
 import java.util.function.UnaryOperator;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 
 /**
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultDelete.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultDelete.java
index a475d71fe5..0b51e0d5cc 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultDelete.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultDelete.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.relational.core.sql;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 
 /**
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultDeleteBuilder.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultDeleteBuilder.java
index 3d2d3169c9..a234b4ff7d 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultDeleteBuilder.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultDeleteBuilder.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.relational.core.sql;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 
 /**
@@ -41,7 +41,7 @@ public DeleteWhere from(Table table) {
 	@Override
 	public DeleteWhereAndOr where(Condition condition) {
 
-		Assert.notNull(condition, "Where Condition must not be null");
+		Assert.notNull(condition, "where must not be null");
 		this.where = condition;
 		return this;
 	}
@@ -50,6 +50,8 @@ public DeleteWhereAndOr where(Condition condition) {
 	public DeleteWhereAndOr and(Condition condition) {
 
 		Assert.notNull(condition, "Condition must not be null");
+		Assert.state(this.where != null, "where must not be null");
+
 		this.where = this.where.and(condition);
 		return this;
 	}
@@ -58,6 +60,8 @@ public DeleteWhereAndOr and(Condition condition) {
 	public DeleteWhereAndOr or(Condition condition) {
 
 		Assert.notNull(condition, "Condition must not be null");
+		Assert.state(this.where != null, "where must not be null");
+
 		this.where = this.where.or(condition);
 		return this;
 	}
@@ -65,6 +69,8 @@ public DeleteWhereAndOr or(Condition condition) {
 	@Override
 	public Delete build() {
 
+		Assert.state(this.from != null, "from must not be null");
+
 		DefaultDelete delete = new DefaultDelete(this.from, this.where);
 
 		DeleteValidator.validate(delete);
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultInsert.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultInsert.java
index 2a5d4540bd..82483b9434 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultInsert.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultInsert.java
@@ -18,7 +18,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.StringUtils;
 
@@ -35,7 +35,9 @@ class DefaultInsert implements Insert {
 	private final Values values;
 
 	DefaultInsert(@Nullable Table into, List columns, List values) {
-		this.into = new Into(into);
+
+		// TODO: this is weird. can we really have an insert without table?
+		this.into = into == null ? new Into() : new Into(into);
 		this.columns = new ArrayList<>(columns);
 		this.values = new Values(new ArrayList<>(values));
 	}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultInsertBuilder.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultInsertBuilder.java
index 91d2942df5..8d40a4bc2c 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultInsertBuilder.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultInsertBuilder.java
@@ -20,7 +20,7 @@
 import java.util.Collection;
 import java.util.List;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 
 /**
@@ -33,8 +33,8 @@ class DefaultInsertBuilder
 		implements InsertBuilder, InsertBuilder.InsertIntoColumnsAndValuesWithBuild, InsertBuilder.InsertValuesWithBuild {
 
 	private @Nullable Table into;
-	private List columns = new ArrayList<>();
-	private List values = new ArrayList<>();
+	private final List columns = new ArrayList<>();
+	private final List values = new ArrayList<>();
 
 	@Override
 	public InsertIntoColumnsAndValuesWithBuild into(Table table) {
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultSelect.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultSelect.java
index 9aa5975dd1..8e7ea32344 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultSelect.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultSelect.java
@@ -21,7 +21,7 @@
 import java.util.OptionalLong;
 import java.util.function.Consumer;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 
 /**
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultSelectBuilder.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultSelectBuilder.java
index 0bc6fe2d36..561a696846 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultSelectBuilder.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultSelectBuilder.java
@@ -20,11 +20,12 @@
 import java.util.Collection;
 import java.util.List;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.relational.core.sql.Join.JoinType;
 import org.springframework.data.relational.core.sql.SelectBuilder.SelectAndFrom;
 import org.springframework.data.relational.core.sql.SelectBuilder.SelectFromAndJoin;
 import org.springframework.data.relational.core.sql.SelectBuilder.SelectWhereAndOr;
-import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
 
 /**
  * Default {@link SelectBuilder} implementation.
@@ -155,6 +156,8 @@ public SelectWhereAndOr where(Condition condition) {
 	@Override
 	public SelectWhereAndOr and(Condition condition) {
 
+		Assert.state(this.where != null, "Where must not be null");
+
 		where = where.and(condition);
 		return this;
 	}
@@ -162,6 +165,8 @@ public SelectWhereAndOr and(Condition condition) {
 	@Override
 	public SelectWhereAndOr or(Condition condition) {
 
+		Assert.state(this.where != null, "Where must not be null");
+
 		where = where.or(condition);
 		return this;
 	}
@@ -274,6 +279,9 @@ private void finishCondition() {
 				return;
 			}
 
+			Assert.state(from != null, "from must not be null");
+			Assert.state(to != null, "to must not be null");
+
 			Comparison comparison = Comparison.create(from, "=", to);
 
 			if (condition == null) {
@@ -285,7 +293,11 @@ private void finishCondition() {
 		}
 
 		private Join finishJoin() {
+
 			finishCondition();
+
+			Assert.state(condition != null, "condition must not be null");
+
 			return new Join(joinType, table, condition);
 		}
 
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultSqlIdentifier.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultSqlIdentifier.java
index f8de07d72a..0da4a768ff 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultSqlIdentifier.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultSqlIdentifier.java
@@ -19,7 +19,7 @@
 import java.util.Iterator;
 import java.util.function.UnaryOperator;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 
 /**
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultUpdate.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultUpdate.java
index f6c4c0c377..655dbfcd60 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultUpdate.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultUpdate.java
@@ -18,7 +18,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.StringUtils;
 
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultUpdateBuilder.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultUpdateBuilder.java
index d0a745d783..fcecdcb61f 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultUpdateBuilder.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DefaultUpdateBuilder.java
@@ -20,10 +20,10 @@
 import java.util.Collection;
 import java.util.List;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.relational.core.sql.UpdateBuilder.UpdateAssign;
 import org.springframework.data.relational.core.sql.UpdateBuilder.UpdateWhere;
 import org.springframework.data.relational.core.sql.UpdateBuilder.UpdateWhereAndOr;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
@@ -35,7 +35,7 @@
 class DefaultUpdateBuilder implements UpdateBuilder, UpdateWhere, UpdateWhereAndOr, UpdateAssign {
 
 	private @Nullable Table table;
-	private List assignments = new ArrayList<>();
+	private final List assignments = new ArrayList<>();
 	private @Nullable Condition where;
 
 	@Override
@@ -90,6 +90,7 @@ public UpdateWhereAndOr where(Condition condition) {
 	public UpdateWhereAndOr and(Condition condition) {
 
 		Assert.notNull(condition, "Condition must not be null");
+		Assert.state(this.where != null, "Where must not be null");
 
 		this.where = this.where.and(condition);
 
@@ -100,6 +101,7 @@ public UpdateWhereAndOr and(Condition condition) {
 	public UpdateWhereAndOr or(Condition condition) {
 
 		Assert.notNull(condition, "Condition must not be null");
+		Assert.state(this.where != null, "Where must not be null");
 
 		this.where = this.where.and(condition);
 
@@ -108,6 +110,9 @@ public UpdateWhereAndOr or(Condition condition) {
 
 	@Override
 	public Update build() {
+
+		Assert.state(this.table != null, "Table must not be null");
+
 		return new DefaultUpdate(this.table, this.assignments, this.where);
 	}
 }
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Literal.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Literal.java
index d9c46e6cfc..af6ff61a9d 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Literal.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Literal.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.relational.core.sql;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 /**
  * Represents a literal.
@@ -25,7 +25,7 @@
  */
 public class Literal extends AbstractSegment implements Expression {
 
-	private @Nullable T content;
+	private final @Nullable T content;
 
 	Literal(@Nullable T content) {
 		this.content = content;
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/NumericLiteral.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/NumericLiteral.java
index 743201e5e1..55cc229881 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/NumericLiteral.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/NumericLiteral.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.relational.core.sql;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 /**
  * Represents a {@link Number} literal.
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/OrderByField.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/OrderByField.java
index 2044366440..81d861c7e0 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/OrderByField.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/OrderByField.java
@@ -15,10 +15,10 @@
  */
 package org.springframework.data.relational.core.sql;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.domain.Sort;
 import org.springframework.data.domain.Sort.Direction;
 import org.springframework.data.domain.Sort.NullHandling;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
@@ -31,7 +31,7 @@
 public class OrderByField extends AbstractSegment {
 
 	private final Expression expression;
-	private final @Nullable Sort.Direction direction;
+	private final Sort.@Nullable Direction direction;
 	private final Sort.NullHandling nullHandling;
 
 	private OrderByField(Expression expression, @Nullable Direction direction, NullHandling nullHandling) {
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SQL.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SQL.java
index 22dabaf31f..d06f4aad7d 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SQL.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SQL.java
@@ -15,8 +15,8 @@
  */
 package org.springframework.data.relational.core.sql;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.relational.core.sql.BindMarker.NamedBindMarker;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Select.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Select.java
index b04da5cf82..87c3cce8eb 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Select.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Select.java
@@ -18,7 +18,7 @@
 import java.util.List;
 import java.util.OptionalLong;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 /**
  * AST for a {@code SELECT} statement. Visiting order:
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/StringLiteral.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/StringLiteral.java
index 898b4b8254..34c4508356 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/StringLiteral.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/StringLiteral.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.relational.core.sql;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 /**
  * Represents a {@link CharSequence} literal.
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Table.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Table.java
index 55677b4def..6497e86ceb 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Table.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Table.java
@@ -17,7 +17,7 @@
 
 import java.util.Objects;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 
 /**
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/package-info.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/package-info.java
index 7332ca9a79..3dc456e956 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/package-info.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/package-info.java
@@ -9,9 +9,7 @@
  * 
  * @since 1.1
  */
-@NonNullApi
-@NonNullFields
+@NullMarked
 package org.springframework.data.relational.core.sql;
 
-import org.springframework.lang.NonNullApi;
-import org.springframework.lang.NonNullFields;
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/AnalyticFunctionVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/AnalyticFunctionVisitor.java
index c14e04b277..c20cb17682 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/AnalyticFunctionVisitor.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/AnalyticFunctionVisitor.java
@@ -15,11 +15,11 @@
  */
 package org.springframework.data.relational.core.sql.render;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.relational.core.sql.AnalyticFunction;
 import org.springframework.data.relational.core.sql.OrderBy;
 import org.springframework.data.relational.core.sql.SimpleFunction;
 import org.springframework.data.relational.core.sql.Visitable;
-import org.springframework.lang.Nullable;
 
 /**
  * Renderer for {@link AnalyticFunction}. Uses a {@link RenderTarget} to call back for render results.
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/BetweenVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/BetweenVisitor.java
index c43eefad1c..13c96e2d02 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/BetweenVisitor.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/BetweenVisitor.java
@@ -15,11 +15,11 @@
  */
 package org.springframework.data.relational.core.sql.render;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.relational.core.sql.Between;
 import org.springframework.data.relational.core.sql.Condition;
 import org.springframework.data.relational.core.sql.Expression;
 import org.springframework.data.relational.core.sql.Visitable;
-import org.springframework.lang.Nullable;
 
 /**
  * {@link org.springframework.data.relational.core.sql.Visitor} rendering comparison {@link Condition}. Uses a
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/CastVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/CastVisitor.java
index 3bdee8df97..b0a8e97eea 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/CastVisitor.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/CastVisitor.java
@@ -17,9 +17,9 @@
 
 import java.util.StringJoiner;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.relational.core.sql.Cast;
 import org.springframework.data.relational.core.sql.Visitable;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ColumnVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ColumnVisitor.java
index 4b5ad003bb..239677662f 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ColumnVisitor.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ColumnVisitor.java
@@ -15,11 +15,11 @@
  */
 package org.springframework.data.relational.core.sql.render;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.relational.core.sql.Column;
 import org.springframework.data.relational.core.sql.SqlIdentifier;
 import org.springframework.data.relational.core.sql.TableLike;
 import org.springframework.data.relational.core.sql.Visitable;
-import org.springframework.lang.Nullable;
 
 /**
  * Renderer for {@link Column}s. Renders a column as {@literal >table<.>column<} or
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ComparisonVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ComparisonVisitor.java
index f1e5a12e57..bbdba2a488 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ComparisonVisitor.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ComparisonVisitor.java
@@ -15,11 +15,11 @@
  */
 package org.springframework.data.relational.core.sql.render;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.relational.core.sql.Comparison;
 import org.springframework.data.relational.core.sql.Condition;
 import org.springframework.data.relational.core.sql.Expression;
 import org.springframework.data.relational.core.sql.Visitable;
-import org.springframework.lang.Nullable;
 
 /**
  * {@link org.springframework.data.relational.core.sql.Visitor} rendering comparison {@link Condition}. Uses a
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ConditionVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ConditionVisitor.java
index 925915b430..8c668181f5 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ConditionVisitor.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ConditionVisitor.java
@@ -15,8 +15,8 @@
  */
 package org.springframework.data.relational.core.sql.render;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.relational.core.sql.*;
-import org.springframework.lang.Nullable;
 
 /**
  * {@link org.springframework.data.relational.core.sql.Visitor} delegating {@link Condition} rendering to condition
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/DelegatingVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/DelegatingVisitor.java
index 95da9908b2..30337579f7 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/DelegatingVisitor.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/DelegatingVisitor.java
@@ -17,9 +17,9 @@
 
 import java.util.Stack;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.relational.core.sql.Visitable;
 import org.springframework.data.relational.core.sql.Visitor;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
@@ -48,7 +48,7 @@
  */
 abstract class DelegatingVisitor implements Visitor {
 
-	private Stack delegation = new Stack<>();
+	private final Stack delegation = new Stack<>();
 
 	/**
 	 * Invoked for a {@link Visitable segment} when entering the segment.
@@ -148,8 +148,8 @@ private Delegation doLeave0(Visitable segment) {
 	 */
 	static class Delegation {
 
-		private static Delegation RETAIN = new Delegation(true, false, null);
-		private static Delegation LEAVE = new Delegation(false, true, null);
+		private static final Delegation RETAIN = new Delegation(true, false, null);
+		private static final Delegation LEAVE = new Delegation(false, true, null);
 
 		private final boolean retain;
 		private final boolean leave;
@@ -188,7 +188,8 @@ boolean isLeave() {
 
 		DelegatingVisitor getDelegate() {
 
-			Assert.state(isDelegate(), "No delegate available");
+			Assert.state(delegate != null, "No delegate available");
+
 			return delegate;
 		}
 	}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ExpressionVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ExpressionVisitor.java
index 40c21e1976..2099e527b5 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ExpressionVisitor.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ExpressionVisitor.java
@@ -15,8 +15,8 @@
  */
 package org.springframework.data.relational.core.sql.render;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.relational.core.sql.*;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/FilteredSingleConditionRenderSupport.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/FilteredSingleConditionRenderSupport.java
index f02b214fd8..c1bdc62a41 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/FilteredSingleConditionRenderSupport.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/FilteredSingleConditionRenderSupport.java
@@ -17,10 +17,10 @@
 
 import java.util.function.Predicate;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.relational.core.sql.Condition;
 import org.springframework.data.relational.core.sql.Expression;
 import org.springframework.data.relational.core.sql.Visitable;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
@@ -85,10 +85,10 @@ protected boolean hasDelegatedRendering() {
 	 */
 	protected CharSequence consumeRenderedPart() {
 
-		Assert.state(hasDelegatedRendering(), "Rendering not delegated; Cannot consume delegated rendering part");
+		// inlined hasDelegate, so NullAway understands it.
+		Assert.state(current != null, "Rendering not delegated; Cannot consume delegated rendering part");
 
 		PartRenderer current = this.current;
-		this.current = null;
 		return current.getRenderedPart();
 	}
 }
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/FilteredSubtreeVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/FilteredSubtreeVisitor.java
index 5aae4df41b..57e5041868 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/FilteredSubtreeVisitor.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/FilteredSubtreeVisitor.java
@@ -17,9 +17,9 @@
 
 import java.util.function.Predicate;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.relational.core.sql.Visitable;
 import org.springframework.data.relational.core.sql.Visitor;
-import org.springframework.lang.Nullable;
 
 /**
  * Filtering {@link DelegatingVisitor visitor} applying a {@link Predicate filter}. Typically used as base class for
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/FromTableVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/FromTableVisitor.java
index f1ff2a29e4..0964d7b962 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/FromTableVisitor.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/FromTableVisitor.java
@@ -15,11 +15,11 @@
  */
 package org.springframework.data.relational.core.sql.render;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.relational.core.sql.Aliased;
 import org.springframework.data.relational.core.sql.From;
 import org.springframework.data.relational.core.sql.InlineQuery;
 import org.springframework.data.relational.core.sql.TableLike;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/LikeVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/LikeVisitor.java
index 8649ac04b3..9c047c9b50 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/LikeVisitor.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/LikeVisitor.java
@@ -15,11 +15,11 @@
  */
 package org.springframework.data.relational.core.sql.render;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.relational.core.sql.Condition;
 import org.springframework.data.relational.core.sql.Expression;
 import org.springframework.data.relational.core.sql.Like;
 import org.springframework.data.relational.core.sql.Visitable;
-import org.springframework.lang.Nullable;
 
 /**
  * {@link org.springframework.data.relational.core.sql.Visitor} rendering comparison {@link Condition}. Uses a
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/NameRenderer.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/NameRenderer.java
index 66bdb808c6..fc507f2bca 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/NameRenderer.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/NameRenderer.java
@@ -22,6 +22,7 @@
 import org.springframework.data.relational.core.sql.SqlIdentifier;
 import org.springframework.data.relational.core.sql.Table;
 import org.springframework.data.relational.core.sql.TableLike;
+import org.springframework.util.Assert;
 
 /**
  * Utility to render {@link Column} and {@link Table} names using {@link SqlIdentifier} and {@link RenderContext} to
@@ -91,8 +92,12 @@ static CharSequence fullyQualifiedReference(RenderContext context, Column column
 			return render(context, namingStrategy.getReferenceName(column));
 		}
 
-		return render(context, SqlIdentifier.from(namingStrategy.getReferenceName(column.getTable()),
-				namingStrategy.getReferenceName(column)));
+		TableLike table = column.getTable();
+
+		Assert.notNull(table, "Table must not be null");
+
+		return render(context,
+				SqlIdentifier.from(namingStrategy.getReferenceName(table), namingStrategy.getReferenceName(column)));
 	}
 
 	/**
@@ -106,8 +111,11 @@ static CharSequence fullyQualifiedUnaliasedReference(RenderContext context, Colu
 
 		RenderNamingStrategy namingStrategy = context.getNamingStrategy();
 
-		return render(context,
-				SqlIdentifier.from(namingStrategy.getReferenceName(column.getTable()), namingStrategy.getName(column)));
+		TableLike table = column.getTable();
+
+		Assert.notNull(table, "Table must not be null");
+
+		return render(context, SqlIdentifier.from(namingStrategy.getReferenceName(table), namingStrategy.getName(column)));
 	}
 
 	/**
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/NestedConditionVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/NestedConditionVisitor.java
index 25511b22da..1868a91c4b 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/NestedConditionVisitor.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/NestedConditionVisitor.java
@@ -15,10 +15,10 @@
  */
 package org.springframework.data.relational.core.sql.render;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.relational.core.sql.Condition;
 import org.springframework.data.relational.core.sql.NestedCondition;
 import org.springframework.data.relational.core.sql.Visitable;
-import org.springframework.lang.Nullable;
 
 /**
  * Renderer for {@link NestedCondition}. Uses a {@link RenderTarget} to call back for render results.
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/NotConditionVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/NotConditionVisitor.java
index 058fa1c3f4..8386b4a890 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/NotConditionVisitor.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/NotConditionVisitor.java
@@ -15,10 +15,10 @@
  */
 package org.springframework.data.relational.core.sql.render;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.relational.core.sql.Condition;
 import org.springframework.data.relational.core.sql.Not;
 import org.springframework.data.relational.core.sql.Visitable;
-import org.springframework.lang.Nullable;
 
 /**
  * Renderer for {@link Not}. Uses a {@link RenderTarget} to call back for render results.
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/OrderByClauseVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/OrderByClauseVisitor.java
index 1fc2594b8d..699237982c 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/OrderByClauseVisitor.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/OrderByClauseVisitor.java
@@ -15,14 +15,13 @@
  */
 package org.springframework.data.relational.core.sql.render;
 
-
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.relational.core.sql.CaseExpression;
 import org.springframework.data.relational.core.sql.Column;
 import org.springframework.data.relational.core.sql.Expressions;
 import org.springframework.data.relational.core.sql.OrderByField;
 import org.springframework.data.relational.core.sql.SimpleFunction;
 import org.springframework.data.relational.core.sql.Visitable;
-import org.springframework.lang.Nullable;
 
 /**
  * {@link PartRenderer} for {@link OrderByField}s.
@@ -40,8 +39,7 @@ class OrderByClauseVisitor extends TypedSubtreeVisitor implements
 
 	private final StringBuilder builder = new StringBuilder();
 
-	@Nullable
-	private PartRenderer delegate;
+	@Nullable private PartRenderer delegate;
 
 	private boolean first = true;
 
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/TypedSingleConditionRenderSupport.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/TypedSingleConditionRenderSupport.java
index e338008c5e..618de372d3 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/TypedSingleConditionRenderSupport.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/TypedSingleConditionRenderSupport.java
@@ -15,11 +15,11 @@
  */
 package org.springframework.data.relational.core.sql.render;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.relational.core.sql.Condition;
 import org.springframework.data.relational.core.sql.Expression;
 import org.springframework.data.relational.core.sql.Visitable;
 import org.springframework.data.relational.core.sql.When;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
@@ -81,7 +81,8 @@ protected boolean hasDelegatedRendering() {
 	 */
 	protected CharSequence consumeRenderedPart() {
 
-		Assert.state(hasDelegatedRendering(), "Rendering not delegated; Cannot consume delegated rendering part");
+		// hasDelegateRendering inlined for NullAway
+		Assert.state(current != null, "Rendering not delegated; Cannot consume delegated rendering part");
 
 		PartRenderer current = this.current;
 		this.current = null;
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/TypedSubtreeVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/TypedSubtreeVisitor.java
index 149785a622..64bedbf5a6 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/TypedSubtreeVisitor.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/TypedSubtreeVisitor.java
@@ -17,10 +17,10 @@
 
 import java.util.function.Predicate;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.core.ResolvableType;
 import org.springframework.data.relational.core.sql.Visitable;
 import org.springframework.data.relational.core.sql.Visitor;
-import org.springframework.lang.Nullable;
 import org.springframework.util.ConcurrentReferenceHashMap;
 
 /**
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ValuesVisitor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ValuesVisitor.java
index bf548325b3..9a1400c60a 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ValuesVisitor.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/ValuesVisitor.java
@@ -15,10 +15,10 @@
  */
 package org.springframework.data.relational.core.sql.render;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.relational.core.sql.Expression;
 import org.springframework.data.relational.core.sql.Values;
 import org.springframework.data.relational.core.sql.Visitable;
-import org.springframework.lang.Nullable;
 
 /**
  * Renderer for {@link Values}. Uses a {@link RenderTarget} to call back for render results.
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/package-info.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/package-info.java
index 34c541d4c3..2020edb521 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/package-info.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/package-info.java
@@ -1,9 +1,7 @@
 /**
  * SQL rendering utilities to render SQL from the Statement Builder API.
  */
-@NonNullApi
-@NonNullFields
+@NullMarked
 package org.springframework.data.relational.core.sql.render;
 
-import org.springframework.lang.NonNullApi;
-import org.springframework.lang.NonNullFields;
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sqlgeneration/SingleQuerySqlGenerator.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sqlgeneration/SingleQuerySqlGenerator.java
index 2038f721ed..b9ca873328 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sqlgeneration/SingleQuerySqlGenerator.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sqlgeneration/SingleQuerySqlGenerator.java
@@ -20,6 +20,7 @@
 import java.util.List;
 import java.util.Map;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.mapping.PersistentProperty;
 import org.springframework.data.mapping.PersistentPropertyPath;
 import org.springframework.data.mapping.PersistentPropertyPaths;
@@ -31,7 +32,6 @@
 import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
 import org.springframework.data.relational.core.sql.*;
 import org.springframework.data.relational.core.sql.render.SqlRenderer;
-import org.springframework.lang.Nullable;
 
 /**
  * A {@link SqlGenerator} that creates SQL statements for loading complete aggregates with a single statement.
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sqlgeneration/SqlGenerator.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sqlgeneration/SqlGenerator.java
index 35245f3514..38ab11f0c2 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sqlgeneration/SqlGenerator.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sqlgeneration/SqlGenerator.java
@@ -15,9 +15,9 @@
  */
 package org.springframework.data.relational.core.sqlgeneration;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
 import org.springframework.data.relational.core.sql.Condition;
-import org.springframework.lang.Nullable;
 
 /**
  * Generates SQL statements for loading aggregates.
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/domain/RowDocument.java b/spring-data-relational/src/main/java/org/springframework/data/relational/domain/RowDocument.java
index 2ecf2597fa..c4ec632af3 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/domain/RowDocument.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/domain/RowDocument.java
@@ -23,7 +23,7 @@
 import java.util.function.BiFunction;
 import java.util.function.Function;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 import org.springframework.util.LinkedCaseInsensitiveMap;
 import org.springframework.util.ObjectUtils;
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/domain/SqlSort.java b/spring-data-relational/src/main/java/org/springframework/data/relational/domain/SqlSort.java
index ccc9509231..b662fb1e19 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/domain/SqlSort.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/domain/SqlSort.java
@@ -23,8 +23,8 @@
 import java.util.function.Predicate;
 import java.util.regex.Pattern;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.domain.Sort;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
@@ -42,7 +42,7 @@ public class SqlSort extends Sort {
 	private static final long serialVersionUID = 1L;
 
 	private SqlSort(Direction direction, List paths) {
-		this(Collections. emptyList(), direction, paths);
+		this(Collections.emptyList(), direction, paths);
 	}
 
 	private SqlSort(List orders, @Nullable Direction direction, List paths) {
@@ -84,7 +84,7 @@ public static void validate(Sort.Order order) {
 
 		if (!predicate.test(property)) {
 			throw new IllegalArgumentException(
-					"order fields that are not marked as unsafe must only consist of digits, letter, '.', '_', and '\'. If you want to sort by arbitrary expressions please use RelationalSort.unsafe. Note that such expressions become part of SQL statements and therefore need to be sanitized to prevent SQL injection attacks.");
+					"order fields that are not marked as unsafe must only consist of digits, letter, '.', '_', and ''. If you want to sort by arbitrary expressions please use RelationalSort.unsafe. Note that such expressions become part of SQL statements and therefore need to be sanitized to prevent SQL injection attacks.");
 		}
 	}
 
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/repository/query/CriteriaFactory.java b/spring-data-relational/src/main/java/org/springframework/data/relational/repository/query/CriteriaFactory.java
index eed288b240..ac1e10f3fd 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/repository/query/CriteriaFactory.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/repository/query/CriteriaFactory.java
@@ -70,33 +70,36 @@ public Criteria createCriteria(Part part) {
 
 		switch (type) {
 			case BETWEEN: {
-				ParameterMetadata geParamMetadata = parameterMetadataProvider.next(part);
-				ParameterMetadata leParamMetadata = parameterMetadataProvider.next(part);
-				return criteriaStep.between(geParamMetadata.getValue(), leParamMetadata.getValue());
+
+				Object geValue = requireNextValue(part, "geValue");
+				Object leValue = requireNextValue(part, "leValue");
+
+				return criteriaStep.between(geValue, leValue);
 			}
 			case AFTER:
 			case GREATER_THAN: {
-				ParameterMetadata paramMetadata = parameterMetadataProvider.next(part);
-				return criteriaStep.greaterThan(paramMetadata.getValue());
+				Object value = requireNextValue(part);
+
+				return criteriaStep.greaterThan(value);
 			}
 			case GREATER_THAN_EQUAL: {
-				ParameterMetadata paramMetadata = parameterMetadataProvider.next(part);
-				return criteriaStep.greaterThanOrEquals(paramMetadata.getValue());
+				Object value = requireNextValue(part);
+				return criteriaStep.greaterThanOrEquals(value);
 			}
 			case BEFORE:
 			case LESS_THAN: {
-				ParameterMetadata paramMetadata = parameterMetadataProvider.next(part);
-				return criteriaStep.lessThan(paramMetadata.getValue());
+				Object value = requireNextValue(part);
+				return criteriaStep.lessThan(value);
 			}
 			case LESS_THAN_EQUAL: {
-				ParameterMetadata paramMetadata = parameterMetadataProvider.next(part);
-				return criteriaStep.lessThanOrEquals(paramMetadata.getValue());
+				Object value = requireNextValue(part);
+				return criteriaStep.lessThanOrEquals(value);
 			}
 			case IN:
 			case NOT_IN: {
-				ParameterMetadata paramMetadata = parameterMetadataProvider.next(part);
-				Criteria criteria = part.getType() == Part.Type.IN ? criteriaStep.in(asCollection(paramMetadata.getValue()))
-						: criteriaStep.notIn(asCollection(paramMetadata.getValue()));
+				Object value = requireNextValue(part);
+				Criteria criteria = part.getType() == Part.Type.IN ? criteriaStep.in(asCollection(value))
+						: criteriaStep.notIn(asCollection(value));
 				return criteria.ignoreCase(shouldIgnoreCase(part) && checkCanUpperCase(part, part.getProperty().getType()));
 			}
 			case STARTING_WITH:
@@ -106,30 +109,51 @@ public Criteria createCriteria(Part part) {
 			case LIKE:
 			case NOT_LIKE: {
 				ParameterMetadata paramMetadata = parameterMetadataProvider.next(part);
+				Object value = paramMetadata.value();
+
+				Assert.state(value != null, "value must not be null");
+
 				Criteria criteria = part.getType() == Part.Type.NOT_LIKE || part.getType() == Part.Type.NOT_CONTAINING
-						? criteriaStep.notLike(paramMetadata.getValue())
-						: criteriaStep.like(paramMetadata.getValue());
+						? criteriaStep.notLike(value)
+						: criteriaStep.like(value);
 				return criteria
-						.ignoreCase(shouldIgnoreCase(part) && checkCanUpperCase(part, propertyType, paramMetadata.getType()));
+						.ignoreCase(shouldIgnoreCase(part) && checkCanUpperCase(part, propertyType, paramMetadata.type()));
 			}
 			case SIMPLE_PROPERTY: {
 				ParameterMetadata paramMetadata = parameterMetadataProvider.next(part);
-				if (paramMetadata.getValue() == null) {
+				if (paramMetadata.value() == null) {
 					return criteriaStep.isNull();
 				}
-				return criteriaStep.is(paramMetadata.getValue())
-						.ignoreCase(shouldIgnoreCase(part) && checkCanUpperCase(part, propertyType, paramMetadata.getType()));
+				return criteriaStep.is(paramMetadata.value())
+						.ignoreCase(shouldIgnoreCase(part) && checkCanUpperCase(part, propertyType, paramMetadata.type()));
 			}
 			case NEGATING_SIMPLE_PROPERTY: {
 				ParameterMetadata paramMetadata = parameterMetadataProvider.next(part);
-				return criteriaStep.not(paramMetadata.getValue())
-						.ignoreCase(shouldIgnoreCase(part) && checkCanUpperCase(part, propertyType, paramMetadata.getType()));
+				Object value = paramMetadata.value();
+
+				Assert.state(value != null, "value must not be null");
+
+				return criteriaStep.not(value)
+						.ignoreCase(shouldIgnoreCase(part) && checkCanUpperCase(part, propertyType, paramMetadata.type()));
 			}
 			default:
 				throw new IllegalArgumentException("Unsupported keyword " + type);
 		}
 	}
 
+	private Object requireNextValue(Part part, String geValue_must_not_be_null) {
+
+		Object value = parameterMetadataProvider.next(part).value();
+
+		Assert.state(value != null, geValue_must_not_be_null + " must not be null");
+
+		return value;
+	}
+
+	private Object requireNextValue(Part part) {
+		return requireNextValue(part, "value");
+	}
+
 	/**
 	 * Checks whether comparison should be done in case-insensitive way.
 	 *
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/repository/query/ParameterMetadata.java b/spring-data-relational/src/main/java/org/springframework/data/relational/repository/query/ParameterMetadata.java
index df55c34676..7f38f24180 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/repository/query/ParameterMetadata.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/repository/query/ParameterMetadata.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.relational.repository.query;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 import org.springframework.util.Assert;
 
 /**
@@ -23,30 +23,12 @@
  *
  * @since 2.0
  */
-class ParameterMetadata {
+record ParameterMetadata(String name, @Nullable Object value, Class type) {
 
-	private final String name;
-	private final @Nullable Object value;
-	private final Class type;
-
-	public ParameterMetadata(String name, @Nullable Object value, Class type) {
+	ParameterMetadata {
 
 		Assert.notNull(type, "Parameter type must not be null");
-		this.name = name;
-		this.value = value;
-		this.type = type;
 	}
 
-	public String getName() {
-		return name;
-	}
 
-	@Nullable
-	public Object getValue() {
-		return value;
-	}
-
-	public Class getType() {
-		return type;
-	}
 }
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/repository/query/ParameterMetadataProvider.java b/spring-data-relational/src/main/java/org/springframework/data/relational/repository/query/ParameterMetadataProvider.java
index 35a3ee92b3..4cb7aab2c9 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/repository/query/ParameterMetadataProvider.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/repository/query/ParameterMetadataProvider.java
@@ -19,12 +19,12 @@
 import java.util.Iterator;
 import java.util.List;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.relational.core.dialect.Escaper;
 import org.springframework.data.relational.core.query.ValueFunction;
 import org.springframework.data.repository.query.Parameter;
 import org.springframework.data.repository.query.Parameters;
 import org.springframework.data.repository.query.parser.Part;
-import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
 
 /**
@@ -58,8 +58,7 @@ public ParameterMetadataProvider(RelationalParameterAccessor accessor) {
 	 * @param bindableParameterValueIterator iterator over bindable parameter values
 	 * @param parameters method parameters (must not be {@literal null})
 	 */
-	private ParameterMetadataProvider(Parameters parameters,
-			@Nullable Iterator bindableParameterValueIterator) {
+	private ParameterMetadataProvider(Parameters parameters, Iterator bindableParameterValueIterator) {
 
 		Assert.notNull(parameters, "Parameters must not be null");
 
@@ -140,8 +139,8 @@ protected Object prepareParameterValue(@Nullable Object value, Class valueTyp
 		return switch (partType) {
 			case STARTING_WITH -> (ValueFunction) escaper -> escaper.escape(value.toString()) + "%";
 			case ENDING_WITH -> (ValueFunction) escaper -> "%" + escaper.escape(value.toString());
-			case CONTAINING, NOT_CONTAINING -> (ValueFunction) escaper -> "%" + escaper.escape(value.toString())
-					+ "%";
+			case CONTAINING, NOT_CONTAINING ->
+				(ValueFunction) escaper -> "%" + escaper.escape(value.toString()) + "%";
 			default -> value;
 		};
 	}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/repository/query/package-info.java b/spring-data-relational/src/main/java/org/springframework/data/relational/repository/query/package-info.java
index ccd616a69d..8c929e4a00 100755
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/repository/query/package-info.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/repository/query/package-info.java
@@ -1,7 +1,7 @@
 /**
  * Query support for relational database repositories.
  */
-@NonNullApi
+@NullMarked
 package org.springframework.data.relational.repository.query;
 
-import org.springframework.lang.NonNullApi;
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/repository/support/MappingRelationalEntityInformation.java b/spring-data-relational/src/main/java/org/springframework/data/relational/repository/support/MappingRelationalEntityInformation.java
index 98f43519fc..208c86615b 100755
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/repository/support/MappingRelationalEntityInformation.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/repository/support/MappingRelationalEntityInformation.java
@@ -15,11 +15,11 @@
  */
 package org.springframework.data.relational.repository.support;
 
+import org.jspecify.annotations.Nullable;
 import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
 import org.springframework.data.relational.core.sql.SqlIdentifier;
 import org.springframework.data.relational.repository.query.RelationalEntityInformation;
 import org.springframework.data.repository.core.support.PersistentEntityInformation;
-import org.springframework.lang.Nullable;
 
 /**
  * {@link RelationalEntityInformation} implementation using a {@link RelationalPersistentEntity} instance to lookup the
@@ -77,7 +77,7 @@ public MappingRelationalEntityInformation(RelationalPersistentEntity entity,
 	 */
 	@SuppressWarnings("unchecked")
 	private MappingRelationalEntityInformation(RelationalPersistentEntity entity, @Nullable String customTableName,
-											   @Nullable Class idType) {
+			@Nullable Class idType) {
 
 		super(entity);
 
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/repository/support/TableNameQueryPreprocessor.java b/spring-data-relational/src/main/java/org/springframework/data/relational/repository/support/TableNameQueryPreprocessor.java
index 6a2c17da85..93f25d8448 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/repository/support/TableNameQueryPreprocessor.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/repository/support/TableNameQueryPreprocessor.java
@@ -70,7 +70,11 @@ public String transform(String query) {
 		query = quoteExpressionsParameter(query);
 		Expression expression = parser.parseExpression(query, ParserContext.TEMPLATE_EXPRESSION);
 
-		return unquoteParameterExpressions(expression.getValue(evaluationContext, String.class));
+		String value = expression.getValue(evaluationContext, String.class);
+
+		Assert.state(value != null, "Value must not be null");
+
+		return unquoteParameterExpressions(value);
 	}
 
 	private static String unquoteParameterExpressions(String result) {
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/repository/support/package-info.java b/spring-data-relational/src/main/java/org/springframework/data/relational/repository/support/package-info.java
index 28aeb25142..fa3c233e98 100755
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/repository/support/package-info.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/repository/support/package-info.java
@@ -1,7 +1,7 @@
 /**
  * Support infrastructure for query derivation of relational database repositories.
  */
-@NonNullApi
+@NullMarked
 package org.springframework.data.relational.repository.support;
 
-import org.springframework.lang.NonNullApi;
+import org.jspecify.annotations.NullMarked;
diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/DbActionTestSupport.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/DbActionTestSupport.java
index 51e398aa36..0465367d74 100644
--- a/spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/DbActionTestSupport.java
+++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/DbActionTestSupport.java
@@ -15,7 +15,7 @@
  */
 package org.springframework.data.relational.core.conversion;
 
-import org.springframework.lang.Nullable;
+import org.jspecify.annotations.Nullable;
 
 /**
  * Utility class for analyzing DbActions in tests.
@@ -32,7 +32,7 @@ private DbActionTestSupport() {
 	static String extractPath(DbAction action) {
 
 		if (action instanceof DbAction.WithPropertyPath) {
-			return ((DbAction.WithPropertyPath) action).getPropertyPath().toDotPath();
+			return ((DbAction.WithPropertyPath) action).propertyPath().toDotPath();
 		}
 
 		return "";
@@ -46,7 +46,7 @@ static boolean isWithDependsOn(DbAction dbAction) {
 	static Class actualEntityType(DbAction a) {
 
 		if (a instanceof DbAction.WithEntity) {
-			return ((DbAction.WithEntity) a).getEntity().getClass();
+			return ((DbAction.WithEntity) a).entity().getClass();
 		}
 		return null;
 	}
@@ -55,7 +55,7 @@ static Class actualEntityType(DbAction a) {
 	static IdValueSource insertIdValueSource(DbAction action) {
 
 		if (action instanceof DbAction.WithEntity) {
-			return ((DbAction.WithEntity) action).getIdValueSource();
+			return ((DbAction.WithEntity) action).idValueSource();
 		} else if (action instanceof DbAction.BatchInsert) {
 			return ((DbAction.BatchInsert) action).getBatchValue();
 		} else if (action instanceof DbAction.BatchInsertRoot) {
diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/DeleteBatchingAggregateChangeTest.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/DeleteBatchingAggregateChangeTest.java
index e5e1f73ab1..1f5347b88a 100644
--- a/spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/DeleteBatchingAggregateChangeTest.java
+++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/DeleteBatchingAggregateChangeTest.java
@@ -73,7 +73,7 @@ void yieldsDeleteActionsAsBatchDeletes_groupedByPath_whenGroupContainsMultipleDe
 		change.add(aggregateChange);
 
 		List> actions = extractActions(change);
-		assertThat(actions).extracting(DbAction::getClass, DbAction::getEntityType) //
+		assertThat(actions).extracting(DbAction::getClass, DbAction::entityType) //
 				.containsExactly(Tuple.tuple(DbAction.BatchDelete.class, Intermediate.class));
 		assertThat(getBatchWithValueAction(actions, Intermediate.class, DbAction.BatchDelete.class).getActions())
 				.containsExactly(intermediateDelete1, intermediateDelete2);
@@ -165,7 +165,7 @@ void yieldsDeleteRootActionsWithoutVersionAsBatchDeleteRoots_whenGroupContainsMu
 		change.add(aggregateChange4);
 
 		List> actions = extractActions(change);
-		assertThat(actions).extracting(DbAction::getClass, DbAction::getEntityType).containsExactly( //
+		assertThat(actions).extracting(DbAction::getClass, DbAction::entityType).containsExactly( //
 				Tuple.tuple(DbAction.BatchDeleteRoot.class, Root.class), //
 				Tuple.tuple(DbAction.DeleteRoot.class, Root.class), //
 				Tuple.tuple(DbAction.DeleteRoot.class, Root.class));
@@ -194,7 +194,7 @@ private  List, Object>> getBatchWit
 
 		return actions.stream() //
 				.filter(dbAction -> dbAction.getClass().equals(batchActionType)) //
-				.filter(dbAction -> dbAction.getEntityType().equals(entityType)) //
+				.filter(dbAction -> dbAction.entityType().equals(entityType)) //
 				.map(dbAction -> (DbAction.BatchWithValue, Object>) dbAction).collect(Collectors.toList());
 	}
 
diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/RelationalEntityDeleteWriterUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/RelationalEntityDeleteWriterUnitTests.java
index 11e0238b95..cc22fca64c 100644
--- a/spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/RelationalEntityDeleteWriterUnitTests.java
+++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/RelationalEntityDeleteWriterUnitTests.java
@@ -56,7 +56,7 @@ public void deleteDeletesTheEntityAndReferencedEntities() {
 		converter.write(entity.id, aggregateChange);
 
 		assertThat(extractActions(aggregateChange))
-				.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath) //
+				.extracting(DbAction::getClass, DbAction::entityType, DbActionTestSupport::extractPath) //
 				.containsExactly( //
 						Tuple.tuple(AcquireLockRoot.class, SomeEntity.class, ""), //
 						Tuple.tuple(Delete.class, YetAnother.class, "other.yetAnother"), //
@@ -75,7 +75,7 @@ public void deleteDeletesTheEntityAndNoReferencedEntities() {
 		converter.write(entity.id, aggregateChange);
 
 		assertThat(extractActions(aggregateChange))
-				.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath) //
+				.extracting(DbAction::getClass, DbAction::entityType, DbActionTestSupport::extractPath) //
 				.containsExactly(Tuple.tuple(DeleteRoot.class, SingleEntity.class, ""));
 	}
 
@@ -87,7 +87,7 @@ public void deleteAllDeletesAllEntitiesAndReferencedEntities() {
 		converter.write(null, aggregateChange);
 
 		assertThat(extractActions(aggregateChange))
-				.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath) //
+				.extracting(DbAction::getClass, DbAction::entityType, DbActionTestSupport::extractPath) //
 				.containsExactly( //
 						Tuple.tuple(AcquireLockAllRoot.class, SomeEntity.class, ""), //
 						Tuple.tuple(DeleteAll.class, YetAnother.class, "other.yetAnother"), //
@@ -104,7 +104,7 @@ public void deleteAllDeletesAllEntitiesAndNoReferencedEntities() {
 		converter.write(null, aggregateChange);
 
 		assertThat(extractActions(aggregateChange))
-				.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath) //
+				.extracting(DbAction::getClass, DbAction::entityType, DbActionTestSupport::extractPath) //
 				.containsExactly(Tuple.tuple(DeleteAllRoot.class, SingleEntity.class, ""));
 	}
 
@@ -119,7 +119,7 @@ public void deleteDoesNotDeleteReadOnlyReferences() {
 		converter.write(entity.id, aggregateChange);
 
 		assertThat(extractActions(aggregateChange))
-				.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath) //
+				.extracting(DbAction::getClass, DbAction::entityType, DbActionTestSupport::extractPath) //
 				.containsExactly( //
 						Tuple.tuple(DeleteRoot.class, WithReadOnlyReference.class, "") //
 				);
@@ -136,7 +136,7 @@ public void deleteAllDoesNotDeleteReadOnlyReferences() {
 		converter.write(null, aggregateChange);
 
 		assertThat(extractActions(aggregateChange))
-				.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath) //
+				.extracting(DbAction::getClass, DbAction::entityType, DbActionTestSupport::extractPath) //
 				.containsExactly( //
 						Tuple.tuple(DeleteAllRoot.class, WithReadOnlyReference.class, "") //
 				);
diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/RelationalEntityInsertWriterUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/RelationalEntityInsertWriterUnitTests.java
index f6014894a6..a4e570884a 100644
--- a/spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/RelationalEntityInsertWriterUnitTests.java
+++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/RelationalEntityInsertWriterUnitTests.java
@@ -48,7 +48,7 @@ public void newEntityGetsConvertedToOneInsert() {
 		new RelationalEntityInsertWriter(context).write(entity, aggregateChange);
 
 		assertThat(extractActions(aggregateChange)) //
-				.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath,
+				.extracting(DbAction::getClass, DbAction::entityType, DbActionTestSupport::extractPath,
 						DbActionTestSupport::actualEntityType, DbActionTestSupport::isWithDependsOn) //
 				.containsExactly( //
 						tuple(InsertRoot.class, SingleReferenceEntity.class, "", SingleReferenceEntity.class, false) //
@@ -65,7 +65,7 @@ public void existingEntityGetsNotConvertedToDeletePlusUpdate() {
 		new RelationalEntityInsertWriter(context).write(entity, aggregateChange);
 
 		assertThat(extractActions(aggregateChange)) //
-				.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath,
+				.extracting(DbAction::getClass, DbAction::entityType, DbActionTestSupport::extractPath,
 						DbActionTestSupport::actualEntityType, DbActionTestSupport::isWithDependsOn) //
 				.containsExactly( //
 						tuple(InsertRoot.class, SingleReferenceEntity.class, "", SingleReferenceEntity.class, false) //
diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/RelationalEntityUpdateWriterUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/RelationalEntityUpdateWriterUnitTests.java
index 0ba6c41d58..07941df24f 100644
--- a/spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/RelationalEntityUpdateWriterUnitTests.java
+++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/RelationalEntityUpdateWriterUnitTests.java
@@ -49,7 +49,7 @@ public void existingEntityGetsConvertedToDeletePlusUpdate() {
 		new RelationalEntityUpdateWriter(context).write(entity, aggregateChange);
 
 		assertThat(extractActions(aggregateChange)) //
-				.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath,
+				.extracting(DbAction::getClass, DbAction::entityType, DbActionTestSupport::extractPath,
 						DbActionTestSupport::actualEntityType, DbActionTestSupport::isWithDependsOn) //
 				.containsExactly( //
 						tuple(DbAction.UpdateRoot.class, SingleReferenceEntity.class, "", SingleReferenceEntity.class, false), //
diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/RelationalEntityWriterUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/RelationalEntityWriterUnitTests.java
index 81154ccbec..022d468004 100644
--- a/spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/RelationalEntityWriterUnitTests.java
+++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/RelationalEntityWriterUnitTests.java
@@ -15,6 +15,16 @@
  */
 package org.springframework.data.relational.core.conversion;
 
+import static org.assertj.core.api.Assertions.*;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.junit.jupiter.MockitoExtension;
@@ -30,16 +40,6 @@
 import org.springframework.data.relational.core.mapping.Embedded.OnEmpty;
 import org.springframework.data.relational.core.mapping.RelationalMappingContext;
 import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
-import org.springframework.lang.Nullable;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import static org.assertj.core.api.Assertions.*;
 
 /**
  * Unit tests for the {@link RelationalEntityWriter}
@@ -84,7 +84,7 @@ public void newEntityGetsConvertedToOneInsert() {
 
 		assertThat(extractActions(aggregateChange)) //
 				.extracting(DbAction::getClass, //
-						DbAction::getEntityType, //
+						DbAction::entityType, //
 						DbActionTestSupport::extractPath, //
 						DbActionTestSupport::actualEntityType, //
 						DbActionTestSupport::isWithDependsOn, //
@@ -105,7 +105,7 @@ void newEntityWithPrimitiveLongId_insertDoesNotIncludeId_whenIdValueIsZero() {
 
 		assertThat(extractActions(aggregateChange)) //
 				.extracting(DbAction::getClass, //
-						DbAction::getEntityType, //
+						DbAction::entityType, //
 						DbActionTestSupport::extractPath, //
 						DbActionTestSupport::actualEntityType, //
 						DbActionTestSupport::isWithDependsOn, //
@@ -126,7 +126,7 @@ void newEntityWithPrimitiveIntId_insertDoesNotIncludeId_whenIdValueIsZero() {
 
 		assertThat(extractActions(aggregateChange)) //
 				.extracting(DbAction::getClass, //
-						DbAction::getEntityType, //
+						DbAction::entityType, //
 						DbActionTestSupport::extractPath, //
 						DbActionTestSupport::actualEntityType, //
 						DbActionTestSupport::isWithDependsOn, //
@@ -149,7 +149,7 @@ public void newEntityGetsConvertedToOneInsertByEmbeddedEntities() {
 
 		assertThat(extractActions(aggregateChange)) //
 				.extracting(DbAction::getClass, //
-						DbAction::getEntityType, //
+						DbAction::entityType, //
 						DbActionTestSupport::extractPath, //
 						DbActionTestSupport::actualEntityType, //
 						DbActionTestSupport::isWithDependsOn, //
@@ -172,7 +172,7 @@ public void newEntityWithReferenceGetsConvertedToTwoInserts() {
 
 		assertThat(extractActions(aggregateChange)) //
 				.extracting(DbAction::getClass, //
-						DbAction::getEntityType, //
+						DbAction::entityType, //
 						DbActionTestSupport::extractPath, //
 						DbActionTestSupport::actualEntityType, //
 						DbActionTestSupport::isWithDependsOn, //
@@ -198,7 +198,7 @@ void newEntityWithReference_whenReferenceHasPrimitiveId_insertDoesNotIncludeId_w
 
 		assertThat(extractActions(aggregateChange)) //
 				.extracting(DbAction::getClass, //
-						DbAction::getEntityType, //
+						DbAction::entityType, //
 						DbActionTestSupport::extractPath, //
 						DbActionTestSupport::actualEntityType, //
 						DbActionTestSupport::isWithDependsOn, //
@@ -224,7 +224,7 @@ public void existingEntityGetsConvertedToDeletePlusUpdate() {
 
 		assertThat(extractActions(aggregateChange)) //
 				.extracting(DbAction::getClass, //
-						DbAction::getEntityType, //
+						DbAction::entityType, //
 						DbActionTestSupport::extractPath, //
 						DbActionTestSupport::actualEntityType, //
 						DbActionTestSupport::isWithDependsOn, //
@@ -247,13 +247,14 @@ public void newReferenceTriggersDeletePlusInsert() {
 
 		assertThat(extractActions(aggregateChange)) //
 				.extracting(DbAction::getClass, //
-						DbAction::getEntityType, //
+						DbAction::entityType, //
 						DbActionTestSupport::extractPath, //
 						DbActionTestSupport::actualEntityType, //
 						DbActionTestSupport::isWithDependsOn, //
 						DbActionTestSupport::insertIdValueSource) //
 				.containsExactly( //
-						tuple(UpdateRoot.class, SingleReferenceEntity.class, "", SingleReferenceEntity.class, false, IdValueSource.PROVIDED), //
+						tuple(UpdateRoot.class, SingleReferenceEntity.class, "", SingleReferenceEntity.class, false,
+								IdValueSource.PROVIDED), //
 						tuple(Delete.class, Element.class, "other", null, false, null), //
 						tuple(Insert.class, Element.class, "other", Element.class, true, IdValueSource.GENERATED) //
 				);
@@ -269,7 +270,7 @@ public void newEntityWithEmptySetResultsInSingleInsert() {
 
 		assertThat(extractActions(aggregateChange)) //
 				.extracting(DbAction::getClass, //
-						DbAction::getEntityType, //
+						DbAction::entityType, //
 						DbActionTestSupport::extractPath, //
 						DbActionTestSupport::actualEntityType, //
 						DbActionTestSupport::isWithDependsOn, //
@@ -290,7 +291,7 @@ public void newEntityWithSetContainingMultipleElementsResultsInAnInsertForEach()
 
 		List> actions = extractActions(aggregateChange);
 		assertThat(actions).extracting(DbAction::getClass, //
-				DbAction::getEntityType, //
+				DbAction::entityType, //
 				DbActionTestSupport::extractPath, //
 				DbActionTestSupport::actualEntityType, //
 				DbActionTestSupport::isWithDependsOn, //
@@ -323,7 +324,7 @@ public void cascadingReferencesTriggerCascadingActions() {
 
 		List> actions = extractActions(aggregateChange);
 		assertThat(actions).extracting(DbAction::getClass, //
-				DbAction::getEntityType, //
+				DbAction::entityType, //
 				DbActionTestSupport::extractPath, //
 				DbActionTestSupport::actualEntityType, //
 				DbActionTestSupport::isWithDependsOn, //
@@ -363,13 +364,14 @@ public void cascadingReferencesTriggerCascadingActionsForUpdate() {
 
 		List> actions = extractActions(aggregateChange);
 		assertThat(actions).extracting(DbAction::getClass, //
-				DbAction::getEntityType, //
+				DbAction::entityType, //
 				DbActionTestSupport::extractPath, //
 				DbActionTestSupport::actualEntityType, //
 				DbActionTestSupport::isWithDependsOn, //
 				DbActionTestSupport::insertIdValueSource) //
 				.containsExactly( //
-						tuple(UpdateRoot.class, CascadingReferenceEntity.class, "", CascadingReferenceEntity.class, false, IdValueSource.PROVIDED), //
+						tuple(UpdateRoot.class, CascadingReferenceEntity.class, "", CascadingReferenceEntity.class, false,
+								IdValueSource.PROVIDED), //
 						tuple(Delete.class, Element.class, "other.element", null, false, null),
 						tuple(Delete.class, CascadingReferenceMiddleElement.class, "other", null, false, null),
 						tuple(Insert.class, CascadingReferenceMiddleElement.class, "other", CascadingReferenceMiddleElement.class,
@@ -392,7 +394,7 @@ public void newEntityWithEmptyMapResultsInSingleInsert() {
 		new RelationalEntityWriter(context).write(entity, aggregateChange);
 
 		assertThat(extractActions(aggregateChange)).extracting(DbAction::getClass, //
-				DbAction::getEntityType, //
+				DbAction::entityType, //
 				DbActionTestSupport::extractPath, //
 				DbActionTestSupport::insertIdValueSource) //
 				.containsExactly( //
@@ -411,7 +413,7 @@ public void newEntityWithMapResultsInAdditionalInsertPerElement() {
 
 		List> actions = extractActions(aggregateChange);
 		assertThat(actions).extracting(DbAction::getClass, //
-				DbAction::getEntityType, //
+				DbAction::entityType, //
 				this::getMapKey, //
 				DbActionTestSupport::extractPath, //
 				DbActionTestSupport::insertIdValueSource) //
@@ -451,7 +453,7 @@ public void newEntityWithFullMapResultsInAdditionalInsertPerElement() {
 
 		List> actions = extractActions(aggregateChange);
 		assertThat(actions).extracting(DbAction::getClass, //
-				DbAction::getEntityType, //
+				DbAction::entityType, //
 				this::getMapKey, //
 				DbActionTestSupport::extractPath, //
 				DbActionTestSupport::insertIdValueSource) //
@@ -481,7 +483,7 @@ public void newEntityWithEmptyListResultsInSingleInsert() {
 		new RelationalEntityWriter(context).write(entity, aggregateChange);
 
 		assertThat(extractActions(aggregateChange)).extracting(DbAction::getClass, //
-				DbAction::getEntityType, //
+				DbAction::entityType, //
 				DbActionTestSupport::extractPath, //
 				DbActionTestSupport::insertIdValueSource) //
 				.containsExactly( //
@@ -500,7 +502,7 @@ public void newEntityWithListResultsInAdditionalInsertPerElement() {
 
 		List> actions = extractActions(aggregateChange);
 		assertThat(actions).extracting(DbAction::getClass, //
-				DbAction::getEntityType, //
+				DbAction::entityType, //
 				this::getListKey, //
 				DbActionTestSupport::extractPath, //
 				DbActionTestSupport::insertIdValueSource) //
@@ -523,7 +525,7 @@ public void mapTriggersDeletePlusInsert() {
 
 		assertThat(extractActions(aggregateChange)) //
 				.extracting(DbAction::getClass, //
-						DbAction::getEntityType, //
+						DbAction::entityType, //
 						this::getMapKey, //
 						DbActionTestSupport::extractPath, //
 						DbActionTestSupport::insertIdValueSource) //
@@ -546,7 +548,7 @@ public void listTriggersDeletePlusInsert() {
 
 		assertThat(extractActions(aggregateChange)) //
 				.extracting(DbAction::getClass, //
-						DbAction::getEntityType, //
+						DbAction::entityType, //
 						this::getListKey, //
 						DbActionTestSupport::extractPath, //
 						DbActionTestSupport::insertIdValueSource) //
@@ -570,7 +572,7 @@ public void multiLevelQualifiedReferencesWithId() {
 
 		assertThat(extractActions(aggregateChange)) //
 				.extracting(DbAction::getClass, //
-						DbAction::getEntityType, //
+						DbAction::entityType, //
 						a -> getQualifier(a, listMapContainerMaps), //
 						a -> getQualifier(a, listMapContainerElements), //
 						DbActionTestSupport::extractPath, //
@@ -591,14 +593,13 @@ public void multiLevelQualifiedReferencesWithOutId() {
 		listMapContainer.maps.add(new NoIdMapContainer());
 		listMapContainer.maps.get(0).elements.put("one", new NoIdElement());
 
-		RootAggregateChange aggregateChange = MutableAggregateChange.forSave(listMapContainer,
-				1L);
+		RootAggregateChange aggregateChange = MutableAggregateChange.forSave(listMapContainer, 1L);
 
 		new RelationalEntityWriter(context).write(listMapContainer, aggregateChange);
 
 		assertThat(extractActions(aggregateChange)) //
 				.extracting(DbAction::getClass, //
-						DbAction::getEntityType, //
+						DbAction::entityType, //
 						a -> getQualifier(a, noIdListMapContainerMaps), //
 						a -> getQualifier(a, noIdListMapContainerElements), //
 						DbActionTestSupport::extractPath, //
@@ -624,7 +625,7 @@ public void savingANullEmbeddedWithEntity() {
 
 		assertThat(extractActions(aggregateChange)) //
 				.extracting(DbAction::getClass, //
-						DbAction::getEntityType, //
+						DbAction::entityType, //
 						DbActionTestSupport::extractPath, //
 						DbActionTestSupport::actualEntityType, //
 						DbActionTestSupport::isWithDependsOn, //
@@ -642,14 +643,13 @@ public void savingInnerNullEmbeddedWithEntity() {
 		root.other = new EmbeddedReferenceChainEntity(null);
 		// the embedded is null !!!
 
-		RootAggregateChange aggregateChange = MutableAggregateChange
-				.forSave(root);
+		RootAggregateChange aggregateChange = MutableAggregateChange.forSave(root);
 
 		new RelationalEntityWriter(context).write(root, aggregateChange);
 
 		assertThat(extractActions(aggregateChange)) //
 				.extracting(DbAction::getClass, //
-						DbAction::getEntityType, //
+						DbAction::entityType, //
 						DbActionTestSupport::extractPath, //
 						DbActionTestSupport::actualEntityType, //
 						DbActionTestSupport::isWithDependsOn, //
@@ -676,7 +676,7 @@ void newEntityWithCollection_whenElementHasPrimitiveId_doesNotIncludeId_whenIdVa
 
 		List> actions = extractActions(aggregateChange);
 		assertThat(actions).extracting(DbAction::getClass, //
-				DbAction::getEntityType, //
+				DbAction::entityType, //
 				DbActionTestSupport::extractPath, //
 				DbActionTestSupport::actualEntityType, //
 				DbActionTestSupport::isWithDependsOn, //
@@ -702,11 +702,11 @@ public void readOnlyReferenceDoesNotCreateInsertsOnCreation() {
 		new RelationalEntityWriter(context).write(entity, aggregateChange);
 
 		assertThat(extractActions(aggregateChange)) //
-				.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath,
+				.extracting(DbAction::getClass, DbAction::entityType, DbActionTestSupport::extractPath,
 						DbActionTestSupport::actualEntityType, DbActionTestSupport::isWithDependsOn) //
 				.containsExactly( //
 						tuple(InsertRoot.class, WithReadOnlyReference.class, "", WithReadOnlyReference.class, false) //
-						// no insert for element
+				// no insert for element
 				);
 
 	}
@@ -722,11 +722,11 @@ public void readOnlyReferenceDoesNotCreateDeletesOrInsertsDuringUpdate() {
 		new RelationalEntityWriter(context).write(entity, aggregateChange);
 
 		assertThat(extractActions(aggregateChange)) //
-				.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath,
+				.extracting(DbAction::getClass, DbAction::entityType, DbActionTestSupport::extractPath,
 						DbActionTestSupport::actualEntityType, DbActionTestSupport::isWithDependsOn) //
 				.containsExactly( //
 						tuple(UpdateRoot.class, WithReadOnlyReference.class, "", WithReadOnlyReference.class, false) //
-						// no insert for element
+				// no insert for element
 				);
 
 	}
@@ -764,7 +764,7 @@ private Object getListKey(DbAction a) {
 	private Object getQualifier(DbAction a, PersistentPropertyPath path) {
 
 		return a instanceof DbAction.WithDependingOn //
-				? ((DbAction.WithDependingOn) a).getQualifiers().get(path) //
+				? ((DbAction.WithDependingOn) a).qualifiers().get(path) //
 				: null;
 	}
 
@@ -799,8 +799,7 @@ static class PrimitiveIntIdEntity {
 
 	static class SingleReferenceEntity {
 
-		@Id
-		final Long id;
+		@Id final Long id;
 		Element other;
 		// should not trigger own DbAction
 		String name;
@@ -812,10 +811,8 @@ public SingleReferenceEntity(Long id) {
 
 	static class EmbeddedReferenceEntity {
 
-		@Id
-		final Long id;
-		@Embedded(onEmpty = OnEmpty.USE_NULL, prefix = "prefix_")
-		Element other;
+		@Id final Long id;
+		@Embedded(onEmpty = OnEmpty.USE_NULL, prefix = "prefix_") Element other;
 
 		public EmbeddedReferenceEntity(Long id) {
 			this.id = id;
@@ -824,10 +821,8 @@ public EmbeddedReferenceEntity(Long id) {
 
 	static class EmbeddedReferenceChainEntity {
 
-		@Id
-		final Long id;
-		@Embedded(onEmpty = OnEmpty.USE_NULL, prefix = "prefix_")
-		ElementReference other;
+		@Id final Long id;
+		@Embedded(onEmpty = OnEmpty.USE_NULL, prefix = "prefix_") ElementReference other;
 
 		public EmbeddedReferenceChainEntity(Long id) {
 			this.id = id;
@@ -836,8 +831,7 @@ public EmbeddedReferenceChainEntity(Long id) {
 
 	static class RootWithEmbeddedReferenceChainEntity {
 
-		@Id
-		final Long id;
+		@Id final Long id;
 		EmbeddedReferenceChainEntity other;
 
 		public RootWithEmbeddedReferenceChainEntity(Long id) {
@@ -847,8 +841,7 @@ public RootWithEmbeddedReferenceChainEntity(Long id) {
 
 	static class ReferenceWoIdEntity {
 
-		@Id
-		final Long id;
+		@Id final Long id;
 		NoIdElement other;
 		// should not trigger own DbAction
 		String name;
@@ -860,8 +853,7 @@ public ReferenceWoIdEntity(Long id) {
 
 	private static class CascadingReferenceMiddleElement {
 
-		@Id
-		final Long id;
+		@Id final Long id;
 		final Set element = new HashSet<>();
 
 		public CascadingReferenceMiddleElement(Long id) {
@@ -871,8 +863,7 @@ public CascadingReferenceMiddleElement(Long id) {
 
 	private static class CascadingReferenceEntity {
 
-		@Id
-		final Long id;
+		@Id final Long id;
 		final Set other = new HashSet<>();
 
 		public CascadingReferenceEntity(Long id) {
@@ -882,8 +873,7 @@ public CascadingReferenceEntity(Long id) {
 
 	private static class SetContainer {
 
-		@Id
-		final Long id;
+		@Id final Long id;
 		Set elements = new HashSet<>();
 
 		public SetContainer(Long id) {
@@ -893,8 +883,7 @@ public SetContainer(Long id) {
 
 	private static class ListMapContainer {
 
-		@Id
-		final Long id;
+		@Id final Long id;
 		List maps = new ArrayList<>();
 
 		public ListMapContainer(Long id) {
@@ -904,8 +893,7 @@ public ListMapContainer(Long id) {
 
 	private static class MapContainer {
 
-		@Id
-		final Long id;
+		@Id final Long id;
 		Map elements = new HashMap<>();
 
 		public MapContainer(Long id) {
@@ -915,8 +903,7 @@ public MapContainer(Long id) {
 
 	private static class ListContainer {
 
-		@Id
-		final Long id;
+		@Id final Long id;
 		List elements = new ArrayList<>();
 
 		public ListContainer(Long id) {
@@ -924,27 +911,15 @@ public ListContainer(Long id) {
 		}
 	}
 
-	private static class Element {
-		@Id
-		final Long id;
-
-		public Element(Long id) {
-			this.id = id;
-		}
+	private record Element(@Id Long id) {
 	}
 
-	private static class ElementReference {
-		final Element element;
-
-		public ElementReference(Element element) {
-			this.element = element;
-		}
+	private record ElementReference(Element element) {
 	}
 
 	private static class NoIdListMapContainer {
 
-		@Id
-		final Long id;
+		@Id final Long id;
 		List maps = new ArrayList<>();
 
 		public NoIdListMapContainer(Long id) {
@@ -956,24 +931,20 @@ private static class NoIdMapContainer {
 
 		Map elements = new HashMap<>();
 
-		public NoIdMapContainer() {
-		}
+		public NoIdMapContainer() {}
 	}
 
 	private static class NoIdElement {
 		// empty classes feel weird.
 		String name;
 
-		public NoIdElement() {
-		}
+		public NoIdElement() {}
 	}
 
 	private static class WithReadOnlyReference {
 
-		@Id
-		final Long id;
-		@ReadOnlyProperty
-		Element readOnly;
+		@Id final Long id;
+		@ReadOnlyProperty Element readOnly;
 
 		public WithReadOnlyReference(Long id) {
 			this.id = id;
diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/SaveBatchingAggregateChangeTest.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/SaveBatchingAggregateChangeTest.java
index 92b719d962..92b28ad56b 100644
--- a/spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/SaveBatchingAggregateChangeTest.java
+++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/conversion/SaveBatchingAggregateChangeTest.java
@@ -79,7 +79,7 @@ void yieldsSingleInsertRoot_followedByUpdateRoot_asIndividualActions() {
 			change.add(aggregateChange2);
 
 			assertThat(extractActions(change)) //
-					.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::insertIdValueSource)
+					.extracting(DbAction::getClass, DbAction::entityType, DbActionTestSupport::insertIdValueSource)
 					.containsExactly( //
 							Tuple.tuple(DbAction.InsertRoot.class, Root.class, IdValueSource.GENERATED), //
 							Tuple.tuple(DbAction.UpdateRoot.class, Root.class, IdValueSource.PROVIDED));
@@ -110,7 +110,7 @@ void yieldsMultipleMatchingInsertRoot_followedByUpdateRoot_asBatchInsertRootActi
 
 			List> actions = extractActions(change);
 			assertThat(actions) //
-					.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::insertIdValueSource)
+					.extracting(DbAction::getClass, DbAction::entityType, DbActionTestSupport::insertIdValueSource)
 					.containsExactly( //
 							Tuple.tuple(DbAction.BatchInsertRoot.class, Root.class, IdValueSource.GENERATED), //
 							Tuple.tuple(DbAction.UpdateRoot.class, Root.class, IdValueSource.PROVIDED));
@@ -177,7 +177,7 @@ void yieldsMultipleMatchingInsertRoot_followedByNonMatchingInsertRoot_asBatchIns
 
 			List> actions = extractActions(change);
 			assertThat(actions) //
-					.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::insertIdValueSource)
+					.extracting(DbAction::getClass, DbAction::entityType, DbActionTestSupport::insertIdValueSource)
 					.containsExactly( //
 							Tuple.tuple(DbAction.BatchInsertRoot.class, Root.class, IdValueSource.GENERATED), //
 							Tuple.tuple(DbAction.InsertRoot.class, Root.class, IdValueSource.PROVIDED));
@@ -204,7 +204,7 @@ void yieldsMultipleMatchingInsertRoot_asBatchInsertRootAction() {
 
 			List> actions = extractActions(change);
 			assertThat(actions) //
-					.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::insertIdValueSource)
+					.extracting(DbAction::getClass, DbAction::entityType, DbActionTestSupport::insertIdValueSource)
 					.containsExactly(Tuple.tuple(DbAction.BatchInsertRoot.class, Root.class, IdValueSource.GENERATED));
 			assertThat(getBatchWithValueAction(actions, Root.class, DbAction.BatchInsertRoot.class).getActions())
 					.containsExactly(root1Insert, root2Insert);
@@ -228,14 +228,14 @@ void yieldsPreviouslyYieldedInsertRoot_asBatchInsertRootAction_whenAdditionalMat
 			change.add(aggregateChange1);
 
 			assertThat(extractActions(change)) //
-					.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::insertIdValueSource)
+					.extracting(DbAction::getClass, DbAction::entityType, DbActionTestSupport::insertIdValueSource)
 					.containsExactly(Tuple.tuple(DbAction.InsertRoot.class, Root.class, IdValueSource.GENERATED));
 
 			change.add(aggregateChange2);
 
 			List> actions = extractActions(change);
 			assertThat(actions) //
-					.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::insertIdValueSource)
+					.extracting(DbAction::getClass, DbAction::entityType, DbActionTestSupport::insertIdValueSource)
 					.containsExactly(Tuple.tuple(DbAction.BatchInsertRoot.class, Root.class, IdValueSource.GENERATED));
 			assertThat(getBatchWithValueAction(actions, Root.class, DbAction.BatchInsertRoot.class).getActions())
 					.containsExactly(root1Insert, root2Insert);
@@ -263,7 +263,7 @@ void yieldsRootActionsBeforeDeleteActions() {
 		change.add(aggregateChange1);
 		change.add(aggregateChange2);
 
-		assertThat(extractActions(change)).extracting(DbAction::getClass, DbAction::getEntityType).containsExactly( //
+		assertThat(extractActions(change)).extracting(DbAction::getClass, DbAction::entityType).containsExactly( //
 				Tuple.tuple(DbAction.UpdateRoot.class, Root.class), //
 				Tuple.tuple(DbAction.InsertRoot.class, Root.class), //
 				Tuple.tuple(DbAction.Delete.class, Intermediate.class));
@@ -296,7 +296,7 @@ void yieldsNestedDeleteActionsInTreeOrderFromLeavesToRoot() {
 		change.add(aggregateChange2);
 
 		List> actions = extractActions(change);
-		assertThat(actions).extracting(DbAction::getClass, DbAction::getEntityType).containsSubsequence(
+		assertThat(actions).extracting(DbAction::getClass, DbAction::entityType).containsSubsequence(
 				Tuple.tuple(DbAction.Delete.class, Leaf.class), //
 				Tuple.tuple(DbAction.BatchDelete.class, Intermediate.class));
 		assertThat(getBatchWithValueAction(actions, Intermediate.class, DbAction.BatchDelete.class).getActions())
@@ -321,7 +321,7 @@ void yieldsDeleteActionsAsBatchDeletes_groupedByPath_whenGroupContainsMultipleDe
 		change.add(aggregateChange);
 
 		List> actions = extractActions(change);
-		assertThat(actions).extracting(DbAction::getClass, DbAction::getEntityType) //
+		assertThat(actions).extracting(DbAction::getClass, DbAction::entityType) //
 				.containsExactly( //
 						Tuple.tuple(DbAction.UpdateRoot.class, Root.class), //
 						Tuple.tuple(DbAction.BatchDelete.class, Intermediate.class));
@@ -354,7 +354,7 @@ void yieldsDeleteActionsBeforeInsertActions() {
 		change.add(aggregateChange1);
 		change.add(aggregateChange2);
 
-		assertThat(extractActions(change)).extracting(DbAction::getClass, DbAction::getEntityType).containsSubsequence( //
+		assertThat(extractActions(change)).extracting(DbAction::getClass, DbAction::entityType).containsSubsequence( //
 				Tuple.tuple(DbAction.Delete.class, Intermediate.class), //
 				Tuple.tuple(DbAction.BatchInsert.class, Intermediate.class));
 	}
@@ -382,7 +382,7 @@ void yieldsInsertActionsAsBatchInserts_groupedByIdValueSource() {
 
 		List> actions = extractActions(change);
 		assertThat(actions)
-				.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::insertIdValueSource) //
+				.extracting(DbAction::getClass, DbAction::entityType, DbActionTestSupport::insertIdValueSource) //
 				.containsSubsequence( //
 						Tuple.tuple(DbAction.InsertRoot.class, Root.class, IdValueSource.GENERATED), //
 						Tuple.tuple(DbAction.BatchInsert.class, Intermediate.class, IdValueSource.PROVIDED)) //
@@ -433,7 +433,7 @@ void yieldsNestedInsertActionsInTreeOrderFromRootToLeaves() {
 
 		List> actions = extractActions(change);
 		assertThat(actions)
-				.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::insertIdValueSource)
+				.extracting(DbAction::getClass, DbAction::entityType, DbActionTestSupport::insertIdValueSource)
 				.containsSubsequence( //
 						Tuple.tuple(DbAction.BatchInsert.class, Intermediate.class, IdValueSource.GENERATED),
 						Tuple.tuple(DbAction.BatchInsert.class, Leaf.class, IdValueSource.GENERATED));
@@ -470,7 +470,7 @@ void yieldsInsertsWithSameLengthReferences_asSeparateInserts() {
 
 		List> actions = extractActions(change);
 		assertThat(actions)
-				.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::insertIdValueSource)
+				.extracting(DbAction::getClass, DbAction::entityType, DbActionTestSupport::insertIdValueSource)
 				.containsSubsequence( //
 						Tuple.tuple(DbAction.BatchInsert.class, Intermediate.class, IdValueSource.GENERATED),
 						Tuple.tuple(DbAction.BatchInsert.class, Intermediate.class, IdValueSource.GENERATED));
@@ -509,7 +509,7 @@ private  List, Object>> getBatchWit
 
 		return actions.stream() //
 				.filter(dbAction -> dbAction.getClass().equals(batchActionType)) //
-				.filter(dbAction -> dbAction.getEntityType().equals(entityType)) //
+				.filter(dbAction -> dbAction.entityType().equals(entityType)) //
 				.map(dbAction -> (DbAction.BatchWithValue, Object>) dbAction).collect(Collectors.toList());
 	}
 
diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentPropertyUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentPropertyUnitTests.java
index 86f3c53858..08157df34c 100644
--- a/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentPropertyUnitTests.java
+++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentPropertyUnitTests.java
@@ -24,6 +24,7 @@
 import java.time.ZonedDateTime;
 import java.util.Date;
 import java.util.List;
+import java.util.Objects;
 import java.util.function.BiConsumer;
 
 import org.assertj.core.api.SoftAssertions;
@@ -45,8 +46,8 @@
  */
 class BasicRelationalPersistentPropertyUnitTests {
 
-	private RelationalMappingContext context = new RelationalMappingContext();
-	private RelationalPersistentEntity entity = context.getRequiredPersistentEntity(DummyEntity.class);
+	private final RelationalMappingContext context = new RelationalMappingContext();
+	private final RelationalPersistentEntity entity = context.getRequiredPersistentEntity(DummyEntity.class);
 
 	@Test // DATAJDBC-106
 	void detectsAnnotatedColumnName() {
@@ -104,17 +105,19 @@ void detectsEmbeddedEntity() {
 
 			RelationalPersistentProperty property = requiredPersistentEntity.getRequiredPersistentProperty(name);
 
-			softly.assertThat(property.isEmbedded()) //
-					.describedAs(name + " is embedded") //
-					.isEqualTo(prefix != null);
+			if (!prefix.isEmpty()) {
+				softly.assertThat(property.isEmbedded()) //
+						.describedAs(name + " is embedded") //
+						.isTrue();
+			}
 
 			softly.assertThat(property.getEmbeddedPrefix()) //
 					.describedAs(name + " prefix") //
 					.isEqualTo(prefix);
 		};
 
-		checkEmbedded.accept("someList", null);
-		checkEmbedded.accept("id", null);
+		checkEmbedded.accept("someList", "");
+		checkEmbedded.accept("id", "");
 		checkEmbedded.accept("embeddableEntity", "");
 		checkEmbedded.accept("prefixedEmbeddableEntity", "prefix");
 
@@ -341,77 +344,67 @@ public void setPrefixedEmbeddableEntity(EmbeddableEntity prefixedEmbeddableEntit
 		public boolean equals(final Object o) {
 			if (o == this)
 				return true;
-			if (!(o instanceof DummyEntity))
+			if (!(o instanceof DummyEntity other))
 				return false;
-			final DummyEntity other = (DummyEntity) o;
-			if (!other.canEqual((Object) this))
+			if (!other.canEqual(this))
 				return false;
 			final Object this$id = this.getId();
 			final Object other$id = other.getId();
-			if (this$id == null ? other$id != null : !this$id.equals(other$id))
+			if (!Objects.equals(this$id, other$id))
 				return false;
 			final Object this$someEnum = this.getSomeEnum();
 			final Object other$someEnum = other.getSomeEnum();
-			if (this$someEnum == null ? other$someEnum != null : !this$someEnum.equals(other$someEnum))
+			if (!Objects.equals(this$someEnum, other$someEnum))
 				return false;
 			final Object this$localDateTime = this.getLocalDateTime();
 			final Object other$localDateTime = other.getLocalDateTime();
-			if (this$localDateTime == null ? other$localDateTime != null : !this$localDateTime.equals(other$localDateTime))
+			if (!Objects.equals(this$localDateTime, other$localDateTime))
 				return false;
 			final Object this$zonedDateTime = this.getZonedDateTime();
 			final Object other$zonedDateTime = other.getZonedDateTime();
-			if (this$zonedDateTime == null ? other$zonedDateTime != null : !this$zonedDateTime.equals(other$zonedDateTime))
+			if (!Objects.equals(this$zonedDateTime, other$zonedDateTime))
 				return false;
 			final Object this$listOfString = this.getListOfString();
 			final Object other$listOfString = other.getListOfString();
-			if (this$listOfString == null ? other$listOfString != null : !this$listOfString.equals(other$listOfString))
+			if (!Objects.equals(this$listOfString, other$listOfString))
 				return false;
 			if (!java.util.Arrays.deepEquals(this.getArrayOfString(), other.getArrayOfString()))
 				return false;
 			final Object this$listOfEntity = this.getListOfEntity();
 			final Object other$listOfEntity = other.getListOfEntity();
-			if (this$listOfEntity == null ? other$listOfEntity != null : !this$listOfEntity.equals(other$listOfEntity))
+			if (!Objects.equals(this$listOfEntity, other$listOfEntity))
 				return false;
 			if (!java.util.Arrays.deepEquals(this.getArrayOfEntity(), other.getArrayOfEntity()))
 				return false;
 			final Object this$someList = this.getSomeList();
 			final Object other$someList = other.getSomeList();
-			if (this$someList == null ? other$someList != null : !this$someList.equals(other$someList))
+			if (!Objects.equals(this$someList, other$someList))
 				return false;
 			final Object this$name = this.getName();
 			final Object other$name = other.getName();
-			if (this$name == null ? other$name != null : !this$name.equals(other$name))
+			if (!Objects.equals(this$name, other$name))
 				return false;
 			final Object this$spelExpression1 = this.getSpelExpression1();
 			final Object other$spelExpression1 = other.getSpelExpression1();
-			if (this$spelExpression1 == null ? other$spelExpression1 != null
-					: !this$spelExpression1.equals(other$spelExpression1))
+			if (!Objects.equals(this$spelExpression1, other$spelExpression1))
 				return false;
 			final Object this$littleBobbyTables = this.getLittleBobbyTables();
 			final Object other$littleBobbyTables = other.getLittleBobbyTables();
-			if (this$littleBobbyTables == null ? other$littleBobbyTables != null
-					: !this$littleBobbyTables.equals(other$littleBobbyTables))
+			if (!Objects.equals(this$littleBobbyTables, other$littleBobbyTables))
 				return false;
 			final Object this$poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot = this
 					.getPoorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot();
 			final Object other$poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot = other
 					.getPoorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot();
-			if (this$poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot == null
-					? other$poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot != null
-					: !this$poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot
-							.equals(other$poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot))
+			if (!Objects.equals(this$poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot, other$poorDeveloperProgrammaticallyAskingToShootThemselvesInTheFoot))
 				return false;
 			final Object this$embeddableEntity = this.getEmbeddableEntity();
 			final Object other$embeddableEntity = other.getEmbeddableEntity();
-			if (this$embeddableEntity == null ? other$embeddableEntity != null
-					: !this$embeddableEntity.equals(other$embeddableEntity))
+			if (!Objects.equals(this$embeddableEntity, other$embeddableEntity))
 				return false;
 			final Object this$prefixedEmbeddableEntity = this.getPrefixedEmbeddableEntity();
 			final Object other$prefixedEmbeddableEntity = other.getPrefixedEmbeddableEntity();
-			if (this$prefixedEmbeddableEntity == null ? other$prefixedEmbeddableEntity != null
-					: !this$prefixedEmbeddableEntity.equals(other$prefixedEmbeddableEntity))
-				return false;
-			return true;
+			return Objects.equals(this$prefixedEmbeddableEntity, other$prefixedEmbeddableEntity);
 		}
 
 		boolean canEqual(final Object other) {
@@ -479,49 +472,38 @@ private enum SomeEnum {
 	}
 
 	// DATAJDBC-111
-	private static class EmbeddableEntity {
-		private final String embeddedTest;
-
-		public EmbeddableEntity(String embeddedTest) {
-			this.embeddedTest = embeddedTest;
-		}
+		private record EmbeddableEntity(String embeddedTest) {
 
-		String getEmbeddedTest() {
-			return this.embeddedTest;
-		}
 
 		public boolean equals(final Object o) {
-			if (o == this)
-				return true;
-			if (!(o instanceof EmbeddableEntity))
-				return false;
-			final EmbeddableEntity other = (EmbeddableEntity) o;
-			if (!other.canEqual((Object) this))
-				return false;
-			final Object this$embeddedTest = this.getEmbeddedTest();
-			final Object other$embeddedTest = other.getEmbeddedTest();
-			if (this$embeddedTest == null ? other$embeddedTest != null : !this$embeddedTest.equals(other$embeddedTest))
-				return false;
-			return true;
-		}
-
-		boolean canEqual(final Object other) {
-			return other instanceof EmbeddableEntity;
+				if (o == this)
+					return true;
+				if (!(o instanceof EmbeddableEntity other))
+					return false;
+				if (!other.canEqual(this))
+					return false;
+				final Object this$embeddedTest = this.embeddedTest();
+				final Object other$embeddedTest = other.embeddedTest();
+				return Objects.equals(this$embeddedTest, other$embeddedTest);
+			}
+
+			boolean canEqual(final Object other) {
+				return other instanceof EmbeddableEntity;
+			}
+
+			public int hashCode() {
+				final int PRIME = 59;
+				int result = 1;
+				final Object $embeddedTest = this.embeddedTest();
+				result = result * PRIME + ($embeddedTest == null ? 43 : $embeddedTest.hashCode());
+				return result;
+			}
+
+			public String toString() {
+				return "BasicRelationalPersistentPropertyUnitTests.EmbeddableEntity(embeddedTest=" + this.embeddedTest() + ")";
+			}
 		}
 
-		public int hashCode() {
-			final int PRIME = 59;
-			int result = 1;
-			final Object $embeddedTest = this.getEmbeddedTest();
-			result = result * PRIME + ($embeddedTest == null ? 43 : $embeddedTest.hashCode());
-			return result;
-		}
-
-		public String toString() {
-			return "BasicRelationalPersistentPropertyUnitTests.EmbeddableEntity(embeddedTest=" + this.getEmbeddedTest() + ")";
-		}
-	}
-
 	@SuppressWarnings("unused")
 	private static class OtherEntity {}
 
diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/repository/query/ParameterMetadataProviderUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/repository/query/ParameterMetadataProviderUnitTests.java
index 7d79a06def..c53f8534ca 100644
--- a/spring-data-relational/src/test/java/org/springframework/data/relational/repository/query/ParameterMetadataProviderUnitTests.java
+++ b/spring-data-relational/src/test/java/org/springframework/data/relational/repository/query/ParameterMetadataProviderUnitTests.java
@@ -44,8 +44,8 @@ public void shouldCreateValueFunctionForContains() throws Exception {
 
 		ParameterMetadata metadata = getParameterMetadata("findByNameContains", "hell%o");
 
-		assertThat(metadata.getValue()).isInstanceOf(ValueFunction.class);
-		ValueFunction function = (ValueFunction) metadata.getValue();
+		assertThat(metadata.value()).isInstanceOf(ValueFunction.class);
+		ValueFunction function = (ValueFunction) metadata.value();
 		assertThat(function.apply(Escaper.DEFAULT)).isEqualTo("%hell\\%o%");
 	}
 
@@ -54,8 +54,8 @@ public void shouldCreateValueFunctionForStartingWith() throws Exception {
 
 		ParameterMetadata metadata = getParameterMetadata("findByNameStartingWith", "hell%o");
 
-		assertThat(metadata.getValue()).isInstanceOf(ValueFunction.class);
-		ValueFunction function = (ValueFunction) metadata.getValue();
+		assertThat(metadata.value()).isInstanceOf(ValueFunction.class);
+		ValueFunction function = (ValueFunction) metadata.value();
 		assertThat(function.apply(Escaper.DEFAULT)).isEqualTo("hell\\%o%");
 	}
 
@@ -64,7 +64,7 @@ public void shouldCreateValue() throws Exception {
 
 		ParameterMetadata metadata = getParameterMetadata("findByName", "hell%o");
 
-		assertThat(metadata.getValue()).isEqualTo("hell%o");
+		assertThat(metadata.value()).isEqualTo("hell%o");
 	}
 
 	private ParameterMetadata getParameterMetadata(String methodName, Object value) throws Exception {
diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/repository/query/RelationalExampleMapperTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/repository/query/RelationalExampleMapperTests.java
index a29ef24845..e42ddc8083 100644
--- a/spring-data-relational/src/test/java/org/springframework/data/relational/repository/query/RelationalExampleMapperTests.java
+++ b/spring-data-relational/src/test/java/org/springframework/data/relational/repository/query/RelationalExampleMapperTests.java
@@ -25,6 +25,7 @@
 import java.util.Map;
 import java.util.Objects;
 
+import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.springframework.data.annotation.Id;
@@ -32,7 +33,6 @@
 import org.springframework.data.domain.ExampleMatcher;
 import org.springframework.data.relational.core.mapping.RelationalMappingContext;
 import org.springframework.data.relational.core.query.Query;
-import org.springframework.lang.Nullable;
 
 /**
  * Verify that the {@link RelationalExampleMapper} properly turns {@link Example}s into {@link Query}'s.

From 093e43cdb065375df31dafe8ea2b68824bd34b35 Mon Sep 17 00:00:00 2001
From: Mark Paluch 
Date: Tue, 2 Sep 2025 14:32:14 +0200
Subject: [PATCH 3/5] Polishing.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Refine nullability annotation formatting, reduce warnings (final fields, isEmpty vs. length() == null), introduce getRequired…() methods to avoid Assert sprawl. Convert internal classes to records. Fix generic types (refactoring leftovers).

Replace package-info imports with fully-qualified annotation name to align formatting.
---
 .../jdbc/core/convert/AggregateReader.java    |  9 +--
 .../core/convert/BatchInsertStrategy.java     |  3 +
 .../convert/CascadingDataAccessStrategy.java  | 13 ++--
 .../jdbc/core/convert/DataAccessStrategy.java |  9 +--
 .../convert/DefaultDataAccessStrategy.java    |  9 ++-
 .../convert/DelegatingDataAccessStrategy.java |  5 +-
 .../core/convert/InsertStrategyFactory.java   | 16 ++--
 .../core/convert/JdbcIdentifierBuilder.java   |  4 +-
 .../core/convert/MappingJdbcConverter.java    |  7 +-
 .../data/jdbc/core/convert/QueryMapper.java   | 18 ++---
 .../convert/QueryMappingConfiguration.java    |  4 +-
 .../convert/ReadingDataAccessStrategy.java    |  3 +-
 .../convert/RowDocumentExtractorSupport.java  | 10 +--
 .../SingleQueryDataAccessStrategy.java        |  3 +-
 ...SingleQueryFallbackDataAccessStrategy.java |  2 +-
 .../data/jdbc/core/convert/package-info.java  |  3 +-
 .../data/jdbc/core/dialect/package-info.java  |  3 +-
 .../core/mapping/schema/package-info.java     |  4 +-
 .../data/jdbc/core/package-info.java          |  3 +-
 .../mybatis/MyBatisDataAccessStrategy.java    | 12 +--
 .../data/jdbc/mybatis/package-info.java       |  3 +-
 .../DefaultQueryMappingConfiguration.java     |  5 +-
 .../jdbc/repository/config/package-info.java  |  3 +-
 .../query/AbstractDelegatingRowMapper.java    | 11 +--
 .../query/CallbackCapableRowMapper.java       |  3 +-
 .../repository/query/ConvertingRowMapper.java |  4 +-
 .../jdbc/repository/query/package-info.java   |  3 +-
 .../support/JdbcRepositoryFactory.java        |  2 +-
 .../jdbc/repository/support/package-info.java |  6 +-
 .../data/jdbc/support/package-info.java       |  3 +-
 .../data/r2dbc/convert/R2dbcConverters.java   | 24 +++---
 .../r2dbc/convert/RowPropertyAccessor.java    | 75 -------------------
 .../SequenceEntityCallbackDelegate.java       |  2 +-
 .../r2dbc/core/DefaultStatementMapper.java    |  2 -
 .../data/r2dbc/core/R2dbcEntityTemplate.java  | 24 +++---
 .../core/ReactiveDeleteOperationSupport.java  |  8 +-
 .../core/ReactiveInsertOperationSupport.java  |  8 +-
 .../core/ReactiveSelectOperationSupport.java  |  8 +-
 .../core/ReactiveUpdateOperationSupport.java  |  8 +-
 .../data/r2dbc/core/StatementMapper.java      |  5 +-
 .../data/r2dbc/dialect/package-info.java      |  4 +-
 .../data/r2dbc/mapping/package-info.java      |  4 +-
 .../data/r2dbc/query/QueryMapper.java         | 23 ++----
 ...R2dbcRepositoryConfigurationExtension.java |  4 -
 .../ExpressionEvaluatingParameterBinder.java  |  2 +-
 .../repository/query/ExpressionQuery.java     | 14 +---
 .../query/PreparedOperationBindableQuery.java |  7 +-
 .../query/R2dbcParameterAccessor.java         |  7 +-
 .../query/StringBasedR2dbcQuery.java          |  8 +-
 .../r2dbc/repository/query/package-info.java  |  4 +-
 .../support/SimpleR2dbcRepository.java        |  2 +-
 .../query/ExpressionQueryUnitTests.java       |  2 +-
 .../data/relational/aot/package-info.java     |  4 +-
 .../relational/core/conversion/DbAction.java  |  2 +-
 .../DefaultRootAggregateChange.java           |  2 +-
 .../conversion/DeleteAggregateChange.java     |  2 +-
 .../MappingRelationalConverter.java           | 20 +++--
 .../core/conversion/RelationalConverter.java  |  2 +-
 .../core/conversion/WritingContext.java       |  2 +-
 .../core/conversion/package-info.java         |  4 +-
 .../data/relational/core/dialect/Escaper.java |  3 +-
 .../relational/core/dialect/package-info.java |  4 +-
 .../core/mapping/AggregatePath.java           | 22 +++++-
 .../BasicRelationalPersistentEntity.java      |  3 +-
 .../BasicRelationalPersistentProperty.java    |  9 +--
 .../core/mapping/DefaultAggregatePath.java    |  7 +-
 .../core/mapping/EmbeddedContext.java         |  7 +-
 .../core/mapping/InsertOnlyProperty.java      |  1 -
 .../core/mapping/event/package-info.java      |  4 +-
 .../relational/core/mapping/package-info.java |  4 +-
 .../data/relational/core/query/Criteria.java  |  5 +-
 .../core/query/CriteriaDefinition.java        | 41 +++++++---
 .../data/relational/core/query/Update.java    | 10 +--
 .../relational/core/query/ValueFunction.java  |  3 +-
 .../core/sql/AbstractImportValidator.java     |  4 +-
 .../relational/core/sql/AnalyticFunction.java | 16 ++--
 .../relational/core/sql/CaseExpression.java   |  2 +-
 .../data/relational/core/sql/Column.java      | 40 +++++++---
 .../relational/core/sql/DeleteValidator.java  |  4 +-
 .../data/relational/core/sql/Expressions.java |  2 +-
 .../core/sql/IdentifierProcessing.java        |  2 +-
 .../data/relational/core/sql/Literal.java     |  3 +-
 .../relational/core/sql/NumericLiteral.java   |  3 +-
 .../relational/core/sql/SelectValidator.java  |  8 +-
 .../relational/core/sql/StringLiteral.java    |  3 +-
 .../core/sql/SubselectExpression.java         |  1 -
 .../relational/core/sql/package-info.java     |  4 +-
 .../sql/render/AnalyticFunctionVisitor.java   |  6 +-
 .../core/sql/render/AssignmentVisitor.java    |  2 +-
 .../core/sql/render/BetweenVisitor.java       |  6 --
 .../sql/render/CaseExpressionVisitor.java     |  3 +-
 .../core/sql/render/CastVisitor.java          |  6 +-
 .../core/sql/render/ComparisonVisitor.java    |  2 +-
 .../core/sql/render/DelegatingVisitor.java    |  8 +-
 .../sql/render/DeleteStatementVisitor.java    | 16 ++--
 .../core/sql/render/FromTableVisitor.java     |  2 +-
 .../sql/render/InsertStatementVisitor.java    |  8 +-
 .../core/sql/render/NameRenderer.java         | 10 +--
 .../core/sql/render/OrderByClauseVisitor.java |  2 +-
 .../core/sql/render/SegmentListVisitor.java   |  3 +-
 .../core/sql/render/SelectListVisitor.java    |  2 +-
 .../sql/render/SelectStatementVisitor.java    | 32 ++++----
 .../core/sql/render/SubselectVisitor.java     |  2 +-
 .../sql/render/UpdateStatementVisitor.java    | 24 +++---
 .../core/sql/render/WhenVisitor.java          | 50 ++++++-------
 .../core/sql/render/package-info.java         |  4 +-
 .../core/sqlgeneration/AliasFactory.java      |  1 +
 .../data/relational/domain/RowDocument.java   |  8 +-
 .../data/relational/domain/SqlSort.java       |  5 +-
 ...RelationalParametersParameterAccessor.java |  8 +-
 .../repository/query/package-info.java        |  4 +-
 .../repository/support/package-info.java      |  4 +-
 .../DefaultIdentifierProcessingUnitTests.java | 10 +--
 113 files changed, 394 insertions(+), 515 deletions(-)
 delete mode 100644 spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/RowPropertyAccessor.java

diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/AggregateReader.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/AggregateReader.java
index bf4526e267..fb2bf028fb 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/AggregateReader.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/AggregateReader.java
@@ -94,8 +94,7 @@ public String keyColumn(AggregatePath path) {
 	 * @return the found aggregate root, or {@literal null} if not found.
 	 * @param  aggregator type.
 	 */
-	@Nullable
-	public  T findById(Object id, RelationalPersistentEntity entity) {
+	public  @Nullable T findById(Object id, RelationalPersistentEntity entity) {
 
 		Query query = Query.query(Criteria.where(entity.getRequiredIdProperty().getName()).is(id)).limit(1);
 
@@ -111,8 +110,7 @@ public  T findById(Object id, RelationalPersistentEntity entity) {
 	 * @param  aggregator type.
 	 */
 	@SuppressWarnings("NullAway") // NullAway complains about the ResultSetExtractor returning null, which should be ok.
-	@Nullable
-	public  T findOne(Query query, RelationalPersistentEntity entity) {
+	public  @Nullable T findOne(Query query, RelationalPersistentEntity entity) {
 		return doFind(query, entity, rs -> extractZeroOrOne(rs, entity));
 	}
 
@@ -159,7 +157,8 @@ public  List findAll(Query query, RelationalPersistentEntity entity) {
 	}
 
 	@SuppressWarnings("ConstantConditions")
-	private  R doFind(Query query, RelationalPersistentEntity entity, ResultSetExtractor extractor) {
+	private  R doFind(Query query, RelationalPersistentEntity entity,
+			ResultSetExtractor extractor) {
 
 		MapSqlParameterSource parameterSource = new MapSqlParameterSource();
 		Condition condition = createCondition(query, parameterSource, entity);
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BatchInsertStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BatchInsertStrategy.java
index 7a63d216e6..9be6f860a5 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BatchInsertStrategy.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BatchInsertStrategy.java
@@ -15,6 +15,8 @@
  */
 package org.springframework.data.jdbc.core.convert;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.jdbc.core.namedparam.SqlParameterSource;
 
 /**
@@ -32,5 +34,6 @@ interface BatchInsertStrategy {
 	 *         elements will be {@code null}.
 	 * @since 2.4
 	 */
+	@Nullable
 	Object[] execute(String sql, SqlParameterSource[] sqlParameterSources);
 }
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/CascadingDataAccessStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/CascadingDataAccessStrategy.java
index be81bf1c23..283612d8bf 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/CascadingDataAccessStrategy.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/CascadingDataAccessStrategy.java
@@ -24,6 +24,8 @@
 import java.util.function.Function;
 import java.util.stream.Stream;
 
+import org.jspecify.annotations.Nullable;
+
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.domain.Sort;
 import org.springframework.data.mapping.PersistentPropertyPath;
@@ -74,12 +76,14 @@ public NamedParameterJdbcOperations getJdbcOperations() {
 	}
 
 	@Override
-	public  Object insert(T instance, Class domainType, Identifier identifier, IdValueSource idValueSource) {
+	public  @Nullable Object insert(T instance, Class domainType, Identifier identifier,
+			IdValueSource idValueSource) {
 		return collect(das -> das.insert(instance, domainType, identifier, idValueSource));
 	}
 
 	@Override
-	public  Object[] insert(List> insertSubjects, Class domainType, IdValueSource idValueSource) {
+	public  @Nullable Object[] insert(List> insertSubjects, Class domainType,
+			IdValueSource idValueSource) {
 		return collect(das -> das.insert(insertSubjects, domainType, idValueSource));
 	}
 
@@ -144,7 +148,7 @@ public long count(Class domainType) {
 	}
 
 	@Override
-	public  T findById(Object id, Class domainType) {
+	public  T findById(Object id, Class domainType) {
 		return collect(das -> das.findById(id, domainType));
 	}
 
@@ -224,8 +228,7 @@ public  long count(Query query, Class domainType) {
 		return collect(das -> das.count(query, domainType));
 	}
 
-	private  T collect(Function function) {
-
+	private  T collect(Function function) {
 		return strategies.stream().collect(new FunctionCollector<>(function));
 	}
 
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DataAccessStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DataAccessStrategy.java
index 4d08c00f36..7a41626841 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DataAccessStrategy.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DataAccessStrategy.java
@@ -73,8 +73,7 @@ public interface DataAccessStrategy extends ReadingDataAccessStrategy, RelationR
 	 * @return the id generated by the database if any.
 	 * @since 2.4
 	 */
-	@Nullable
-	 Object insert(T instance, Class domainType, Identifier identifier, IdValueSource idValueSource);
+	 @Nullable Object insert(T instance, Class domainType, Identifier identifier, IdValueSource idValueSource);
 
 	/**
 	 * Inserts the data of multiple entities.
@@ -88,7 +87,8 @@ public interface DataAccessStrategy extends ReadingDataAccessStrategy, RelationR
 	 *         elements will be {@code null}.
 	 * @since 2.4
 	 */
-	 Object[] insert(List> insertSubjects, Class domainType, IdValueSource idValueSource);
+	 @Nullable Object[] insert(List> insertSubjects, Class domainType,
+			IdValueSource idValueSource);
 
 	/**
 	 * Updates the data of a single entity in the database. Referenced entities don't get handled.
@@ -255,8 +255,7 @@ public interface DataAccessStrategy extends ReadingDataAccessStrategy, RelationR
 	 * @return Might return {@code null}.
 	 */
 	@Override
-	@Nullable
-	 T findById(Object id, Class domainType);
+	 T findById(Object id, Class domainType);
 
 	/**
 	 * Loads all entities of the given type.
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java
index 59445dc1f9..e8cdee7dae 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java
@@ -130,7 +130,8 @@ public NamedParameterJdbcOperations getJdbcOperations() {
 	}
 
 	@Override
-	public  Object[] insert(List> insertSubjects, Class domainType, IdValueSource idValueSource) {
+	public  @Nullable Object[] insert(List> insertSubjects, Class domainType,
+			IdValueSource idValueSource) {
 
 		Assert.notEmpty(insertSubjects, "Batch insert must contain at least one InsertSubject");
 
@@ -282,7 +283,8 @@ public long count(Class domainType) {
 	}
 
 	@Override
-	public  @Nullable T findById(Object id, Class domainType) {
+	@SuppressWarnings("NullAway")
+	public  T findById(Object id, Class domainType) {
 
 		String findOneSql = sql(domainType).getFindOne();
 		SqlIdentifierParameterSource parameter = sqlParametersFactory.forQueryById(id, domainType);
@@ -331,7 +333,6 @@ public  Stream streamAllByIds(Iterable ids, Class domainType) {
 	}
 
 	@Override
-	@SuppressWarnings("unchecked")
 	public List findAllByPath(Identifier identifier,
 			PersistentPropertyPath propertyPath) {
 
@@ -363,7 +364,7 @@ public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
 				if (!path.hasIdProperty() && path.isQualified()) {
 
 					TableInfo tableInfo = path.getTableInfo();
-					identifierToUse = identifierToUse.withPart(tableInfo.requiredQualifierColumnInfo().name(), rowNum,
+					identifierToUse = identifierToUse.withPart(tableInfo.getRequiredQualifierColumnInfo().name(), rowNum,
 							Object.class);
 				}
 
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DelegatingDataAccessStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DelegatingDataAccessStrategy.java
index fad57b737c..f3d625c4a4 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DelegatingDataAccessStrategy.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DelegatingDataAccessStrategy.java
@@ -75,7 +75,8 @@ public NamedParameterJdbcOperations getJdbcOperations() {
 	}
 
 	@Override
-	public  Object[] insert(List> insertSubjects, Class domainType, IdValueSource idValueSource) {
+	public  @Nullable Object[] insert(List> insertSubjects, Class domainType,
+			IdValueSource idValueSource) {
 		return delegate.insert(insertSubjects, domainType, idValueSource);
 	}
 
@@ -141,7 +142,7 @@ public long count(Class domainType) {
 	}
 
 	@Override
-	public  @Nullable T findById(Object id, Class domainType) {
+	public  T findById(Object id, Class domainType) {
 
 		Assert.notNull(delegate, "Delegate is null");
 
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/InsertStrategyFactory.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/InsertStrategyFactory.java
index 3f47d5a700..7ba7635320 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/InsertStrategyFactory.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/InsertStrategyFactory.java
@@ -73,22 +73,22 @@ BatchInsertStrategy batchInsertStrategy(IdValueSource idValueSource, @Nullable S
 	private record DefaultInsertStrategy(NamedParameterJdbcOperations jdbcOperations) implements InsertStrategy {
 
 		@Override
-			public @Nullable Object execute(String sql, SqlParameterSource sqlParameterSource) {
+		public @Nullable Object execute(String sql, SqlParameterSource sqlParameterSource) {
 
-				jdbcOperations.update(sql, sqlParameterSource);
-				return null;
-			}
+			jdbcOperations.update(sql, sqlParameterSource);
+			return null;
 		}
+	}
 
 	private record DefaultBatchInsertStrategy(
 			NamedParameterJdbcOperations jdbcOperations) implements BatchInsertStrategy {
 
 		@Override
-			public Object[] execute(String sql, SqlParameterSource[] sqlParameterSources) {
+		public @Nullable Object[] execute(String sql, SqlParameterSource[] sqlParameterSources) {
 
-				jdbcOperations.batchUpdate(sql, sqlParameterSources);
-				return new Object[sqlParameterSources.length];
-			}
+			jdbcOperations.batchUpdate(sql, sqlParameterSources);
+			return new Object[sqlParameterSources.length];
 		}
+	}
 
 }
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcIdentifierBuilder.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcIdentifierBuilder.java
index b9eba04ba8..95ac1e3eaa 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcIdentifierBuilder.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcIdentifierBuilder.java
@@ -93,8 +93,8 @@ public JdbcIdentifierBuilder withQualifier(AggregatePath path, Object value) {
 		Assert.notNull(value, "Value must not be null");
 
 		AggregatePath.TableInfo tableInfo = path.getTableInfo();
-		identifier = identifier.withPart(tableInfo.requiredQualifierColumnInfo().name(), value,
-				tableInfo.requiredQualifierColumnType());
+		identifier = identifier.withPart(tableInfo.getRequiredQualifierColumnInfo().name(), value,
+				tableInfo.getRequiredQualifierColumnType());
 
 		return this;
 	}
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MappingJdbcConverter.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MappingJdbcConverter.java
index 22f3309909..e01bda8fb1 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MappingJdbcConverter.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MappingJdbcConverter.java
@@ -374,7 +374,7 @@ protected RelationalPropertyValueProvider newValueProvider(RowDocumentAccessor d
 	 * @param ex the offending {@code SQLException}
 	 * @return a DataAccessException wrapping the {@code SQLException} (never {@code null})
 	 */
-	private DataAccessException translateException(String task, @org.jspecify.annotations.Nullable String sql,
+	private DataAccessException translateException(String task, @Nullable String sql,
 			SQLException ex) {
 		DataAccessException dae = exceptionTranslator.translate(task, sql, ex);
 		return (dae != null ? dae : new UncategorizedSQLException(task, sql, ex));
@@ -556,10 +556,7 @@ private static AggregatePath smartAppend(AggregatePath base, AggregatePath suffi
 		if (owner.equals(base.getRequiredLeafEntity())) {
 			return base.append(suffix);
 		} else {
-			AggregatePath tail = suffix.getTail();
-
-			Assert.state(tail != null, "Tail must not be null");
-
+			AggregatePath tail = suffix.getRequiredTail();
 			return smartAppend(base, tail);
 		}
 	}
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/QueryMapper.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/QueryMapper.java
index 56b7b8651b..08fd83ff0a 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/QueryMapper.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/QueryMapper.java
@@ -126,9 +126,7 @@ Expression getMappedObject(Expression expression, @Nullable RelationalPersistent
 		if (expression instanceof Column column) {
 
 			Field field = createPropertyField(entity, column.getName());
-			TableLike table = column.getTable();
-
-			Assert.state(table != null, String.format("The column %s must have a table set", column));
+			TableLike table = column.getRequiredTable();
 
 			Column columnFromTable = table.column(field.getMappedColumnName());
 			return column instanceof Aliased aliased ? columnFromTable.as(aliased.getAlias()) : columnFromTable;
@@ -184,10 +182,7 @@ private Condition unroll(CriteriaDefinition criteria, Table table, @Nullable Rel
 
 		while (current.hasPrevious()) {
 
-			CriteriaDefinition previous = current.getPrevious();
-
-			Assert.state(previous != null, "Previous must not be null");
-
+			CriteriaDefinition previous = current.getRequiredPrevious();
 			forwardChain.put(previous, current);
 			current = previous;
 		}
@@ -332,7 +327,7 @@ private Condition mapCondition(CriteriaDefinition criteria, MapSqlParameterSourc
 	 * @param property the property to which the value relates. It determines the type to convert to. Must not be
 	 *          {@literal null}.
 	 * @param value the value to be converted.
-	 * @return a non null {@link JdbcValue} holding the converted value and the appropriate JDBC type information.
+	 * @return a non-null {@link JdbcValue} holding the converted value and the appropriate JDBC type information.
 	 */
 	private JdbcValue convertToJdbcValue(RelationalPersistentProperty property, @Nullable Object value) {
 
@@ -348,7 +343,6 @@ private JdbcValue convertToJdbcValue(RelationalPersistentProperty property, @Nul
 			Object secondValue = second.getValue();
 
 			Assert.state(firstValue != null, "First value must not be null");
-
 			Assert.state(secondValue != null, "Second value must not be null");
 
 			return JdbcValue.of(Pair.of(firstValue, secondValue), first.getJdbcType());
@@ -356,7 +350,7 @@ private JdbcValue convertToJdbcValue(RelationalPersistentProperty property, @Nul
 
 		if (value instanceof Iterable) {
 
-			List mapped = new ArrayList<>();
+			List<@Nullable Object> mapped = new ArrayList<>();
 			SQLType jdbcType = null;
 
 			for (Object o : (Iterable) value) {
@@ -375,7 +369,8 @@ private JdbcValue convertToJdbcValue(RelationalPersistentProperty property, @Nul
 		if (value.getClass().isArray()) {
 
 			Object[] valueAsArray = (Object[]) value;
-			Object[] mappedValueArray = new Object[valueAsArray.length];
+			@Nullable
+			Object[] mappedValueArray = new Object @Nullable [valueAsArray.length];
 			SQLType jdbcType = null;
 
 			for (int i = 0; i < valueAsArray.length; i++) {
@@ -486,7 +481,6 @@ protected Object convertValue(@Nullable Object value, TypeInformation typeInf
 					: TypeInformation.OBJECT);
 
 			Assert.state(first != null, "First value must not be null");
-
 			Assert.state(second != null, "Second value must not be null");
 
 			return Pair.of(first, second);
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/QueryMappingConfiguration.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/QueryMappingConfiguration.java
index 42174aa834..85c7d54307 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/QueryMappingConfiguration.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/QueryMappingConfiguration.java
@@ -29,7 +29,7 @@
 public interface QueryMappingConfiguration {
 
 	@Nullable
-	default  RowMapper getRowMapper(Class type) {
+	default  RowMapper getRowMapper(Class type) {
 		return null;
 	}
 
@@ -39,7 +39,7 @@ default  RowMapper getRowMapper(Class type) {
 	QueryMappingConfiguration EMPTY = new QueryMappingConfiguration() {
 
 		@Override
-		public  @Nullable RowMapper getRowMapper(Class type) {
+		public  @Nullable RowMapper getRowMapper(Class type) {
 			return null;
 		}
 
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/ReadingDataAccessStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/ReadingDataAccessStrategy.java
index c7d01f6e58..964f8636d1 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/ReadingDataAccessStrategy.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/ReadingDataAccessStrategy.java
@@ -41,8 +41,7 @@ interface ReadingDataAccessStrategy {
 	 * @param  the type of the entity.
 	 * @return Might return {@code null}.
 	 */
-	@Nullable
-	 T findById(Object id, Class domainType);
+	 T findById(Object id, Class domainType);
 
 	/**
 	 * Loads all entities of the given type.
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/RowDocumentExtractorSupport.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/RowDocumentExtractorSupport.java
index 6e60e0bf47..d7245d6188 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/RowDocumentExtractorSupport.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/RowDocumentExtractorSupport.java
@@ -109,7 +109,7 @@ public Object getObject(RS row, String columnName) {
 
 			Integer index = columnMap.get(columnName);
 
-			Assert.state(index != null, "Column not found");
+			Assert.state(index != null, () -> "Column '%s' not found".formatted(columnName));
 
 			return adapter.getObject(row, index);
 		}
@@ -499,9 +499,9 @@ public void add(Object key, @Nullable Object value) {
 		}
 
 		@Override
-		public List get() {
+		public List<@Nullable Object> get() {
 
-			List result = new ArrayList<>(list.size());
+			List<@Nullable Object> result = new ArrayList<>(list.size());
 
 			// TODO: How do we go about padding? Should we insert null values?
 			list.forEach((index, o) -> {
@@ -519,7 +519,7 @@ public List get() {
 
 	private static class MapContainer extends CollectionContainer {
 
-		private final Map map = new LinkedHashMap<>();
+		private final Map map = new LinkedHashMap<>();
 
 		@Override
 		public void add(Object key, @Nullable Object value) {
@@ -527,7 +527,7 @@ public void add(Object key, @Nullable Object value) {
 		}
 
 		@Override
-		public Map get() {
+		public Map get() {
 			return new LinkedHashMap<>(map);
 		}
 	}
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SingleQueryDataAccessStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SingleQueryDataAccessStrategy.java
index 840a2cee19..dcd8708379 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SingleQueryDataAccessStrategy.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SingleQueryDataAccessStrategy.java
@@ -50,7 +50,8 @@ public SingleQueryDataAccessStrategy(Dialect dialect, JdbcConverter converter,
 	}
 
 	@Override
-	public  @Nullable T findById(Object id, Class domainType) {
+	@SuppressWarnings("NullAway")
+	public  T findById(Object id, Class domainType) {
 		return aggregateReader.findById(id, getPersistentEntity(domainType));
 	}
 
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SingleQueryFallbackDataAccessStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SingleQueryFallbackDataAccessStrategy.java
index 778c8a66e1..b76bcdec0e 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SingleQueryFallbackDataAccessStrategy.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SingleQueryFallbackDataAccessStrategy.java
@@ -56,7 +56,7 @@ public SingleQueryFallbackDataAccessStrategy(SqlGeneratorSource sqlGeneratorSour
 	}
 
 	@Override
-	public  @Nullable T findById(Object id, Class domainType) {
+	public  T findById(Object id, Class domainType) {
 
 		if (isSingleSelectQuerySupported(domainType)) {
 			return singleSelectDelegate.findById(id, domainType);
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/package-info.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/package-info.java
index e991b443e4..0039b772ab 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/package-info.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/package-info.java
@@ -1,7 +1,6 @@
 /**
  * JDBC-specific conversion classes.
  */
-@NullMarked
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.jdbc.core.convert;
 
-import org.jspecify.annotations.NullMarked;
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/package-info.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/package-info.java
index bf63b66b33..0d1d6f6d74 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/package-info.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/package-info.java
@@ -1,7 +1,6 @@
 /**
  * JDBC-specific Dialect implementations.
  */
-@NullMarked
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.jdbc.core.dialect;
 
-import org.jspecify.annotations.NullMarked;
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/package-info.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/package-info.java
index e8d6d1f33b..74259de15f 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/package-info.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/schema/package-info.java
@@ -1,7 +1,7 @@
 /**
  * Schema creation and schema update integration with Liquibase.
  */
-@NullMarked
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.jdbc.core.mapping.schema;
 
-import org.jspecify.annotations.NullMarked;
+
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/package-info.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/package-info.java
index 10d70d6b5a..c9928dd407 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/package-info.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/package-info.java
@@ -1,7 +1,6 @@
 /**
  * Core JDBC implementation.
  */
-@NullMarked
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.jdbc.core;
 
-import org.jspecify.annotations.NullMarked;
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java
index 46a00dba6e..0ccc36e9f2 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java
@@ -175,7 +175,8 @@ public void setNamespaceStrategy(NamespaceStrategy namespaceStrategy) {
 	}
 
 	@Override
-	public  Object[] insert(List> insertSubjects, Class domainType, IdValueSource idValueSource) {
+	public  @Nullable Object[] insert(List> insertSubjects, Class domainType,
+			IdValueSource idValueSource) {
 
 		return insertSubjects.stream().map(
 				insertSubject -> insert(insertSubject.getInstance(), domainType, insertSubject.getIdentifier(), idValueSource))
@@ -278,7 +279,7 @@ public  void acquireLockAll(LockMode lockMode, Class domainType) {
 	}
 
 	@Override
-	public  T findById(Object id, Class domainType) {
+	public  T findById(Object id, Class domainType) {
 
 		String statement = namespace(domainType) + ".findById";
 		MyBatisContext parameter = new MyBatisContext(id, null, domainType, Collections.emptyMap());
@@ -414,12 +415,7 @@ private SqlSession sqlSession() {
 	}
 
 	private static String toDashPath(PersistentPropertyPath propertyPath) {
-
-		String dotPath = propertyPath.toDotPath();
-		if (dotPath == null) {
-			return "";
-		}
-		return dotPath.replaceAll("\\.", "-");
+		return propertyPath.toDotPath().replaceAll("\\.", "-");
 	}
 
 	private Class getOwnerTyp(PersistentPropertyPath propertyPath) {
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/package-info.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/package-info.java
index 3acfbcdf23..7f50a3441f 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/package-info.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/package-info.java
@@ -1,4 +1,3 @@
-@NullMarked
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.jdbc.mybatis;
 
-import org.jspecify.annotations.NullMarked;
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DefaultQueryMappingConfiguration.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DefaultQueryMappingConfiguration.java
index 6537983199..ed5e27f1da 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DefaultQueryMappingConfiguration.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DefaultQueryMappingConfiguration.java
@@ -21,8 +21,7 @@ public class DefaultQueryMappingConfiguration implements QueryMappingConfigurati
 
 	private final Map, RowMapper> mappers = new LinkedHashMap<>();
 
-	@Nullable
-	public  RowMapper getRowMapper(Class type) {
+	public  @Nullable RowMapper getRowMapper(Class type) {
 
 		Assert.notNull(type, "Type must not be null");
 
@@ -41,7 +40,7 @@ public  RowMapper getRowMapper(Class type) {
 	}
 
 	/**
-	 * Registers a the given {@link RowMapper} as to be used for the given type.
+	 * Registers a given {@link RowMapper} as to be used for the given type.
 	 *
 	 * @return this instance, so this can be used as a fluent interface.
 	 */
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/package-info.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/package-info.java
index d5f5cf7bb5..ed20a2ed77 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/package-info.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/package-info.java
@@ -1,4 +1,3 @@
-@NullMarked
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.jdbc.repository.config;
 
-import org.jspecify.annotations.NullMarked;
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/AbstractDelegatingRowMapper.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/AbstractDelegatingRowMapper.java
index 07b9e80f63..8a7d9f2108 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/AbstractDelegatingRowMapper.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/AbstractDelegatingRowMapper.java
@@ -29,7 +29,7 @@
  * @author Mikhail Polivakha
  * @since 4.0
  */
-public abstract class AbstractDelegatingRowMapper implements RowMapper {
+public abstract class AbstractDelegatingRowMapper implements RowMapper {
 
 	private final RowMapper delegate;
 
@@ -44,11 +44,7 @@ protected AbstractDelegatingRowMapper(RowMapper delegate) {
 	public T mapRow(ResultSet rs, int rowNum) throws SQLException {
 
 		T intermediate = delegate.mapRow(rs, rowNum);
-		T result = postProcessMapping(intermediate);
-
-		Assert.state(result != null, "Result must not be null");
-
-		return result;
+		return postProcessMapping(intermediate);
 	}
 
 	/**
@@ -56,8 +52,7 @@ public T mapRow(ResultSet rs, int rowNum) throws SQLException {
 	 *
 	 * @return the mapped entity after applying post-processing logic
 	 */
-	@Nullable
-	protected T postProcessMapping(@Nullable T object) {
+	protected T postProcessMapping(T object) {
 		return object;
 	}
 }
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/CallbackCapableRowMapper.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/CallbackCapableRowMapper.java
index 3a16b48850..e91c6acc07 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/CallbackCapableRowMapper.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/CallbackCapableRowMapper.java
@@ -48,8 +48,7 @@ public CallbackCapableRowMapper(RowMapper delegate, ApplicationEventPublisher
 	}
 
 	@Override
-	@Nullable
-	public T postProcessMapping(@Nullable T object) {
+	public T postProcessMapping(T object) {
 
 		if (object != null) {
 
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/ConvertingRowMapper.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/ConvertingRowMapper.java
index cf03d50ce6..b9bc53a9ad 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/ConvertingRowMapper.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/ConvertingRowMapper.java
@@ -38,8 +38,8 @@ public ConvertingRowMapper(RowMapper delegate, Converter
 	}
 
 	@Override
-	@Nullable
-	public Object postProcessMapping(@Nullable Object object) {
+	@SuppressWarnings("NullAway")
+	public @Nullable Object postProcessMapping(@Nullable Object object) {
 		return object != null ? converter.convert(object) : null;
 	}
 }
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/package-info.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/package-info.java
index 5ec7f203d4..7da1d6c4ec 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/package-info.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/package-info.java
@@ -1,7 +1,6 @@
 /**
  * Query derivation mechanism for JDBC specific repositories.
  */
-@NullMarked
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.jdbc.repository.query;
 
-import org.jspecify.annotations.NullMarked;
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java
index 28693dd146..eaa82c3c4a 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java
@@ -59,7 +59,7 @@ public class JdbcRepositoryFactory extends RepositoryFactorySupport implements A
 	private final JdbcAggregateOperations operations;
 	private final NamedParameterJdbcOperations jdbcOperations;
 
-	private EntityCallbacks entityCallbacks = EntityCallbacks.create();
+	private @Nullable EntityCallbacks entityCallbacks;
 	private ApplicationEventPublisher publisher = event -> {};
 	private @Nullable BeanFactory beanFactory;
 	private QueryMappingConfiguration queryMappingConfiguration = QueryMappingConfiguration.EMPTY;
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/package-info.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/package-info.java
index 53160d4fe3..0ad3e843c8 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/package-info.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/package-info.java
@@ -1,4 +1,6 @@
-@NullMarked
+/**
+ * Support classes for integration of the repository programming model with JDBC.
+ */
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.jdbc.repository.support;
 
-import org.jspecify.annotations.NullMarked;
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/support/package-info.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/support/package-info.java
index 825eb43b9b..717975b6cb 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/support/package-info.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/support/package-info.java
@@ -1,4 +1,3 @@
-@NullMarked
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.jdbc.support;
 
-import org.jspecify.annotations.NullMarked;
diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/R2dbcConverters.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/R2dbcConverters.java
index b7cbc6c175..479102317d 100644
--- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/R2dbcConverters.java
+++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/R2dbcConverters.java
@@ -75,7 +75,7 @@ public enum RowToBooleanConverter implements Converter {
 		INSTANCE;
 
 		@Override
-		public Boolean convert(Row row) {
+		public @Nullable Boolean convert(Row row) {
 			return row.get(0, Boolean.class);
 		}
 	}
@@ -90,7 +90,7 @@ public enum RowToLocalDateConverter implements Converter {
 		INSTANCE;
 
 		@Override
-		public LocalDate convert(Row row) {
+		public @Nullable LocalDate convert(Row row) {
 			return row.get(0, LocalDate.class);
 		}
 	}
@@ -105,7 +105,7 @@ public enum RowToLocalDateTimeConverter implements Converter
 		INSTANCE;
 
 		@Override
-		public LocalDateTime convert(Row row) {
+		public @Nullable LocalDateTime convert(Row row) {
 			return row.get(0, LocalDateTime.class);
 		}
 	}
@@ -120,7 +120,7 @@ public enum RowToLocalTimeConverter implements Converter {
 		INSTANCE;
 
 		@Override
-		public LocalTime convert(Row row) {
+		public @Nullable LocalTime convert(Row row) {
 			return row.get(0, LocalTime.class);
 		}
 	}
@@ -151,13 +151,7 @@ public  Converter getConverter(Class targetType) {
 			return new RowToNumber<>(targetType);
 		}
 
-		static class RowToNumber implements Converter {
-
-			private final Class targetType;
-
-			RowToNumber(Class targetType) {
-				this.targetType = targetType;
-			}
+		record RowToNumber(Class targetType) implements Converter {
 
 			@Override
 			public @Nullable T convert(Row source) {
@@ -179,7 +173,7 @@ public enum RowToOffsetDateTimeConverter implements Converter {
 		INSTANCE;
 
 		@Override
-		public String convert(Row row) {
+		public @Nullable String convert(Row row) {
 			return row.get(0, String.class);
 		}
 	}
@@ -209,7 +203,7 @@ public enum RowToUuidConverter implements Converter {
 		INSTANCE;
 
 		@Override
-		public UUID convert(Row row) {
+		public @Nullable UUID convert(Row row) {
 			return row.get(0, UUID.class);
 		}
 	}
@@ -224,7 +218,7 @@ public enum RowToZonedDateTimeConverter implements Converter
 		INSTANCE;
 
 		@Override
-		public ZonedDateTime convert(Row row) {
+		public @Nullable ZonedDateTime convert(Row row) {
 			return row.get(0, ZonedDateTime.class);
 		}
 	}
diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/RowPropertyAccessor.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/RowPropertyAccessor.java
deleted file mode 100644
index 7141a4ad37..0000000000
--- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/RowPropertyAccessor.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2013-2025 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.data.r2dbc.convert;
-
-import io.r2dbc.spi.Row;
-import io.r2dbc.spi.RowMetadata;
-
-import org.jspecify.annotations.Nullable;
-import org.springframework.expression.EvaluationContext;
-import org.springframework.expression.PropertyAccessor;
-import org.springframework.expression.TypedValue;
-
-/**
- * {@link PropertyAccessor} to read values from a {@link Row}.
- *
- * @author Mark Paluch
- * @since 1.2
- */
-class RowPropertyAccessor implements PropertyAccessor {
-
-	private final @Nullable RowMetadata rowMetadata;
-
-	RowPropertyAccessor(@Nullable RowMetadata rowMetadata) {
-		this.rowMetadata = rowMetadata;
-	}
-
-	@Override
-	public Class[] getSpecificTargetClasses() {
-		return new Class[] { Row.class };
-	}
-
-	@Override
-	public boolean canRead(EvaluationContext context, @Nullable Object target, String name) {
-		return rowMetadata != null && target != null && RowMetadataUtils.containsColumn(rowMetadata, name);
-	}
-
-	@Override
-	public TypedValue read(EvaluationContext context, @Nullable Object target, String name) {
-
-		if (target == null) {
-			return TypedValue.NULL;
-		}
-
-		Object value = ((Row) target).get(name);
-
-		if (value == null) {
-			return TypedValue.NULL;
-		}
-
-		return new TypedValue(value);
-	}
-
-	@Override
-	public boolean canWrite(EvaluationContext context, @Nullable Object target, String name) {
-		return false;
-	}
-
-	@Override
-	public void write(EvaluationContext context, @Nullable Object target, String name, @Nullable Object newValue) {
-		throw new UnsupportedOperationException();
-	}
-}
diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/SequenceEntityCallbackDelegate.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/SequenceEntityCallbackDelegate.java
index 2af3e78c3d..710620fbfa 100644
--- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/SequenceEntityCallbackDelegate.java
+++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/SequenceEntityCallbackDelegate.java
@@ -93,7 +93,7 @@ private Mono getSequenceValue(RelationalPersistentProperty property) {
 			return Mono.empty();
 		}
 
-		if (sequence != null && !dialect.getIdGeneration().sequencesSupported()) {
+		if (!dialect.getIdGeneration().sequencesSupported()) {
 			LOG.warn("""
 					Entity type '%s' is marked for sequence usage but configured dialect '%s'
 					does not support sequences. Falling back to identity columns.
diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/DefaultStatementMapper.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/DefaultStatementMapper.java
index 195aa10859..49a7057a7a 100644
--- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/DefaultStatementMapper.java
+++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/DefaultStatementMapper.java
@@ -247,8 +247,6 @@ private PreparedOperation getMappedObject(DeleteSpec deleteSpec,
 
 		if (criteria != null && !criteria.isEmpty()) {
 
-			Assert.state(criteria != null, "DeleteSpec must have a criteria");
-
 			BoundCondition boundCondition = this.updateMapper.getMappedObject(bindMarkers, criteria, table, entity);
 
 			bindings = boundCondition.getBindings();
diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java
index d2af66384a..6f65043b05 100644
--- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java
+++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java
@@ -909,23 +909,23 @@ public  RowsFetchSpec getRowsFetchSpec(DatabaseClient.GenericExecuteSpec e
 	 *
 	 * @param 
 	 */
-		private record UnwrapOptionalFetchSpecAdapter(RowsFetchSpec> delegate) implements RowsFetchSpec {
+	private record UnwrapOptionalFetchSpecAdapter(RowsFetchSpec> delegate) implements RowsFetchSpec {
 
 		@Override
-			public Mono one() {
-				return delegate.one().handle((optional, sink) -> optional.ifPresent(sink::next));
-			}
+		public Mono one() {
+			return delegate.one().handle((optional, sink) -> optional.ifPresent(sink::next));
+		}
 
-			@Override
-			public Mono first() {
-				return delegate.first().handle((optional, sink) -> optional.ifPresent(sink::next));
-			}
+		@Override
+		public Mono first() {
+			return delegate.first().handle((optional, sink) -> optional.ifPresent(sink::next));
+		}
 
-			@Override
-			public Flux all() {
-				return delegate.all().handle((optional, sink) -> optional.ifPresent(sink::next));
-			}
+		@Override
+		public Flux all() {
+			return delegate.all().handle((optional, sink) -> optional.ifPresent(sink::next));
 		}
+	}
 
 	/**
 	 * {@link RowsFetchSpec} adapter applying {@link #maybeCallAfterConvert(Object, SqlIdentifier)} to each emitted
diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveDeleteOperationSupport.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveDeleteOperationSupport.java
index 732ffb8698..4c6b0fff33 100644
--- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveDeleteOperationSupport.java
+++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveDeleteOperationSupport.java
@@ -28,13 +28,7 @@
  * @author Mark Paluch
  * @since 1.1
  */
-class ReactiveDeleteOperationSupport implements ReactiveDeleteOperation {
-
-	private final R2dbcEntityTemplate template;
-
-	ReactiveDeleteOperationSupport(R2dbcEntityTemplate template) {
-		this.template = template;
-	}
+record ReactiveDeleteOperationSupport(R2dbcEntityTemplate template) implements ReactiveDeleteOperation {
 
 	@Override
 	public ReactiveDelete delete(Class domainType) {
diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveInsertOperationSupport.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveInsertOperationSupport.java
index 4930dde7ce..ce4e6386f6 100644
--- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveInsertOperationSupport.java
+++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveInsertOperationSupport.java
@@ -27,13 +27,7 @@
  * @author Mark Paluch
  * @since 1.1
  */
-class ReactiveInsertOperationSupport implements ReactiveInsertOperation {
-
-	private final R2dbcEntityTemplate template;
-
-	ReactiveInsertOperationSupport(R2dbcEntityTemplate template) {
-		this.template = template;
-	}
+record ReactiveInsertOperationSupport(R2dbcEntityTemplate template) implements ReactiveInsertOperation {
 
 	@Override
 	public  ReactiveInsert insert(Class domainType) {
diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveSelectOperationSupport.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveSelectOperationSupport.java
index 2bf8ad008c..4879d7b919 100644
--- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveSelectOperationSupport.java
+++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveSelectOperationSupport.java
@@ -31,13 +31,7 @@
  * @author Mikhail Polivakha
  * @since 1.1
  */
-class ReactiveSelectOperationSupport implements ReactiveSelectOperation {
-
-	private final R2dbcEntityTemplate template;
-
-	ReactiveSelectOperationSupport(R2dbcEntityTemplate template) {
-		this.template = template;
-	}
+record ReactiveSelectOperationSupport(R2dbcEntityTemplate template) implements ReactiveSelectOperation {
 
 	@Override
 	public  ReactiveSelect select(Class domainType) {
diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveUpdateOperationSupport.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveUpdateOperationSupport.java
index a90d91d8cd..8d6b3de7df 100644
--- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveUpdateOperationSupport.java
+++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/ReactiveUpdateOperationSupport.java
@@ -29,13 +29,7 @@
  * @author Mark Paluch
  * @since 1.1
  */
-class ReactiveUpdateOperationSupport implements ReactiveUpdateOperation {
-
-	private final R2dbcEntityTemplate template;
-
-	ReactiveUpdateOperationSupport(R2dbcEntityTemplate template) {
-		this.template = template;
-	}
+record ReactiveUpdateOperationSupport(R2dbcEntityTemplate template) implements ReactiveUpdateOperation {
 
 	@Override
 	public ReactiveUpdate update(Class domainType) {
diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/StatementMapper.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/StatementMapper.java
index 0b7d50c850..1a9896bce1 100644
--- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/StatementMapper.java
+++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/StatementMapper.java
@@ -421,7 +421,7 @@ public SelectSpec lock(LockMode lockMode) {
 
 		/**
 		 * The used lockmode
-		 * 
+		 *
 		 * @return might be null if no lockmode defined.
 		 */
 		@Nullable
@@ -641,8 +641,7 @@ public SqlIdentifier getTable() {
 			return this.table;
 		}
 
-		@Nullable
-		public CriteriaDefinition getCriteria() {
+		public @Nullable CriteriaDefinition getCriteria() {
 			return this.criteria;
 		}
 	}
diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/package-info.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/package-info.java
index f1d472ceec..1fa2d25463 100644
--- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/package-info.java
+++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/dialect/package-info.java
@@ -1,7 +1,7 @@
 /**
  * Dialects abstract the SQL dialect of the underlying database.
  */
-@NullMarked
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.r2dbc.dialect;
 
-import org.jspecify.annotations.NullMarked;
+
diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/mapping/package-info.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/mapping/package-info.java
index 9050656f43..9892206dd0 100644
--- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/mapping/package-info.java
+++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/mapping/package-info.java
@@ -1,7 +1,7 @@
 /**
  * Domain objects for R2DBC.
  */
-@NullMarked
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.r2dbc.mapping;
 
-import org.jspecify.annotations.NullMarked;
+
diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/query/QueryMapper.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/query/QueryMapper.java
index a7cb5eb31b..963e171b9e 100644
--- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/query/QueryMapper.java
+++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/query/QueryMapper.java
@@ -193,7 +193,7 @@ public List getMappedObjects(Expression expression, @Nullable Relati
 		if (expression instanceof Column column) {
 
 			Field field = createPropertyField(entity, column.getName());
-			TableLike table = column.getTable();
+			TableLike table = column.getRequiredTable();
 
 			if (field.isEmbedded()) {
 
@@ -204,16 +204,12 @@ public List getMappedObjects(Expression expression, @Nullable Relati
 
 				for (RelationalPersistentProperty embeddedProperty : embeddedEntity) {
 
-					Assert.state(table != null, "Embedded table must not be null");
-
 					expressions.addAll(getMappedObjects(Column.create(embeddedProperty.getName(), table), embeddedEntity));
 				}
 
 				return expressions;
 			}
 
-			Assert.state(table != null, "Table must not be null");
-
 			Column columnFromTable = table.column(field.getMappedColumnName());
 			return List.of(column instanceof Aliased ? columnFromTable.as(((Aliased) column).getAlias()) : columnFromTable);
 		}
@@ -273,10 +269,7 @@ private Condition unroll(CriteriaDefinition criteria, Table table, @Nullable Rel
 
 		while (current.hasPrevious()) {
 
-			CriteriaDefinition previous = current.getPrevious();
-
-			Assert.state(previous != null, "Previous criteria must not be null");
-
+			CriteriaDefinition previous = current.getRequiredPrevious();
 			forwardChain.put(previous, current);
 			current = previous;
 		}
@@ -452,13 +445,14 @@ static PersistentPropertyAccessor getEmbeddedPropertyAccessor(@Nullable
 		}
 
 		return new PersistentPropertyAccessor<>() {
+
 			@Override
-			public void setProperty(PersistentProperty property, @org.jspecify.annotations.Nullable Object value) {
+			public void setProperty(PersistentProperty property, @Nullable Object value) {
 
 			}
 
 			@Override
-			public @org.jspecify.annotations.Nullable Object getProperty(PersistentProperty property) {
+			public @Nullable Object getProperty(PersistentProperty property) {
 				return null;
 			}
 
@@ -750,7 +744,7 @@ public boolean isEmbedded() {
 			return false;
 		}
 
-		public @org.jspecify.annotations.Nullable RelationalPersistentProperty getProperty() {
+		public @Nullable RelationalPersistentProperty getProperty() {
 			return null;
 		}
 
@@ -846,8 +840,7 @@ public SqlIdentifier getMappedColumnName() {
 		 * @param pathExpression the path expression to use.
 		 * @return
 		 */
-		@Nullable
-		private PersistentPropertyPath getPath(String pathExpression) {
+		private @Nullable PersistentPropertyPath getPath(String pathExpression) {
 
 			try {
 
@@ -905,7 +898,7 @@ public boolean isEmbedded() {
 		}
 
 		@Override
-		public @org.jspecify.annotations.Nullable RelationalPersistentProperty getProperty() {
+		public @Nullable RelationalPersistentProperty getProperty() {
 			return this.property;
 		}
 	}
diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/config/R2dbcRepositoryConfigurationExtension.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/config/R2dbcRepositoryConfigurationExtension.java
index ef2c055631..754e1d2e14 100644
--- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/config/R2dbcRepositoryConfigurationExtension.java
+++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/config/R2dbcRepositoryConfigurationExtension.java
@@ -27,7 +27,6 @@
 import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource;
 import org.springframework.data.repository.config.RepositoryConfigurationExtension;
 import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport;
-import org.springframework.data.repository.config.XmlRepositoryConfigurationSource;
 import org.springframework.data.repository.core.RepositoryMetadata;
 
 /**
@@ -61,9 +60,6 @@ protected Collection> getIdentifyingTypes() {
 		return Collections.singleton(R2dbcRepository.class);
 	}
 
-	@Override
-	public void postProcess(BeanDefinitionBuilder builder, XmlRepositoryConfigurationSource config) {}
-
 	@Override
 	public void postProcess(BeanDefinitionBuilder builder, AnnotationRepositoryConfigurationSource config) {
 
diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/ExpressionEvaluatingParameterBinder.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/ExpressionEvaluatingParameterBinder.java
index e149fa702b..72ce6c8bfc 100644
--- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/ExpressionEvaluatingParameterBinder.java
+++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/ExpressionEvaluatingParameterBinder.java
@@ -155,7 +155,7 @@ private boolean isNamedParameterReferencedFromQuery(Optional name) {
 		return namedParameters.computeIfAbsent(name.get(), it -> {
 
 			Pattern namedParameterPattern = Pattern.compile("(\\W)[:#$@]" + Pattern.quote(it) + "(\\W|$)");
-			return namedParameterPattern.matcher(expressionQuery.getQuery()).find();
+			return namedParameterPattern.matcher(expressionQuery.query()).find();
 		});
 	}
 
diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/ExpressionQuery.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/ExpressionQuery.java
index a95af3ed42..f5be768fa9 100644
--- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/ExpressionQuery.java
+++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/ExpressionQuery.java
@@ -29,18 +29,10 @@
  * @author Mark Paluch
  * @since 1.1
  */
-class ExpressionQuery {
+record ExpressionQuery(String query, Map parameterMap) {
 
 	private static final String SYNTHETIC_PARAMETER_TEMPLATE = "__synthetic_%d__";
 
-	private final String query;
-	private final Map parameterMap;
-
-	private ExpressionQuery(String query, Map parameterMap) {
-		this.query = query;
-		this.parameterMap = parameterMap;
-	}
-
 	/**
 	 * Create a {@link ExpressionQuery} from a {@code query}.
 	 *
@@ -49,7 +41,6 @@ private ExpressionQuery(String query, Map parameterMap)
 	 */
 	public static ExpressionQuery create(ValueExpressionParser parser, String query) {
 
-
 		ValueExpressionQueryRewriter rewriter = ValueExpressionQueryRewriter.of(parser,
 				(counter, expression) -> String.format(SYNTHETIC_PARAMETER_TEMPLATE, counter), String::concat);
 		ValueExpressionQueryRewriter.ParsedQuery parsed = rewriter.parse(query);
@@ -57,9 +48,6 @@ public static ExpressionQuery create(ValueExpressionParser parser, String query)
 		return new ExpressionQuery(parsed.getQueryString(), parsed.getParameterMap());
 	}
 
-	public String getQuery() {
-		return query;
-	}
 
 	public Map getBindings() {
 		return parameterMap;
diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/PreparedOperationBindableQuery.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/PreparedOperationBindableQuery.java
index 6ed3699c64..4ca9f87411 100644
--- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/PreparedOperationBindableQuery.java
+++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/PreparedOperationBindableQuery.java
@@ -27,20 +27,17 @@
  * @author Mark Paluch
  * @author Will Easterling
  */
-class PreparedOperationBindableQuery implements BindableQuery {
-
-	private final PreparedOperation preparedQuery;
+record PreparedOperationBindableQuery(PreparedOperation preparedQuery) implements BindableQuery {
 
 	/**
 	 * Creates new instance of this class with the given {@link PreparedOperation}.
 	 *
 	 * @param preparedQuery prepared SQL query, must not be {@literal null}.
 	 */
-	PreparedOperationBindableQuery(PreparedOperation preparedQuery) {
+	PreparedOperationBindableQuery {
 
 		Assert.notNull(preparedQuery, "Prepared query must not be null");
 
-		this.preparedQuery = preparedQuery;
 	}
 
 	@Override
diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcParameterAccessor.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcParameterAccessor.java
index 90e3e8d58a..8d03634490 100644
--- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcParameterAccessor.java
+++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcParameterAccessor.java
@@ -39,13 +39,13 @@
  */
 class R2dbcParameterAccessor extends RelationalParametersParameterAccessor {
 
-	private final Object[] values;
+	private final @Nullable Object[] values;
 	private final R2dbcQueryMethod method;
 
 	/**
 	 * Creates a new {@link R2dbcParameterAccessor}.
 	 */
-	public R2dbcParameterAccessor(R2dbcQueryMethod method, Object... values) {
+	public R2dbcParameterAccessor(R2dbcQueryMethod method, @Nullable Object... values) {
 
 		super(method, values);
 
@@ -57,8 +57,9 @@ public R2dbcParameterAccessor(R2dbcQueryMethod method, Object... values) {
 	 * @see org.springframework.data.relational.repository.query.RelationalParametersParameterAccessor#getValues()
 	 */
 	@Override
-	public Object[] getValues() {
+	public @Nullable Object[] getValues() {
 
+		@Nullable
 		Object[] result = new Object[values.length];
 		for (int i = 0; i < result.length; i++) {
 			result[i] = getValue(i);
diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/StringBasedR2dbcQuery.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/StringBasedR2dbcQuery.java
index fe0a6ccc0b..cbe60b1d74 100644
--- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/StringBasedR2dbcQuery.java
+++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/StringBasedR2dbcQuery.java
@@ -167,7 +167,7 @@ private Mono getExpressionEvaluator(RelationalParameterA
 
 	@Override
 	public String toString() {
-		return getClass().getSimpleName() + " [" + expressionQuery.getQuery() + ']';
+		return getClass().getSimpleName() + " [" + expressionQuery.query() + ']';
 	}
 
 	private class ExpandedQuery implements PreparedOperation {
@@ -187,7 +187,7 @@ public ExpandedQuery(RelationalParameterAccessor accessor, ValueEvaluationContex
 
 			remainderByName = new LinkedHashMap<>(recordedBindings.byName);
 			remainderByIndex = new LinkedHashMap<>(recordedBindings.byIndex);
-			expanded = dataAccessStrategy.processNamedParameters(expressionQuery.getQuery(), (index, name) -> {
+			expanded = dataAccessStrategy.processNamedParameters(expressionQuery.query(), (index, name) -> {
 
 				if (recordedBindings.byName.containsKey(name)) {
 					remainderByName.remove(name);
@@ -205,7 +205,7 @@ public ExpandedQuery(RelationalParameterAccessor accessor, ValueEvaluationContex
 
 		@Override
 		public String getSource() {
-			return expressionQuery.getQuery();
+			return expressionQuery.query();
 		}
 
 		@Override
@@ -225,7 +225,7 @@ public String toQuery() {
 
 		@Override
 		public String toString() {
-			return String.format("Original: [%s], Expanded: [%s]", expressionQuery.getQuery(), expanded.toQuery());
+			return String.format("Original: [%s], Expanded: [%s]", expressionQuery.query(), expanded.toQuery());
 		}
 	}
 
diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/package-info.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/package-info.java
index ec67ff63d3..47991f3088 100644
--- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/package-info.java
+++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/query/package-info.java
@@ -1,7 +1,7 @@
 /**
  * Query support for R2DBC repositories.
  */
-@NullMarked
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.r2dbc.repository.query;
 
-import org.jspecify.annotations.NullMarked;
+
diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/support/SimpleR2dbcRepository.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/support/SimpleR2dbcRepository.java
index 43733350ff..0d3d362c8c 100644
--- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/support/SimpleR2dbcRepository.java
+++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/repository/support/SimpleR2dbcRepository.java
@@ -380,7 +380,7 @@ public void doWithPersistentProperty(RelationalPersistentProperty persistentProp
 
 					Object property = accessor.getProperty(persistentProperty);
 
-					Assert.state(property != null, "Property must not be null");
+					Assert.state(property != null, () -> "Property '%s' must not be null".formatted(persistentProperty));
 
 					criteriaHolder[0] = criteriaHolder[0].and(persistentProperty.getName()).is(property);
 				}
diff --git a/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/repository/query/ExpressionQueryUnitTests.java b/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/repository/query/ExpressionQueryUnitTests.java
index 882923e826..a089265700 100644
--- a/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/repository/query/ExpressionQueryUnitTests.java
+++ b/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/repository/query/ExpressionQueryUnitTests.java
@@ -39,7 +39,7 @@ void bindsMultipleExpressionParametersCorrectly() {
 		ExpressionQuery query = ExpressionQuery
 				.create(ValueExpressionParser.create(), "INSERT IGNORE INTO table (x, y) VALUES (:#{#point.x}, :${point.y})");
 
-		assertThat(query.getQuery())
+		assertThat(query.query())
 				.isEqualTo("INSERT IGNORE INTO table (x, y) VALUES (:__synthetic_0__, :__synthetic_1__)");
 
 		Map bindings = query.getBindings();
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/aot/package-info.java b/spring-data-relational/src/main/java/org/springframework/data/relational/aot/package-info.java
index 8b67ec252d..c40c5e326a 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/aot/package-info.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/aot/package-info.java
@@ -1,7 +1,7 @@
 /**
  * Ahead of Time processing utilities for Spring Data Relational.
  */
-@NullMarked
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.relational.aot;
 
-import org.jspecify.annotations.NullMarked;
+
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DbAction.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DbAction.java
index daad66457e..03f126daa7 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DbAction.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DbAction.java
@@ -123,7 +123,7 @@ class UpdateRoot implements WithRoot {
 
 		private T entity;
 
-		@Nullable private final Number previousVersion;
+		private @Nullable final Number previousVersion;
 
 		public UpdateRoot(T entity, @Nullable Number previousVersion) {
 			this.entity = entity;
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DefaultRootAggregateChange.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DefaultRootAggregateChange.java
index dbdca7fb93..a6947c9c6b 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DefaultRootAggregateChange.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DefaultRootAggregateChange.java
@@ -40,7 +40,7 @@ class DefaultRootAggregateChange implements RootAggregateChange {
 	private DbAction.@Nullable WithRoot rootAction;
 
 	/** The previous version assigned to the instance being changed, if available */
-	@Nullable private final Number previousVersion;
+	private @Nullable final Number previousVersion;
 
 	DefaultRootAggregateChange(Kind kind, Class entityType, @Nullable Number previousVersion) {
 
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DeleteAggregateChange.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DeleteAggregateChange.java
index 5596e739b6..91fb2f9cde 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DeleteAggregateChange.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/DeleteAggregateChange.java
@@ -38,7 +38,7 @@ public class DeleteAggregateChange implements MutableAggregateChange {
 	private final List> actions = new ArrayList<>();
 
 	/** The previous version assigned to the instance being changed, if available */
-	@Nullable private final Number previousVersion;
+	private @Nullable final Number previousVersion;
 
 	DeleteAggregateChange(Class entityType, @Nullable Number previousVersion) {
 		this.entityType = entityType;
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java
index bbb9e28132..85f018ed74 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java
@@ -26,6 +26,7 @@
 import java.util.function.Predicate;
 
 import org.jspecify.annotations.Nullable;
+
 import org.springframework.beans.BeansException;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
@@ -45,7 +46,16 @@
 import org.springframework.data.mapping.PersistentPropertyAccessor;
 import org.springframework.data.mapping.PersistentPropertyPathAccessor;
 import org.springframework.data.mapping.context.MappingContext;
-import org.springframework.data.mapping.model.*;
+import org.springframework.data.mapping.model.CachingValueExpressionEvaluatorFactory;
+import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
+import org.springframework.data.mapping.model.EntityInstantiator;
+import org.springframework.data.mapping.model.ParameterValueProvider;
+import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider;
+import org.springframework.data.mapping.model.PropertyValueProvider;
+import org.springframework.data.mapping.model.SimpleTypeHolder;
+import org.springframework.data.mapping.model.SpELContext;
+import org.springframework.data.mapping.model.ValueExpressionEvaluator;
+import org.springframework.data.mapping.model.ValueExpressionParameterValueProvider;
 import org.springframework.data.projection.EntityProjection;
 import org.springframework.data.projection.EntityProjectionIntrospector;
 import org.springframework.data.projection.EntityProjectionIntrospector.ProjectionPredicate;
@@ -264,7 +274,7 @@ String getColumnName(RelationalPersistentProperty prop) {
 		return populateProperties(context, mappedEntity, documentAccessor, evaluator, instance);
 	}
 
-	private @Nullable Object doReadOrProject(ConversionContext context, RowDocument source, TypeInformation typeHint,
+	private Object doReadOrProject(ConversionContext context, RowDocument source, TypeInformation typeHint,
 			EntityProjection typeDescriptor) {
 
 		if (typeDescriptor.isProjection()) {
@@ -375,7 +385,8 @@ protected  S readAggregate(ConversionContext context, RowDocumentAccessor doc
 	 * @param targetType the {@link Map} {@link TypeInformation} to be used to unmarshall this {@link RowDocument}.
 	 * @return the converted {@link Map}, will never be {@literal null}.
 	 */
-	protected Map readMap(ConversionContext context, Map source, TypeInformation targetType) {
+	protected Map<@Nullable Object, @Nullable Object> readMap(ConversionContext context, Map source,
+			TypeInformation targetType) {
 
 		Assert.notNull(source, "Document must not be null");
 		Assert.notNull(targetType, "TypeInformation must not be null");
@@ -388,8 +399,7 @@ protected Map readMap(ConversionContext context, Map sourc
 
 		Class rawKeyType = keyType != null ? keyType.getType() : Object.class;
 
-		Map map = CollectionFactory.createMap(mapType, rawKeyType,
-				((Map) source).size());
+		Map<@Nullable Object, @Nullable Object> map = CollectionFactory.createMap(mapType, rawKeyType, source.size());
 
 		source.forEach((k, v) -> {
 
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/RelationalConverter.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/RelationalConverter.java
index ab196d77e1..5476d8b513 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/RelationalConverter.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/RelationalConverter.java
@@ -101,7 +101,7 @@ public interface RelationalConverter {
 	 * @param  aggregate type.
 	 * @since 3.2
 	 */
-	 @Nullable R read(Class type, RowDocument source);
+	 R read(Class type, RowDocument source);
 
 	/**
 	 * Read a relational value into the desired {@link TypeInformation destination type}.
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/WritingContext.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/WritingContext.java
index 6e41093044..cbad9797c8 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/WritingContext.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/WritingContext.java
@@ -50,7 +50,7 @@ class WritingContext {
 	private final Map> previousActions = new HashMap<>();
 	private final Map, List> nodesCache = new HashMap<>();
 	private final IdValueSource rootIdValueSource;
-	@Nullable private final Number previousVersion;
+	private @Nullable final Number previousVersion;
 	private final RootAggregateChange aggregateChange;
 
 	WritingContext(RelationalMappingContext context, T root, RootAggregateChange aggregateChange) {
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/package-info.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/package-info.java
index 11e4e5dc81..dee83b5ebc 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/package-info.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/package-info.java
@@ -1,4 +1,4 @@
-@NullMarked
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.relational.core.conversion;
 
-import org.jspecify.annotations.NullMarked;
+
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Escaper.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Escaper.java
index 931db6c675..bbaf8414f6 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Escaper.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Escaper.java
@@ -88,8 +88,7 @@ public char getEscapeCharacter() {
 	 * @param value value to be escaped
 	 * @return escaped value
 	 */
-	@Nullable
-	public String escape(@Nullable String value) {
+	public @Nullable String escape(@Nullable String value) {
 
 		if (value == null) {
 			return null;
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/package-info.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/package-info.java
index 866051986a..8ebba64bd1 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/package-info.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/package-info.java
@@ -1,7 +1,7 @@
 /**
  * Dialects abstract the SQL dialect of the underlying database.
  */
-@NullMarked
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.relational.core.dialect;
 
-import org.jspecify.annotations.NullMarked;
+
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/AggregatePath.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/AggregatePath.java
index 5df80219ff..f603a5ea04 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/AggregatePath.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/AggregatePath.java
@@ -261,6 +261,24 @@ default Stream stream() {
 	@Nullable
 	AggregatePath getTail();
 
+	/**
+	 * The required path resulting from removing the first element of the {@link AggregatePath} or throwing
+	 * {@link IllegalStateException} for any {@link AggregatePath} having less than two elements.
+	 *
+	 * @throws IllegalStateException for any {@link AggregatePath} having less than two elements.
+	 * @since 4.0
+	 */
+	default AggregatePath getRequiredTail() {
+
+		AggregatePath tail = getTail();
+
+		if (tail == null) {
+			throw new IllegalStateException("No tail available");
+		}
+
+		return tail;
+	}
+
 	/**
 	 * Subtract the {@literal basePath} from {@literal this} {@literal AggregatePath} by removing the {@literal basePath}
 	 * from the beginning of {@literal this}.
@@ -342,7 +360,7 @@ static TableInfo of(AggregatePath path) {
 
 		}
 
-		public ColumnInfo requiredQualifierColumnInfo() {
+		public ColumnInfo getRequiredQualifierColumnInfo() {
 
 			Assert.notNull(qualifierColumnInfo, "ColumnInfo for qualifier columns must not be null");
 
@@ -451,7 +469,7 @@ public ColumnInfos effectiveIdColumnInfos() {
 			return backReferenceColumnInfos.isEmpty() ? idColumnInfos : backReferenceColumnInfos;
 		}
 
-		public Class requiredQualifierColumnType() {
+		public Class getRequiredQualifierColumnType() {
 
 			Class type = qualifierColumnType();
 
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentEntity.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentEntity.java
index 16de527310..766666dbd1 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentEntity.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentEntity.java
@@ -97,8 +97,7 @@ class BasicRelationalPersistentEntity extends BasicPersistentEntity owner) {
 	}
 
 	@Override
-	@Nullable
-	public SqlIdentifier getKeyColumn() {
+	public @Nullable SqlIdentifier getKeyColumn() {
 
 		if (!isQualified()) {
 			return null;
@@ -285,9 +283,8 @@ public boolean isInsertOnly() {
 		return findAnnotation(InsertOnlyProperty.class) != null;
 	}
 
-	@Nullable
 	@Override
-	public SqlIdentifier getSequence() {
+	public @Nullable SqlIdentifier getSequence() {
 		return this.sequence;
 	}
 
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/DefaultAggregatePath.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/DefaultAggregatePath.java
index 7a54d1336d..0a108588b9 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/DefaultAggregatePath.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/DefaultAggregatePath.java
@@ -112,8 +112,8 @@ public AggregatePath append(AggregatePath path) {
 		RelationalPersistentProperty baseProperty = path.getRequiredBaseProperty();
 		AggregatePath appended = append(baseProperty);
 		AggregatePath tail = path.getTail();
-		return tail == null ? appended : appended.append(tail);
 
+		return tail == null ? appended : appended.append(tail);
 	}
 
 	private AggregatePath doGetAggegatePath(RelationalPersistentProperty property) {
@@ -302,10 +302,7 @@ public AggregatePath getSubPathBasedOn(Class baseType) {
 			return this;
 		}
 
-		AggregatePath tail = getTail();
-
-		Assert.state(tail != null, "Tail must not be null");
-
+		AggregatePath tail = getRequiredTail();
 		return tail.getSubPathBasedOn(baseType);
 	}
 
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/EmbeddedContext.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/EmbeddedContext.java
index d3157a2cb0..4cf98ccf9f 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/EmbeddedContext.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/EmbeddedContext.java
@@ -28,11 +28,8 @@ public String withEmbeddedPrefix(String name) {
 		if (!ownerProperty.isEmbedded()) {
 			return name;
 		}
-		String embeddedPrefix = ownerProperty.getEmbeddedPrefix();
-		if (embeddedPrefix != null) {
-			return embeddedPrefix + name;
-		}
 
-		return name;
+		String embeddedPrefix = ownerProperty.getEmbeddedPrefix();
+		return embeddedPrefix + name;
 	}
 }
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/InsertOnlyProperty.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/InsertOnlyProperty.java
index 26d39237ea..fc26673b10 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/InsertOnlyProperty.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/InsertOnlyProperty.java
@@ -30,6 +30,5 @@
 @Retention(RetentionPolicy.RUNTIME)
 @Target({ ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE })
 @Documented
-
 public @interface InsertOnlyProperty {
 }
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/package-info.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/package-info.java
index 4de3b377e0..a5946ecfa7 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/package-info.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/package-info.java
@@ -1,4 +1,4 @@
-@NullMarked
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.relational.core.mapping.event;
 
-import org.jspecify.annotations.NullMarked;
+
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/package-info.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/package-info.java
index 0f42569e8a..a79ac42737 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/package-info.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/package-info.java
@@ -1,4 +1,4 @@
-@NullMarked
+@org.jspecify.annotations.NullMarked
 package org.springframework.data.relational.core.mapping;
 
-import org.jspecify.annotations.NullMarked;
+
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Criteria.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Criteria.java
index f8c54724f5..aa24ffbefa 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Criteria.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Criteria.java
@@ -422,10 +422,7 @@ private void unroll(CriteriaDefinition criteria, StringBuilder stringBuilder) {
 
 		while (current.hasPrevious()) {
 
-			CriteriaDefinition previous = current.getPrevious();
-
-			Assert.state(previous != null, "Previous criteria must not be null");
-
+			CriteriaDefinition previous = current.getRequiredPrevious();
 			forwardChain.put(previous, current);
 			current = previous;
 		}
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/CriteriaDefinition.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/CriteriaDefinition.java
index 3fcb52170b..81092ffaa3 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/CriteriaDefinition.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/CriteriaDefinition.java
@@ -33,19 +33,19 @@
 public interface CriteriaDefinition {
 
 	/**
-	 * Static factory method to create an empty {@link CriteriaDefinition}.
+	 * Static factory method to create an empty {@code CriteriaDefinition}.
 	 *
-	 * @return an empty {@link CriteriaDefinition}.
+	 * @return an empty {@code CriteriaDefinition}.
 	 */
 	static CriteriaDefinition empty() {
 		return Criteria.EMPTY;
 	}
 
 	/**
-	 * Create a new {@link CriteriaDefinition} and combine it as group with {@code AND} using the provided {@link List
+	 * Create a new {@code CriteriaDefinition} and combine it as group with {@code AND} using the provided {@link List
 	 * Criterias}.
 	 *
-	 * @return new {@link CriteriaDefinition}.
+	 * @return new {@code CriteriaDefinition}.
 	 */
 	static CriteriaDefinition from(CriteriaDefinition... criteria) {
 
@@ -56,10 +56,10 @@ static CriteriaDefinition from(CriteriaDefinition... criteria) {
 	}
 
 	/**
-	 * Create a new {@link CriteriaDefinition} and combine it as group with {@code AND} using the provided {@link List
+	 * Create a new {@code CriteriaDefinition} and combine it as group with {@code AND} using the provided {@link List
 	 * Criterias}.
 	 *
-	 * @return new {@link CriteriaDefinition}.
+	 * @return new {@code CriteriaDefinition}.
 	 * @since 1.1
 	 */
 	static CriteriaDefinition from(List criteria) {
@@ -79,7 +79,7 @@ static CriteriaDefinition from(List criteria) {
 	}
 
 	/**
-	 * @return {@literal true} if this {@link Criteria} is empty.
+	 * @return {@literal true} if this {@code CriteriaDefinition} is empty.
 	 */
 	boolean isGroup();
 
@@ -111,25 +111,42 @@ static CriteriaDefinition from(List criteria) {
 	boolean isIgnoreCase();
 
 	/**
-	 * @return the previous {@link CriteriaDefinition} object. Can be {@literal null} if there is no previous
-	 *         {@link CriteriaDefinition}.
+	 * @return the previous {@code CriteriaDefinition} object. Can be {@literal null} if there is no previous
+	 *         {@code CriteriaDefinition}.
 	 * @see #hasPrevious()
 	 */
 	@Nullable
 	CriteriaDefinition getPrevious();
 
 	/**
-	 * @return {@literal true} if this {@link Criteria} has a previous one.
+	 * @return the required previous {@code CriteriaDefinition} object or throws an {@link IllegalStateException} if there
+	 *         is no previous {@code CriteriaDefinition}.
+	 * @see #hasPrevious()
+	 * @see #getPrevious()
+	 * @since 4.0
+	 */
+	default CriteriaDefinition getRequiredPrevious() {
+
+		CriteriaDefinition previous = getPrevious();
+		if (previous == null) {
+			throw new IllegalStateException("No previous CriteriaDefinition available");
+		}
+
+		return previous;
+	}
+
+	/**
+	 * @return {@literal true} if this {@code CriteriaDefinition} has a previous one.
 	 */
 	boolean hasPrevious();
 
 	/**
-	 * @return {@literal true} if this {@link Criteria} is empty.
+	 * @return {@literal true} if this {@code CriteriaDefinition} is empty.
 	 */
 	boolean isEmpty();
 
 	/**
-	 * @return {@link Combinator} to combine this criteria with a previous one.
+	 * @return {@link Combinator} to combine this {@code CriteriaDefinition} with a previous one.
 	 */
 	Combinator getCombinator();
 
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Update.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Update.java
index fc2bde1366..2facc0bfeb 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Update.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/Update.java
@@ -36,9 +36,9 @@ public class Update {
 
 	private static final Update EMPTY = new Update(Collections.emptyMap());
 
-	private final Map columnsToUpdate;
+	private final Map columnsToUpdate;
 
-	private Update(Map columnsToUpdate) {
+	private Update(Map columnsToUpdate) {
 		this.columnsToUpdate = columnsToUpdate;
 	}
 
@@ -48,7 +48,7 @@ private Update(Map columnsToUpdate) {
 	 * @param assignments must not be {@literal null}.
 	 * @return
 	 */
-	public static Update from(Map assignments) {
+	public static Update from(Map assignments) {
 		return new Update(new LinkedHashMap<>(assignments));
 	}
 
@@ -94,7 +94,7 @@ public Update set(SqlIdentifier column, @Nullable Object value) {
 	 *
 	 * @return
 	 */
-	public Map getAssignments() {
+	public Map getAssignments() {
 		return Collections.unmodifiableMap(this.columnsToUpdate);
 	}
 
@@ -102,7 +102,7 @@ private Update addMultiFieldOperation(SqlIdentifier key, @Nullable Object value)
 
 		Assert.notNull(key, "Column for update must not be null");
 
-		Map updates = new LinkedHashMap<>(this.columnsToUpdate);
+		Map updates = new LinkedHashMap<>(this.columnsToUpdate);
 		updates.put(key, value);
 
 		return new Update(updates);
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/ValueFunction.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/ValueFunction.java
index 80cc6e7802..c366803076 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/ValueFunction.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/query/ValueFunction.java
@@ -32,7 +32,7 @@
  * @see Supplier
  */
 @FunctionalInterface
-public interface ValueFunction extends Function {
+public interface ValueFunction extends Function {
 
 	/**
 	 * Produces a value by considering the given {@link Escaper}.
@@ -40,7 +40,6 @@ public interface ValueFunction extends Function {
 	 * @param escaper the escaper to use.
 	 * @return the return value, may be {@literal null}.
 	 */
-	@Nullable
 	@Override
 	T apply(Escaper escaper);
 
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/AbstractImportValidator.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/AbstractImportValidator.java
index 81323976cc..73680a1ef2 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/AbstractImportValidator.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/AbstractImportValidator.java
@@ -28,8 +28,8 @@
  */
 abstract class AbstractImportValidator implements Visitor {
 
-	Set

requiredByWhere = new HashSet<>(); - Set
from = new HashSet<>(); + final Set requiredByWhere = new HashSet<>(); + final Set from = new HashSet<>(); @Nullable Visitable parent; @Override diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/AnalyticFunction.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/AnalyticFunction.java index fb4edc9a9e..86a1145a67 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/AnalyticFunction.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/AnalyticFunction.java @@ -20,7 +20,7 @@ /** * Represents an analytic function, also known as windowing function - * + * * @author Jens Schauder * @since 2.7 */ @@ -47,7 +47,7 @@ private AnalyticFunction(SimpleFunction function, Partition partition, OrderBy o /** * Specify the {@literal PARTITION BY} clause of an analytic function - * + * * @param partitionBy Typically, column but other expressions are fine to. * @return a new {@literal AnalyticFunction} is partitioned by the given expressions, overwriting any expression * previously present. @@ -58,7 +58,7 @@ public AnalyticFunction partitionBy(Expression... partitionBy) { /** * Specify the {@literal PARTITION BY} clause of an analytic function - * + * * @param partitionBy Typically, column but other expressions are fine to. * @return a new {@literal AnalyticFunction} is partitioned by the given expressions, overwriting any expression * previously present. @@ -70,7 +70,7 @@ public AnalyticFunction partitionBy(Collection partitionBy /** * Specify the {@literal ORDER BY} clause of an analytic function - * + * * @param orderBy Typically, column but other expressions are fine to. * @return a new {@literal AnalyticFunction} is ordered by the given expressions, overwriting any expression * previously present. @@ -81,7 +81,7 @@ public AnalyticFunction orderBy(OrderByField... orderBy) { /** * Specify the {@literal ORDER BY} clause of an analytic function - * + * * @param orderBy Typically, column but other expressions are fine to. * @return a new {@literal AnalyticFunction} is ordered by the given expressions, overwriting any expression * previously present. @@ -93,7 +93,7 @@ public AnalyticFunction orderBy(Collection orderBy) { /** * Specify the {@literal ORDER BY} clause of an analytic function - * + * * @param orderBy array of {@link Expression}. Typically, column but other expressions are fine to. * @return a new {@literal AnalyticFunction} is ordered by the given expressions, overwriting any expression * previously present. @@ -107,11 +107,11 @@ public AnalyticFunction orderBy(Expression... orderBy) { return new AnalyticFunction(function, partition, new OrderBy(orderByFields)); } - public AliasedAnalyticFunction as(String alias) { + public AnalyticFunction as(String alias) { return new AliasedAnalyticFunction(this, SqlIdentifier.unquoted(alias)); } - public AliasedAnalyticFunction as(SqlIdentifier alias) { + public AnalyticFunction as(SqlIdentifier alias) { return new AliasedAnalyticFunction(this, alias); } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/CaseExpression.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/CaseExpression.java index b95aa800a4..fb3d7a616f 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/CaseExpression.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/CaseExpression.java @@ -26,7 +26,7 @@ public class CaseExpression extends AbstractSegment implements Expression { private final List whenList; - @Nullable private final Expression elseExpression; + private @Nullable final Expression elseExpression; private static Segment[] children(List whenList, @Nullable Expression elseExpression) { diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Column.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Column.java index 69abbe0690..6e6007ae9a 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Column.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Column.java @@ -19,13 +19,14 @@ import org.jspecify.annotations.Nullable; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; /** * Column name within a {@code SELECT … FROM} clause. *

* Renders to: {@code } or {@code .}. *

- * + * * @author Mark Paluch * @author Jens Schauder * @since 1.1 @@ -33,20 +34,20 @@ public class Column extends AbstractSegment implements Expression, Named { private final SqlIdentifier name; - private final TableLike table; + private final @Nullable TableLike table; - Column(String name, TableLike table) { + Column(String name, @Nullable TableLike table) { - super(table); + super(table == null ? new Segment[0] : new Segment[] { table }); Assert.notNull(name, "Name must not be null"); this.name = SqlIdentifier.unquoted(name); this.table = table; } - Column(SqlIdentifier name, TableLike table) { + Column(SqlIdentifier name, @Nullable TableLike table) { - super(table); + super(table == null ? new Segment[0] : new Segment[] { table }); Assert.notNull(name, "Name must not be null"); this.name = name; @@ -340,14 +341,29 @@ public SqlIdentifier getReferenceName() { * @return the {@link Table}. Can be {@literal null} if the column was not referenced in the context of a * {@link Table}. */ - @Nullable - public TableLike getTable() { + public @Nullable TableLike getTable() { + return table; + } + + /** + * @return the required {@link Table}, throws {@link IllegalStateException} if the column was not referenced in the + * context of a {@link Table}. + * @throws IllegalStateException if the column was not referenced in the context of a {@link Table}. + * @since 4.0 + */ + public TableLike getRequiredTable() { + + TableLike table = getTable(); + + if (table == null) { + throw new IllegalStateException("Column '%s' is not associated with a Table".formatted(getName())); + } + return table; } @Override public String toString() { - return getPrefix() + name; } @@ -372,7 +388,7 @@ public boolean equals(@Nullable Object o) { return false; } Column column = (Column) o; - return name.equals(column.name) && table.equals(column.table); + return name.equals(column.name) && ObjectUtils.nullSafeEquals(table, column.table); } @Override @@ -387,12 +403,12 @@ static class AliasedColumn extends Column implements Aliased { private final SqlIdentifier alias; - private AliasedColumn(String name, TableLike table, String alias) { + private AliasedColumn(String name, @Nullable TableLike table, String alias) { super(name, table); this.alias = SqlIdentifier.unquoted(alias); } - private AliasedColumn(SqlIdentifier name, TableLike table, SqlIdentifier alias) { + private AliasedColumn(SqlIdentifier name, @Nullable TableLike table, SqlIdentifier alias) { super(name, table); this.alias = alias; } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DeleteValidator.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DeleteValidator.java index 166a28a601..acafc5c049 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DeleteValidator.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/DeleteValidator.java @@ -20,7 +20,7 @@ *

* Validates that all {@link Column}s using a table qualifier have a table import from the {@code FROM} clause. *

- * + * * @author Mark Paluch * @since 1.1 */ @@ -40,7 +40,7 @@ private void doValidate(Delete select) { select.visit(this); - for (Table table : requiredByWhere) { + for (TableLike table : requiredByWhere) { if (!from.contains(table)) { throw new IllegalStateException( String.format("Required table [%s] by a WHERE predicate not imported by FROM %s", table, from)); diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Expressions.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Expressions.java index db6a348ec5..6f1681297d 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Expressions.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Expressions.java @@ -29,7 +29,7 @@ */ public abstract class Expressions { - private static Expression ASTERISK = new SimpleExpression("*"); + private static final Expression ASTERISK = new SimpleExpression("*"); /** * @return a new asterisk {@code *} expression. diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/IdentifierProcessing.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/IdentifierProcessing.java index f5f93e0fbf..1120b8e687 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/IdentifierProcessing.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/IdentifierProcessing.java @@ -42,7 +42,7 @@ public interface IdentifierProcessing { * @param letterCasing {@link LetterCasing} rules for identifier normalization. * @return a new {@link IdentifierProcessing} object. */ - static DefaultIdentifierProcessing create(Quoting quoting, LetterCasing letterCasing) { + static IdentifierProcessing create(Quoting quoting, LetterCasing letterCasing) { return new DefaultIdentifierProcessing(quoting, letterCasing); } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Literal.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Literal.java index af6ff61a9d..f3d09defec 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Literal.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Literal.java @@ -34,8 +34,7 @@ public class Literal extends AbstractSegment implements Expression { /** * @return the content of the literal. */ - @Nullable - public T getContent() { + public @Nullable T getContent() { return content; } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/NumericLiteral.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/NumericLiteral.java index 55cc229881..9a1d55afab 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/NumericLiteral.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/NumericLiteral.java @@ -30,8 +30,7 @@ public class NumericLiteral extends Literal { } @Override - @Nullable - public Number getContent() { + public @Nullable Number getContent() { return super.getContent(); } } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SelectValidator.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SelectValidator.java index 60fddb8459..e3195adc26 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SelectValidator.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/SelectValidator.java @@ -34,10 +34,10 @@ class SelectValidator extends AbstractImportValidator { private final Stack