Skip to content

Commit 31b722a

Browse files
committed
Guard access to identifier property.
We now check in all places where we optionally use the Id property that an entity actually has an Id property and fall back leniently if the entity doesn't have an identifier property. Also, we use IdentifierAccessor consistently if the property is an identifier property. See #711
1 parent 6a43de0 commit 31b722a

File tree

4 files changed

+54
-4
lines changed

4 files changed

+54
-4
lines changed

src/main/java/org/springframework/data/r2dbc/convert/MappingR2dbcConverter.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.springframework.core.convert.ConversionService;
3232
import org.springframework.dao.InvalidDataAccessApiUsageException;
3333
import org.springframework.data.convert.CustomConversions;
34+
import org.springframework.data.mapping.IdentifierAccessor;
3435
import org.springframework.data.mapping.MappingException;
3536
import org.springframework.data.mapping.PersistentProperty;
3637
import org.springframework.data.mapping.PersistentPropertyAccessor;
@@ -370,7 +371,14 @@ private void writeProperties(OutboundRow sink, RelationalPersistentEntity<?> ent
370371
continue;
371372
}
372373

373-
Object value = accessor.getProperty(property);
374+
Object value;
375+
376+
if (property.isIdProperty()) {
377+
IdentifierAccessor identifierAccessor = entity.getIdentifierAccessor(accessor.getBean());
378+
value = identifierAccessor.getIdentifier();
379+
} else {
380+
value = accessor.getProperty(property);
381+
}
374382

375383
if (value == null) {
376384
writeNullInternal(sink, property);
@@ -600,6 +608,10 @@ public <T> BiFunction<Row, RowMetadata, T> populateIdIfNecessary(T object) {
600608
Class<?> userClass = ClassUtils.getUserClass(object);
601609
RelationalPersistentEntity<?> entity = getMappingContext().getRequiredPersistentEntity(userClass);
602610

611+
if (!entity.hasIdProperty()) {
612+
return (row, rowMetadata) -> object;
613+
}
614+
603615
return (row, metadata) -> {
604616

605617
PersistentPropertyAccessor<?> propertyAccessor = entity.getPropertyAccessor(object);

src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
import org.springframework.data.relational.core.query.Query;
6666
import org.springframework.data.relational.core.query.Update;
6767
import org.springframework.data.relational.core.sql.Expression;
68+
import org.springframework.data.relational.core.sql.Expressions;
6869
import org.springframework.data.relational.core.sql.Functions;
6970
import org.springframework.data.relational.core.sql.SqlIdentifier;
7071
import org.springframework.data.relational.core.sql.Table;
@@ -322,7 +323,11 @@ Mono<Long> doCount(Query query, Class<?> entityClass, SqlIdentifier tableName) {
322323
StatementMapper.SelectSpec selectSpec = statementMapper //
323324
.createSelect(tableName) //
324325
.doWithTable((table, spec) -> {
325-
return spec.withProjection(Functions.count(table.column(entity.getRequiredIdProperty().getColumnName())));
326+
327+
Expression countExpression = entity.hasIdProperty()
328+
? table.column(entity.getRequiredIdProperty().getColumnName())
329+
: Expressions.asterisk();
330+
return spec.withProjection(Functions.count(countExpression));
326331
});
327332

328333
Optional<CriteriaDefinition> criteria = query.getCriteria();
@@ -834,6 +839,7 @@ protected <T> Mono<T> maybeCallAfterConvert(T object, SqlIdentifier table) {
834839
}
835840

836841
private <T> Query getByIdQuery(T entity, RelationalPersistentEntity<?> persistentEntity) {
842+
837843
if (!persistentEntity.hasIdProperty()) {
838844
throw new MappingException("No id property found for object of type " + persistentEntity.getType() + "!");
839845
}

src/main/java/org/springframework/data/r2dbc/repository/query/R2dbcQueryCreator.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.springframework.data.relational.core.query.Criteria;
3030
import org.springframework.data.relational.core.sql.Column;
3131
import org.springframework.data.relational.core.sql.Expression;
32+
import org.springframework.data.relational.core.sql.Expressions;
3233
import org.springframework.data.relational.core.sql.Functions;
3334
import org.springframework.data.relational.core.sql.SqlIdentifier;
3435
import org.springframework.data.relational.core.sql.Table;
@@ -164,8 +165,11 @@ private Expression[] getSelectProjection() {
164165
.collect(Collectors.toList());
165166
} else if (tree.isCountProjection()) {
166167

167-
SqlIdentifier idColumn = entityMetadata.getTableEntity().getRequiredIdProperty().getColumnName();
168-
expressions = Collections.singletonList(Functions.count(table.column(idColumn)));
168+
Expression countExpression = entityMetadata.getTableEntity().hasIdProperty()
169+
? table.column(entityMetadata.getTableEntity().getRequiredIdProperty().getColumnName())
170+
: Expressions.asterisk();
171+
172+
expressions = Collections.singletonList(Functions.count(countExpression));
169173
} else {
170174
expressions = dataAccessStrategy.getAllColumns(entityToRead).stream()
171175
.map(table::column)

src/test/java/org/springframework/data/r2dbc/convert/MappingR2dbcConverterUnitTests.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import org.springframework.data.annotation.Transient;
4444
import org.springframework.data.convert.ReadingConverter;
4545
import org.springframework.data.convert.WritingConverter;
46+
import org.springframework.data.domain.Persistable;
4647
import org.springframework.data.r2dbc.dialect.PostgresDialect;
4748
import org.springframework.data.r2dbc.mapping.OutboundRow;
4849
import org.springframework.data.r2dbc.mapping.R2dbcMappingContext;
@@ -250,6 +251,18 @@ void considersConverterBeforeEntityConstruction() {
250251
assertThat(result.person).isNull();
251252
}
252253

254+
@Test // GH-711
255+
void writeShouldObtainIdFromIdentifierAccessor() {
256+
257+
PersistableEntity entity = new PersistableEntity();
258+
entity.id = null;
259+
260+
OutboundRow row = new OutboundRow();
261+
converter.write(entity, row);
262+
263+
assertThat(row).containsEntry(SqlIdentifier.unquoted("id"), Parameter.from(42L));
264+
}
265+
253266
@AllArgsConstructor
254267
static class Person {
255268
@Id String id;
@@ -406,4 +419,19 @@ static class SimplePerson {
406419
this.name = name;
407420
}
408421
}
422+
423+
static class PersistableEntity implements Persistable<Long> {
424+
425+
@Id String id;
426+
427+
@Override
428+
public Long getId() {
429+
return 42L;
430+
}
431+
432+
@Override
433+
public boolean isNew() {
434+
return false;
435+
}
436+
}
409437
}

0 commit comments

Comments
 (0)