I am doing the learning on Broadcom's Spring Academy to be up to date with the latest best practices. I bumped Spring Boot to 4.0.0 instead of the 3.0.0 on Spring Academy. But I am having some issues with PagingAndSortingRepository interface. I can succesfully use pagination /demo?page=0&size=1 and sorting /demo?sort=amount,desc but I cannot use the combination /demo?page=0&size=1&sort=amount,desc. And I cannot figure out how to fix it with JDBC.
build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '4.0.0'
id 'io.spring.dependency-management' version '1.1.7'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
description = 'Demo project for Spring Boot'
java {
sourceCompatibility = '17'
}
repositories {
mavenCentral()
}
dependencies {
// Rest API
implementation 'org.springframework.boot:spring-boot-starter-webmvc'
testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test'
// Database
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
runtimeOnly 'com.h2database:h2'
// Security
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
testImplementation 'org.springframework.security:spring-security-test'
// Test
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
tasks.named('test') {
useJUnitPlatform()
}
test {
testLogging {
events "passed", "skipped", "failed" //, "standardOut", "standardError"
showExceptions true
exceptionFormat "full"
showCauses true
showStackTraces true
// Change from false to true
showStandardStreams = true
}
}
DemoRepository.java
interface DemoRepository extends CrudRepository<Demo, Long>, PagingAndSortingRepository<Demo, Long> {
boolean existsByIdAndOwner(Long id, String owner);
Demo findByIdAndOwner(Long id, String owner);
Page<Demo> findByOwner(String owner, Pageable pageable);
}
DemoController.java (I commented the problematic line proposed on Spring Academy and replaced it by the simpler one above.)
@GetMapping
private ResponseEntity<List<Demo>> readDemos(Pageable pageable, Principal principal) {
Page<Demo> page = demoRepository.findByOwner(principal.getName(),
PageRequest.of(
pageable.getPageNumber(),
pageable.getPageSize(),
pageable.getSort()
// pageable.getSortOr(Sort.by(Sort.Direction.ASC, "amount"))
));
return ResponseEntity.ok(page.getContent());
}
DemoApplicationTests.java (If I use the previous code, it works fine. But if I use the commented out line, it breaks.)
@Test
void readDemos_sorting() {
EntityExchangeResult<String> result = client.get()
.uri("/demo?page=0&size=1&sort=amount,desc")
.header(HttpHeaders.AUTHORIZATION, authHeader)
.exchange()
.expectStatus()
.isOk()
.expectHeader()
.contentTypeCompatibleWith(MediaType.APPLICATION_JSON)
.expectBody(String.class)
.returnResult();
String responseBody = result.getResponseBody();
DocumentContext documentContext = JsonPath.parse(responseBody);
JSONArray read = documentContext.read("$[*]");
assertThat(read.size()).isEqualTo(3);
double amount = documentContext.read("$[0].amount");
assertThat(amount).isEqualTo(150.00);
}
Error when using the commented out line:
org.h2.jdbc.JdbcSQLSyntaxErrorException: Column "DEMO.AMOUNT" must be in the GROUP BY list; SQL statement:
SELECT COUNT(*) FROM "DEMO" WHERE "DEMO"."OWNER" = ? ORDER BY "DEMO"."AMOUNT" ASC [90016-240]
Also, other tests testing the sorting logic fail, whereas they work when I use pageable.getSort().
One one hand, I can use pageable.getSort() to support a default call without any sorting params; it also supports sort params; but I cannot combine those sort params with the pagination params or it fails.
On the other hand, if I use pageable.getSortOr(Sort.by(Sort.Direction.ASC, "amount")), all sorting fails, and if I use pagination params without sorting params, I get that SQL error.
View the full code on GitHub
/demo?page=0&size=1sort=amount,desc, Correction: The correct URL is/demo?page=0&size=1&sort=amount,desc. Reason: We need to add the&before thesortparameter. (2) Question: Why does the assertion expect a size of3? Given the parameterspage=0&size=1, it should only return1record.