From 5a234bcef340fa6444f95f7ec7dac3d29c5b4c51 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Thu, 9 Jan 2025 13:21:54 +0100 Subject: [PATCH] Add AOT Repositories example. Closes #695 --- jpa/aot-generation/pom.xml | 89 +++++++++++++ .../example/springdata/aot/AotJpaApp.java | 30 +++++ .../main/java/example/springdata/aot/CLR.java | 124 ++++++++++++++++++ .../java/example/springdata/aot/Post.java | 68 ++++++++++ .../java/example/springdata/aot/User.java | 114 ++++++++++++++++ .../springdata/aot/UserProjection.java | 26 ++++ .../springdata/aot/UserRepository.java | 57 ++++++++ .../src/main/resources/application.properties | 5 + jpa/pom.xml | 19 +++ mongodb/aot-generation/pom.xml | 71 ++++++++++ .../main/java/example/springdata/aot/App.java | 30 +++++ .../main/java/example/springdata/aot/CLR.java | 114 ++++++++++++++++ .../java/example/springdata/aot/Post.java | 56 ++++++++ .../java/example/springdata/aot/User.java | 105 +++++++++++++++ .../springdata/aot/UserProjection.java | 28 ++++ .../springdata/aot/UserRepository.java | 44 +++++++ .../src/main/resources/logback.xml | 28 ++++ .../src/main/resources/spring.properties | 1 + mongodb/pom.xml | 3 +- pom.xml | 3 + 20 files changed, 1014 insertions(+), 1 deletion(-) create mode 100644 jpa/aot-generation/pom.xml create mode 100644 jpa/aot-generation/src/main/java/example/springdata/aot/AotJpaApp.java create mode 100644 jpa/aot-generation/src/main/java/example/springdata/aot/CLR.java create mode 100644 jpa/aot-generation/src/main/java/example/springdata/aot/Post.java create mode 100644 jpa/aot-generation/src/main/java/example/springdata/aot/User.java create mode 100644 jpa/aot-generation/src/main/java/example/springdata/aot/UserProjection.java create mode 100644 jpa/aot-generation/src/main/java/example/springdata/aot/UserRepository.java create mode 100644 jpa/aot-generation/src/main/resources/application.properties create mode 100644 mongodb/aot-generation/pom.xml create mode 100644 mongodb/aot-generation/src/main/java/example/springdata/aot/App.java create mode 100644 mongodb/aot-generation/src/main/java/example/springdata/aot/CLR.java create mode 100644 mongodb/aot-generation/src/main/java/example/springdata/aot/Post.java create mode 100644 mongodb/aot-generation/src/main/java/example/springdata/aot/User.java create mode 100644 mongodb/aot-generation/src/main/java/example/springdata/aot/UserProjection.java create mode 100644 mongodb/aot-generation/src/main/java/example/springdata/aot/UserRepository.java create mode 100644 mongodb/aot-generation/src/main/resources/logback.xml create mode 100644 mongodb/aot-generation/src/main/resources/spring.properties diff --git a/jpa/aot-generation/pom.xml b/jpa/aot-generation/pom.xml new file mode 100644 index 00000000..c8c2e9cf --- /dev/null +++ b/jpa/aot-generation/pom.xml @@ -0,0 +1,89 @@ + + + 4.0.0 + + org.springframework.data.examples + spring-data-jpa-examples + 2.0.0.BUILD-SNAPSHOT + + + org.example + aot-generation + + + UTF-8 + 7.0.0.Beta5 + + + + + org.jspecify + jspecify + 1.0.0 + + + + com.querydsl + querydsl-jpa + 5.1.0 + jakarta + + + com.querydsl + querydsl-apt + 5.1.0 + jakarta + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + com.querydsl + querydsl-jpa + 5.1.0 + jakarta + + + com.querydsl + querydsl-apt + 5.1.0 + jakarta + + + jakarta.persistence + jakarta.persistence-api + 3.2.0 + + + + + target/generated-test-sources + + target/generated-sources + + + + + org.springframework.boot + spring-boot-maven-plugin + + + process-aot + + process-aot + + + + + + + + diff --git a/jpa/aot-generation/src/main/java/example/springdata/aot/AotJpaApp.java b/jpa/aot-generation/src/main/java/example/springdata/aot/AotJpaApp.java new file mode 100644 index 00000000..5256d51e --- /dev/null +++ b/jpa/aot-generation/src/main/java/example/springdata/aot/AotJpaApp.java @@ -0,0 +1,30 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.springdata.aot; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author Christoph Strobl + */ +@SpringBootApplication +public class AotJpaApp { + + public static void main(String[] args) { + SpringApplication.run(AotJpaApp.class, args); + } +} diff --git a/jpa/aot-generation/src/main/java/example/springdata/aot/CLR.java b/jpa/aot-generation/src/main/java/example/springdata/aot/CLR.java new file mode 100644 index 00000000..aaa2ed51 --- /dev/null +++ b/jpa/aot-generation/src/main/java/example/springdata/aot/CLR.java @@ -0,0 +1,124 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.springdata.aot; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Slice; +import org.springframework.stereotype.Component; + +/** + * @author Christoph Strobl + * @since 2025/01 + */ +@Component +public class CLR implements CommandLineRunner { + + @Autowired UserRepository repository; + + @Override + public void run(String... args) throws Exception { + + User luke = new User("id-1", "luke"); + luke.setFirstname("Luke"); + luke.setLastname("Skywalker"); +// Post lukeP1 = new Post("I have a bad feeling about this."); +// em.persist(lukeP1); +// luke.setPosts(List.of(lukeP1)); + + User leia = new User("id-2", "leia"); + leia.setFirstname("Leia"); + leia.setLastname("Organa"); + + User han = new User("id-3", "han"); + han.setFirstname("Han"); + han.setLastname("Solo"); +// Post hanP1 = new Post("It's the ship that made the Kessel Run in less than 12 Parsecs."); +// em.persist(hanP1); +// han.setPosts(List.of(hanP1)); + + User chewbacca = new User("id-4", "chewbacca"); + User yoda = new User("id-5", "yoda"); + Post yodaP1 = new Post("Do. Or do not. There is no try."); + Post yodaP2 = new Post("Decide you must, how to serve them best. If you leave now, help them you could; but you would destroy all for which they have fought, and suffered."); +// em.persist(yodaP1); +// em.persist(yodaP2); +// yoda.setPosts(List.of(yodaP1, yodaP2)); + + User vader = new User("id-6", "vader"); + vader.setFirstname("Anakin"); + vader.setLastname("Skywalker"); +// Post vaderP1 = new Post("I am your father"); +// em.persist(vaderP1); +// vader.setPosts(List.of(vaderP1)); + + User kylo = new User("id-7", "kylo"); + kylo.setFirstname("Ben"); + kylo.setLastname("Solo"); + + repository.saveAll(List.of(luke, leia, han, chewbacca, yoda, vader, kylo)); + + System.out.println("------- annotated multi -------"); + System.out.println(repository.usersWithUsernamesStartingWith("l")); + + System.out.println("------- derived single -------"); + System.out.println(repository.findUserByUsername("yoda")); + +// System.out.println("------- derived nested.path -------"); +// System.out.println(repository.findUserByPostsMessageLike("father")); + + System.out.println("------- derived optional -------"); + System.out.println(repository.findOptionalUserByUsername("yoda")); + + System.out.println("------- derived count -------"); + Long count = repository.countUsersByLastnameLike("Sky"); + System.out.println("user count " + count); + + System.out.println("------- derived exists -------"); + Boolean exists = repository.existsByUsername("vader"); + System.out.println("user exists " + exists); + + System.out.println("------- derived multi -------"); + System.out.println(repository.findUserByLastnameStartingWith("Sky")); + + System.out.println("------- derived sorted -------"); + System.out.println(repository.findUserByLastnameStartingWithOrderByFirstname("Sky")); + + System.out.println("------- derived page -------"); + Page page0 = repository.findUserByLastnameStartingWith("S", PageRequest.of(0, 2)); + System.out.println("page0: " + page0); + System.out.println("page0.content: " + page0.getContent()); + + Page page1 = repository.findUserByLastnameStartingWith("S", PageRequest.of(1, 2)); + System.out.println("page1: " + page1); + System.out.println("page1.content: " + page1.getContent()); + + System.out.println("------- derived slice -------"); + Slice slice0 = repository.findUserByUsernameAfter("luke", PageRequest.of(0, 2)); + System.out.println("slice0: " + slice0); + System.out.println("slice0.content: " + slice0.getContent()); + + System.out.println("------- derived top -------"); + System.out.println(repository.findTop2UsersByLastnameStartingWith("S")); + +// System.out.println("------- derived with fields -------"); +// System.out.println(repository.findJustUsernameBy()); + } +} diff --git a/jpa/aot-generation/src/main/java/example/springdata/aot/Post.java b/jpa/aot-generation/src/main/java/example/springdata/aot/Post.java new file mode 100644 index 00000000..e950f2aa --- /dev/null +++ b/jpa/aot-generation/src/main/java/example/springdata/aot/Post.java @@ -0,0 +1,68 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.springdata.aot; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Random; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; + +/** + * @author Christoph Strobl + * @since 2025/01 + */ +@Entity +public class Post { + + @Id + @GeneratedValue + private Long id; + + private String message; + private Instant date; + + public Post() { + } + + public Post(String message) { + this.message = message; + this.date = Instant.now().minus(new Random().nextLong(100), ChronoUnit.MINUTES); + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public Instant getDate() { + return date; + } + + public void setDate(Instant date) { + this.date = date; + } + + @Override + public String toString() { + return message; + } +} diff --git a/jpa/aot-generation/src/main/java/example/springdata/aot/User.java b/jpa/aot-generation/src/main/java/example/springdata/aot/User.java new file mode 100644 index 00000000..2a5d37d6 --- /dev/null +++ b/jpa/aot-generation/src/main/java/example/springdata/aot/User.java @@ -0,0 +1,114 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.springdata.aot; + +import java.time.Instant; +import java.util.List; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; + +/** + * @author Christoph Strobl + * @since 2025/01 + */ +@Entity(name = "users") +public class User { + + @Id + private String id; + private String username; + + @Column(name = "first_name") String firstname; + @Column(name = "last_name") String lastname; + +// @OneToMany +// private List posts; + + Instant registrationDate; + Instant lastSeen; + + public User() { + } + + public User(String id, String username) { + this.id = id; + this.username = username; + } + + public String getId() { + return id; + } + + public String getUsername() { + return username; + } + + public String getFirstname() { + return firstname; + } + + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + public String getLastname() { + return lastname; + } + + public void setLastname(String lastname) { + this.lastname = lastname; + } + + public Instant getRegistrationDate() { + return registrationDate; + } + + public void setRegistrationDate(Instant registrationDate) { + this.registrationDate = registrationDate; + } + + public Instant getLastSeen() { + return lastSeen; + } + + public void setLastSeen(Instant lastSeen) { + this.lastSeen = lastSeen; + } + +// public List getPosts() { +// return posts; +// } +// +// public void setPosts(List posts) { +// this.posts = posts; +// } + + @Override + public String toString() { + return "User{" + + "id='" + id + '\'' + + ", username='" + username + '\'' + + ", firstname='" + firstname + '\'' + + ", lastname='" + lastname + '\'' + + ", registrationDate=" + registrationDate + + ", lastSeen=" + lastSeen + +// ", posts=" + posts + + '}'; + } +} diff --git a/jpa/aot-generation/src/main/java/example/springdata/aot/UserProjection.java b/jpa/aot-generation/src/main/java/example/springdata/aot/UserProjection.java new file mode 100644 index 00000000..8ac144a1 --- /dev/null +++ b/jpa/aot-generation/src/main/java/example/springdata/aot/UserProjection.java @@ -0,0 +1,26 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.springdata.aot; + +import java.time.Instant; + +/** + * @author Christoph Strobl + * @since 2025/01 + */ +public record UserProjection(String username, Instant registrationDate) { + +} diff --git a/jpa/aot-generation/src/main/java/example/springdata/aot/UserRepository.java b/jpa/aot-generation/src/main/java/example/springdata/aot/UserRepository.java new file mode 100644 index 00000000..4d28f9a5 --- /dev/null +++ b/jpa/aot-generation/src/main/java/example/springdata/aot/UserRepository.java @@ -0,0 +1,57 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.springdata.aot; + +import java.util.List; +import java.util.Optional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.querydsl.QuerydslPredicateExecutor; +import org.springframework.data.repository.CrudRepository; + +/** + * @author Christoph Strobl + * @since 2025/01 + */ +public interface UserRepository extends CrudRepository, QuerydslPredicateExecutor { + + User findUserByUsername(String username); + + Optional findOptionalUserByUsername(String username); + + Long countUsersByLastnameLike(String lastname); + + Boolean existsByUsername(String username); + + List findUserByLastnameLike(String lastname); + + List findUserByLastnameStartingWithOrderByFirstname(String lastname); + + List findTop2UsersByLastnameStartingWith(String lastname); + + Slice findUserByUsernameAfter(String username, Pageable pageable); + + List findUserByLastnameStartingWith(String lastname); + + Page findUserByLastnameStartingWith(String lastname, Pageable page); + + @Query("SELECT u FROM example.springdata.aot.User u WHERE u.username LIKE ?1%") + List usersWithUsernamesStartingWith(String username); + +} diff --git a/jpa/aot-generation/src/main/resources/application.properties b/jpa/aot-generation/src/main/resources/application.properties new file mode 100644 index 00000000..a036d6f1 --- /dev/null +++ b/jpa/aot-generation/src/main/resources/application.properties @@ -0,0 +1,5 @@ +spring.jpa.defer-datasource-initialization=true +spring.jpa.properties.hibernate.boot.allow_jdbc_metadata_access=false +spring.aot.repositories.enabled=true +#logging.level.org.springframework.data.repository.aot.generate.RepositoryContributor=trace + diff --git a/jpa/pom.xml b/jpa/pom.xml index 80661b14..622bb759 100644 --- a/jpa/pom.xml +++ b/jpa/pom.xml @@ -29,6 +29,7 @@ vavr multitenant graalvm-native + aot-generation @@ -38,6 +39,24 @@ spring-boot-starter-data-jpa + + org.springframework.data + spring-data-commons + 4.0.0-SNAPSHOT + + + + org.springframework.data + spring-data-jpa + 4.0.0-SNAPSHOT + + + + jakarta.persistence + jakarta.persistence-api + 3.2.0 + + org.hsqldb hsqldb diff --git a/mongodb/aot-generation/pom.xml b/mongodb/aot-generation/pom.xml new file mode 100644 index 00000000..c7105144 --- /dev/null +++ b/mongodb/aot-generation/pom.xml @@ -0,0 +1,71 @@ + + + 4.0.0 + + org.springframework.data.examples + spring-data-mongodb-examples + 2.0.0.BUILD-SNAPSHOT + + + org.example + aot-generation + + + 21 + 21 + UTF-8 + + + + + org.springframework.data + spring-data-commons + 4.0.x-GENERATED-REPOSITORIES-SNAPSHOT + + + org.springframework.data + spring-data-mongodb + 5.0.0-GENERATED-REPOSITORIES-SNAPSHOT + + + org.mongodb + bson + 5.3.1 + + + org.mongodb + mongodb-driver-core + 5.3.1 + + + org.mongodb + mongodb-driver-sync + 5.3.1 + + + org.jspecify + jspecify + 1.0.0 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + process-aot + + process-aot + + + + + + + + diff --git a/mongodb/aot-generation/src/main/java/example/springdata/aot/App.java b/mongodb/aot-generation/src/main/java/example/springdata/aot/App.java new file mode 100644 index 00000000..b63c97cd --- /dev/null +++ b/mongodb/aot-generation/src/main/java/example/springdata/aot/App.java @@ -0,0 +1,30 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.springdata.aot; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author Christoph Strobl + */ +@SpringBootApplication +public class App { + + public static void main(String[] args) { + SpringApplication.run(App.class, args); + } +} diff --git a/mongodb/aot-generation/src/main/java/example/springdata/aot/CLR.java b/mongodb/aot-generation/src/main/java/example/springdata/aot/CLR.java new file mode 100644 index 00000000..1ccacfaa --- /dev/null +++ b/mongodb/aot-generation/src/main/java/example/springdata/aot/CLR.java @@ -0,0 +1,114 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.springdata.aot; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Slice; +import org.springframework.stereotype.Component; + +/** + * @author Christoph Strobl + * @since 2025/01 + */ +@Component +public class CLR implements CommandLineRunner { + + @Autowired UserRepository repository; + + @Override + public void run(String... args) throws Exception { + + User luke = new User("id-1", "luke"); + luke.setFirstname("Luke"); + luke.setLastname("Skywalker"); + luke.setPosts(List.of(new Post("I have a bad feeling about this."))); + + User leia = new User("id-2", "leia"); + leia.setFirstname("Leia"); + leia.setLastname("Organa"); + + User han = new User("id-3", "han"); + han.setFirstname("Han"); + han.setLastname("Solo"); + han.setPosts(List.of(new Post("It's the ship that made the Kessel Run in less than 12 Parsecs."))); + + User chewbacca = new User("id-4", "chewbacca"); + User yoda = new User("id-5", "yoda"); + yoda.setPosts(List.of(new Post("Do. Or do not. There is no try."), new Post("Decide you must, how to serve them best. If you leave now, help them you could; but you would destroy all for which they have fought, and suffered."))); + + User vader = new User("id-6", "vader"); + vader.setFirstname("Anakin"); + vader.setLastname("Skywalker"); + vader.setPosts(List.of(new Post("I am your father"))); + + User kylo = new User("id-7", "kylo"); + kylo.setFirstname("Ben"); + kylo.setLastname("Solo"); + + repository.saveAll(List.of(luke, leia, han, chewbacca, yoda, vader, kylo)); + + System.out.println("------- annotated multi -------"); + System.out.println(repository.usersWithUsernamesStartingWith("l")); + + System.out.println("------- derived single -------"); + System.out.println(repository.findUserByUsername("yoda")); + + System.out.println("------- derived nested.path -------"); + System.out.println(repository.findUserByPostsMessageLike("father")); + + System.out.println("------- derived optional -------"); + System.out.println(repository.findOptionalUserByUsername("yoda")); + + System.out.println("------- derived count -------"); + Long count = repository.countUsersByLastnameLike("Sky"); + System.out.println("user count " + count); + + System.out.println("------- derived exists -------"); + Boolean exists = repository.existsByUsername("vader"); + System.out.println("user exists " + exists); + + System.out.println("------- derived multi -------"); + System.out.println(repository.findUserByLastnameLike("Sky")); + + System.out.println("------- derived sorted -------"); + System.out.println(repository.findUserByLastnameLikeOrderByFirstname("Sky")); + + System.out.println("------- derived page -------"); + Page page0 = repository.findUserByLastnameStartingWith("S", PageRequest.of(0, 2)); + System.out.println("page0: " + page0); + System.out.println("page0.content: " + page0.getContent()); + + Page page1 = repository.findUserByLastnameStartingWith("S", PageRequest.of(1, 2)); + System.out.println("page1: " + page1); + System.out.println("page1.content: " + page1.getContent()); + + System.out.println("------- derived slice -------"); + Slice slice0 = repository.findUserByUsernameAfter("luke", PageRequest.of(0, 2)); + System.out.println("slice0: " + slice0); + System.out.println("slice0.content: " + slice0.getContent()); + + System.out.println("------- derived top -------"); + System.out.println(repository.findTop2UsersByLastnameLike("S")); + + System.out.println("------- derived with fields -------"); + System.out.println(repository.findJustUsernameBy()); + } +} diff --git a/mongodb/aot-generation/src/main/java/example/springdata/aot/Post.java b/mongodb/aot-generation/src/main/java/example/springdata/aot/Post.java new file mode 100644 index 00000000..9ca0d2f6 --- /dev/null +++ b/mongodb/aot-generation/src/main/java/example/springdata/aot/Post.java @@ -0,0 +1,56 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.springdata.aot; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Random; + +/** + * @author Christoph Strobl + * @since 2025/01 + */ +public class Post { + + private String message; + private Instant date; + + public Post(String message) { + this.message = message; + this.date = Instant.now().minus(new Random().nextLong(100), ChronoUnit.MINUTES); + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public Instant getDate() { + return date; + } + + public void setDate(Instant date) { + this.date = date; + } + + @Override + public String toString() { + return message; + } +} diff --git a/mongodb/aot-generation/src/main/java/example/springdata/aot/User.java b/mongodb/aot-generation/src/main/java/example/springdata/aot/User.java new file mode 100644 index 00000000..56317490 --- /dev/null +++ b/mongodb/aot-generation/src/main/java/example/springdata/aot/User.java @@ -0,0 +1,105 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.springdata.aot; + +import java.time.Instant; +import java.util.List; + +import org.springframework.data.mongodb.core.mapping.Field; + +/** + * @author Christoph Strobl + * @since 2025/01 + */ +public class User { + + private final String id; + private final String username; + + @Field("first_name") String firstname; + @Field("last_name") String lastname; + + private List posts; + + Instant registrationDate; + Instant lastSeen; + + public User(String id, String username) { + this.id = id; + this.username = username; + } + + public String getId() { + return id; + } + + public String getUsername() { + return username; + } + + public String getFirstname() { + return firstname; + } + + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + public String getLastname() { + return lastname; + } + + public void setLastname(String lastname) { + this.lastname = lastname; + } + + public Instant getRegistrationDate() { + return registrationDate; + } + + public void setRegistrationDate(Instant registrationDate) { + this.registrationDate = registrationDate; + } + + public Instant getLastSeen() { + return lastSeen; + } + + public void setLastSeen(Instant lastSeen) { + this.lastSeen = lastSeen; + } + + public List getPosts() { + return posts; + } + + public void setPosts(List posts) { + this.posts = posts; + } + + @Override + public String toString() { + return "User{" + + "id='" + id + '\'' + + ", username='" + username + '\'' + + ", firstname='" + firstname + '\'' + + ", lastname='" + lastname + '\'' + + ", registrationDate=" + registrationDate + + ", lastSeen=" + lastSeen + + ", posts=" + posts + + '}'; + } +} diff --git a/mongodb/aot-generation/src/main/java/example/springdata/aot/UserProjection.java b/mongodb/aot-generation/src/main/java/example/springdata/aot/UserProjection.java new file mode 100644 index 00000000..d243ba3a --- /dev/null +++ b/mongodb/aot-generation/src/main/java/example/springdata/aot/UserProjection.java @@ -0,0 +1,28 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.springdata.aot; + +import java.time.Instant; + +/** + * @author Christoph Strobl + * @since 2025/01 + */ +public interface UserProjection { + + String getUsername(); + Instant getRegistrationDate(); +} diff --git a/mongodb/aot-generation/src/main/java/example/springdata/aot/UserRepository.java b/mongodb/aot-generation/src/main/java/example/springdata/aot/UserRepository.java new file mode 100644 index 00000000..0565148a --- /dev/null +++ b/mongodb/aot-generation/src/main/java/example/springdata/aot/UserRepository.java @@ -0,0 +1,44 @@ +package example.springdata.aot; + +import java.util.List; +import java.util.Optional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.data.mongodb.repository.Query; +import org.springframework.data.repository.CrudRepository; + +/** + * @author Christoph Strobl + * @since 2025/01 + */ +public interface UserRepository extends CrudRepository { + + User findUserByUsername(String username); + + Optional findOptionalUserByUsername(String username); + + Long countUsersByLastnameLike(String lastname); + + Boolean existsByUsername(String username); + + List findUserByLastnameLike(String lastname); + + List findUserByPostsMessageLike(String part); + + List findUserByLastnameLikeOrderByFirstname(String lastname); + + List findTop2UsersByLastnameLike(String lastname); + + Slice findUserByUsernameAfter(String username, Pageable pageable); + + Page findUserByLastnameStartingWith(String lastname, Pageable page); + + @Query("{ 'username' : { $regex: '?0.*', $options: 'i' } }") + List usersWithUsernamesStartingWith(String username); + + @Query(fields = "{ 'username' : 1 }", sort = "{ 'username' : -1 }") + List findJustUsernameBy(); + +} diff --git a/mongodb/aot-generation/src/main/resources/logback.xml b/mongodb/aot-generation/src/main/resources/logback.xml new file mode 100644 index 00000000..1be6becb --- /dev/null +++ b/mongodb/aot-generation/src/main/resources/logback.xml @@ -0,0 +1,28 @@ + + + + + + %d %5p %40.40c:%4L - %m%n + + + + + + + + + + + + + + + + + + + + diff --git a/mongodb/aot-generation/src/main/resources/spring.properties b/mongodb/aot-generation/src/main/resources/spring.properties new file mode 100644 index 00000000..fb68fc02 --- /dev/null +++ b/mongodb/aot-generation/src/main/resources/spring.properties @@ -0,0 +1 @@ +spring.aot.repositories.enabled=true diff --git a/mongodb/pom.xml b/mongodb/pom.xml index c1798a21..920f82fa 100644 --- a/mongodb/pom.xml +++ b/mongodb/pom.xml @@ -36,7 +36,8 @@ linking util fragment-spi - + aot-generation + diff --git a/pom.xml b/pom.xml index 801bef88..d2ab97b6 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,10 @@ 1.1.3 + 21 + 21 UTF-8 + 2025.1.0-SNAPSHOT