Skip to content

Commit d6e2dfd

Browse files
committed
#220 - Use EntityOperations in SimpleR2dbcRepository.
1 parent e50451f commit d6e2dfd

File tree

3 files changed

+66
-114
lines changed

3 files changed

+66
-114
lines changed

Diff for: src/main/java/org/springframework/data/r2dbc/repository/support/R2dbcRepositoryFactory.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.springframework.data.projection.ProjectionFactory;
2323
import org.springframework.data.r2dbc.convert.R2dbcConverter;
2424
import org.springframework.data.r2dbc.core.DatabaseClient;
25+
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
2526
import org.springframework.data.r2dbc.core.ReactiveDataAccessStrategy;
2627
import org.springframework.data.r2dbc.repository.R2dbcRepository;
2728
import org.springframework.data.r2dbc.repository.query.R2dbcQueryMethod;
@@ -92,8 +93,8 @@ protected Object getTargetRepository(RepositoryInformation information) {
9293
RelationalEntityInformation<?, ?> entityInformation = getEntityInformation(information.getDomainType(),
9394
information);
9495

95-
return getTargetRepositoryViaReflection(information, entityInformation, this.databaseClient, this.converter,
96-
this.dataAccessStrategy);
96+
return getTargetRepositoryViaReflection(information, entityInformation,
97+
new R2dbcEntityTemplate(this.databaseClient, this.dataAccessStrategy), this.converter);
9798
}
9899

99100
/*

Diff for: src/main/java/org/springframework/data/r2dbc/repository/support/SimpleR2dbcRepository.java

+55-104
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,19 @@
1818
import reactor.core.publisher.Flux;
1919
import reactor.core.publisher.Mono;
2020

21-
import java.util.Collections;
22-
import java.util.List;
23-
2421
import org.reactivestreams.Publisher;
2522

26-
import org.springframework.dao.TransientDataAccessResourceException;
2723
import org.springframework.data.r2dbc.convert.R2dbcConverter;
2824
import org.springframework.data.r2dbc.core.DatabaseClient;
29-
import org.springframework.data.r2dbc.core.PreparedOperation;
25+
import org.springframework.data.r2dbc.core.R2dbcEntityOperations;
26+
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
3027
import org.springframework.data.r2dbc.core.ReactiveDataAccessStrategy;
31-
import org.springframework.data.r2dbc.core.StatementMapper;
3228
import org.springframework.data.r2dbc.query.Criteria;
29+
import org.springframework.data.r2dbc.query.Query;
3330
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
34-
import org.springframework.data.relational.core.sql.Functions;
35-
import org.springframework.data.relational.core.sql.Select;
36-
import org.springframework.data.relational.core.sql.StatementBuilder;
37-
import org.springframework.data.relational.core.sql.Table;
38-
import org.springframework.data.relational.core.sql.render.SqlRenderer;
3931
import org.springframework.data.relational.repository.query.RelationalEntityInformation;
4032
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
33+
import org.springframework.data.util.Lazy;
4134
import org.springframework.transaction.annotation.Transactional;
4235
import org.springframework.util.Assert;
4336

@@ -51,16 +44,45 @@
5144
public class SimpleR2dbcRepository<T, ID> implements ReactiveCrudRepository<T, ID> {
5245

5346
private final RelationalEntityInformation<T, ID> entity;
54-
private final DatabaseClient databaseClient;
55-
private final R2dbcConverter converter;
56-
private final ReactiveDataAccessStrategy accessStrategy;
47+
private final R2dbcEntityOperations entityOperations;
48+
private final Lazy<RelationalPersistentProperty> idProperty;
49+
50+
/**
51+
* Create a new {@link SimpleR2dbcRepository}.
52+
*
53+
* @param entity
54+
* @param entityOperations
55+
* @param converter
56+
* @since 1.1
57+
*/
58+
SimpleR2dbcRepository(RelationalEntityInformation<T, ID> entity, R2dbcEntityOperations entityOperations,
59+
R2dbcConverter converter) {
60+
61+
this.entity = entity;
62+
this.entityOperations = entityOperations;
63+
this.idProperty = Lazy.of(() -> converter //
64+
.getMappingContext() //
65+
.getRequiredPersistentEntity(this.entity.getJavaType()) //
66+
.getRequiredIdProperty());
67+
}
5768

69+
/**
70+
* Create a new {@link SimpleR2dbcRepository}.
71+
*
72+
* @param entity
73+
* @param databaseClient
74+
* @param converter
75+
* @param accessStrategy
76+
*/
5877
public SimpleR2dbcRepository(RelationalEntityInformation<T, ID> entity, DatabaseClient databaseClient,
5978
R2dbcConverter converter, ReactiveDataAccessStrategy accessStrategy) {
79+
6080
this.entity = entity;
61-
this.databaseClient = databaseClient;
62-
this.converter = converter;
63-
this.accessStrategy = accessStrategy;
81+
this.entityOperations = new R2dbcEntityTemplate(databaseClient);
82+
this.idProperty = Lazy.of(() -> converter //
83+
.getMappingContext() //
84+
.getRequiredPersistentEntity(this.entity.getJavaType()) //
85+
.getRequiredIdProperty());
6486
}
6587

6688
/* (non-Javadoc)
@@ -73,28 +95,10 @@ public <S extends T> Mono<S> save(S objectToSave) {
7395
Assert.notNull(objectToSave, "Object to save must not be null!");
7496

7597
if (this.entity.isNew(objectToSave)) {
76-
77-
return this.databaseClient.insert() //
78-
.into(this.entity.getJavaType()) //
79-
.table(this.entity.getTableName()).using(objectToSave) //
80-
.map(this.converter.populateIdIfNecessary(objectToSave)) //
81-
.first() //
82-
.defaultIfEmpty(objectToSave);
98+
return this.entityOperations.insert(objectToSave);
8399
}
84100

85-
return this.databaseClient.update() //
86-
.table(this.entity.getJavaType()) //
87-
.table(this.entity.getTableName()).using(objectToSave) //
88-
.fetch().rowsUpdated().handle((rowsUpdated, sink) -> {
89-
90-
if (rowsUpdated == 0) {
91-
sink.error(new TransientDataAccessResourceException(
92-
String.format("Failed to update table [%s]. Row with Id [%s] does not exist.",
93-
this.entity.getTableName(), this.entity.getId(objectToSave))));
94-
} else {
95-
sink.next(objectToSave);
96-
}
97-
});
101+
return this.entityOperations.update(objectToSave);
98102
}
99103

100104
/* (non-Javadoc)
@@ -129,20 +133,7 @@ public Mono<T> findById(ID id) {
129133

130134
Assert.notNull(id, "Id must not be null!");
131135

132-
List<String> columns = this.accessStrategy.getAllColumns(this.entity.getJavaType());
133-
String idProperty = getIdProperty().getName();
134-
135-
StatementMapper mapper = this.accessStrategy.getStatementMapper().forType(this.entity.getJavaType());
136-
StatementMapper.SelectSpec selectSpec = mapper.createSelect(this.entity.getTableName()) //
137-
.withProjection(columns) //
138-
.withCriteria(Criteria.where(idProperty).is(id));
139-
140-
PreparedOperation<?> operation = mapper.getMappedObject(selectSpec);
141-
142-
return this.databaseClient.execute(operation) //
143-
.as(this.entity.getJavaType()) //
144-
.fetch() //
145-
.one();
136+
return this.entityOperations.selectOne(getIdQuery(id), this.entity.getJavaType());
146137
}
147138

148139
/* (non-Javadoc)
@@ -161,19 +152,7 @@ public Mono<Boolean> existsById(ID id) {
161152

162153
Assert.notNull(id, "Id must not be null!");
163154

164-
String idProperty = getIdProperty().getName();
165-
166-
StatementMapper mapper = this.accessStrategy.getStatementMapper().forType(this.entity.getJavaType());
167-
StatementMapper.SelectSpec selectSpec = mapper.createSelect(this.entity.getTableName())
168-
.withProjection(Collections.singletonList(idProperty)) //
169-
.withCriteria(Criteria.where(idProperty).is(id));
170-
171-
PreparedOperation<?> operation = mapper.getMappedObject(selectSpec);
172-
173-
return this.databaseClient.execute(operation) //
174-
.map((r, md) -> r) //
175-
.first() //
176-
.hasElement();
155+
return this.entityOperations.exists(getIdQuery(id), this.entity.getJavaType());
177156
}
178157

179158
/* (non-Javadoc)
@@ -189,7 +168,7 @@ public Mono<Boolean> existsById(Publisher<ID> publisher) {
189168
*/
190169
@Override
191170
public Flux<T> findAll() {
192-
return this.databaseClient.select().from(this.entity.getJavaType()).fetch().all();
171+
return this.entityOperations.select(Query.empty(), this.entity.getJavaType());
193172
}
194173

195174
/* (non-Javadoc)
@@ -217,17 +196,9 @@ public Flux<T> findAllById(Publisher<ID> idPublisher) {
217196
return Flux.empty();
218197
}
219198

220-
List<String> columns = this.accessStrategy.getAllColumns(this.entity.getJavaType());
221199
String idProperty = getIdProperty().getName();
222200

223-
StatementMapper mapper = this.accessStrategy.getStatementMapper().forType(this.entity.getJavaType());
224-
StatementMapper.SelectSpec selectSpec = mapper.createSelect(this.entity.getTableName()) //
225-
.withProjection(columns) //
226-
.withCriteria(Criteria.where(idProperty).in(ids));
227-
228-
PreparedOperation<?> operation = mapper.getMappedObject(selectSpec);
229-
230-
return this.databaseClient.execute(operation).as(this.entity.getJavaType()).fetch().all();
201+
return this.entityOperations.select(Query.query(Criteria.where(idProperty).in(ids)), this.entity.getJavaType());
231202
});
232203
}
233204

@@ -236,17 +207,7 @@ public Flux<T> findAllById(Publisher<ID> idPublisher) {
236207
*/
237208
@Override
238209
public Mono<Long> count() {
239-
240-
Table table = Table.create(this.entity.getTableName());
241-
Select select = StatementBuilder //
242-
.select(Functions.count(table.column(getIdProperty().getColumnName()))) //
243-
.from(table) //
244-
.build();
245-
246-
return this.databaseClient.execute(SqlRenderer.toString(select)) //
247-
.map((r, md) -> r.get(0, Long.class)) //
248-
.first() //
249-
.defaultIfEmpty(0L);
210+
return this.entityOperations.count(Query.empty(), this.entity.getJavaType());
250211
}
251212

252213
/* (non-Javadoc)
@@ -258,13 +219,7 @@ public Mono<Void> deleteById(ID id) {
258219

259220
Assert.notNull(id, "Id must not be null!");
260221

261-
return this.databaseClient.delete() //
262-
.from(this.entity.getJavaType()) //
263-
.table(this.entity.getTableName()) //
264-
.matching(Criteria.where(getIdProperty().getName()).is(id)) //
265-
.fetch() //
266-
.rowsUpdated() //
267-
.then();
222+
return this.entityOperations.delete(getIdQuery(id), this.entity.getJavaType()).then();
268223
}
269224

270225
/* (non-Javadoc)
@@ -275,20 +230,16 @@ public Mono<Void> deleteById(ID id) {
275230
public Mono<Void> deleteById(Publisher<ID> idPublisher) {
276231

277232
Assert.notNull(idPublisher, "The Id Publisher must not be null!");
278-
StatementMapper statementMapper = this.accessStrategy.getStatementMapper().forType(this.entity.getJavaType());
279233

280234
return Flux.from(idPublisher).buffer().filter(ids -> !ids.isEmpty()).concatMap(ids -> {
281235

282236
if (ids.isEmpty()) {
283237
return Flux.empty();
284238
}
285239

286-
return this.databaseClient.delete() //
287-
.from(this.entity.getJavaType()) //
288-
.table(this.entity.getTableName()) //
289-
.matching(Criteria.where(getIdProperty().getName()).in(ids)) //
290-
.fetch() //
291-
.rowsUpdated();
240+
String idProperty = getIdProperty().getName();
241+
242+
return this.entityOperations.delete(Query.query(Criteria.where(idProperty).in(ids)), this.entity.getJavaType());
292243
}).then();
293244
}
294245

@@ -337,14 +288,14 @@ public Mono<Void> deleteAll(Publisher<? extends T> objectPublisher) {
337288
@Override
338289
@Transactional
339290
public Mono<Void> deleteAll() {
340-
return this.databaseClient.delete().from(this.entity.getTableName()).then();
291+
return this.entityOperations.delete(Query.empty(), this.entity.getJavaType()).then();
341292
}
342293

343294
private RelationalPersistentProperty getIdProperty() {
295+
return this.idProperty.get();
296+
}
344297

345-
return this.converter //
346-
.getMappingContext() //
347-
.getRequiredPersistentEntity(this.entity.getJavaType()) //
348-
.getRequiredIdProperty();
298+
private Query getIdQuery(Object id) {
299+
return Query.query(Criteria.where(getIdProperty().getName()).is(id));
349300
}
350301
}

Diff for: src/test/java/org/springframework/data/r2dbc/repository/support/R2dbcRepositoryFactoryUnitTests.java

+8-8
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,12 @@
2424
import org.mockito.Mock;
2525
import org.mockito.junit.MockitoJUnitRunner;
2626

27-
import org.springframework.data.mapping.context.MappingContext;
27+
import org.springframework.data.annotation.Id;
28+
import org.springframework.data.r2dbc.convert.MappingR2dbcConverter;
2829
import org.springframework.data.r2dbc.convert.R2dbcConverter;
2930
import org.springframework.data.r2dbc.core.DatabaseClient;
3031
import org.springframework.data.r2dbc.core.ReactiveDataAccessStrategy;
31-
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
32+
import org.springframework.data.r2dbc.mapping.R2dbcMappingContext;
3233
import org.springframework.data.relational.repository.query.RelationalEntityInformation;
3334
import org.springframework.data.relational.repository.support.MappingRelationalEntityInformation;
3435
import org.springframework.data.repository.Repository;
@@ -41,18 +42,15 @@
4142
@RunWith(MockitoJUnitRunner.class)
4243
public class R2dbcRepositoryFactoryUnitTests {
4344

45+
R2dbcConverter r2dbcConverter = new MappingR2dbcConverter(new R2dbcMappingContext());
46+
4447
@Mock DatabaseClient databaseClient;
45-
@Mock R2dbcConverter r2dbcConverter;
4648
@Mock ReactiveDataAccessStrategy dataAccessStrategy;
47-
@Mock @SuppressWarnings("rawtypes") MappingContext mappingContext;
48-
@Mock @SuppressWarnings("rawtypes") RelationalPersistentEntity entity;
4949

5050
@Before
5151
@SuppressWarnings("unchecked")
5252
public void before() {
53-
when(mappingContext.getRequiredPersistentEntity(Person.class)).thenReturn(entity);
5453
when(dataAccessStrategy.getConverter()).thenReturn(r2dbcConverter);
55-
when(r2dbcConverter.getMappingContext()).thenReturn(mappingContext);
5654
}
5755

5856
@Test
@@ -75,5 +73,7 @@ public void createsRepositoryWithIdTypeLong() {
7573

7674
interface MyPersonRepository extends Repository<Person, Long> {}
7775

78-
static class Person {}
76+
static class Person {
77+
@Id long id;
78+
}
7979
}

0 commit comments

Comments
 (0)