Skip to content

Commit a89c864

Browse files
christophstroblmp911de
authored andcommitted
Add sample for Typed Property Path usage.
1 parent bf0449e commit a89c864

9 files changed

Lines changed: 303 additions & 0 deletions

File tree

README.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ WARNING: If you're done using it, don't forget to shut it down!
120120

121121
== Miscellaneous
122122

123+
* `jdbc/typed-property-path` - Example project to demonstrate usage of `TypedPropertyPath` for refactoring safe queries and sort.
123124
* `mongodb/fragment-spi` - Example project how to use Spring Data Fragment SPI to provide reusable custom extensions.
124125
* `bom` - Example project how to use the Spring Data release train bom in non-Spring-Boot scenarios.
125126
* `map` - Example project to show how to use `Map`-backed repositories.

jdbc/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
<module>jmolecules</module>
2626
<module>jooq</module>
2727
<module>singlequeryloading</module>
28+
<module>typed-property-path</module>
2829
<!-- <module>mybatis</module> -->
2930
<module>graalvm-native</module>
3031
</modules>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
= Spring Data JDBC - Typed Property Path Example
2+
3+
This example demonstrates type-safe property references (TypedPropertyPath) for queries and sort.
4+
5+
== Feature
6+
7+
Instead of brittle string literals:
8+
9+
[source,java]
10+
----
11+
Sort.by("name", "created")
12+
----
13+
14+
you can use method references for compile-time safety and IDE refactoring support:
15+
16+
[source,java]
17+
----
18+
Sort.by(Category::getName, Category::getCreated)
19+
----
20+
21+
== Running
22+
23+
From the repository root:
24+
25+
[source,bash]
26+
----
27+
mvn clean install -pl jdbc/typed-property-path
28+
----
29+
30+
Or run the test class `TypedPropertyPathTests` from your IDE.

jdbc/typed-property-path/pom.xml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<artifactId>spring-data-jdbc-typed-property-path</artifactId>
7+
8+
<parent>
9+
<groupId>org.springframework.data.examples</groupId>
10+
<artifactId>spring-data-jdbc-examples</artifactId>
11+
<version>4.0.0-SNAPSHOT</version>
12+
<relativePath>../pom.xml</relativePath>
13+
</parent>
14+
15+
<name>Spring Data JDBC - Typed Property Path Example</name>
16+
<description>Sample project demonstrating type-safe property paths (TypedPropertyPath) with Sort in Spring Data JDBC</description>
17+
18+
</project>
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Copyright 2026-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package example.springdata.jdbc.typedpropertypath;
17+
18+
import java.time.LocalDateTime;
19+
20+
import org.springframework.data.annotation.Id;
21+
import org.springframework.data.annotation.PersistenceCreator;
22+
23+
/**
24+
* Coarse classification entity for demonstrating type-safe property paths with {@link org.springframework.data.domain.Sort}.
25+
*
26+
* @author Christoph Strobl
27+
*/
28+
public class Category {
29+
30+
private final @Id Long id;
31+
private String name;
32+
private String description;
33+
private LocalDateTime created;
34+
private Long inserted;
35+
36+
public Category(String name, String description) {
37+
this.id = null;
38+
this.name = name;
39+
this.description = description;
40+
this.created = LocalDateTime.now();
41+
this.inserted = null;
42+
}
43+
44+
@PersistenceCreator
45+
Category(Long id, String name, String description, LocalDateTime created, Long inserted) {
46+
this.id = id;
47+
this.name = name;
48+
this.description = description;
49+
this.created = created;
50+
this.inserted = inserted;
51+
}
52+
53+
public void timeStamp() {
54+
if (inserted == null || inserted == 0) {
55+
inserted = System.currentTimeMillis();
56+
}
57+
}
58+
59+
public Long getId() {
60+
return this.id;
61+
}
62+
63+
public String getName() {
64+
return this.name;
65+
}
66+
67+
public String getDescription() {
68+
return this.description;
69+
}
70+
71+
public LocalDateTime getCreated() {
72+
return this.created;
73+
}
74+
75+
public Long getInserted() {
76+
return this.inserted;
77+
}
78+
79+
public void setName(String name) {
80+
this.name = name;
81+
}
82+
83+
public void setDescription(String description) {
84+
this.description = description;
85+
}
86+
87+
public void setCreated(LocalDateTime created) {
88+
this.created = created;
89+
}
90+
91+
public void setInserted(Long inserted) {
92+
this.inserted = inserted;
93+
}
94+
95+
@Override
96+
public String toString() {
97+
return "%s %s".formatted(name, description);
98+
}
99+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2026-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package example.springdata.jdbc.typedpropertypath;
17+
18+
import org.springframework.data.repository.CrudRepository;
19+
import org.springframework.data.repository.ListPagingAndSortingRepository;
20+
21+
/**
22+
* Repository for {@link Category} entities.
23+
*
24+
* @author Christoph Strobl
25+
*/
26+
interface CategoryRepository extends CrudRepository<Category, Long>, ListPagingAndSortingRepository<Category, Long> {
27+
28+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2026-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package example.springdata.jdbc.typedpropertypath;
17+
18+
import org.springframework.context.annotation.Configuration;
19+
import org.springframework.data.jdbc.repository.config.AbstractJdbcConfiguration;
20+
import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories;
21+
22+
/**
23+
* JDBC configuration for the typed property path sample. Enables repository scanning and
24+
* optional entity callbacks.
25+
*
26+
* @author Christoph Strobl
27+
*/
28+
@Configuration
29+
@EnableJdbcRepositories
30+
public class TypedPropertyPathJdbcConfiguration extends AbstractJdbcConfiguration {
31+
32+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
CREATE TABLE IF NOT EXISTS category (id INTEGER IDENTITY PRIMARY KEY, name VARCHAR(100), description VARCHAR(2000), created DATETIME, inserted BIGINT);
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright 2026-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package example.springdata.jdbc.typedpropertypath;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
import static org.springframework.data.relational.core.query.Criteria.where;
20+
import static org.springframework.data.relational.core.query.Query.query;
21+
22+
import java.util.List;
23+
24+
import org.junit.jupiter.api.BeforeEach;
25+
import org.junit.jupiter.api.Test;
26+
import org.springframework.beans.factory.annotation.Autowired;
27+
import org.springframework.boot.jdbc.test.autoconfigure.AutoConfigureJdbc;
28+
import org.springframework.boot.test.context.SpringBootTest;
29+
import org.springframework.data.domain.Sort;
30+
import org.springframework.data.jdbc.core.JdbcAggregateTemplate;
31+
32+
/**
33+
* Demonstrates type-safe property paths with {@link Sort}.
34+
*
35+
* @author Christoph Strobl
36+
*/
37+
@SpringBootTest(classes = TypedPropertyPathJdbcConfiguration.class)
38+
@AutoConfigureJdbc
39+
class TypedPropertyPathTests {
40+
41+
@Autowired
42+
CategoryRepository repository;
43+
44+
@Autowired
45+
JdbcAggregateTemplate jdbcAggregateTemplate;
46+
47+
@BeforeEach
48+
void clearData() {
49+
repository.deleteAll();
50+
}
51+
52+
@Test
53+
void queryFilterWithTypedPropertyPath() {
54+
55+
repository.save(new Category("Porsche", "Sports cars"));
56+
repository.save(new Category("BMW", "Luxury cars"));
57+
repository.save(new Category("Porsche", "SUV series"));
58+
59+
List<Category> all = jdbcAggregateTemplate.findAll(
60+
query(where(Category::getName).is("Porsche")),
61+
Category.class);
62+
63+
assertThat(all).hasSize(2);
64+
assertThat(all).extracting(Category::getName).containsOnly("Porsche");
65+
assertThat(all).extracting(Category::getDescription)
66+
.containsExactlyInAnyOrder("Sports cars", "SUV series");
67+
}
68+
69+
@Test
70+
void sortBySinglePropertyWithTypedPropertyPath() {
71+
72+
repository.save(new Category("Porsche", "Sports cars"));
73+
repository.save(new Category("Audi", "German luxury"));
74+
repository.save(new Category("Mercedes", "Premium"));
75+
76+
List<Category> all = repository.findAll(Sort.by(Category::getName));
77+
78+
assertThat(all).extracting(Category::getName).containsExactly("Audi", "Mercedes", "Porsche");
79+
}
80+
81+
@Test
82+
void sortByMultiplePropertiesWithTypedPropertyPath() {
83+
84+
repository.save(new Category("Porsche", "Cayenne"));
85+
repository.save(new Category("Porsche", "911"));
86+
repository.save(new Category("BMW", "911"));
87+
88+
List<Category> all = repository.findAll(Sort.by(Category::getName, Category::getDescription));
89+
90+
assertThat(all).extracting(Category::toString)
91+
.containsExactly("BMW 911", "Porsche 911", "Porsche Cayenne");
92+
}
93+
}

0 commit comments

Comments
 (0)