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 7379f4eda8..431e5fe2af 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
@@ -92,859 +92,867 @@
*/
public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAware, ApplicationContextAware {
- private final DatabaseClient databaseClient;
-
- private final ReactiveDataAccessStrategy dataAccessStrategy;
-
- private final MappingContext extends RelationalPersistentEntity>, ? extends RelationalPersistentProperty> mappingContext;
-
- private final SpelAwareProxyProjectionFactory projectionFactory;
-
- private @Nullable ReactiveEntityCallbacks entityCallbacks;
-
- /**
- * Create a new {@link R2dbcEntityTemplate} given {@link ConnectionFactory}.
- *
- * @param connectionFactory must not be {@literal null}.
- * @since 1.2
- */
- public R2dbcEntityTemplate(ConnectionFactory connectionFactory) {
-
- Assert.notNull(connectionFactory, "ConnectionFactory must not be null");
-
- R2dbcDialect dialect = DialectResolver.getDialect(connectionFactory);
-
- this.databaseClient = DatabaseClient.builder().connectionFactory(connectionFactory)
- .bindMarkers(dialect.getBindMarkersFactory()).build();
- this.dataAccessStrategy = new DefaultReactiveDataAccessStrategy(dialect);
- this.mappingContext = dataAccessStrategy.getConverter().getMappingContext();
- this.projectionFactory = new SpelAwareProxyProjectionFactory();
- }
-
- /**
- * Create a new {@link R2dbcEntityTemplate} given {@link DatabaseClient}.
- *
- * @param databaseClient must not be {@literal null}.
- * @param dialect the dialect to use, must not be {@literal null}.
- * @since 1.2
- */
- public R2dbcEntityTemplate(DatabaseClient databaseClient, R2dbcDialect dialect) {
- this(databaseClient, new DefaultReactiveDataAccessStrategy(dialect));
- }
-
- /**
- * Create a new {@link R2dbcEntityTemplate} given {@link DatabaseClient}, {@link R2dbcDialect} and
- * {@link R2dbcConverter}.
- *
- * @param databaseClient must not be {@literal null}.
- * @param dialect the dialect to use, must not be {@literal null}.
- * @param converter the dialect to use, must not be {@literal null}.
- * @since 1.2
- */
- public R2dbcEntityTemplate(DatabaseClient databaseClient, R2dbcDialect dialect, R2dbcConverter converter) {
- this(databaseClient, new DefaultReactiveDataAccessStrategy(dialect, converter));
- }
-
- /**
- * Create a new {@link R2dbcEntityTemplate} given {@link DatabaseClient} and {@link ReactiveDataAccessStrategy}.
- *
- * @param databaseClient must not be {@literal null}.
- * @since 1.2
- */
- public R2dbcEntityTemplate(DatabaseClient databaseClient, ReactiveDataAccessStrategy strategy) {
-
- Assert.notNull(databaseClient, "DatabaseClient must not be null");
- Assert.notNull(strategy, "ReactiveDataAccessStrategy must not be null");
-
- this.databaseClient = databaseClient;
- this.dataAccessStrategy = strategy;
- this.mappingContext = strategy.getConverter().getMappingContext();
- this.projectionFactory = new SpelAwareProxyProjectionFactory();
- }
-
- /*
- * (non-Javadoc)
- * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#getDatabaseClient()
- */
- @Override
- public DatabaseClient getDatabaseClient() {
- return this.databaseClient;
- }
-
- /*
- * (non-Javadoc)
- * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#getDataAccessStrategy()
- */
- @Override
- public ReactiveDataAccessStrategy getDataAccessStrategy() {
- return this.dataAccessStrategy;
- }
-
- /*
- * (non-Javadoc)
- * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#getConverter()
- */
- @Override
- public R2dbcConverter getConverter() {
- return this.dataAccessStrategy.getConverter();
- }
-
- /*
- * (non-Javadoc)
- * @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org.springframework.beans.factory.BeanFactory)
- * @deprecated since 1.2 in favor of #setApplicationContext.
- */
- @Override
- @Deprecated
- public void setBeanFactory(BeanFactory beanFactory) throws BeansException {}
-
- /*
- * (non-Javadoc)
- * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
- */
- @Override
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
-
- if (entityCallbacks == null) {
- setEntityCallbacks(ReactiveEntityCallbacks.create(applicationContext));
- }
-
- projectionFactory.setBeanFactory(applicationContext);
- projectionFactory.setBeanClassLoader(applicationContext.getClassLoader());
- }
-
- /**
- * Set the {@link ReactiveEntityCallbacks} instance to use when invoking
- * {@link org.springframework.data.mapping.callback.ReactiveEntityCallbacks callbacks} like the
- * {@link BeforeSaveCallback}.
- *
- * Overrides potentially existing {@link ReactiveEntityCallbacks}.
- *
- * @param entityCallbacks must not be {@literal null}.
- * @throws IllegalArgumentException if the given instance is {@literal null}.
- * @since 1.2
- */
- public void setEntityCallbacks(ReactiveEntityCallbacks entityCallbacks) {
-
- Assert.notNull(entityCallbacks, "EntityCallbacks must not be null");
- this.entityCallbacks = entityCallbacks;
- }
-
- // -------------------------------------------------------------------------
- // Methods dealing with org.springframework.data.r2dbc.core.FluentR2dbcOperations
- // -------------------------------------------------------------------------
-
- /*
- * (non-Javadoc)
- * @see org.springframework.data.r2dbc.core.ReactiveSelectOperation#select(java.lang.Class)
- */
- @Override
- public ReactiveSelect select(Class domainType) {
- return new ReactiveSelectOperationSupport(this).select(domainType);
- }
-
- /*
- * (non-Javadoc)
- * @see org.springframework.data.r2dbc.core.ReactiveInsertOperation#insert(java.lang.Class)
- */
- @Override
- public ReactiveInsert insert(Class domainType) {
- return new ReactiveInsertOperationSupport(this).insert(domainType);
- }
-
- /*
- * (non-Javadoc)
- * @see org.springframework.data.r2dbc.core.ReactiveUpdateOperation#update(java.lang.Class)
- */
- @Override
- public ReactiveUpdate update(Class> domainType) {
- return new ReactiveUpdateOperationSupport(this).update(domainType);
- }
-
- /*
- * (non-Javadoc)
- * @see org.springframework.data.r2dbc.core.ReactiveDeleteOperation#delete(java.lang.Class)
- */
- @Override
- public ReactiveDelete delete(Class> domainType) {
- return new ReactiveDeleteOperationSupport(this).delete(domainType);
- }
-
- // -------------------------------------------------------------------------
- // Methods dealing with org.springframework.data.r2dbc.query.Query
- // -------------------------------------------------------------------------
-
- /*
- * (non-Javadoc)
- * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#count(org.springframework.data.r2dbc.query.Query, java.lang.Class)
- */
- @Override
- public Mono count(Query query, Class> entityClass) throws DataAccessException {
-
- Assert.notNull(query, "Query must not be null");
- Assert.notNull(entityClass, "Entity class must not be null");
-
- return doCount(query, entityClass, getTableName(entityClass));
- }
-
- Mono doCount(Query query, Class> entityClass, SqlIdentifier tableName) {
-
- RelationalPersistentEntity> entity = getRequiredEntity(entityClass);
- StatementMapper statementMapper = dataAccessStrategy.getStatementMapper().forType(entityClass);
-
- StatementMapper.SelectSpec selectSpec = statementMapper //
- .createSelect(tableName) //
- .doWithTable((table, spec) -> {
-
- Expression countExpression = entity.hasIdProperty()
- ? table.column(entity.getRequiredIdProperty().getColumnName())
- : Expressions.just("1");
- return spec.withProjection(Functions.count(countExpression));
- });
-
- Optional criteria = query.getCriteria();
- if (criteria.isPresent()) {
- selectSpec = criteria.map(selectSpec::withCriteria).orElse(selectSpec);
- }
-
- PreparedOperation> operation = statementMapper.getMappedObject(selectSpec);
-
- return this.databaseClient.sql(operation) //
- .map((r, md) -> r.get(0, Long.class)) //
- .first() //
- .defaultIfEmpty(0L);
- }
-
- /*
- * (non-Javadoc)
- * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#exists(org.springframework.data.r2dbc.query.Query, java.lang.Class)
- */
- @Override
- public Mono exists(Query query, Class> entityClass) throws DataAccessException {
-
- Assert.notNull(query, "Query must not be null");
- Assert.notNull(entityClass, "Entity class must not be null");
-
- return doExists(query, entityClass, getTableName(entityClass));
- }
-
- Mono doExists(Query query, Class> entityClass, SqlIdentifier tableName) {
-
- RelationalPersistentEntity> entity = getRequiredEntity(entityClass);
- StatementMapper statementMapper = dataAccessStrategy.getStatementMapper().forType(entityClass);
-
- StatementMapper.SelectSpec selectSpec = statementMapper.createSelect(tableName).limit(1);
- if (entity.hasIdProperty()) {
- selectSpec = selectSpec //
- .withProjection(entity.getRequiredIdProperty().getColumnName());
-
- } else {
- selectSpec = selectSpec.withProjection(Expressions.just("1"));
- }
-
- Optional criteria = query.getCriteria();
- if (criteria.isPresent()) {
- selectSpec = criteria.map(selectSpec::withCriteria).orElse(selectSpec);
- }
-
- PreparedOperation> operation = statementMapper.getMappedObject(selectSpec);
-
- return this.databaseClient.sql(operation) //
- .map((r, md) -> r) //
- .first() //
- .hasElement();
- }
-
- /*
- * (non-Javadoc)
- * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#select(org.springframework.data.r2dbc.query.Query, java.lang.Class)
- */
- @Override
- public Flux select(Query query, Class entityClass) throws DataAccessException {
-
- Assert.notNull(query, "Query must not be null");
- Assert.notNull(entityClass, "Entity class must not be null");
+ private final DatabaseClient databaseClient;
+
+ private final ReactiveDataAccessStrategy dataAccessStrategy;
+
+ private final MappingContext extends RelationalPersistentEntity>, ? extends RelationalPersistentProperty> mappingContext;
+
+ private final SpelAwareProxyProjectionFactory projectionFactory;
+
+ private @Nullable ReactiveEntityCallbacks entityCallbacks;
+
+ /**
+ * Create a new {@link R2dbcEntityTemplate} given {@link ConnectionFactory}.
+ *
+ * @param connectionFactory must not be {@literal null}.
+ * @since 1.2
+ */
+ public R2dbcEntityTemplate(ConnectionFactory connectionFactory) {
+
+ Assert.notNull(connectionFactory, "ConnectionFactory must not be null");
+
+ R2dbcDialect dialect = DialectResolver.getDialect(connectionFactory);
+
+ this.databaseClient = DatabaseClient.builder().connectionFactory(connectionFactory)
+ .bindMarkers(dialect.getBindMarkersFactory()).build();
+ this.dataAccessStrategy = new DefaultReactiveDataAccessStrategy(dialect);
+ this.mappingContext = dataAccessStrategy.getConverter().getMappingContext();
+ this.projectionFactory = new SpelAwareProxyProjectionFactory();
+ }
+
+ /**
+ * Create a new {@link R2dbcEntityTemplate} given {@link DatabaseClient}.
+ *
+ * @param databaseClient must not be {@literal null}.
+ * @param dialect the dialect to use, must not be {@literal null}.
+ * @since 1.2
+ */
+ public R2dbcEntityTemplate(DatabaseClient databaseClient, R2dbcDialect dialect) {
+ this(databaseClient, new DefaultReactiveDataAccessStrategy(dialect));
+ }
+
+ /**
+ * Create a new {@link R2dbcEntityTemplate} given {@link DatabaseClient}, {@link R2dbcDialect} and
+ * {@link R2dbcConverter}.
+ *
+ * @param databaseClient must not be {@literal null}.
+ * @param dialect the dialect to use, must not be {@literal null}.
+ * @param converter the dialect to use, must not be {@literal null}.
+ * @since 1.2
+ */
+ public R2dbcEntityTemplate(DatabaseClient databaseClient, R2dbcDialect dialect, R2dbcConverter converter) {
+ this(databaseClient, new DefaultReactiveDataAccessStrategy(dialect, converter));
+ }
+
+ /**
+ * Create a new {@link R2dbcEntityTemplate} given {@link DatabaseClient} and {@link ReactiveDataAccessStrategy}.
+ *
+ * @param databaseClient must not be {@literal null}.
+ * @since 1.2
+ */
+ public R2dbcEntityTemplate(DatabaseClient databaseClient, ReactiveDataAccessStrategy strategy) {
+
+ Assert.notNull(databaseClient, "DatabaseClient must not be null");
+ Assert.notNull(strategy, "ReactiveDataAccessStrategy must not be null");
+
+ this.databaseClient = databaseClient;
+ this.dataAccessStrategy = strategy;
+ this.mappingContext = strategy.getConverter().getMappingContext();
+ this.projectionFactory = new SpelAwareProxyProjectionFactory();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#getDatabaseClient()
+ */
+ @Override
+ public DatabaseClient getDatabaseClient() {
+ return this.databaseClient;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#getDataAccessStrategy()
+ */
+ @Override
+ public ReactiveDataAccessStrategy getDataAccessStrategy() {
+ return this.dataAccessStrategy;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#getConverter()
+ */
+ @Override
+ public R2dbcConverter getConverter() {
+ return this.dataAccessStrategy.getConverter();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org.springframework.beans.factory.BeanFactory)
+ * @deprecated since 1.2 in favor of #setApplicationContext.
+ */
+ @Override
+ @Deprecated
+ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
+ */
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+
+ if (entityCallbacks == null) {
+ setEntityCallbacks(ReactiveEntityCallbacks.create(applicationContext));
+ }
+
+ projectionFactory.setBeanFactory(applicationContext);
+ projectionFactory.setBeanClassLoader(applicationContext.getClassLoader());
+ }
+
+ /**
+ * Set the {@link ReactiveEntityCallbacks} instance to use when invoking
+ * {@link org.springframework.data.mapping.callback.ReactiveEntityCallbacks callbacks} like the
+ * {@link BeforeSaveCallback}.
+ *
+ * Overrides potentially existing {@link ReactiveEntityCallbacks}.
+ *
+ * @param entityCallbacks must not be {@literal null}.
+ * @throws IllegalArgumentException if the given instance is {@literal null}.
+ * @since 1.2
+ */
+ public void setEntityCallbacks(ReactiveEntityCallbacks entityCallbacks) {
+
+ Assert.notNull(entityCallbacks, "EntityCallbacks must not be null");
+ this.entityCallbacks = entityCallbacks;
+ }
+
+ // -------------------------------------------------------------------------
+ // Methods dealing with org.springframework.data.r2dbc.core.FluentR2dbcOperations
+ // -------------------------------------------------------------------------
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.core.ReactiveSelectOperation#select(java.lang.Class)
+ */
+ @Override
+ public ReactiveSelect select(Class domainType) {
+ return new ReactiveSelectOperationSupport(this).select(domainType);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.core.ReactiveInsertOperation#insert(java.lang.Class)
+ */
+ @Override
+ public ReactiveInsert insert(Class domainType) {
+ return new ReactiveInsertOperationSupport(this).insert(domainType);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.core.ReactiveUpdateOperation#update(java.lang.Class)
+ */
+ @Override
+ public ReactiveUpdate update(Class> domainType) {
+ return new ReactiveUpdateOperationSupport(this).update(domainType);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.core.ReactiveDeleteOperation#delete(java.lang.Class)
+ */
+ @Override
+ public ReactiveDelete delete(Class> domainType) {
+ return new ReactiveDeleteOperationSupport(this).delete(domainType);
+ }
+
+ // -------------------------------------------------------------------------
+ // Methods dealing with org.springframework.data.r2dbc.query.Query
+ // -------------------------------------------------------------------------
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#count(org.springframework.data.r2dbc.query.Query, java.lang.Class)
+ */
+ @Override
+ public Mono count(Query query, Class> entityClass) throws DataAccessException {
+
+ Assert.notNull(query, "Query must not be null");
+ Assert.notNull(entityClass, "Entity class must not be null");
+
+ return doCount(query, entityClass, getTableName(entityClass));
+ }
+
+ Mono doCount(Query query, Class> entityClass, SqlIdentifier tableName) {
+
+ RelationalPersistentEntity> entity = getRequiredEntity(entityClass);
+ StatementMapper statementMapper = dataAccessStrategy.getStatementMapper().forType(entityClass);
+
+ StatementMapper.SelectSpec selectSpec = statementMapper //
+ .createSelect(tableName) //
+ .doWithTable((table, spec) -> {
+
+ Expression countExpression = entity.hasIdProperty()
+ ? table.column(entity.getRequiredIdProperty().getColumnName())
+ : Expressions.just("1");
+ return spec.withProjection(Functions.count(countExpression));
+ });
+
+ Optional criteria = query.getCriteria();
+ if (criteria.isPresent()) {
+ selectSpec = criteria.map(selectSpec::withCriteria).orElse(selectSpec);
+ }
+
+ PreparedOperation> operation = statementMapper.getMappedObject(selectSpec);
+
+ return this.databaseClient.sql(operation) //
+ .map((r, md) -> r.get(0, Long.class)) //
+ .first() //
+ .defaultIfEmpty(0L);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#exists(org.springframework.data.r2dbc.query.Query, java.lang.Class)
+ */
+ @Override
+ public Mono exists(Query query, Class> entityClass) throws DataAccessException {
+
+ Assert.notNull(query, "Query must not be null");
+ Assert.notNull(entityClass, "Entity class must not be null");
+
+ return doExists(query, entityClass, getTableName(entityClass));
+ }
+
+ Mono doExists(Query query, Class> entityClass, SqlIdentifier tableName) {
+
+ RelationalPersistentEntity> entity = getRequiredEntity(entityClass);
+ StatementMapper statementMapper = dataAccessStrategy.getStatementMapper().forType(entityClass);
+
+ StatementMapper.SelectSpec selectSpec = statementMapper.createSelect(tableName).limit(1);
+ if (entity.hasIdProperty()) {
+ selectSpec = selectSpec //
+ .withProjection(entity.getRequiredIdProperty().getColumnName());
+
+ } else {
+ selectSpec = selectSpec.withProjection(Expressions.just("1"));
+ }
+
+ Optional criteria = query.getCriteria();
+ if (criteria.isPresent()) {
+ selectSpec = criteria.map(selectSpec::withCriteria).orElse(selectSpec);
+ }
+
+ PreparedOperation> operation = statementMapper.getMappedObject(selectSpec);
+
+ return this.databaseClient.sql(operation) //
+ .map((r, md) -> r) //
+ .first() //
+ .hasElement();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#select(org.springframework.data.r2dbc.query.Query, java.lang.Class)
+ */
+ @Override
+ public Flux select(Query query, Class entityClass) throws DataAccessException {
+
+ Assert.notNull(query, "Query must not be null");
+ Assert.notNull(entityClass, "Entity class must not be null");
- SqlIdentifier tableName = getTableName(entityClass);
- return doSelect(query, entityClass, tableName, entityClass, RowsFetchSpec::all);
- }
+ SqlIdentifier tableName = getTableName(entityClass);
+ return doSelect(query, entityClass, tableName, entityClass, RowsFetchSpec::all);
+ }
- @SuppressWarnings("unchecked")
- > P doSelect(Query query, Class> entityClass, SqlIdentifier tableName,
- Class returnType, Function, P> resultHandler) {
+ @SuppressWarnings("unchecked")
+ > P doSelect(Query query, Class> entityClass, SqlIdentifier tableName,
+ Class returnType, Function, P> resultHandler) {
- RowsFetchSpec fetchSpec = doSelect(query, entityClass, tableName, returnType);
+ RowsFetchSpec fetchSpec = doSelect(query, entityClass, tableName, returnType);
- P result = resultHandler.apply(fetchSpec);
+ P result = resultHandler.apply(fetchSpec);
- if (result instanceof Mono) {
- return (P) ((Mono>) result).flatMap(it -> maybeCallAfterConvert(it, tableName));
- }
+ if (result instanceof Mono) {
+ return (P) ((Mono>) result).flatMap(it -> maybeCallAfterConvert(it, tableName));
+ }
- return (P) ((Flux>) result).concatMap(it -> maybeCallAfterConvert(it, tableName));
- }
+ return (P) ((Flux>) result).concatMap(it -> maybeCallAfterConvert(it, tableName));
+ }
- private RowsFetchSpec doSelect(Query query, Class> entityClass, SqlIdentifier tableName,
- Class returnType) {
+ private RowsFetchSpec doSelect(Query query, Class> entityClass, SqlIdentifier tableName,
+ Class returnType) {
- StatementMapper statementMapper = dataAccessStrategy.getStatementMapper().forType(entityClass);
+ StatementMapper statementMapper = dataAccessStrategy.getStatementMapper().forType(entityClass);
- StatementMapper.SelectSpec selectSpec = statementMapper //
- .createSelect(tableName) //
- .doWithTable((table, spec) -> spec.withProjection(getSelectProjection(table, query, returnType)));
+ StatementMapper.SelectSpec selectSpec = statementMapper //
+ .createSelect(tableName) //
+ .doWithTable((table, spec) -> spec.withProjection(getSelectProjection(table, query, returnType)));
- if (query.getLimit() > 0) {
- selectSpec = selectSpec.limit(query.getLimit());
- }
+ if (query.getLimit() > 0) {
+ selectSpec = selectSpec.limit(query.getLimit());
+ }
- if (query.getOffset() > 0) {
- selectSpec = selectSpec.offset(query.getOffset());
- }
+ if (query.getOffset() > 0) {
+ selectSpec = selectSpec.offset(query.getOffset());
+ }
- if (query.isSorted()) {
- selectSpec = selectSpec.withSort(query.getSort());
- }
+ if (query.isSorted()) {
+ selectSpec = selectSpec.withSort(query.getSort());
+ }
- Optional criteria = query.getCriteria();
- if (criteria.isPresent()) {
- selectSpec = criteria.map(selectSpec::withCriteria).orElse(selectSpec);
- }
+ Optional criteria = query.getCriteria();
+ if (criteria.isPresent()) {
+ selectSpec = criteria.map(selectSpec::withCriteria).orElse(selectSpec);
+ }
- PreparedOperation> operation = statementMapper.getMappedObject(selectSpec);
+ PreparedOperation> operation = statementMapper.getMappedObject(selectSpec);
- return getRowsFetchSpec(databaseClient.sql(operation), entityClass, returnType);
- }
+ return getRowsFetchSpec(databaseClient.sql(operation), entityClass, returnType);
+ }
- /*
- * (non-Javadoc)
- * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#selectOne(org.springframework.data.r2dbc.query.Query, java.lang.Class)
- */
- @Override
- public Mono selectOne(Query query, Class entityClass) throws DataAccessException {
- return doSelect(query.isLimited() ? query : query.limit(2), entityClass, getTableName(entityClass), entityClass,
- RowsFetchSpec::one);
- }
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#selectOne(org.springframework.data.r2dbc.query.Query, java.lang.Class)
+ */
+ @Override
+ public Mono selectOne(Query query, Class entityClass) throws DataAccessException {
+ return doSelect(query.isLimited() ? query : query.limit(2), entityClass, getTableName(entityClass), entityClass,
+ RowsFetchSpec::one);
+ }
- /*
- * (non-Javadoc)
- * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#update(org.springframework.data.r2dbc.query.Query, org.springframework.data.r2dbc.query.Update, java.lang.Class)
- */
- @Override
- public Mono update(Query query, Update update, Class> entityClass) throws DataAccessException {
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#update(org.springframework.data.r2dbc.query.Query, org.springframework.data.r2dbc.query.Update, java.lang.Class)
+ */
+ @Override
+ public Mono update(Query query, Update update, Class> entityClass) throws DataAccessException {
- Assert.notNull(query, "Query must not be null");
- Assert.notNull(update, "Update must not be null");
- Assert.notNull(entityClass, "Entity class must not be null");
+ Assert.notNull(query, "Query must not be null");
+ Assert.notNull(update, "Update must not be null");
+ Assert.notNull(entityClass, "Entity class must not be null");
- return doUpdate(query, update, entityClass, getTableName(entityClass));
- }
-
- Mono doUpdate(Query query, Update update, Class> entityClass, SqlIdentifier tableName) {
+ return doUpdate(query, update, entityClass, getTableName(entityClass));
+ }
+
+ Mono doUpdate(Query query, Update update, Class> entityClass, SqlIdentifier tableName) {
- StatementMapper statementMapper = dataAccessStrategy.getStatementMapper().forType(entityClass);
+ StatementMapper statementMapper = dataAccessStrategy.getStatementMapper().forType(entityClass);
- StatementMapper.UpdateSpec selectSpec = statementMapper //
- .createUpdate(tableName, update);
+ StatementMapper.UpdateSpec selectSpec = statementMapper //
+ .createUpdate(tableName, update);
- Optional criteria = query.getCriteria();
- if (criteria.isPresent()) {
- selectSpec = criteria.map(selectSpec::withCriteria).orElse(selectSpec);
- }
+ Optional criteria = query.getCriteria();
+ if (criteria.isPresent()) {
+ selectSpec = criteria.map(selectSpec::withCriteria).orElse(selectSpec);
+ }
- PreparedOperation> operation = statementMapper.getMappedObject(selectSpec);
- return this.databaseClient.sql(operation).fetch().rowsUpdated();
- }
+ PreparedOperation> operation = statementMapper.getMappedObject(selectSpec);
+ return this.databaseClient.sql(operation).fetch().rowsUpdated();
+ }
- /*
- * (non-Javadoc)
- * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#delete(org.springframework.data.r2dbc.query.Query, java.lang.Class)
- */
- @Override
- public Mono delete(Query query, Class> entityClass) throws DataAccessException {
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#delete(org.springframework.data.r2dbc.query.Query, java.lang.Class)
+ */
+ @Override
+ public Mono delete(Query query, Class> entityClass) throws DataAccessException {
- Assert.notNull(query, "Query must not be null");
- Assert.notNull(entityClass, "Entity class must not be null");
+ Assert.notNull(query, "Query must not be null");
+ Assert.notNull(entityClass, "Entity class must not be null");
- return doDelete(query, entityClass, getTableName(entityClass));
- }
+ return doDelete(query, entityClass, getTableName(entityClass));
+ }
- Mono doDelete(Query query, Class> entityClass, SqlIdentifier tableName) {
+ Mono doDelete(Query query, Class> entityClass, SqlIdentifier tableName) {
- StatementMapper statementMapper = dataAccessStrategy.getStatementMapper().forType(entityClass);
+ StatementMapper statementMapper = dataAccessStrategy.getStatementMapper().forType(entityClass);
- StatementMapper.DeleteSpec deleteSpec = statementMapper //
- .createDelete(tableName);
+ StatementMapper.DeleteSpec deleteSpec = statementMapper //
+ .createDelete(tableName);
- Optional criteria = query.getCriteria();
- if (criteria.isPresent()) {
- deleteSpec = criteria.map(deleteSpec::withCriteria).orElse(deleteSpec);
- }
+ Optional criteria = query.getCriteria();
+ if (criteria.isPresent()) {
+ deleteSpec = criteria.map(deleteSpec::withCriteria).orElse(deleteSpec);
+ }
- PreparedOperation> operation = statementMapper.getMappedObject(deleteSpec);
- return this.databaseClient.sql(operation).fetch().rowsUpdated().defaultIfEmpty(0L);
- }
+ PreparedOperation> operation = statementMapper.getMappedObject(deleteSpec);
+ return this.databaseClient.sql(operation).fetch().rowsUpdated().defaultIfEmpty(0L);
+ }
- // -------------------------------------------------------------------------
- // Methods dealing with org.springframework.r2dbc.core.PreparedOperation
- // -------------------------------------------------------------------------
+ // -------------------------------------------------------------------------
+ // Methods dealing with org.springframework.r2dbc.core.PreparedOperation
+ // -------------------------------------------------------------------------
- /*
- * (non-Javadoc)
- * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#query(org.springframework.r2dbc.core.PreparedOperation, java.lang.Class)
- */
- @Override
- public RowsFetchSpec query(PreparedOperation> operation, Class entityClass) {
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#query(org.springframework.r2dbc.core.PreparedOperation, java.lang.Class)
+ */
+ @Override
+ public RowsFetchSpec query(PreparedOperation> operation, Class entityClass) {
- Assert.notNull(operation, "PreparedOperation must not be null");
- Assert.notNull(entityClass, "Entity class must not be null");
+ Assert.notNull(operation, "PreparedOperation must not be null");
+ Assert.notNull(entityClass, "Entity class must not be null");
- return new EntityCallbackAdapter<>(getRowsFetchSpec(databaseClient.sql(operation), entityClass, entityClass),
- getTableNameOrEmpty(entityClass));
- }
+ return new EntityCallbackAdapter<>(getRowsFetchSpec(databaseClient.sql(operation), entityClass, entityClass),
+ getTableNameOrEmpty(entityClass));
+ }
- /*
- * (non-Javadoc)
- * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#query(org.springframework.r2dbc.core.PreparedOperation, java.util.function.BiFunction)
- */
- @Override
- public RowsFetchSpec query(PreparedOperation> operation, BiFunction rowMapper) {
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#query(org.springframework.r2dbc.core.PreparedOperation, java.util.function.BiFunction)
+ */
+ @Override
+ public RowsFetchSpec query(PreparedOperation> operation, BiFunction rowMapper) {
- Assert.notNull(operation, "PreparedOperation must not be null");
- Assert.notNull(rowMapper, "Row mapper must not be null");
+ Assert.notNull(operation, "PreparedOperation must not be null");
+ Assert.notNull(rowMapper, "Row mapper must not be null");
- return new EntityCallbackAdapter<>(databaseClient.sql(operation).map(rowMapper), SqlIdentifier.EMPTY);
- }
+ return new EntityCallbackAdapter<>(databaseClient.sql(operation).map(rowMapper), SqlIdentifier.EMPTY);
+ }
- /*
- * (non-Javadoc)
- * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#query(org.springframework.r2dbc.core.PreparedOperation, java.lang.Class, java.util.function.BiFunction)
- */
- @Override
- public RowsFetchSpec query(PreparedOperation> operation, Class> entityClass,
- BiFunction rowMapper) {
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#query(org.springframework.r2dbc.core.PreparedOperation, java.lang.Class, java.util.function.BiFunction)
+ */
+ @Override
+ public RowsFetchSpec query(PreparedOperation> operation, Class> entityClass,
+ BiFunction rowMapper) {
- Assert.notNull(operation, "PreparedOperation must not be null");
- Assert.notNull(entityClass, "Entity class must not be null");
- Assert.notNull(rowMapper, "Row mapper must not be null");
+ Assert.notNull(operation, "PreparedOperation must not be null");
+ Assert.notNull(entityClass, "Entity class must not be null");
+ Assert.notNull(rowMapper, "Row mapper must not be null");
- return new EntityCallbackAdapter<>(databaseClient.sql(operation).map(rowMapper), getTableNameOrEmpty(entityClass));
- }
+ return new EntityCallbackAdapter<>(databaseClient.sql(operation).map(rowMapper), getTableNameOrEmpty(entityClass));
+ }
- // -------------------------------------------------------------------------
- // Methods dealing with entities
- // -------------------------------------------------------------------------
+ // -------------------------------------------------------------------------
+ // Methods dealing with entities
+ // -------------------------------------------------------------------------
- /*
- * (non-Javadoc)
- * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#insert(java.lang.Object)
- */
- @Override
- public Mono insert(T entity) throws DataAccessException {
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#insert(java.lang.Object)
+ */
+ @Override
+ public Mono insert(T entity) throws DataAccessException {
- Assert.notNull(entity, "Entity must not be null");
+ Assert.notNull(entity, "Entity must not be null");
- return doInsert(entity, getRequiredEntity(entity).getTableName());
- }
+ return doInsert(entity, getRequiredEntity(entity).getTableName());
+ }
- Mono doInsert(T entity, SqlIdentifier tableName) {
+ Mono doInsert(T entity, SqlIdentifier tableName) {
- RelationalPersistentEntity persistentEntity = getRequiredEntity(entity);
+ RelationalPersistentEntity persistentEntity = getRequiredEntity(entity);
- return maybeCallBeforeConvert(entity, tableName).flatMap(onBeforeConvert -> {
+ return maybeCallBeforeConvert(entity, tableName).flatMap(onBeforeConvert -> {
- T initializedEntity = setVersionIfNecessary(persistentEntity, onBeforeConvert);
+ T initializedEntity = setVersionIfNecessary(persistentEntity, onBeforeConvert);
- OutboundRow outboundRow = dataAccessStrategy.getOutboundRow(initializedEntity);
+ OutboundRow outboundRow = dataAccessStrategy.getOutboundRow(initializedEntity);
- potentiallyRemoveId(persistentEntity, outboundRow);
+ potentiallyRemoveId(persistentEntity, outboundRow);
- return maybeCallBeforeSave(initializedEntity, outboundRow, tableName) //
- .flatMap(entityToSave -> doInsert(entityToSave, tableName, outboundRow));
- });
- }
+ return maybeCallBeforeSave(initializedEntity, outboundRow, tableName) //
+ .flatMap(entityToSave -> doInsert(entityToSave, tableName, outboundRow));
+ });
+ }
- private void potentiallyRemoveId(RelationalPersistentEntity> persistentEntity, OutboundRow outboundRow) {
+ private void potentiallyRemoveId(RelationalPersistentEntity> persistentEntity, OutboundRow outboundRow) {
- RelationalPersistentProperty idProperty = persistentEntity.getIdProperty();
- if (idProperty == null) {
- return;
- }
+ RelationalPersistentProperty idProperty = persistentEntity.getIdProperty();
+ if (idProperty == null) {
+ return;
+ }
- SqlIdentifier columnName = idProperty.getColumnName();
- Parameter parameter = outboundRow.get(columnName);
+ SqlIdentifier columnName = idProperty.getColumnName();
+ Parameter parameter = outboundRow.get(columnName);
- if (shouldSkipIdValue(parameter, idProperty)) {
- outboundRow.remove(columnName);
- }
- }
+ if (shouldSkipIdValue(parameter, idProperty)) {
+ outboundRow.remove(columnName);
+ }
+ }
- private boolean shouldSkipIdValue(@Nullable Parameter value, RelationalPersistentProperty property) {
+ private boolean shouldSkipIdValue(@Nullable Parameter value, RelationalPersistentProperty property) {
- if (value == null || value.getValue() == null) {
- return true;
- }
+ if (value == null || value.getValue() == null) {
+ return true;
+ }
- if (value.getValue() instanceof Number) {
- return ((Number) value.getValue()).longValue() == 0L;
- }
+ if (value.getValue() instanceof Number) {
+ return ((Number) value.getValue()).longValue() == 0L;
+ }
- return false;
- }
+ return false;
+ }
- private Mono doInsert(T entity, SqlIdentifier tableName, OutboundRow outboundRow) {
+ private Mono doInsert(T entity, SqlIdentifier tableName, OutboundRow outboundRow) {
- StatementMapper mapper = dataAccessStrategy.getStatementMapper();
- StatementMapper.InsertSpec insert = mapper.createInsert(tableName);
+ StatementMapper mapper = dataAccessStrategy.getStatementMapper();
+ StatementMapper.InsertSpec insert = mapper.createInsert(tableName);
- for (SqlIdentifier column : outboundRow.keySet()) {
- Parameter settableValue = outboundRow.get(column);
- if (settableValue.hasValue()) {
- insert = insert.withColumn(column, settableValue);
- }
- }
+ for (SqlIdentifier column : outboundRow.keySet()) {
+ Parameter settableValue = outboundRow.get(column);
+ if (settableValue.hasValue()) {
+ insert = insert.withColumn(column, settableValue);
+ }
+ }
- PreparedOperation> operation = mapper.getMappedObject(insert);
+ PreparedOperation> operation = mapper.getMappedObject(insert);
- List identifierColumns = dataAccessStrategy.getIdentifierColumns(entity.getClass());
+ List identifierColumns = dataAccessStrategy.getIdentifierColumns(entity.getClass());
- return this.databaseClient.sql(operation) //
- .filter(statement -> {
+ return this.databaseClient.sql(operation) //
+ .filter(statement -> {
- if (identifierColumns.isEmpty()) {
- return statement.returnGeneratedValues();
- }
+ if (identifierColumns.isEmpty()) {
+ return statement.returnGeneratedValues();
+ }
- return statement.returnGeneratedValues(dataAccessStrategy.renderForGeneratedValues(identifierColumns.get(0)));
- }).map(this.dataAccessStrategy.getConverter().populateIdIfNecessary(entity)) //
- .all() //
- .last(entity).flatMap(saved -> maybeCallAfterSave(saved, outboundRow, tableName));
- }
+ return statement.returnGeneratedValues(dataAccessStrategy.renderForGeneratedValues(identifierColumns.get(0)));
+ }).map(this.dataAccessStrategy.getConverter().populateIdIfNecessary(entity)) //
+ .all() //
+ .last(entity).flatMap(saved -> maybeCallAfterSave(saved, outboundRow, tableName));
+ }
- @SuppressWarnings("unchecked")
- private T setVersionIfNecessary(RelationalPersistentEntity persistentEntity, T entity) {
+ @SuppressWarnings("unchecked")
+ private T setVersionIfNecessary(RelationalPersistentEntity persistentEntity, T entity) {
- RelationalPersistentProperty versionProperty = persistentEntity.getVersionProperty();
- if (versionProperty == null) {
- return entity;
- }
+ RelationalPersistentProperty versionProperty = persistentEntity.getVersionProperty();
+ if (versionProperty == null) {
+ return entity;
+ }
- Class> versionPropertyType = versionProperty.getType();
- Long version = versionPropertyType.isPrimitive() ? 1L : 0L;
- ConversionService conversionService = this.dataAccessStrategy.getConverter().getConversionService();
- PersistentPropertyAccessor> propertyAccessor = persistentEntity.getPropertyAccessor(entity);
- propertyAccessor.setProperty(versionProperty, conversionService.convert(version, versionPropertyType));
+ Class> versionPropertyType = versionProperty.getType();
+ Long version = versionPropertyType.isPrimitive() ? 1L : 0L;
+ ConversionService conversionService = this.dataAccessStrategy.getConverter().getConversionService();
+ PersistentPropertyAccessor> propertyAccessor = persistentEntity.getPropertyAccessor(entity);
+ propertyAccessor.setProperty(versionProperty, conversionService.convert(version, versionPropertyType));
- return (T) propertyAccessor.getBean();
- }
+ return (T) propertyAccessor.getBean();
+ }
- /*
- * (non-Javadoc)
- * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#update(java.lang.Object)
- */
- @Override
- public Mono update(T entity) throws DataAccessException {
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#update(java.lang.Object)
+ */
+ @Override
+ public Mono update(T entity) throws DataAccessException {
- Assert.notNull(entity, "Entity must not be null");
+ Assert.notNull(entity, "Entity must not be null");
- return doUpdate(entity, getRequiredEntity(entity).getTableName());
- }
+ return doUpdate(entity, getRequiredEntity(entity).getTableName());
+ }
- private Mono doUpdate(T entity, SqlIdentifier tableName) {
+ private Mono doUpdate(T entity, SqlIdentifier tableName) {
- RelationalPersistentEntity persistentEntity = getRequiredEntity(entity);
+ RelationalPersistentEntity persistentEntity = getRequiredEntity(entity);
- return maybeCallBeforeConvert(entity, tableName).flatMap(onBeforeConvert -> {
+ return maybeCallBeforeConvert(entity, tableName).flatMap(onBeforeConvert -> {
- T entityToUse;
- Criteria matchingVersionCriteria;
+ T entityToUse;
+ Criteria matchingVersionCriteria;
- if (persistentEntity.hasVersionProperty()) {
+ if (persistentEntity.hasVersionProperty()) {
- matchingVersionCriteria = createMatchingVersionCriteria(onBeforeConvert, persistentEntity);
- entityToUse = incrementVersion(persistentEntity, onBeforeConvert);
- } else {
+ matchingVersionCriteria = createMatchingVersionCriteria(onBeforeConvert, persistentEntity);
+ entityToUse = incrementVersion(persistentEntity, onBeforeConvert);
+ } else {
- entityToUse = onBeforeConvert;
- matchingVersionCriteria = null;
- }
+ entityToUse = onBeforeConvert;
+ matchingVersionCriteria = null;
+ }
- OutboundRow outboundRow = dataAccessStrategy.getOutboundRow(entityToUse);
+ OutboundRow outboundRow = dataAccessStrategy.getOutboundRow(entityToUse);
- return maybeCallBeforeSave(entityToUse, outboundRow, tableName) //
- .flatMap(onBeforeSave -> {
+ return maybeCallBeforeSave(entityToUse, outboundRow, tableName) //
+ .flatMap(onBeforeSave -> {
- SqlIdentifier idColumn = persistentEntity.getRequiredIdProperty().getColumnName();
- Parameter id = outboundRow.remove(idColumn);
- Criteria criteria = Criteria.where(dataAccessStrategy.toSql(idColumn)).is(id);
+ SqlIdentifier idColumn = persistentEntity.getRequiredIdProperty().getColumnName();
+ Parameter id = outboundRow.remove(idColumn);
- if (matchingVersionCriteria != null) {
- criteria = criteria.and(matchingVersionCriteria);
- }
+ persistentEntity.forEach(p -> {
+ if (p.isInsertOnly()) {
+ outboundRow.remove(p.getColumnName());
+ }
+ });
- return doUpdate(onBeforeSave, tableName, persistentEntity, criteria, outboundRow);
- });
- });
- }
+ Criteria criteria = Criteria.where(dataAccessStrategy.toSql(idColumn)).is(id);
- @SuppressWarnings({ "unchecked", "rawtypes" })
- private Mono doUpdate(T entity, SqlIdentifier tableName, RelationalPersistentEntity persistentEntity,
- Criteria criteria, OutboundRow outboundRow) {
+ if (matchingVersionCriteria != null) {
+ criteria = criteria.and(matchingVersionCriteria);
+ }
- Update update = Update.from((Map) outboundRow);
+ return doUpdate(onBeforeSave, tableName, persistentEntity, criteria, outboundRow);
+ });
+ });
+ }
- StatementMapper mapper = dataAccessStrategy.getStatementMapper();
- StatementMapper.UpdateSpec updateSpec = mapper.createUpdate(tableName, update).withCriteria(criteria);
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ private Mono doUpdate(T entity, SqlIdentifier tableName, RelationalPersistentEntity persistentEntity,
+ Criteria criteria, OutboundRow outboundRow) {
- PreparedOperation> operation = mapper.getMappedObject(updateSpec);
+ Update update = Update.from((Map) outboundRow);
- return this.databaseClient.sql(operation) //
- .fetch() //
- .rowsUpdated() //
- .handle((rowsUpdated, sink) -> {
+ StatementMapper mapper = dataAccessStrategy.getStatementMapper();
+ StatementMapper.UpdateSpec updateSpec = mapper.createUpdate(tableName, update).withCriteria(criteria);
- if (rowsUpdated != 0) {
- return;
- }
+ PreparedOperation> operation = mapper.getMappedObject(updateSpec);
- if (persistentEntity.hasVersionProperty()) {
- sink.error(new OptimisticLockingFailureException(
- formatOptimisticLockingExceptionMessage(entity, persistentEntity)));
- } else {
- sink.error(new TransientDataAccessResourceException(
- formatTransientEntityExceptionMessage(entity, persistentEntity)));
- }
- }).then(maybeCallAfterSave(entity, outboundRow, tableName));
- }
+ return this.databaseClient.sql(operation) //
+ .fetch() //
+ .rowsUpdated() //
+ .handle((rowsUpdated, sink) -> {
- private String formatOptimisticLockingExceptionMessage(T entity, RelationalPersistentEntity persistentEntity) {
+ if (rowsUpdated != 0) {
+ return;
+ }
- return String.format("Failed to update table [%s]; Version does not match for row with Id [%s]",
- persistentEntity.getTableName(), persistentEntity.getIdentifierAccessor(entity).getIdentifier());
- }
+ if (persistentEntity.hasVersionProperty()) {
+ sink.error(new OptimisticLockingFailureException(
+ formatOptimisticLockingExceptionMessage(entity, persistentEntity)));
+ } else {
+ sink.error(new TransientDataAccessResourceException(
+ formatTransientEntityExceptionMessage(entity, persistentEntity)));
+ }
+ }).then(maybeCallAfterSave(entity, outboundRow, tableName));
+ }
- private String formatTransientEntityExceptionMessage(T entity, RelationalPersistentEntity persistentEntity) {
+ private String formatOptimisticLockingExceptionMessage(T entity, RelationalPersistentEntity persistentEntity) {
- return String.format("Failed to update table [%s]; Row with Id [%s] does not exist",
- persistentEntity.getTableName(), persistentEntity.getIdentifierAccessor(entity).getIdentifier());
- }
+ return String.format("Failed to update table [%s]; Version does not match for row with Id [%s]",
+ persistentEntity.getTableName(), persistentEntity.getIdentifierAccessor(entity).getIdentifier());
+ }
- @SuppressWarnings("unchecked")
- private T incrementVersion(RelationalPersistentEntity persistentEntity, T entity) {
+ private String formatTransientEntityExceptionMessage(T entity, RelationalPersistentEntity persistentEntity) {
- PersistentPropertyAccessor> propertyAccessor = persistentEntity.getPropertyAccessor(entity);
- RelationalPersistentProperty versionProperty = persistentEntity.getVersionProperty();
+ return String.format("Failed to update table [%s]; Row with Id [%s] does not exist",
+ persistentEntity.getTableName(), persistentEntity.getIdentifierAccessor(entity).getIdentifier());
+ }
- ConversionService conversionService = this.dataAccessStrategy.getConverter().getConversionService();
- Object currentVersionValue = propertyAccessor.getProperty(versionProperty);
- long newVersionValue = 1L;
- if (currentVersionValue != null) {
- newVersionValue = conversionService.convert(currentVersionValue, Long.class) + 1;
- }
- Class> versionPropertyType = versionProperty.getType();
- propertyAccessor.setProperty(versionProperty, conversionService.convert(newVersionValue, versionPropertyType));
+ @SuppressWarnings("unchecked")
+ private T incrementVersion(RelationalPersistentEntity persistentEntity, T entity) {
- return (T) propertyAccessor.getBean();
- }
+ PersistentPropertyAccessor> propertyAccessor = persistentEntity.getPropertyAccessor(entity);
+ RelationalPersistentProperty versionProperty = persistentEntity.getVersionProperty();
- private Criteria createMatchingVersionCriteria(T entity, RelationalPersistentEntity persistentEntity) {
+ ConversionService conversionService = this.dataAccessStrategy.getConverter().getConversionService();
+ Object currentVersionValue = propertyAccessor.getProperty(versionProperty);
+ long newVersionValue = 1L;
+ if (currentVersionValue != null) {
+ newVersionValue = conversionService.convert(currentVersionValue, Long.class) + 1;
+ }
+ Class> versionPropertyType = versionProperty.getType();
+ propertyAccessor.setProperty(versionProperty, conversionService.convert(newVersionValue, versionPropertyType));
- PersistentPropertyAccessor> propertyAccessor = persistentEntity.getPropertyAccessor(entity);
- RelationalPersistentProperty versionProperty = persistentEntity.getVersionProperty();
+ return (T) propertyAccessor.getBean();
+ }
- Object version = propertyAccessor.getProperty(versionProperty);
- Criteria.CriteriaStep versionColumn = Criteria.where(dataAccessStrategy.toSql(versionProperty.getColumnName()));
- if (version == null) {
- return versionColumn.isNull();
- } else {
- return versionColumn.is(version);
- }
- }
+ private Criteria createMatchingVersionCriteria(T entity, RelationalPersistentEntity persistentEntity) {
- /*
- * (non-Javadoc)
- * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#delete(java.lang.Object)
- */
- @Override
- public Mono delete(T entity) throws DataAccessException {
+ PersistentPropertyAccessor> propertyAccessor = persistentEntity.getPropertyAccessor(entity);
+ RelationalPersistentProperty versionProperty = persistentEntity.getVersionProperty();
- Assert.notNull(entity, "Entity must not be null");
+ Object version = propertyAccessor.getProperty(versionProperty);
+ Criteria.CriteriaStep versionColumn = Criteria.where(dataAccessStrategy.toSql(versionProperty.getColumnName()));
+ if (version == null) {
+ return versionColumn.isNull();
+ } else {
+ return versionColumn.is(version);
+ }
+ }
- RelationalPersistentEntity> persistentEntity = getRequiredEntity(entity);
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.r2dbc.core.R2dbcEntityOperations#delete(java.lang.Object)
+ */
+ @Override
+ public Mono delete(T entity) throws DataAccessException {
- return delete(getByIdQuery(entity, persistentEntity), persistentEntity.getType()).thenReturn(entity);
- }
+ Assert.notNull(entity, "Entity must not be null");
- protected Mono maybeCallBeforeConvert(T object, SqlIdentifier table) {
+ RelationalPersistentEntity> persistentEntity = getRequiredEntity(entity);
- if (entityCallbacks != null) {
- return entityCallbacks.callback(BeforeConvertCallback.class, object, table);
- }
+ return delete(getByIdQuery(entity, persistentEntity), persistentEntity.getType()).thenReturn(entity);
+ }
- return Mono.just(object);
- }
+ protected Mono maybeCallBeforeConvert(T object, SqlIdentifier table) {
- protected Mono maybeCallBeforeSave(T object, OutboundRow row, SqlIdentifier table) {
+ if (entityCallbacks != null) {
+ return entityCallbacks.callback(BeforeConvertCallback.class, object, table);
+ }
- if (entityCallbacks != null) {
- return entityCallbacks.callback(BeforeSaveCallback.class, object, row, table);
- }
+ return Mono.just(object);
+ }
- return Mono.just(object);
- }
+ protected Mono maybeCallBeforeSave(T object, OutboundRow row, SqlIdentifier table) {
- protected Mono maybeCallAfterSave(T object, OutboundRow row, SqlIdentifier table) {
+ if (entityCallbacks != null) {
+ return entityCallbacks.callback(BeforeSaveCallback.class, object, row, table);
+ }
- if (entityCallbacks != null) {
- return entityCallbacks.callback(AfterSaveCallback.class, object, row, table);
- }
+ return Mono.just(object);
+ }
- return Mono.just(object);
- }
+ protected Mono maybeCallAfterSave(T object, OutboundRow row, SqlIdentifier table) {
- protected Mono maybeCallAfterConvert(T object, SqlIdentifier table) {
+ if (entityCallbacks != null) {
+ return entityCallbacks.callback(AfterSaveCallback.class, object, row, table);
+ }
- if (entityCallbacks != null) {
- return entityCallbacks.callback(AfterConvertCallback.class, object, table);
- }
+ return Mono.just(object);
+ }
- return Mono.just(object);
- }
+ protected Mono maybeCallAfterConvert(T object, SqlIdentifier table) {
- private Query getByIdQuery(T entity, RelationalPersistentEntity> persistentEntity) {
+ if (entityCallbacks != null) {
+ return entityCallbacks.callback(AfterConvertCallback.class, object, table);
+ }
- if (!persistentEntity.hasIdProperty()) {
- throw new MappingException("No id property found for object of type " + persistentEntity.getType());
- }
+ return Mono.just(object);
+ }
- IdentifierAccessor identifierAccessor = persistentEntity.getIdentifierAccessor(entity);
- Object id = identifierAccessor.getRequiredIdentifier();
+ private Query getByIdQuery(T entity, RelationalPersistentEntity> persistentEntity) {
- return Query.query(Criteria.where(persistentEntity.getRequiredIdProperty().getName()).is(id));
- }
+ if (!persistentEntity.hasIdProperty()) {
+ throw new MappingException("No id property found for object of type " + persistentEntity.getType());
+ }
- SqlIdentifier getTableName(Class> entityClass) {
- return getRequiredEntity(entityClass).getTableName();
- }
+ IdentifierAccessor identifierAccessor = persistentEntity.getIdentifierAccessor(entity);
+ Object id = identifierAccessor.getRequiredIdentifier();
- SqlIdentifier getTableNameOrEmpty(Class> entityClass) {
+ return Query.query(Criteria.where(persistentEntity.getRequiredIdProperty().getName()).is(id));
+ }
- RelationalPersistentEntity> entity = this.mappingContext.getPersistentEntity(entityClass);
+ SqlIdentifier getTableName(Class> entityClass) {
+ return getRequiredEntity(entityClass).getTableName();
+ }
- return entity != null ? entity.getTableName() : SqlIdentifier.EMPTY;
- }
+ SqlIdentifier getTableNameOrEmpty(Class> entityClass) {
- private RelationalPersistentEntity> getRequiredEntity(Class> entityClass) {
- return this.mappingContext.getRequiredPersistentEntity(entityClass);
- }
+ RelationalPersistentEntity> entity = this.mappingContext.getPersistentEntity(entityClass);
- private RelationalPersistentEntity getRequiredEntity(T entity) {
- Class> entityType = ProxyUtils.getUserClass(entity);
- return (RelationalPersistentEntity) getRequiredEntity(entityType);
- }
+ return entity != null ? entity.getTableName() : SqlIdentifier.EMPTY;
+ }
- private List getSelectProjection(Table table, Query query, Class returnType) {
+ private RelationalPersistentEntity> getRequiredEntity(Class> entityClass) {
+ return this.mappingContext.getRequiredPersistentEntity(entityClass);
+ }
- if (query.getColumns().isEmpty()) {
+ private RelationalPersistentEntity getRequiredEntity(T entity) {
+ Class> entityType = ProxyUtils.getUserClass(entity);
+ return (RelationalPersistentEntity) getRequiredEntity(entityType);
+ }
- if (returnType.isInterface()) {
+ private List getSelectProjection(Table table, Query query, Class returnType) {
- ProjectionInformation projectionInformation = projectionFactory.getProjectionInformation(returnType);
+ if (query.getColumns().isEmpty()) {
- if (projectionInformation.isClosed()) {
- return projectionInformation.getInputProperties().stream().map(FeatureDescriptor::getName).map(table::column)
- .collect(Collectors.toList());
- }
- }
+ if (returnType.isInterface()) {
- return Collections.singletonList(table.asterisk());
- }
+ ProjectionInformation projectionInformation = projectionFactory.getProjectionInformation(returnType);
- return query.getColumns().stream().map(table::column).collect(Collectors.toList());
- }
+ if (projectionInformation.isClosed()) {
+ return projectionInformation.getInputProperties().stream().map(FeatureDescriptor::getName).map(table::column)
+ .collect(Collectors.toList());
+ }
+ }
- private RowsFetchSpec getRowsFetchSpec(DatabaseClient.GenericExecuteSpec executeSpec, Class> entityClass,
- Class returnType) {
+ return Collections.singletonList(table.asterisk());
+ }
- boolean simpleType;
+ return query.getColumns().stream().map(table::column).collect(Collectors.toList());
+ }
- BiFunction rowMapper;
- if (returnType.isInterface()) {
- simpleType = getConverter().isSimpleType(entityClass);
- rowMapper = dataAccessStrategy.getRowMapper(entityClass)
- .andThen(o -> projectionFactory.createProjection(returnType, o));
- } else {
- simpleType = getConverter().isSimpleType(returnType);
- rowMapper = dataAccessStrategy.getRowMapper(returnType);
- }
+ private RowsFetchSpec getRowsFetchSpec(DatabaseClient.GenericExecuteSpec executeSpec, Class> entityClass,
+ Class returnType) {
- // avoid top-level null values if the read type is a simple one (e.g. SELECT MAX(age) via Integer.class)
- if (simpleType) {
- return new UnwrapOptionalFetchSpecAdapter<>(
- executeSpec.map((row, metadata) -> Optional.ofNullable(rowMapper.apply(row, metadata))));
- }
+ boolean simpleType;
- return executeSpec.map(rowMapper);
- }
+ BiFunction rowMapper;
+ if (returnType.isInterface()) {
+ simpleType = getConverter().isSimpleType(entityClass);
+ rowMapper = dataAccessStrategy.getRowMapper(entityClass)
+ .andThen(o -> projectionFactory.createProjection(returnType, o));
+ } else {
+ simpleType = getConverter().isSimpleType(returnType);
+ rowMapper = dataAccessStrategy.getRowMapper(returnType);
+ }
- /**
- * {@link RowsFetchSpec} adapter emitting values from {@link Optional} if they exist.
- *
- * @param
- */
- private static class UnwrapOptionalFetchSpecAdapter implements RowsFetchSpec {
+ // avoid top-level null values if the read type is a simple one (e.g. SELECT MAX(age) via Integer.class)
+ if (simpleType) {
+ return new UnwrapOptionalFetchSpecAdapter<>(
+ executeSpec.map((row, metadata) -> Optional.ofNullable(rowMapper.apply(row, metadata))));
+ }
- private final RowsFetchSpec> delegate;
+ return executeSpec.map(rowMapper);
+ }
- private UnwrapOptionalFetchSpecAdapter(RowsFetchSpec> delegate) {
- this.delegate = delegate;
- }
+ /**
+ * {@link RowsFetchSpec} adapter emitting values from {@link Optional} if they exist.
+ *
+ * @param
+ */
+ private static class UnwrapOptionalFetchSpecAdapter implements RowsFetchSpec {
- @Override
- public Mono one() {
- return delegate.one().handle((optional, sink) -> optional.ifPresent(sink::next));
- }
+ private final RowsFetchSpec> delegate;
- @Override
- public Mono first() {
- return delegate.first().handle((optional, sink) -> optional.ifPresent(sink::next));
- }
+ private UnwrapOptionalFetchSpecAdapter(RowsFetchSpec> delegate) {
+ this.delegate = delegate;
+ }
- @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
- * object.
- *
- * @param
- */
- private class EntityCallbackAdapter implements RowsFetchSpec {
-
- private final RowsFetchSpec delegate;
- private final SqlIdentifier tableName;
-
- private EntityCallbackAdapter(RowsFetchSpec delegate, SqlIdentifier tableName) {
- this.delegate = delegate;
- this.tableName = tableName;
- }
-
- @Override
- public Mono one() {
- return delegate.one().flatMap(it -> maybeCallAfterConvert(it, tableName));
- }
-
- @Override
- public Mono first() {
- return delegate.first().flatMap(it -> maybeCallAfterConvert(it, tableName));
- }
-
- @Override
- public Flux all() {
- return delegate.all().concatMap(it -> maybeCallAfterConvert(it, tableName));
- }
- }
+ @Override
+ 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 Flux all() {
+ return delegate.all().handle((optional, sink) -> optional.ifPresent(sink::next));
+ }
+ }
+
+ /**
+ * {@link RowsFetchSpec} adapter applying {@link #maybeCallAfterConvert(Object, SqlIdentifier)} to each emitted
+ * object.
+ *
+ * @param
+ */
+ private class EntityCallbackAdapter implements RowsFetchSpec {
+
+ private final RowsFetchSpec delegate;
+ private final SqlIdentifier tableName;
+
+ private EntityCallbackAdapter(RowsFetchSpec delegate, SqlIdentifier tableName) {
+ this.delegate = delegate;
+ this.tableName = tableName;
+ }
+
+ @Override
+ public Mono one() {
+ return delegate.one().flatMap(it -> maybeCallAfterConvert(it, tableName));
+ }
+
+ @Override
+ public Mono first() {
+ return delegate.first().flatMap(it -> maybeCallAfterConvert(it, tableName));
+ }
+
+ @Override
+ public Flux all() {
+ return delegate.all().concatMap(it -> maybeCallAfterConvert(it, tableName));
+ }
+ }
}
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 b9c0a12a06..fc2402d932 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
@@ -25,6 +25,7 @@
import io.r2dbc.spi.test.MockRowMetadata;
import lombok.Value;
import lombok.With;
+import org.springframework.data.relational.core.mapping.InsertOnlyProperty;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
@@ -69,574 +70,659 @@
* @author Mark Paluch
* @author Jose Luis Leon
* @author Robert Heim
+ * @author Jens Schauder
*/
public class R2dbcEntityTemplateUnitTests {
- private org.springframework.r2dbc.core.DatabaseClient client;
- private R2dbcEntityTemplate entityTemplate;
- private StatementRecorder recorder;
+ private org.springframework.r2dbc.core.DatabaseClient client;
+ private R2dbcEntityTemplate entityTemplate;
+ private StatementRecorder recorder;
- @BeforeEach
- void before() {
+ @BeforeEach
+ void before() {
- recorder = StatementRecorder.newInstance();
- client = DatabaseClient.builder().connectionFactory(recorder)
- .bindMarkers(PostgresDialect.INSTANCE.getBindMarkersFactory()).build();
- entityTemplate = new R2dbcEntityTemplate(client, PostgresDialect.INSTANCE);
- }
+ recorder = StatementRecorder.newInstance();
+ client = DatabaseClient.builder().connectionFactory(recorder)
+ .bindMarkers(PostgresDialect.INSTANCE.getBindMarkersFactory()).build();
+ entityTemplate = new R2dbcEntityTemplate(client, PostgresDialect.INSTANCE);
+ }
- @Test // gh-220
- void shouldCountBy() {
+ @Test
+ // gh-220
+ void shouldCountBy() {
- MockRowMetadata metadata = MockRowMetadata.builder()
- .columnMetadata(MockColumnMetadata.builder().name("name").type(R2dbcType.VARCHAR).build()).build();
- MockResult result = MockResult.builder().row(MockRow.builder().identified(0, Long.class, 1L).build()).build();
+ MockRowMetadata metadata = MockRowMetadata.builder()
+ .columnMetadata(MockColumnMetadata.builder().name("name").type(R2dbcType.VARCHAR).build()).build();
+ MockResult result = MockResult.builder().row(MockRow.builder().identified(0, Long.class, 1L).build()).build();
- recorder.addStubbing(s -> s.startsWith("SELECT"), result);
+ recorder.addStubbing(s -> s.startsWith("SELECT"), result);
- entityTemplate.count(Query.query(Criteria.where("name").is("Walter")), Person.class) //
- .as(StepVerifier::create) //
- .expectNext(1L) //
- .verifyComplete();
+ entityTemplate.count(Query.query(Criteria.where("name").is("Walter")), Person.class) //
+ .as(StepVerifier::create) //
+ .expectNext(1L) //
+ .verifyComplete();
- StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("SELECT"));
+ StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("SELECT"));
- assertThat(statement.getSql()).isEqualTo("SELECT COUNT(person.id) FROM person WHERE person.THE_NAME = $1");
- assertThat(statement.getBindings()).hasSize(1).containsEntry(0, Parameter.from("Walter"));
- }
+ assertThat(statement.getSql()).isEqualTo("SELECT COUNT(person.id) FROM person WHERE person.THE_NAME = $1");
+ assertThat(statement.getBindings()).hasSize(1).containsEntry(0, Parameter.from("Walter"));
+ }
- @Test // gh-469
- void shouldProjectExistsResult() {
+ @Test
+ // gh-469
+ void shouldProjectExistsResult() {
- MockRowMetadata metadata = MockRowMetadata.builder()
- .columnMetadata(MockColumnMetadata.builder().name("name").type(R2dbcType.VARCHAR).build()).build();
- MockResult result = MockResult.builder().row(MockRow.builder().identified(0, Object.class, null).build()).build();
+ MockRowMetadata metadata = MockRowMetadata.builder()
+ .columnMetadata(MockColumnMetadata.builder().name("name").type(R2dbcType.VARCHAR).build()).build();
+ MockResult result = MockResult.builder().row(MockRow.builder().identified(0, Object.class, null).build()).build();
- recorder.addStubbing(s -> s.startsWith("SELECT"), result);
+ recorder.addStubbing(s -> s.startsWith("SELECT"), result);
- entityTemplate.select(Person.class) //
- .as(Integer.class) //
- .matching(Query.empty().columns("MAX(age)")) //
- .all() //
- .as(StepVerifier::create) //
- .verifyComplete();
- }
+ entityTemplate.select(Person.class) //
+ .as(Integer.class) //
+ .matching(Query.empty().columns("MAX(age)")) //
+ .all() //
+ .as(StepVerifier::create) //
+ .verifyComplete();
+ }
- @Test // gh-1310
- void shouldProjectExistsResultWithoutId() {
+ @Test
+ // gh-1310
+ void shouldProjectExistsResultWithoutId() {
- MockResult result = MockResult.builder().row(MockRow.builder().identified(0, Object.class, null).build()).build();
+ MockResult result = MockResult.builder().row(MockRow.builder().identified(0, Object.class, null).build()).build();
- recorder.addStubbing(s -> s.startsWith("SELECT 1"), result);
+ recorder.addStubbing(s -> s.startsWith("SELECT 1"), result);
- entityTemplate.select(WithoutId.class).exists() //
- .as(StepVerifier::create) //
- .expectNext(true).verifyComplete();
- }
+ entityTemplate.select(WithoutId.class).exists() //
+ .as(StepVerifier::create) //
+ .expectNext(true).verifyComplete();
+ }
- @Test // gh-1310
- void shouldProjectCountResultWithoutId() {
+ @Test
+ // gh-1310
+ void shouldProjectCountResultWithoutId() {
- MockResult result = MockResult.builder().row(MockRow.builder().identified(0, Long.class, 1L).build()).build();
+ MockResult result = MockResult.builder().row(MockRow.builder().identified(0, Long.class, 1L).build()).build();
- recorder.addStubbing(s -> s.startsWith("SELECT COUNT(1)"), result);
+ recorder.addStubbing(s -> s.startsWith("SELECT COUNT(1)"), result);
- entityTemplate.select(WithoutId.class).count() //
- .as(StepVerifier::create) //
- .expectNext(1L).verifyComplete();
- }
+ entityTemplate.select(WithoutId.class).count() //
+ .as(StepVerifier::create) //
+ .expectNext(1L).verifyComplete();
+ }
- @Test // gh-469
- void shouldExistsByCriteria() {
+ @Test
+ // gh-469
+ void shouldExistsByCriteria() {
- MockRowMetadata metadata = MockRowMetadata.builder()
- .columnMetadata(MockColumnMetadata.builder().name("name").type(R2dbcType.VARCHAR).build()).build();
- MockResult result = MockResult.builder().row(MockRow.builder().identified(0, Long.class, 1L).build()).build();
+ MockRowMetadata metadata = MockRowMetadata.builder()
+ .columnMetadata(MockColumnMetadata.builder().name("name").type(R2dbcType.VARCHAR).build()).build();
+ MockResult result = MockResult.builder().row(MockRow.builder().identified(0, Long.class, 1L).build()).build();
- recorder.addStubbing(s -> s.startsWith("SELECT"), result);
+ recorder.addStubbing(s -> s.startsWith("SELECT"), result);
- entityTemplate.exists(Query.query(Criteria.where("name").is("Walter")), Person.class) //
- .as(StepVerifier::create) //
- .expectNext(true) //
- .verifyComplete();
+ entityTemplate.exists(Query.query(Criteria.where("name").is("Walter")), Person.class) //
+ .as(StepVerifier::create) //
+ .expectNext(true) //
+ .verifyComplete();
- StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("SELECT"));
+ StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("SELECT"));
- assertThat(statement.getSql()).isEqualTo("SELECT person.id FROM person WHERE person.THE_NAME = $1 LIMIT 1");
- assertThat(statement.getBindings()).hasSize(1).containsEntry(0, Parameter.from("Walter"));
- }
+ assertThat(statement.getSql()).isEqualTo("SELECT person.id FROM person WHERE person.THE_NAME = $1 LIMIT 1");
+ assertThat(statement.getBindings()).hasSize(1).containsEntry(0, Parameter.from("Walter"));
+ }
- @Test // gh-220
- void shouldSelectByCriteria() {
+ @Test
+ // gh-220
+ void shouldSelectByCriteria() {
- recorder.addStubbing(s -> s.startsWith("SELECT"), Collections.emptyList());
+ recorder.addStubbing(s -> s.startsWith("SELECT"), Collections.emptyList());
- entityTemplate.select(Query.query(Criteria.where("name").is("Walter")).sort(Sort.by("name")), Person.class) //
- .as(StepVerifier::create) //
- .verifyComplete();
+ entityTemplate.select(Query.query(Criteria.where("name").is("Walter")).sort(Sort.by("name")), Person.class) //
+ .as(StepVerifier::create) //
+ .verifyComplete();
- StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("SELECT"));
+ StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("SELECT"));
- assertThat(statement.getSql())
- .isEqualTo("SELECT person.* FROM person WHERE person.THE_NAME = $1 ORDER BY person.THE_NAME ASC");
- assertThat(statement.getBindings()).hasSize(1).containsEntry(0, Parameter.from("Walter"));
- }
+ assertThat(statement.getSql())
+ .isEqualTo("SELECT person.* FROM person WHERE person.THE_NAME = $1 ORDER BY person.THE_NAME ASC");
+ assertThat(statement.getBindings()).hasSize(1).containsEntry(0, Parameter.from("Walter"));
+ }
- @Test // gh-215
- void selectShouldInvokeCallback() {
+ @Test
+ // gh-215
+ void selectShouldInvokeCallback() {
- MockRowMetadata metadata = MockRowMetadata.builder()
- .columnMetadata(MockColumnMetadata.builder().name("id").type(R2dbcType.INTEGER).build())
- .columnMetadata(MockColumnMetadata.builder().name("THE_NAME").type(R2dbcType.VARCHAR).build()).build();
- MockResult result = MockResult.builder().row(MockRow.builder().identified("id", Object.class, "Walter")
- .identified("THE_NAME", Object.class, "some-name").metadata(metadata).build()).build();
+ MockRowMetadata metadata = MockRowMetadata.builder()
+ .columnMetadata(MockColumnMetadata.builder().name("id").type(R2dbcType.INTEGER).build())
+ .columnMetadata(MockColumnMetadata.builder().name("THE_NAME").type(R2dbcType.VARCHAR).build()).build();
+ MockResult result = MockResult.builder().row(MockRow.builder().identified("id", Object.class, "Walter")
+ .identified("THE_NAME", Object.class, "some-name").metadata(metadata).build()).build();
- recorder.addStubbing(s -> s.startsWith("SELECT"), result);
+ recorder.addStubbing(s -> s.startsWith("SELECT"), result);
- ValueCapturingAfterConvertCallback callback = new ValueCapturingAfterConvertCallback();
+ ValueCapturingAfterConvertCallback callback = new ValueCapturingAfterConvertCallback();
- entityTemplate.setEntityCallbacks(ReactiveEntityCallbacks.create(callback));
+ entityTemplate.setEntityCallbacks(ReactiveEntityCallbacks.create(callback));
- entityTemplate.select(Query.empty(), Person.class) //
- .as(StepVerifier::create) //
- .consumeNextWith(actual -> {
+ entityTemplate.select(Query.empty(), Person.class) //
+ .as(StepVerifier::create) //
+ .consumeNextWith(actual -> {
- assertThat(actual.id).isEqualTo("after-convert");
- assertThat(actual.name).isEqualTo("some-name");
- }).verifyComplete();
+ assertThat(actual.id).isEqualTo("after-convert");
+ assertThat(actual.name).isEqualTo("some-name");
+ }).verifyComplete();
- assertThat(callback.getValues()).hasSize(1);
- }
+ assertThat(callback.getValues()).hasSize(1);
+ }
- @Test // gh-220
- void shouldSelectOne() {
+ @Test
+ // gh-220
+ void shouldSelectOne() {
- recorder.addStubbing(s -> s.startsWith("SELECT"), Collections.emptyList());
+ recorder.addStubbing(s -> s.startsWith("SELECT"), Collections.emptyList());
- entityTemplate.selectOne(Query.query(Criteria.where("name").is("Walter")).sort(Sort.by("name")), Person.class) //
- .as(StepVerifier::create) //
- .verifyComplete();
+ entityTemplate.selectOne(Query.query(Criteria.where("name").is("Walter")).sort(Sort.by("name")), Person.class) //
+ .as(StepVerifier::create) //
+ .verifyComplete();
- StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("SELECT"));
+ StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("SELECT"));
- assertThat(statement.getSql())
- .isEqualTo("SELECT person.* FROM person WHERE person.THE_NAME = $1 ORDER BY person.THE_NAME ASC LIMIT 2");
- assertThat(statement.getBindings()).hasSize(1).containsEntry(0, Parameter.from("Walter"));
- }
+ assertThat(statement.getSql())
+ .isEqualTo("SELECT person.* FROM person WHERE person.THE_NAME = $1 ORDER BY person.THE_NAME ASC LIMIT 2");
+ assertThat(statement.getBindings()).hasSize(1).containsEntry(0, Parameter.from("Walter"));
+ }
- @Test // gh-220, gh-758
- void shouldSelectOneDoNotOverrideExistingLimit() {
+ @Test
+ // gh-220, gh-758
+ void shouldSelectOneDoNotOverrideExistingLimit() {
- recorder.addStubbing(s -> s.startsWith("SELECT"), Collections.emptyList());
+ recorder.addStubbing(s -> s.startsWith("SELECT"), Collections.emptyList());
- entityTemplate
- .selectOne(Query.query(Criteria.where("name").is("Walter")).sort(Sort.by("name")).limit(1), Person.class) //
- .as(StepVerifier::create) //
- .verifyComplete();
+ entityTemplate
+ .selectOne(Query.query(Criteria.where("name").is("Walter")).sort(Sort.by("name")).limit(1), Person.class) //
+ .as(StepVerifier::create) //
+ .verifyComplete();
- StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("SELECT"));
+ StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("SELECT"));
- assertThat(statement.getSql())
- .isEqualTo("SELECT person.* FROM person WHERE person.THE_NAME = $1 ORDER BY person.THE_NAME ASC LIMIT 1");
- assertThat(statement.getBindings()).hasSize(1).containsEntry(0, Parameter.from("Walter"));
- }
+ assertThat(statement.getSql())
+ .isEqualTo("SELECT person.* FROM person WHERE person.THE_NAME = $1 ORDER BY person.THE_NAME ASC LIMIT 1");
+ assertThat(statement.getBindings()).hasSize(1).containsEntry(0, Parameter.from("Walter"));
+ }
- @Test // gh-220
- void shouldUpdateByQuery() {
+ @Test
+ // gh-220
+ void shouldUpdateByQuery() {
- MockRowMetadata metadata = MockRowMetadata.builder()
- .columnMetadata(MockColumnMetadata.builder().name("name").type(R2dbcType.VARCHAR).build()).build();
- MockResult result = MockResult.builder().rowsUpdated(1).build();
+ MockRowMetadata metadata = MockRowMetadata.builder()
+ .columnMetadata(MockColumnMetadata.builder().name("name").type(R2dbcType.VARCHAR).build()).build();
+ MockResult result = MockResult.builder().rowsUpdated(1).build();
- recorder.addStubbing(s -> s.startsWith("UPDATE"), result);
+ recorder.addStubbing(s -> s.startsWith("UPDATE"), result);
- entityTemplate
- .update(Query.query(Criteria.where("name").is("Walter")), Update.update("name", "Heisenberg"), Person.class) //
- .as(StepVerifier::create) //
- .expectNext(1L) //
- .verifyComplete();
+ entityTemplate
+ .update(Query.query(Criteria.where("name").is("Walter")), Update.update("name", "Heisenberg"), Person.class) //
+ .as(StepVerifier::create) //
+ .expectNext(1L) //
+ .verifyComplete();
- StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("UPDATE"));
+ StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("UPDATE"));
- assertThat(statement.getSql()).isEqualTo("UPDATE person SET THE_NAME = $1 WHERE person.THE_NAME = $2");
- assertThat(statement.getBindings()).hasSize(2).containsEntry(0, Parameter.from("Heisenberg")).containsEntry(1,
- Parameter.from("Walter"));
- }
+ assertThat(statement.getSql()).isEqualTo("UPDATE person SET THE_NAME = $1 WHERE person.THE_NAME = $2");
+ assertThat(statement.getBindings()).hasSize(2).containsEntry(0, Parameter.from("Heisenberg")).containsEntry(1,
+ Parameter.from("Walter"));
+ }
- @Test // gh-220
- void shouldDeleteByQuery() {
+ @Test
+ // gh-220
+ void shouldDeleteByQuery() {
- MockRowMetadata metadata = MockRowMetadata.builder()
- .columnMetadata(MockColumnMetadata.builder().name("name").type(R2dbcType.VARCHAR).build()).build();
- MockResult result = MockResult.builder().rowsUpdated(1).build();
+ MockRowMetadata metadata = MockRowMetadata.builder()
+ .columnMetadata(MockColumnMetadata.builder().name("name").type(R2dbcType.VARCHAR).build()).build();
+ MockResult result = MockResult.builder().rowsUpdated(1).build();
- recorder.addStubbing(s -> s.startsWith("DELETE"), result);
+ recorder.addStubbing(s -> s.startsWith("DELETE"), result);
- entityTemplate.delete(Query.query(Criteria.where("name").is("Walter")), Person.class) //
- .as(StepVerifier::create) //
- .expectNext(1L) //
- .verifyComplete();
+ entityTemplate.delete(Query.query(Criteria.where("name").is("Walter")), Person.class) //
+ .as(StepVerifier::create) //
+ .expectNext(1L) //
+ .verifyComplete();
- StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("DELETE"));
+ StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("DELETE"));
- assertThat(statement.getSql()).isEqualTo("DELETE FROM person WHERE person.THE_NAME = $1");
- assertThat(statement.getBindings()).hasSize(1).containsEntry(0, Parameter.from("Walter"));
- }
+ assertThat(statement.getSql()).isEqualTo("DELETE FROM person WHERE person.THE_NAME = $1");
+ assertThat(statement.getBindings()).hasSize(1).containsEntry(0, Parameter.from("Walter"));
+ }
- @Test // gh-220
- void shouldDeleteEntity() {
+ @Test
+ // gh-220
+ void shouldDeleteEntity() {
- Person person = Person.empty() //
- .withId("Walter");
- recorder.addStubbing(s -> s.startsWith("DELETE"), Collections.emptyList());
+ Person person = Person.empty() //
+ .withId("Walter");
+ recorder.addStubbing(s -> s.startsWith("DELETE"), Collections.emptyList());
- entityTemplate.delete(person) //
- .as(StepVerifier::create) //
- .expectNext(person).verifyComplete();
+ entityTemplate.delete(person) //
+ .as(StepVerifier::create) //
+ .expectNext(person).verifyComplete();
- StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("DELETE"));
+ StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("DELETE"));
- assertThat(statement.getSql()).isEqualTo("DELETE FROM person WHERE person.id = $1");
- assertThat(statement.getBindings()).hasSize(1).containsEntry(0, Parameter.from("Walter"));
- }
+ assertThat(statement.getSql()).isEqualTo("DELETE FROM person WHERE person.id = $1");
+ assertThat(statement.getBindings()).hasSize(1).containsEntry(0, Parameter.from("Walter"));
+ }
- @Test // gh-365
- void shouldInsertVersioned() {
+ @Test
+ // gh-365
+ void shouldInsertVersioned() {
- MockRowMetadata metadata = MockRowMetadata.builder().build();
- MockResult result = MockResult.builder().rowMetadata(metadata).rowsUpdated(1).build();
+ MockRowMetadata metadata = MockRowMetadata.builder().build();
+ MockResult result = MockResult.builder().rowMetadata(metadata).rowsUpdated(1).build();
- recorder.addStubbing(s -> s.startsWith("INSERT"), result);
+ recorder.addStubbing(s -> s.startsWith("INSERT"), result);
- entityTemplate.insert(new VersionedPerson("id", 0, "bar")).as(StepVerifier::create) //
- .assertNext(actual -> {
- assertThat(actual.getVersion()).isEqualTo(1);
- }) //
- .verifyComplete();
+ entityTemplate.insert(new VersionedPerson("id", 0, "bar")).as(StepVerifier::create) //
+ .assertNext(actual -> {
+ assertThat(actual.getVersion()).isEqualTo(1);
+ }) //
+ .verifyComplete();
- StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("INSERT"));
+ StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("INSERT"));
- assertThat(statement.getSql()).isEqualTo("INSERT INTO versioned_person (id, version, name) VALUES ($1, $2, $3)");
- assertThat(statement.getBindings()).hasSize(3).containsEntry(0, Parameter.from("id")).containsEntry(1,
- Parameter.from(1L));
- }
+ assertThat(statement.getSql()).isEqualTo("INSERT INTO versioned_person (id, version, name) VALUES ($1, $2, $3)");
+ assertThat(statement.getBindings()).hasSize(3).containsEntry(0, Parameter.from("id")).containsEntry(1,
+ Parameter.from(1L));
+ }
- @Test // gh-557, gh-402
- void shouldSkipDefaultIdValueOnInsert() {
+ @Test
+ // gh-557, gh-402
+ void shouldSkipDefaultIdValueOnInsert() {
- MockRowMetadata metadata = MockRowMetadata.builder().build();
- MockResult result = MockResult.builder().rowMetadata(metadata).rowsUpdated(1).build();
+ MockRowMetadata metadata = MockRowMetadata.builder().build();
+ MockResult result = MockResult.builder().rowMetadata(metadata).rowsUpdated(1).build();
- recorder.addStubbing(s -> s.startsWith("INSERT"), result);
+ recorder.addStubbing(s -> s.startsWith("INSERT"), result);
- entityTemplate.insert(new PersonWithPrimitiveId(0, "bar")).as(StepVerifier::create) //
- .expectNextCount(1) //
- .verifyComplete();
+ entityTemplate.insert(new PersonWithPrimitiveId(0, "bar")).as(StepVerifier::create) //
+ .expectNextCount(1) //
+ .verifyComplete();
- StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("INSERT"));
+ StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("INSERT"));
- assertThat(statement.getSql()).isEqualTo("INSERT INTO person_with_primitive_id (name) VALUES ($1)");
- assertThat(statement.getBindings()).hasSize(1).containsEntry(0, Parameter.from("bar"));
- }
+ assertThat(statement.getSql()).isEqualTo("INSERT INTO person_with_primitive_id (name) VALUES ($1)");
+ assertThat(statement.getBindings()).hasSize(1).containsEntry(0, Parameter.from("bar"));
+ }
- @Test // gh-557, gh-402
- void shouldSkipDefaultIdValueOnVersionedInsert() {
+ @Test
+ // gh-557, gh-402
+ void shouldSkipDefaultIdValueOnVersionedInsert() {
- MockRowMetadata metadata = MockRowMetadata.builder().build();
- MockResult result = MockResult.builder().rowMetadata(metadata).rowsUpdated(1).build();
+ MockRowMetadata metadata = MockRowMetadata.builder().build();
+ MockResult result = MockResult.builder().rowMetadata(metadata).rowsUpdated(1).build();
- recorder.addStubbing(s -> s.startsWith("INSERT"), result);
+ recorder.addStubbing(s -> s.startsWith("INSERT"), result);
- entityTemplate.insert(new VersionedPersonWithPrimitiveId(0, 0, "bar")).as(StepVerifier::create) //
- .assertNext(actual -> {
- assertThat(actual.getVersion()).isEqualTo(1);
- }) //
- .verifyComplete();
+ entityTemplate.insert(new VersionedPersonWithPrimitiveId(0, 0, "bar")).as(StepVerifier::create) //
+ .assertNext(actual -> {
+ assertThat(actual.getVersion()).isEqualTo(1);
+ }) //
+ .verifyComplete();
- StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("INSERT"));
+ StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("INSERT"));
- assertThat(statement.getSql())
- .isEqualTo("INSERT INTO versioned_person_with_primitive_id (version, name) VALUES ($1, $2)");
- assertThat(statement.getBindings()).hasSize(2).containsEntry(0, Parameter.from(1L)).containsEntry(1,
- Parameter.from("bar"));
- }
+ assertThat(statement.getSql())
+ .isEqualTo("INSERT INTO versioned_person_with_primitive_id (version, name) VALUES ($1, $2)");
+ assertThat(statement.getBindings()).hasSize(2).containsEntry(0, Parameter.from(1L)).containsEntry(1,
+ Parameter.from("bar"));
+ }
- @Test // gh-451
- void shouldInsertCorrectlyVersionedAndAudited() {
+ @Test
+ // gh-451
+ void shouldInsertCorrectlyVersionedAndAudited() {
- MockRowMetadata metadata = MockRowMetadata.builder().build();
- MockResult result = MockResult.builder().rowMetadata(metadata).rowsUpdated(1).build();
+ MockRowMetadata metadata = MockRowMetadata.builder().build();
+ MockResult result = MockResult.builder().rowMetadata(metadata).rowsUpdated(1).build();
- recorder.addStubbing(s -> s.startsWith("INSERT"), result);
+ recorder.addStubbing(s -> s.startsWith("INSERT"), result);
- ObjectFactory objectFactory = mock(ObjectFactory.class);
- when(objectFactory.getObject()).thenReturn(new ReactiveIsNewAwareAuditingHandler(
- PersistentEntities.of(entityTemplate.getConverter().getMappingContext())));
+ ObjectFactory objectFactory = mock(ObjectFactory.class);
+ when(objectFactory.getObject()).thenReturn(new ReactiveIsNewAwareAuditingHandler(
+ PersistentEntities.of(entityTemplate.getConverter().getMappingContext())));
- entityTemplate
- .setEntityCallbacks(ReactiveEntityCallbacks.create(new ReactiveAuditingEntityCallback(objectFactory)));
- entityTemplate.insert(new WithAuditingAndOptimisticLocking(null, 0, "Walter", null, null)) //
- .as(StepVerifier::create) //
- .assertNext(actual -> {
- assertThat(actual.getVersion()).isEqualTo(1);
- assertThat(actual.getCreatedDate()).isNotNull();
- }) //
- .verifyComplete();
+ entityTemplate
+ .setEntityCallbacks(ReactiveEntityCallbacks.create(new ReactiveAuditingEntityCallback(objectFactory)));
+ entityTemplate.insert(new WithAuditingAndOptimisticLocking(null, 0, "Walter", null, null)) //
+ .as(StepVerifier::create) //
+ .assertNext(actual -> {
+ assertThat(actual.getVersion()).isEqualTo(1);
+ assertThat(actual.getCreatedDate()).isNotNull();
+ }) //
+ .verifyComplete();
- StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("INSERT"));
+ StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("INSERT"));
- assertThat(statement.getSql()).isEqualTo(
- "INSERT INTO with_auditing_and_optimistic_locking (version, name, created_date, last_modified_date) VALUES ($1, $2, $3, $4)");
- }
+ assertThat(statement.getSql()).isEqualTo(
+ "INSERT INTO with_auditing_and_optimistic_locking (version, name, created_date, last_modified_date) VALUES ($1, $2, $3, $4)");
+ }
- @Test // gh-451
- void shouldUpdateCorrectlyVersionedAndAudited() {
+ @Test
+ // gh-451
+ void shouldUpdateCorrectlyVersionedAndAudited() {
- MockRowMetadata metadata = MockRowMetadata.builder().build();
- MockResult result = MockResult.builder().rowMetadata(metadata).rowsUpdated(1).build();
+ MockRowMetadata metadata = MockRowMetadata.builder().build();
+ MockResult result = MockResult.builder().rowMetadata(metadata).rowsUpdated(1).build();
- recorder.addStubbing(s -> s.startsWith("UPDATE"), result);
+ recorder.addStubbing(s -> s.startsWith("UPDATE"), result);
- ObjectFactory objectFactory = mock(ObjectFactory.class);
- when(objectFactory.getObject()).thenReturn(new ReactiveIsNewAwareAuditingHandler(
- PersistentEntities.of(entityTemplate.getConverter().getMappingContext())));
+ ObjectFactory objectFactory = mock(ObjectFactory.class);
+ when(objectFactory.getObject()).thenReturn(new ReactiveIsNewAwareAuditingHandler(
+ PersistentEntities.of(entityTemplate.getConverter().getMappingContext())));
- entityTemplate
- .setEntityCallbacks(ReactiveEntityCallbacks.create(new ReactiveAuditingEntityCallback(objectFactory)));
- entityTemplate.update(new WithAuditingAndOptimisticLocking(null, 2, "Walter", null, null)) //
- .as(StepVerifier::create) //
- .assertNext(actual -> {
- assertThat(actual.getVersion()).isEqualTo(3);
- assertThat(actual.getCreatedDate()).isNull();
- assertThat(actual.getLastModifiedDate()).isNotNull();
- }) //
- .verifyComplete();
+ entityTemplate
+ .setEntityCallbacks(ReactiveEntityCallbacks.create(new ReactiveAuditingEntityCallback(objectFactory)));
+ entityTemplate.update(new WithAuditingAndOptimisticLocking(null, 2, "Walter", null, null)) //
+ .as(StepVerifier::create) //
+ .assertNext(actual -> {
+ assertThat(actual.getVersion()).isEqualTo(3);
+ assertThat(actual.getCreatedDate()).isNull();
+ assertThat(actual.getLastModifiedDate()).isNotNull();
+ }) //
+ .verifyComplete();
- StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("UPDATE"));
+ StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("UPDATE"));
- assertThat(statement.getSql()).startsWith(
- "UPDATE with_auditing_and_optimistic_locking SET version = $1, name = $2, created_date = $3, last_modified_date = $4");
- }
+ assertThat(statement.getSql()).startsWith(
+ "UPDATE with_auditing_and_optimistic_locking SET version = $1, name = $2, created_date = $3, last_modified_date = $4");
+ }
- @Test // gh-215
- void insertShouldInvokeCallback() {
+ @Test
+ // gh-215
+ void insertShouldInvokeCallback() {
- MockRowMetadata metadata = MockRowMetadata.builder().build();
- MockResult result = MockResult.builder().rowMetadata(metadata).rowsUpdated(1).build();
+ MockRowMetadata metadata = MockRowMetadata.builder().build();
+ MockResult result = MockResult.builder().rowMetadata(metadata).rowsUpdated(1).build();
- recorder.addStubbing(s -> s.startsWith("INSERT"), result);
+ recorder.addStubbing(s -> s.startsWith("INSERT"), result);
- ValueCapturingBeforeConvertCallback beforeConvert = new ValueCapturingBeforeConvertCallback();
- ValueCapturingBeforeSaveCallback beforeSave = new ValueCapturingBeforeSaveCallback();
- ValueCapturingAfterSaveCallback afterSave = new ValueCapturingAfterSaveCallback();
+ ValueCapturingBeforeConvertCallback beforeConvert = new ValueCapturingBeforeConvertCallback();
+ ValueCapturingBeforeSaveCallback beforeSave = new ValueCapturingBeforeSaveCallback();
+ ValueCapturingAfterSaveCallback afterSave = new ValueCapturingAfterSaveCallback();
- entityTemplate.setEntityCallbacks(ReactiveEntityCallbacks.create(beforeConvert, beforeSave, afterSave));
- entityTemplate.insert(Person.empty()).as(StepVerifier::create) //
- .assertNext(actual -> {
- assertThat(actual.id).isEqualTo("after-save");
- assertThat(actual.name).isEqualTo("before-convert");
- assertThat(actual.description).isNull();
- }) //
- .verifyComplete();
+ entityTemplate.setEntityCallbacks(ReactiveEntityCallbacks.create(beforeConvert, beforeSave, afterSave));
+ entityTemplate.insert(Person.empty()).as(StepVerifier::create) //
+ .assertNext(actual -> {
+ assertThat(actual.id).isEqualTo("after-save");
+ assertThat(actual.name).isEqualTo("before-convert");
+ assertThat(actual.description).isNull();
+ }) //
+ .verifyComplete();
- StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("INSERT"));
+ StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("INSERT"));
- assertThat(statement.getSql()).isEqualTo("INSERT INTO person (THE_NAME, description) VALUES ($1, $2)");
- assertThat(statement.getBindings()).hasSize(2).containsEntry(0, Parameter.from("before-convert")).containsEntry(1,
- Parameter.from("before-save"));
- }
+ assertThat(statement.getSql()).isEqualTo("INSERT INTO person (THE_NAME, description) VALUES ($1, $2)");
+ assertThat(statement.getBindings()).hasSize(2).containsEntry(0, Parameter.from("before-convert")).containsEntry(1,
+ Parameter.from("before-save"));
+ }
- @Test // gh-365
- void shouldUpdateVersioned() {
+ @Test
+ // gh-365
+ void shouldUpdateVersioned() {
- MockRowMetadata metadata = MockRowMetadata.builder().build();
- MockResult result = MockResult.builder().rowMetadata(metadata).rowsUpdated(1).build();
+ MockRowMetadata metadata = MockRowMetadata.builder().build();
+ MockResult result = MockResult.builder().rowMetadata(metadata).rowsUpdated(1).build();
- recorder.addStubbing(s -> s.startsWith("UPDATE"), result);
+ recorder.addStubbing(s -> s.startsWith("UPDATE"), result);
- entityTemplate.update(new VersionedPerson("id", 1, "bar")).as(StepVerifier::create) //
- .assertNext(actual -> {
- assertThat(actual.getVersion()).isEqualTo(2);
- }) //
- .verifyComplete();
+ entityTemplate.update(new VersionedPerson("id", 1, "bar")).as(StepVerifier::create) //
+ .assertNext(actual -> {
+ assertThat(actual.getVersion()).isEqualTo(2);
+ }) //
+ .verifyComplete();
- StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("UPDATE"));
+ StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("UPDATE"));
- assertThat(statement.getSql()).isEqualTo(
- "UPDATE versioned_person SET version = $1, name = $2 WHERE versioned_person.id = $3 AND (versioned_person.version = $4)");
- assertThat(statement.getBindings()).hasSize(4).containsEntry(0, Parameter.from(2L)).containsEntry(3,
- Parameter.from(1L));
- }
+ assertThat(statement.getSql()).isEqualTo(
+ "UPDATE versioned_person SET version = $1, name = $2 WHERE versioned_person.id = $3 AND (versioned_person.version = $4)");
+ assertThat(statement.getBindings()).hasSize(4).containsEntry(0, Parameter.from(2L)).containsEntry(3,
+ Parameter.from(1L));
+ }
- @Test // gh-215
- void updateShouldInvokeCallback() {
+ @Test
+ // gh-215
+ void updateShouldInvokeCallback() {
- MockRowMetadata metadata = MockRowMetadata.builder().build();
- MockResult result = MockResult.builder().rowMetadata(metadata).rowsUpdated(1).build();
+ MockRowMetadata metadata = MockRowMetadata.builder().build();
+ MockResult result = MockResult.builder().rowMetadata(metadata).rowsUpdated(1).build();
- recorder.addStubbing(s -> s.startsWith("UPDATE"), result);
+ recorder.addStubbing(s -> s.startsWith("UPDATE"), result);
- ValueCapturingBeforeConvertCallback beforeConvert = new ValueCapturingBeforeConvertCallback();
- ValueCapturingBeforeSaveCallback beforeSave = new ValueCapturingBeforeSaveCallback();
- ValueCapturingAfterSaveCallback afterSave = new ValueCapturingAfterSaveCallback();
+ ValueCapturingBeforeConvertCallback beforeConvert = new ValueCapturingBeforeConvertCallback();
+ ValueCapturingBeforeSaveCallback beforeSave = new ValueCapturingBeforeSaveCallback();
+ ValueCapturingAfterSaveCallback afterSave = new ValueCapturingAfterSaveCallback();
- Person person = Person.empty() //
- .withId("the-id") //
- .withName("name") //
- .withDescription("description");
+ Person person = Person.empty() //
+ .withId("the-id") //
+ .withName("name") //
+ .withDescription("description");
- entityTemplate.setEntityCallbacks(ReactiveEntityCallbacks.create(beforeConvert, beforeSave, afterSave));
- entityTemplate.update(person).as(StepVerifier::create) //
- .assertNext(actual -> {
- assertThat(actual.id).isEqualTo("after-save");
- assertThat(actual.name).isEqualTo("before-convert");
- assertThat(actual.description).isNull();
- }) //
- .verifyComplete();
+ entityTemplate.setEntityCallbacks(ReactiveEntityCallbacks.create(beforeConvert, beforeSave, afterSave));
+ entityTemplate.update(person).as(StepVerifier::create) //
+ .assertNext(actual -> {
+ assertThat(actual.id).isEqualTo("after-save");
+ assertThat(actual.name).isEqualTo("before-convert");
+ assertThat(actual.description).isNull();
+ }) //
+ .verifyComplete();
- StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("UPDATE"));
+ StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("UPDATE"));
- assertThat(statement.getSql()).isEqualTo("UPDATE person SET THE_NAME = $1, description = $2 WHERE person.id = $3");
- assertThat(statement.getBindings()).hasSize(3).containsEntry(0, Parameter.from("before-convert")).containsEntry(1,
- Parameter.from("before-save"));
- }
+ assertThat(statement.getSql()).isEqualTo("UPDATE person SET THE_NAME = $1, description = $2 WHERE person.id = $3");
+ assertThat(statement.getBindings()).hasSize(3).containsEntry(0, Parameter.from("before-convert")).containsEntry(1,
+ Parameter.from("before-save"));
+ }
- @Value
- static class WithoutId {
+ @Test
+ // gh-637
+ void insertIncludesInsertOnlyColumns() {
- String name;
- }
+ MockRowMetadata metadata = MockRowMetadata.builder().build();
+ MockResult result = MockResult.builder().rowMetadata(metadata).rowsUpdated(1).build();
- @Value
- @With
- static class Person {
+ recorder.addStubbing(s -> s.startsWith("INSERT"), result);
- @Id String id;
+ entityTemplate.insert(new WithInsertOnly(null, "Alfred", "insert this")).as(StepVerifier::create) //
+ .expectNextCount(1) //
+ .verifyComplete();
- @Column("THE_NAME") String name;
+ StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("INSERT"));
- String description;
+ assertThat(statement.getSql()).isEqualTo("INSERT INTO with_insert_only (name, insert_only) VALUES ($1, $2)");
+ assertThat(statement.getBindings()).hasSize(2)
+ .containsEntry(0, Parameter.from("Alfred"))
+ .containsEntry(1, Parameter.from("insert this"));
+ }
- public static Person empty() {
- return new Person(null, null, null);
- }
- }
+ @Test
+ // gh-637
+ void updateExcludesInsertOnlyColumns() {
- @Value
- @With
- private static class VersionedPerson {
+ MockRowMetadata metadata = MockRowMetadata.builder().build();
+ MockResult result = MockResult.builder().rowMetadata(metadata).rowsUpdated(1).build();
- @Id String id;
+ recorder.addStubbing(s -> s.startsWith("UPDATE"), result);
- @Version long version;
+ entityTemplate.update(new WithInsertOnly(23L, "Alfred", "don't update this")).as(StepVerifier::create) //
+ .expectNextCount(1) //
+ .verifyComplete();
- String name;
- }
+ StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("UPDATE"));
- @Value
- @With
- private static class PersonWithPrimitiveId {
+ assertThat(statement.getSql()).isEqualTo("UPDATE with_insert_only SET name = $1 WHERE with_insert_only.id = $2");
+ assertThat(statement.getBindings()).hasSize(2)
+ .containsEntry(0, Parameter.from("Alfred"))
+ .containsEntry(1, Parameter.from(23L));
+ }
- @Id int id;
+ @Value
+ static class WithoutId {
- String name;
- }
+ String name;
+ }
- @Value
- @With
- private static class VersionedPersonWithPrimitiveId {
+ @Value
+ @With
+ static class Person {
- @Id int id;
+ @Id
+ String id;
- @Version long version;
+ @Column("THE_NAME")
+ String name;
- String name;
- }
+ String description;
- @Value
- @With
- private static class WithAuditingAndOptimisticLocking {
+ public static Person empty() {
+ return new Person(null, null, null);
+ }
+ }
- @Id String id;
+ @Value
+ @With
+ private static class VersionedPerson {
- @Version long version;
+ @Id
+ String id;
- String name;
+ @Version
+ long version;
- @CreatedDate LocalDateTime createdDate;
- @LastModifiedDate LocalDateTime lastModifiedDate;
- }
+ String name;
+ }
- static class ValueCapturingEntityCallback {
+ @Value
+ @With
+ private static class PersonWithPrimitiveId {
- private final List values = new ArrayList<>(1);
+ @Id
+ int id;
- void capture(T value) {
- values.add(value);
- }
+ String name;
+ }
- public List getValues() {
- return values;
- }
+ @Value
+ @With
+ private static class VersionedPersonWithPrimitiveId {
- @Nullable
- public T getValue() {
- return CollectionUtils.lastElement(values);
- }
- }
+ @Id
+ int id;
- static class ValueCapturingBeforeConvertCallback extends ValueCapturingEntityCallback
- implements BeforeConvertCallback {
+ @Version
+ long version;
- @Override
- public Mono onBeforeConvert(Person entity, SqlIdentifier table) {
+ String name;
+ }
- capture(entity);
- Person person = entity.withName("before-convert");
- return Mono.just(person);
- }
- }
+ @Value
+ @With
+ private static class WithAuditingAndOptimisticLocking {
- static class ValueCapturingBeforeSaveCallback extends ValueCapturingEntityCallback
- implements BeforeSaveCallback {
+ @Id
+ String id;
- @Override
- public Mono onBeforeSave(Person entity, OutboundRow outboundRow, SqlIdentifier table) {
+ @Version
+ long version;
- capture(entity);
- outboundRow.put(SqlIdentifier.unquoted("description"), Parameter.from("before-save"));
- return Mono.just(entity);
- }
- }
+ String name;
- static class ValueCapturingAfterSaveCallback extends ValueCapturingEntityCallback
- implements AfterSaveCallback {
+ @CreatedDate
+ LocalDateTime createdDate;
+ @LastModifiedDate
+ LocalDateTime lastModifiedDate;
+ }
- @Override
- public Mono onAfterSave(Person entity, OutboundRow outboundRow, SqlIdentifier table) {
+ @Value
+ private static class WithInsertOnly {
+ @Id
+ Long id;
- capture(entity);
+ String name;
- Person person = Person.empty() //
- .withId("after-save") //
- .withName(entity.getName());
+ @InsertOnlyProperty
+ String insertOnly;
+ }
- return Mono.just(person);
- }
- }
+ static class ValueCapturingEntityCallback {
- static class ValueCapturingAfterConvertCallback extends ValueCapturingEntityCallback
- implements AfterConvertCallback {
+ private final List values = new ArrayList<>(1);
- @Override
- public Mono onAfterConvert(Person entity, SqlIdentifier table) {
+ void capture(T value) {
+ values.add(value);
+ }
- capture(entity);
- Person person = Person.empty() //
- .withId("after-convert") //
- .withName(entity.getName());
+ public List getValues() {
+ return values;
+ }
- return Mono.just(person);
- }
- }
+ @Nullable
+ public T getValue() {
+ return CollectionUtils.lastElement(values);
+ }
+ }
+
+ static class ValueCapturingBeforeConvertCallback extends ValueCapturingEntityCallback
+ implements BeforeConvertCallback {
+
+ @Override
+ public Mono onBeforeConvert(Person entity, SqlIdentifier table) {
+
+ capture(entity);
+ Person person = entity.withName("before-convert");
+ return Mono.just(person);
+ }
+ }
+
+ static class ValueCapturingBeforeSaveCallback extends ValueCapturingEntityCallback
+ implements BeforeSaveCallback {
+
+ @Override
+ public Mono onBeforeSave(Person entity, OutboundRow outboundRow, SqlIdentifier table) {
+
+ capture(entity);
+ outboundRow.put(SqlIdentifier.unquoted("description"), Parameter.from("before-save"));
+ return Mono.just(entity);
+ }
+ }
+
+ static class ValueCapturingAfterSaveCallback extends ValueCapturingEntityCallback
+ implements AfterSaveCallback {
+
+ @Override
+ public Mono onAfterSave(Person entity, OutboundRow outboundRow, SqlIdentifier table) {
+
+ capture(entity);
+
+ Person person = Person.empty() //
+ .withId("after-save") //
+ .withName(entity.getName());
+
+ return Mono.just(person);
+ }
+ }
+
+ static class ValueCapturingAfterConvertCallback extends ValueCapturingEntityCallback
+ implements AfterConvertCallback