Browse Source

#232 - Guard Repository.save(…) with provided Id with TransientDataAccessException if row does not exist.

We now emit a TransientDataAccessException if an object with a provided Id yields no affected rows. Such an arrangement is typically an indicator for a bug where calling code expects the object to be inserted with a provided Id.
pull/1188/head
Mark Paluch 6 years ago committed by Jens Schauder
parent
commit
4801a4fbc2
No known key found for this signature in database
GPG Key ID: 996B1389BA0721C3
  1. 15
      src/main/java/org/springframework/data/r2dbc/repository/support/SimpleR2dbcRepository.java
  2. 15
      src/test/java/org/springframework/data/r2dbc/repository/support/H2SimpleR2dbcRepositoryIntegrationTests.java

15
src/main/java/org/springframework/data/r2dbc/repository/support/SimpleR2dbcRepository.java

@ -15,7 +15,6 @@ @@ -15,7 +15,6 @@
*/
package org.springframework.data.r2dbc.repository.support;
import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@ -24,6 +23,7 @@ import java.util.List; @@ -24,6 +23,7 @@ import java.util.List;
import org.reactivestreams.Publisher;
import org.springframework.dao.TransientDataAccessResourceException;
import org.springframework.data.r2dbc.convert.R2dbcConverter;
import org.springframework.data.r2dbc.core.DatabaseClient;
import org.springframework.data.r2dbc.core.PreparedOperation;
@ -37,6 +37,7 @@ import org.springframework.data.relational.core.sql.Table; @@ -37,6 +37,7 @@ import org.springframework.data.relational.core.sql.Table;
import org.springframework.data.relational.core.sql.render.SqlRenderer;
import org.springframework.data.relational.repository.query.RelationalEntityInformation;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
/**
@ -83,8 +84,16 @@ public class SimpleR2dbcRepository<T, ID> implements ReactiveCrudRepository<T, I @@ -83,8 +84,16 @@ public class SimpleR2dbcRepository<T, ID> implements ReactiveCrudRepository<T, I
return this.databaseClient.update() //
.table(this.entity.getJavaType()) //
.table(this.entity.getTableName()).using(objectToSave) //
.then() //
.thenReturn(objectToSave);
.fetch().rowsUpdated().handle((rowsUpdated, sink) -> {
if (rowsUpdated == 0) {
sink.error(new TransientDataAccessResourceException(
String.format("Failed to update table [%s]. Row with Id [%s] does not exist.",
this.entity.getTableName(), this.entity.getId(objectToSave))));
} else {
sink.next(objectToSave);
}
});
}
/* (non-Javadoc)

15
src/test/java/org/springframework/data/r2dbc/repository/support/H2SimpleR2dbcRepositoryIntegrationTests.java

@ -29,6 +29,7 @@ import org.junit.runner.RunWith; @@ -29,6 +29,7 @@ import org.junit.runner.RunWith;
import org.springframework.context.annotation.Configuration;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.TransientDataAccessException;
import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration;
import org.springframework.data.r2dbc.testing.H2TestSupport;
import org.springframework.test.context.ContextConfiguration;
@ -82,4 +83,18 @@ public class H2SimpleR2dbcRepositoryIntegrationTests extends AbstractSimpleR2dbc @@ -82,4 +83,18 @@ public class H2SimpleR2dbcRepositoryIntegrationTests extends AbstractSimpleR2dbc
Map<String, Object> map = jdbc.queryForMap("SELECT * FROM legoset");
assertThat(map).containsEntry("name", "SCHAUFELRADBAGGER").containsEntry("manual", 12).containsKey("id");
}
@Test // gh-232
public void updateShouldFailIfRowDoesNotExist() {
LegoSet legoSet = new LegoSet(9999, "SCHAUFELRADBAGGER", 12);
repository.save(legoSet) //
.as(StepVerifier::create) //
.verifyErrorSatisfies(actual -> {
assertThat(actual).isInstanceOf(TransientDataAccessException.class)
.hasMessage("Failed to update table [legoset]. Row with Id [9999] does not exist.");
});
}
}

Loading…
Cancel
Save