Skip to content

Commit

Permalink
#444 - Initialize primitive Id properties upon INSERT.
Browse files Browse the repository at this point in the history
We now propagate auto-generated Id values to primitive Id properties if the value in the domain model is zero. This happens in addition to non-primitive values being null to ensure that generated values end up in the domain model.
  • Loading branch information
mp911de committed Sep 9, 2020
1 parent 158a684 commit 7d3bd1f
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -604,13 +604,22 @@ public <T> BiFunction<Row, RowMetadata, T> populateIdIfNecessary(T object) {
PersistentPropertyAccessor<?> propertyAccessor = entity.getPropertyAccessor(object);
RelationalPersistentProperty idProperty = entity.getRequiredIdProperty();

if (propertyAccessor.getProperty(idProperty) != null) {
return object;
boolean idPropertyUpdateNeeded = false;

Object id = propertyAccessor.getProperty(idProperty);
if (idProperty.getType().isPrimitive()) {
idPropertyUpdateNeeded = id instanceof Number && ((Number) id).longValue() == 0;
} else {
idPropertyUpdateNeeded = id == null;
}

return potentiallySetId(row, metadata, propertyAccessor, idProperty) //
if (idPropertyUpdateNeeded) {
return potentiallySetId(row, metadata, propertyAccessor, idProperty) //
? (T) propertyAccessor.getBean() //
: object;
}

return object;
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

import org.junit.Before;
import org.junit.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.OptimisticLockingFailureException;
Expand Down Expand Up @@ -105,26 +106,27 @@ public void before() {
*/
protected abstract String getCreateTableStatement();

@Test
@Test // gh-444
public void shouldSaveNewObject() {

LegoSet legoSet = new LegoSet(null, "SCHAUFELRADBAGGER", 12);

repository.save(legoSet) //
repository.save(new LegoSet(0, "SCHAUFELRADBAGGER", 12)) //
.as(StepVerifier::create) //
.consumeNextWith(actual -> {

assertThat(actual.getId()).isNotNull();
}).verifyComplete();

Map<String, Object> map = jdbc.queryForMap("SELECT * FROM legoset");
assertThat(map).containsEntry("name", "SCHAUFELRADBAGGER").containsEntry("manual", 12).containsKey("id");
repository.save(new LegoSet(0, "SCHAUFELRADBAGGER", 12)) //
.as(StepVerifier::create) //
.consumeNextWith(actual -> {

assertThat(actual.getId()).isGreaterThan(0);
}).verifyComplete();
}

@Test // gh-93
public void shouldSaveNewObjectAndSetVersionIfWrapperVersionPropertyExists() {

LegoSetVersionable legoSet = new LegoSetVersionable(null, "SCHAUFELRADBAGGER", 12, null);
LegoSetVersionable legoSet = new LegoSetVersionable(0, "SCHAUFELRADBAGGER", 12, null);

repository.save(legoSet) //
.as(StepVerifier::create) //
Expand All @@ -142,7 +144,7 @@ public void shouldSaveNewObjectAndSetVersionIfWrapperVersionPropertyExists() {
@Test // gh-93
public void shouldSaveNewObjectAndSetVersionIfPrimitiveVersionPropertyExists() {

LegoSetPrimitiveVersionable legoSet = new LegoSetPrimitiveVersionable(null, "SCHAUFELRADBAGGER", 12, 0);
LegoSetPrimitiveVersionable legoSet = new LegoSetPrimitiveVersionable(0, "SCHAUFELRADBAGGER", 12, 0);

repository.save(legoSet) //
.as(StepVerifier::create) //
Expand Down Expand Up @@ -216,10 +218,10 @@ public void shouldFailWithOptimistickLockingWhenVersionDoesNotMatchOnUpdate() {
@Test
public void shouldSaveObjectsUsingIterable() {

LegoSet legoSet1 = new LegoSet(null, "SCHAUFELRADBAGGER", 12);
LegoSet legoSet2 = new LegoSet(null, "FORSCHUNGSSCHIFF", 13);
LegoSet legoSet3 = new LegoSet(null, "RALLYEAUTO", 14);
LegoSet legoSet4 = new LegoSet(null, "VOLTRON", 15);
LegoSet legoSet1 = new LegoSet(0, "SCHAUFELRADBAGGER", 12);
LegoSet legoSet2 = new LegoSet(0, "FORSCHUNGSSCHIFF", 13);
LegoSet legoSet3 = new LegoSet(0, "RALLYEAUTO", 14);
LegoSet legoSet4 = new LegoSet(0, "VOLTRON", 15);

repository.saveAll(Arrays.asList(legoSet1, legoSet2, legoSet3, legoSet4)) //
.map(LegoSet::getManual) //
Expand All @@ -237,8 +239,8 @@ public void shouldSaveObjectsUsingIterable() {
@Test
public void shouldSaveObjectsUsingPublisher() {

LegoSet legoSet1 = new LegoSet(null, "SCHAUFELRADBAGGER", 12);
LegoSet legoSet2 = new LegoSet(null, "FORSCHUNGSSCHIFF", 13);
LegoSet legoSet1 = new LegoSet(0, "SCHAUFELRADBAGGER", 12);
LegoSet legoSet2 = new LegoSet(0, "FORSCHUNGSSCHIFF", 13);

repository.saveAll(Flux.just(legoSet1, legoSet2)) //
.as(StepVerifier::create) //
Expand Down Expand Up @@ -468,27 +470,11 @@ public void shouldDeleteAllUsingPublisher() {
@Table("legoset")
@AllArgsConstructor
@NoArgsConstructor
static class LegoSet implements Persistable<Integer> {
@Id Integer id;
static class LegoSet {
@Id int id;
String name;
Integer manual;

@Override
public boolean isNew() {
return id == null;
}
}

static class AlwaysNewLegoSet extends LegoSet {

AlwaysNewLegoSet(Integer id, String name, Integer manual) {
super(id, name, manual);
}

@Override
public boolean isNew() {
return true;
}
}

@Data
Expand All @@ -497,7 +483,7 @@ public boolean isNew() {
static class LegoSetVersionable extends LegoSet {
@Version Integer version;

public LegoSetVersionable(Integer id, String name, Integer manual, Integer version) {
public LegoSetVersionable(int id, String name, Integer manual, Integer version) {
super(id, name, manual);
this.version = version;
}
Expand All @@ -509,7 +495,7 @@ public LegoSetVersionable(Integer id, String name, Integer manual, Integer versi
static class LegoSetPrimitiveVersionable extends LegoSet {
@Version int version;

public LegoSetPrimitiveVersionable(Integer id, String name, Integer manual, int version) {
public LegoSetPrimitiveVersionable(int id, String name, Integer manual, int version) {
super(id, name, manual);
this.version = version;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import static org.assertj.core.api.Assertions.*;

import io.r2dbc.spi.ConnectionFactory;
import lombok.AllArgsConstructor;
import lombok.Data;
import reactor.test.StepVerifier;

import java.util.Map;
Expand All @@ -27,11 +29,19 @@
import org.junit.Test;
import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.TransientDataAccessException;
import org.springframework.data.annotation.Id;
import org.springframework.data.domain.Persistable;
import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration;
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
import org.springframework.data.r2dbc.testing.H2TestSupport;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.repository.query.RelationalEntityInformation;
import org.springframework.data.relational.repository.support.MappingRelationalEntityInformation;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

Expand All @@ -44,6 +54,10 @@
@ContextConfiguration
public class H2SimpleR2dbcRepositoryIntegrationTests extends AbstractSimpleR2dbcRepositoryIntegrationTests {

@Autowired private R2dbcEntityTemplate entityTemplate;

@Autowired private RelationalMappingContext mappingContext;

@Configuration
static class IntegrationTestConfiguration extends AbstractR2dbcConfiguration {

Expand All @@ -67,21 +81,30 @@ protected String getCreateTableStatement() {
public void shouldInsertNewObjectWithGivenId() {

try {
this.jdbc.execute("DROP TABLE legoset");
this.jdbc.execute("DROP TABLE always_new");
} catch (DataAccessException e) {}

this.jdbc.execute(H2TestSupport.CREATE_TABLE_LEGOSET);
this.jdbc.execute("CREATE TABLE always_new (\n" //
+ " id integer PRIMARY KEY,\n" //
+ " name varchar(255) NOT NULL" //
+ ");");

AlwaysNewLegoSet legoSet = new AlwaysNewLegoSet(9999, "SCHAUFELRADBAGGER", 12);
RelationalEntityInformation<AlwaysNew, Long> entityInformation = new MappingRelationalEntityInformation<>(
(RelationalPersistentEntity<AlwaysNew>) mappingContext.getRequiredPersistentEntity(AlwaysNew.class));

repository.save(legoSet) //
SimpleR2dbcRepository<AlwaysNew, Long> repository = new SimpleR2dbcRepository<>(entityInformation, entityTemplate,
entityTemplate.getConverter());

AlwaysNew alwaysNew = new AlwaysNew(9999L, "SCHAUFELRADBAGGER");

repository.save(alwaysNew) //
.as(StepVerifier::create) //
.consumeNextWith( //
actual -> assertThat(actual.getId()).isEqualTo(9999) //
).verifyComplete();

Map<String, Object> map = jdbc.queryForMap("SELECT * FROM legoset");
assertThat(map).containsEntry("name", "SCHAUFELRADBAGGER").containsEntry("manual", 12).containsKey("id");
Map<String, Object> map = jdbc.queryForMap("SELECT * FROM always_new");
assertThat(map).containsEntry("name", "SCHAUFELRADBAGGER").containsKey("id");
}

@Test // gh-232
Expand All @@ -97,4 +120,17 @@ public void updateShouldFailIfRowDoesNotExist() {
.hasMessage("Failed to update table [legoset]. Row with Id [9999] does not exist.");
});
}

@Data
@AllArgsConstructor
static class AlwaysNew implements Persistable<Long> {

@Id Long id;
String name;

@Override
public boolean isNew() {
return true;
}
}
}

0 comments on commit 7d3bd1f

Please sign in to comment.