Browse Source

Improve count and exists query projections.

We now use COUNT(*) and SELECT 1 for count respective exists queries to enable database optimizers instead of using the key columns.

Closes #1647
3.1.x
Mark Paluch 2 years ago
parent
commit
9d8455f3e3
No known key found for this signature in database
GPG Key ID: 4406B84C1661DCD1
  1. 21
      spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java
  2. 6
      spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplateUnitTests.java
  3. 4
      spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/core/ReactiveSelectOperationUnitTests.java

21
spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java

@ -247,18 +247,11 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw @@ -247,18 +247,11 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
Mono<Long> 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));
});
.withProjection(Functions.count(Expressions.just("*")));
Optional<CriteriaDefinition> criteria = query.getCriteria();
if (criteria.isPresent()) {
@ -284,17 +277,9 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw @@ -284,17 +277,9 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
Mono<Boolean> 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"));
}
StatementMapper.SelectSpec selectSpec = statementMapper.createSelect(tableName).limit(1)
.withProjection(Expressions.just("1"));
Optional<CriteriaDefinition> criteria = query.getCriteria();
if (criteria.isPresent()) {

6
spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplateUnitTests.java

@ -104,7 +104,7 @@ public class R2dbcEntityTemplateUnitTests { @@ -104,7 +104,7 @@ public class R2dbcEntityTemplateUnitTests {
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.getSql()).isEqualTo("SELECT COUNT(*) FROM person WHERE person.THE_NAME = $1");
assertThat(statement.getBindings()).hasSize(1).containsEntry(0, Parameter.from("Walter"));
}
@ -145,7 +145,7 @@ public class R2dbcEntityTemplateUnitTests { @@ -145,7 +145,7 @@ public class R2dbcEntityTemplateUnitTests {
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(*)"), result);
entityTemplate.select(WithoutId.class).count() //
.as(StepVerifier::create) //
@ -169,7 +169,7 @@ public class R2dbcEntityTemplateUnitTests { @@ -169,7 +169,7 @@ public class R2dbcEntityTemplateUnitTests {
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.getSql()).isEqualTo("SELECT 1 FROM person WHERE person.THE_NAME = $1 LIMIT 1");
assertThat(statement.getBindings()).hasSize(1).containsEntry(0, Parameter.from("Walter"));
}

4
spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/core/ReactiveSelectOperationUnitTests.java

@ -193,7 +193,7 @@ public class ReactiveSelectOperationUnitTests { @@ -193,7 +193,7 @@ public class ReactiveSelectOperationUnitTests {
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.getSql()).isEqualTo("SELECT 1 FROM person WHERE person.THE_NAME = $1 LIMIT 1");
}
@Test // gh-220
@ -216,7 +216,7 @@ public class ReactiveSelectOperationUnitTests { @@ -216,7 +216,7 @@ public class ReactiveSelectOperationUnitTests {
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.getSql()).isEqualTo("SELECT COUNT(*) FROM person WHERE person.THE_NAME = $1");
}
static class Person {

Loading…
Cancel
Save