From 9d8455f3e37a17ebeccfc907a887954b4f07d0b5 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 6 Nov 2023 10:35:05 +0100 Subject: [PATCH] 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 --- .../data/r2dbc/core/R2dbcEntityTemplate.java | 21 +++---------------- .../core/R2dbcEntityTemplateUnitTests.java | 6 +++--- .../ReactiveSelectOperationUnitTests.java | 4 ++-- 3 files changed, 8 insertions(+), 23 deletions(-) diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java index f88283fe5..37c78ab8d 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java @@ -247,18 +247,11 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw Mono 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 criteria = query.getCriteria(); if (criteria.isPresent()) { @@ -284,17 +277,9 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw Mono 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 criteria = query.getCriteria(); if (criteria.isPresent()) { diff --git a/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplateUnitTests.java b/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplateUnitTests.java index d7cf608cc..344aecb63 100644 --- a/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplateUnitTests.java +++ b/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplateUnitTests.java @@ -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 { 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 { 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")); } diff --git a/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/core/ReactiveSelectOperationUnitTests.java b/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/core/ReactiveSelectOperationUnitTests.java index eb8c332c7..38daa35ba 100644 --- a/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/core/ReactiveSelectOperationUnitTests.java +++ b/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/core/ReactiveSelectOperationUnitTests.java @@ -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 { 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 {