Browse Source

Update spring tests to current release of spring-boot (#8876)

* Update spring-boot to 3.2.4

* Disable open-in-view globally for all tests
open-in-view is unfortunately enabled by default for historical reasons, see https://github.com/spring-projects/spring-boot/issues/7107

This significantly improves performance for requests where db connection is not needed at all, i.e. json & plaintext
db ~3% improvement
json ~17% improvement
query ~no change
update ~no change
fortune ~5% improvement
plaintext ~29% improvement

* Make spring-webflux tests work again
Replaces old milestone version of spring boot with current release 3.2.5
Also replaces ParalllelGC with default one, i.e. G1GC

* Update spring-boot to v3.3.0

* Update to java 21

* Enable virtual threads

* Fix excessive number of queries in JPA tests
... query and follow up update statement must be enclosed in a transaction
Martin Konôpka 1 year ago
parent
commit
bba72a8b14
25 changed files with 171 additions and 228 deletions
  1. 5 10
      frameworks/Java/spring-webflux/benchmark_config.json
  2. 10 78
      frameworks/Java/spring-webflux/pom.xml
  3. 3 3
      frameworks/Java/spring-webflux/spring-webflux-jdbc.dockerfile
  4. 3 3
      frameworks/Java/spring-webflux/spring-webflux-mongo.dockerfile
  5. 3 3
      frameworks/Java/spring-webflux/spring-webflux-pgclient.dockerfile
  6. 3 3
      frameworks/Java/spring-webflux/spring-webflux-rxjdbc.dockerfile
  7. 3 3
      frameworks/Java/spring-webflux/spring-webflux.dockerfile
  8. 7 8
      frameworks/Java/spring-webflux/src/main/java/benchmark/App.java
  9. 1 1
      frameworks/Java/spring-webflux/src/main/java/benchmark/config/JdbcConfig.java
  10. 2 2
      frameworks/Java/spring-webflux/src/main/java/benchmark/config/PgClientConfig.java
  11. 3 3
      frameworks/Java/spring-webflux/src/main/java/benchmark/config/R2dbcConfig.java
  12. 2 1
      frameworks/Java/spring-webflux/src/main/java/benchmark/config/ReactiveMongoConfig.java
  13. 2 6
      frameworks/Java/spring-webflux/src/main/java/benchmark/config/RxJdbcConfig.java
  14. 6 8
      frameworks/Java/spring-webflux/src/main/java/benchmark/repository/R2dbcDbRepository.java
  15. 1 1
      frameworks/Java/spring-webflux/src/main/java/benchmark/web/WebfluxRouter.java
  16. 42 6
      frameworks/Java/spring-webflux/src/main/resources/application.yml
  17. 1 2
      frameworks/Java/spring/pom.xml
  18. 2 21
      frameworks/Java/spring/spring-jpa.dockerfile
  19. 2 21
      frameworks/Java/spring/spring-mongo.dockerfile
  20. 3 22
      frameworks/Java/spring/spring.dockerfile
  21. 1 1
      frameworks/Java/spring/src/main/java/hello/App.java
  22. 9 0
      frameworks/Java/spring/src/main/java/hello/UpdateWorldService.java
  23. 42 0
      frameworks/Java/spring/src/main/java/hello/UpdateWorldServiceImpl.java
  24. 10 21
      frameworks/Java/spring/src/main/java/hello/controller/HelloController.java
  25. 5 1
      frameworks/Java/spring/src/main/resources/application.yml

+ 5 - 10
frameworks/Java/spring-webflux/benchmark_config.json

@@ -20,8 +20,7 @@
       "database_os": "Linux",
       "display_name": "spring-webflux-r2dbc",
       "notes": "",
-      "versus": "spring",
-      "tags": ["broken"]
+      "versus": "spring"
     },
     "mongo": {
       "db_url": "/db",
@@ -41,8 +40,7 @@
       "database_os": "Linux",
       "display_name": "spring-webflux-mongo",
       "notes": "",
-      "versus": "spring",
-      "tags": ["broken"]
+      "versus": "spring"
     },
     "pgclient": {
       "db_url": "/db",
@@ -63,8 +61,7 @@
       "database_os": "Linux",
       "display_name": "spring-webflux-pgclient",
       "notes": "",
-      "versus": "spring",
-      "tags": ["broken"]
+      "versus": "spring"
     },
     "rxjdbc": {
       "db_url": "/db",
@@ -85,8 +82,7 @@
       "database_os": "Linux",
       "display_name": "spring-webflux-rxjdbc",
       "notes": "",
-      "versus": "spring",
-      "tags": ["broken"]
+      "versus": "spring"
     },
     "jdbc": {
       "db_url": "/db",
@@ -107,8 +103,7 @@
       "database_os": "Linux",
       "display_name": "spring-webflux-jdbc",
       "notes": "",
-      "versus": "spring",
-      "tags": ["broken"]
+      "versus": "spring"
     }
   }]
 }

+ 10 - 78
frameworks/Java/spring-webflux/pom.xml

@@ -13,19 +13,14 @@
     <parent>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-parent</artifactId>
-        <version>2.2.0.M2</version>
+        <version>3.3.0</version>
     </parent>
 
     <properties>
-        <maven.compiler.source>11</maven.compiler.source>
-        <maven.compiler.target>11</maven.compiler.target>
+        <java.version>17</java.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-        <spring-data-r2dbc.version>1.0.0.M2</spring-data-r2dbc.version>
-        <postgresql.version>42.7.2</postgresql.version>
         <pgclient.version>0.11.4</pgclient.version>
-        <rxjava2-jdbc.version>0.2.4</rxjava2-jdbc.version>
-        <r2dbc-postgresql.version>1.0.0.M7</r2dbc-postgresql.version>
-        <r2dbc-pool.version>1.0.0.BUILD-SNAPSHOT</r2dbc-pool.version>
+        <rxjava2-jdbc.version>0.2.14</rxjava2-jdbc.version>
     </properties>
 
     <dependencies>
@@ -48,7 +43,6 @@
         <dependency>
             <groupId>org.postgresql</groupId>
             <artifactId>postgresql</artifactId>
-            <version>${postgresql.version}</version>
         </dependency>
         <dependency>
             <groupId>io.reactiverse</groupId>
@@ -61,86 +55,25 @@
             <version>${rxjava2-jdbc.version}</version>
         </dependency>
 
-        <dependency>
-            <groupId>io.r2dbc</groupId>
-            <artifactId>r2dbc-postgresql</artifactId>
-            <version>${r2dbc-postgresql.version}</version>
-        </dependency>
+		<dependency>
+		    <groupId>org.postgresql</groupId>
+		    <artifactId>r2dbc-postgresql</artifactId>
+		</dependency>
         <dependency>
             <groupId>io.r2dbc</groupId>
             <artifactId>r2dbc-pool</artifactId>
-            <version>${r2dbc-pool.version}</version>
         </dependency>
         <dependency>
             <groupId>org.springframework.data</groupId>
             <artifactId>spring-data-r2dbc</artifactId>
-            <version>${spring-data-r2dbc.version}</version>
-<!-- Exclude Spring framework 2.2.0.M1 and use 2.2.0.M2 -->
-            <exclusions>
-				<exclusion>
-					<groupId>org.springframework</groupId>
-					<artifactId>spring-tx</artifactId>
-				</exclusion>
-				<exclusion>
-					<groupId>org.springframework</groupId>
-					<artifactId>spring-context</artifactId>
-				</exclusion>
-				<exclusion>
-					<groupId>org.springframework</groupId>
-					<artifactId>spring-core</artifactId>
-				</exclusion>
-				<exclusion>
-					<groupId>org.springframework</groupId>
-					<artifactId>spring-beans</artifactId>
-				</exclusion>
-            </exclusions>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework</groupId>
-            <artifactId>spring-tx</artifactId>
-            <version>5.2.0.M2</version>
         </dependency>
         <dependency>
-            <groupId>org.springframework</groupId>
-            <artifactId>spring-context</artifactId>
-            <version>5.2.22.BUILD-SNAPSHOT</version>
+        	<groupId>org.springframework.boot</groupId>
+        	<artifactId>spring-boot-configuration-processor</artifactId>
+        	<optional>true</optional>
         </dependency>
-        <dependency>
-            <groupId>org.springframework</groupId>
-            <artifactId>spring-core</artifactId>
-            <version>5.2.24.RELEASE</version>
-        </dependency>
-        <dependency>
-            <groupId>org.springframework</groupId>
-            <artifactId>spring-beans</artifactId>
-            <version>5.2.22.BUILD-SNAPSHOT</version>
-        </dependency>
-<!-- End of 2.2.0.M1 and 2.2.0.M2 dependency preparations -->
     </dependencies>
 
-    <repositories>
-        <repository>
-            <id>spring-libs-snapshot</id>
-            <name>Spring Snapshots</name>
-            <url>https://repo.spring.io/libs-snapshot</url>
-        </repository>
-        <repository>
-            <id>spring-snapshots</id>
-            <name>Spring Snapshots</name>
-            <url>https://repo.spring.io/snapshot</url>
-            <snapshots>
-                <enabled>true</enabled>
-            </snapshots>
-        </repository>
-    </repositories>
-    <pluginRepositories>
-        <pluginRepository>
-            <id>spring-libs-snapshot</id>
-            <name>Spring Snapshots</name>
-            <url>https://repo.spring.io/libs-snapshot</url>
-        </pluginRepository>
-    </pluginRepositories>
-
     <build>
         <finalName>${project.artifactId}</finalName>
         <plugins>
@@ -151,7 +84,6 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
-                <version>3.8.0</version>
                 <configuration>
                     <debug>false</debug>
                 </configuration>

+ 3 - 3
frameworks/Java/spring-webflux/spring-webflux-jdbc.dockerfile

@@ -1,13 +1,13 @@
-FROM maven:3.8.5-openjdk-17-slim as maven
+FROM maven:3.9.6-eclipse-temurin-21 as maven
 WORKDIR /spring
 COPY src src
 COPY pom.xml pom.xml
 RUN mvn package -q
 
-FROM openjdk:17.0-jdk-slim
+FROM eclipse-temurin:21.0.3_9-jre-jammy
 WORKDIR /spring
 COPY --from=maven /spring/target/spring-webflux-benchmark.jar app.jar
 
 EXPOSE 8080
 
-CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-Dlogging.level.root=OFF", "-jar", "app.jar", "--spring.profiles.active=jdbc,postgres"]
+CMD ["java", "-server", "-XX:+UseNUMA", "-Dlogging.level.root=OFF", "-jar", "app.jar", "--spring.profiles.active=jdbc,postgres"]

+ 3 - 3
frameworks/Java/spring-webflux/spring-webflux-mongo.dockerfile

@@ -1,13 +1,13 @@
-FROM maven:3.8.5-openjdk-17-slim as maven
+FROM maven:3.9.6-eclipse-temurin-21 as maven
 WORKDIR /spring
 COPY src src
 COPY pom.xml pom.xml
 RUN mvn package -q
 
-FROM openjdk:17.0-jdk-slim
+FROM eclipse-temurin:21.0.3_9-jre-jammy
 WORKDIR /spring
 COPY --from=maven /spring/target/spring-webflux-benchmark.jar app.jar
 
 EXPOSE 8080
 
-CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-Dlogging.level.root=OFF", "-jar", "app.jar", "--spring.profiles.active=mongo"]
+CMD ["java", "-server", "-XX:+UseNUMA", "-Dlogging.level.root=OFF", "-jar", "app.jar", "--spring.profiles.active=mongo"]

+ 3 - 3
frameworks/Java/spring-webflux/spring-webflux-pgclient.dockerfile

@@ -1,13 +1,13 @@
-FROM maven:3.8.5-openjdk-17-slim as maven
+FROM maven:3.9.6-eclipse-temurin-21 as maven
 WORKDIR /spring
 COPY src src
 COPY pom.xml pom.xml
 RUN mvn package -q
 
-FROM openjdk:17.0-jdk-slim
+FROM eclipse-temurin:21.0.3_9-jre-jammy
 WORKDIR /spring
 COPY --from=maven /spring/target/spring-webflux-benchmark.jar app.jar
 
 EXPOSE 8080
 
-CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-Dlogging.level.root=OFF", "-jar", "app.jar", "--spring.profiles.active=pgclient,postgres"]
+CMD ["java", "-server", "-XX:+UseNUMA", "-Dlogging.level.root=OFF", "-jar", "app.jar", "--spring.profiles.active=pgclient,postgres"]

+ 3 - 3
frameworks/Java/spring-webflux/spring-webflux-rxjdbc.dockerfile

@@ -1,13 +1,13 @@
-FROM maven:3.8.5-openjdk-17-slim as maven
+FROM maven:3.9.6-eclipse-temurin-21 as maven
 WORKDIR /spring
 COPY src src
 COPY pom.xml pom.xml
 RUN mvn package -q
 
-FROM openjdk:17.0-jdk-slim
+FROM eclipse-temurin:21.0.3_9-jre-jammy
 WORKDIR /spring
 COPY --from=maven /spring/target/spring-webflux-benchmark.jar app.jar
 
 EXPOSE 8080
 
-CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-Dlogging.level.root=OFF", "-jar", "app.jar", "--spring.profiles.active=rxjdbc,postgres"]
+CMD ["java", "-server", "-XX:+UseNUMA", "-Dlogging.level.root=OFF", "-jar", "app.jar", "--spring.profiles.active=rxjdbc,postgres"]

+ 3 - 3
frameworks/Java/spring-webflux/spring-webflux.dockerfile

@@ -1,13 +1,13 @@
-FROM maven:3.8.5-openjdk-17-slim as maven
+FROM maven:3.9.6-eclipse-temurin-21 as maven
 WORKDIR /spring
 COPY src src
 COPY pom.xml pom.xml
 RUN mvn package -q
 
-FROM openjdk:17.0-jdk-slim
+FROM eclipse-temurin:21.0.3_9-jre-jammy
 WORKDIR /spring
 COPY --from=maven /spring/target/spring-webflux-benchmark.jar app.jar
 
 EXPOSE 8080
 
-CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-Dlogging.level.root=OFF", "-jar", "app.jar", "--spring.profiles.active=r2dbc,postgres"]
+CMD ["java", "-server", "-XX:+UseNUMA", "-Dlogging.level.root=OFF", "-jar", "app.jar", "--spring.profiles.active=r2dbc,postgres"]

+ 7 - 8
frameworks/Java/spring-webflux/src/main/java/benchmark/App.java

@@ -1,10 +1,10 @@
 package benchmark;
 
+import java.util.concurrent.Executors;
+
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration;
-import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.boot.web.reactive.result.view.MustacheViewResolver;
 import org.springframework.context.annotation.Bean;
@@ -12,12 +12,11 @@ import org.springframework.scheduling.annotation.EnableScheduling;
 import org.springframework.web.reactive.config.EnableWebFlux;
 import org.springframework.web.reactive.config.ViewResolverRegistry;
 import org.springframework.web.reactive.config.WebFluxConfigurer;
+
 import reactor.core.scheduler.Scheduler;
 import reactor.core.scheduler.Schedulers;
 
-import java.util.concurrent.Executors;
-
-@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, MongoReactiveDataAutoConfiguration.class })
+@SpringBootApplication
 @EnableWebFlux
 @EnableScheduling
 @EnableConfigurationProperties
@@ -31,17 +30,17 @@ public class App implements WebFluxConfigurer {
     }
 
     @Bean
-    public ServerFilter serverFilter() {
+    ServerFilter serverFilter() {
         return new ServerFilter();
     }
 
     @Bean
-    public DateHandler dateHandler() {
+    DateHandler dateHandler() {
         return new DateHandler();
     }
 
     @Bean
-    public Scheduler ioScheduler() {
+    Scheduler ioScheduler() {
         return Schedulers.fromExecutor(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2));
     }
 

+ 1 - 1
frameworks/Java/spring-webflux/src/main/java/benchmark/config/JdbcConfig.java

@@ -13,7 +13,7 @@ import javax.sql.DataSource;
 public class JdbcConfig {
 
     @Bean
-    public DataSource datasource(DataSourceProperties dataSourceProperties) {
+    DataSource datasource(DataSourceProperties dataSourceProperties) {
         HikariDataSource dataSource = dataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
         dataSource.setMaximumPoolSize(Runtime.getRuntime().availableProcessors() * 2);
 

+ 2 - 2
frameworks/Java/spring-webflux/src/main/java/benchmark/config/PgClientConfig.java

@@ -24,12 +24,12 @@ public class PgClientConfig {
     private String password;
 
     @Bean
-    public Vertx vertx() {
+    Vertx vertx() {
         return Vertx.vertx();
     }
 
     @Bean
-    public PgClients pgClients(Vertx vertx) {
+    PgClients pgClients(Vertx vertx) {
         List<PgClient> clients = new ArrayList<>();
 
         for (int i = 0; i < Runtime.getRuntime().availableProcessors(); i++) {

+ 3 - 3
frameworks/Java/spring-webflux/src/main/java/benchmark/config/R2dbcConfig.java

@@ -9,7 +9,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Profile;
-import org.springframework.data.r2dbc.core.DatabaseClient;
+import org.springframework.r2dbc.core.DatabaseClient;
 
 import static io.r2dbc.spi.ConnectionFactoryOptions.*;
 
@@ -44,7 +44,7 @@ public class R2dbcConfig {
     }
 
     @Bean
-    public ConnectionFactory connectionFactory() {
+    ConnectionFactory connectionFactory() {
         return ConnectionFactories.get(ConnectionFactoryOptions.builder()
                 .option(DRIVER,"pool")
                 .option(PROTOCOL,"postgresql")
@@ -57,7 +57,7 @@ public class R2dbcConfig {
     }
 
     @Bean
-    public DatabaseClient databaseClient(ConnectionFactory connectionFactory) {
+    DatabaseClient databaseClient(ConnectionFactory connectionFactory) {
         return DatabaseClient.create(connectionFactory);
     }
 }

+ 2 - 1
frameworks/Java/spring-webflux/src/main/java/benchmark/config/ReactiveMongoConfig.java

@@ -19,6 +19,7 @@ public class ReactiveMongoConfig extends AbstractReactiveMongoConfiguration {
     private String url;
     private String name;
 
+    @Override
     @Bean
     public MongoClient reactiveMongoClient() {
         LoggerFactory.getLogger(getClass()).info("Connecting to mongo url: {}/{}", url, name);
@@ -31,7 +32,7 @@ public class ReactiveMongoConfig extends AbstractReactiveMongoConfiguration {
     }
 
     @Bean
-    public ReactiveMongoTemplate reactiveMongoTemplate() {
+    ReactiveMongoTemplate reactiveMongoTemplate() {
         return new ReactiveMongoTemplate(reactiveMongoClient(), getDatabaseName());
     }
 

+ 2 - 6
frameworks/Java/spring-webflux/src/main/java/benchmark/config/RxJdbcConfig.java

@@ -9,21 +9,17 @@ import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Profile;
 
-import java.sql.SQLException;
-
 @Configuration
 @Profile("rxjdbc")
 public class RxJdbcConfig {
     @Bean
-    public Database database(DataSourceProperties dsProps) throws SQLException {
+    Database database(DataSourceProperties dsProps) {
         NonBlockingConnectionPool pool =
                 Pools.nonBlocking()
                         .maxPoolSize(Runtime.getRuntime().availableProcessors() * 2)
                         .connectionProvider(ConnectionProvider.from(dsProps.getUrl(), dsProps.getUsername(), dsProps.getPassword()))
                         .build();
 
-        Database db = Database.from(pool);
-
-        return db;
+        return Database.from(pool);
     }
 }

+ 6 - 8
frameworks/Java/spring-webflux/src/main/java/benchmark/repository/R2dbcDbRepository.java

@@ -3,7 +3,7 @@ package benchmark.repository;
 import benchmark.model.Fortune;
 import benchmark.model.World;
 import org.springframework.context.annotation.Profile;
-import org.springframework.data.r2dbc.core.DatabaseClient;
+import org.springframework.r2dbc.core.DatabaseClient;
 import org.springframework.stereotype.Component;
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
@@ -19,17 +19,16 @@ public class R2dbcDbRepository implements DbRepository {
 
     @Override
     public Mono<World> getWorld(int id) {
-        return databaseClient.execute()
+        return databaseClient
                 .sql("SELECT id, randomnumber FROM world WHERE id = $1")
                 .bind("$1", id)
-                .as(World.class)
-                .fetch()
+                .map((row, rowMetaData) -> new World(row.get("id", Integer.class), row.get("randomnumber", Integer.class)))
                 .first();
 
     }
 
     public Mono<World> updateWorld(World world) {
-        return databaseClient.execute()
+        return databaseClient
                 .sql("UPDATE world SET randomnumber=$2 WHERE id = $1")
                 .bind("$1", world.id)
                 .bind("$2", world.randomnumber)
@@ -47,10 +46,9 @@ public class R2dbcDbRepository implements DbRepository {
 
     @Override
     public Flux<Fortune> fortunes() {
-        return databaseClient.execute()
+        return databaseClient
                 .sql("SELECT id, message FROM fortune")
-                .as(Fortune.class)
-                .fetch()
+                .map((row, rowMetaData) -> new Fortune(row.get("id", Integer.class), row.get("message", String.class)))
                 .all();
     }
 }

+ 1 - 1
frameworks/Java/spring-webflux/src/main/java/benchmark/web/WebfluxRouter.java

@@ -12,7 +12,7 @@ import static org.springframework.web.reactive.function.server.RequestPredicates
 public class WebfluxRouter {
 
     @Bean
-    public RouterFunction<ServerResponse> route(WebfluxHandler handler) {
+    RouterFunction<ServerResponse> route(WebfluxHandler handler) {
         return RouterFunctions
                 .route(
                         GET("/plaintext"),

+ 42 - 6
frameworks/Java/spring-webflux/src/main/resources/application.yml

@@ -1,6 +1,12 @@
+spring:
+  jpa:
+    open-in-view: false
+
 ---
 spring:
-  profiles: postgres
+  config:
+    activate:
+      on-profile: postgres
   datasource:
     url: jdbc:postgresql://${database.host}:${database.port}/${database.name}
     username: ${database.username}
@@ -15,23 +21,53 @@ database:
 
 ---
 spring:
-  profiles: jdbc
+  config:
+    activate:
+      on-profile: jdbc
+  autoconfigure:
+    exclude:
+    - org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration
+    - org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration 
 
 ---
 spring:
-  profiles: pgclient
+  config:
+    activate:
+      on-profile: pgclient
+  autoconfigure:
+    exclude:
+    - org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration
+    - org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration 
 
 ---
 spring:
-  profiles: rxjdbc
+  config:
+    activate:
+      on-profile: rxjdbc
+  autoconfigure:
+    exclude:
+    - org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration
+    - org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration 
 
 ---
 spring:
-  profiles: r2dbc
+  config:
+    activate:
+      on-profile: r2dbc
+  autoconfigure:
+    exclude:
+    - org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration
+    - org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration 
 
 ---
 spring:
-  profiles: mongo
+  config:
+    activate:
+      on-profile: mongo
+  autoconfigure:
+    exclude:
+    - org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration 
+    - org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration 
 
 database:
   url: mongodb://tfb-database:27017/?waitQueueMultiple=200

+ 1 - 2
frameworks/Java/spring/pom.xml

@@ -11,12 +11,11 @@
 	<parent>
 		<groupId>org.springframework.boot</groupId>
 		<artifactId>spring-boot-starter-parent</artifactId>
-		<version>3.0.0</version>
+		<version>3.3.0</version>
 	</parent>
 
 	<properties>
 		<java.version>17</java.version>
-		<postgresql.version>42.5.1</postgresql.version>
 	</properties>
 
 	<dependencies>

+ 2 - 21
frameworks/Java/spring/spring-jpa.dockerfile

@@ -1,30 +1,11 @@
-FROM eclipse-temurin:17 as jre-build
-
-# Create a custom Java runtime
-RUN $JAVA_HOME/bin/jlink \
-         --add-modules ALL-MODULE-PATH \
-         --strip-debug \
-         --no-man-pages \
-         --no-header-files \
-         --compress=2 \
-         --output /javaruntime
-
-FROM maven:3.8.5-openjdk-17-slim as maven
-ENV JAVA_HOME=/opt/java/openjdk
-ENV PATH "${JAVA_HOME}/bin:${PATH}"
-COPY --from=jre-build /javaruntime $JAVA_HOME
-
+FROM maven:3.9.6-eclipse-temurin-21 as maven
 RUN mvn -version
 WORKDIR /spring
 COPY src src
 COPY pom.xml pom.xml
 RUN mvn package -q
 
-FROM debian:bullseye-slim
-ENV JAVA_HOME=/opt/java/openjdk
-ENV PATH "${JAVA_HOME}/bin:${PATH}"
-COPY --from=jre-build /javaruntime $JAVA_HOME
-
+FROM eclipse-temurin:21.0.3_9-jre-jammy
 RUN java -version
 WORKDIR /spring
 COPY --from=maven /spring/target/hello-spring-1.0-SNAPSHOT.jar app.jar

+ 2 - 21
frameworks/Java/spring/spring-mongo.dockerfile

@@ -1,30 +1,11 @@
-FROM eclipse-temurin:17 as jre-build
-
-# Create a custom Java runtime
-RUN $JAVA_HOME/bin/jlink \
-         --add-modules ALL-MODULE-PATH \
-         --strip-debug \
-         --no-man-pages \
-         --no-header-files \
-         --compress=2 \
-         --output /javaruntime
-
-FROM maven:3.8.5-openjdk-17-slim as maven
-ENV JAVA_HOME=/opt/java/openjdk
-ENV PATH "${JAVA_HOME}/bin:${PATH}"
-COPY --from=jre-build /javaruntime $JAVA_HOME
-
+FROM maven:3.9.6-eclipse-temurin-21 as maven
 RUN mvn -version
 WORKDIR /spring
 COPY src src
 COPY pom.xml pom.xml
 RUN mvn package -q
 
-FROM debian:bullseye-slim
-ENV JAVA_HOME=/opt/java/openjdk
-ENV PATH "${JAVA_HOME}/bin:${PATH}"
-COPY --from=jre-build /javaruntime $JAVA_HOME
-
+FROM eclipse-temurin:21.0.3_9-jre-jammy
 RUN java -version
 WORKDIR /spring
 COPY --from=maven /spring/target/hello-spring-1.0-SNAPSHOT.jar app.jar

+ 3 - 22
frameworks/Java/spring/spring.dockerfile

@@ -1,34 +1,15 @@
-FROM eclipse-temurin:17 as jre-build
-
-# Create a custom Java runtime
-RUN $JAVA_HOME/bin/jlink \
-         --add-modules ALL-MODULE-PATH \
-         --strip-debug \
-         --no-man-pages \
-         --no-header-files \
-         --compress=2 \
-         --output /javaruntime
-
-FROM maven:3.8.5-openjdk-17-slim as maven
-ENV JAVA_HOME=/opt/java/openjdk
-ENV PATH "${JAVA_HOME}/bin:${PATH}"
-COPY --from=jre-build /javaruntime $JAVA_HOME
-
+FROM maven:3.9.6-eclipse-temurin-21 as maven
 RUN mvn -version
 WORKDIR /spring
 COPY src src
 COPY pom.xml pom.xml
 RUN mvn package -q
 
-FROM debian:bullseye-slim
-ENV JAVA_HOME=/opt/java/openjdk
-ENV PATH "${JAVA_HOME}/bin:${PATH}"
-COPY --from=jre-build /javaruntime $JAVA_HOME
-
+FROM eclipse-temurin:21.0.3_9-jre-jammy
 RUN java -version
 WORKDIR /spring
 COPY --from=maven /spring/target/hello-spring-1.0-SNAPSHOT.jar app.jar
 
 EXPOSE 8080
 
-CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseG1GC", "-XX:+DisableExplicitGC", "-XX:+UseStringDeduplication", "-Dlogging.level.root=OFF", "-jar", "app.jar", "--spring.profiles.active=jdbc"]
+CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+DisableExplicitGC", "-XX:+UseStringDeduplication", "-Dlogging.level.root=OFF", "-jar", "app.jar", "--spring.profiles.active=jdbc"]

+ 1 - 1
frameworks/Java/spring/src/main/java/hello/App.java

@@ -28,7 +28,7 @@ public class App {
 
 	@Bean
 	@Profile({ "jdbc", "jpa" })
-	public DataSource datasource(DataSourceProperties dataSourceProperties) {
+	DataSource datasource(DataSourceProperties dataSourceProperties) {
 		HikariDataSource dataSource = dataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class)
 				.build();
 		dataSource.setMaximumPoolSize(Runtime.getRuntime().availableProcessors() * 2);

+ 9 - 0
frameworks/Java/spring/src/main/java/hello/UpdateWorldService.java

@@ -0,0 +1,9 @@
+package hello;
+
+import hello.model.World;
+
+public interface UpdateWorldService {
+
+	World updateWorld(int worldId);
+
+}

+ 42 - 0
frameworks/Java/spring/src/main/java/hello/UpdateWorldServiceImpl.java

@@ -0,0 +1,42 @@
+package hello;
+
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import hello.controller.HelloController;
+import hello.model.World;
+import hello.repository.DbRepository;
+
+@Service
+public class UpdateWorldServiceImpl implements UpdateWorldService {
+
+	private DbRepository dbRepository;
+	
+	public UpdateWorldServiceImpl(DbRepository dbRepository) {
+		this.dbRepository = dbRepository;
+	}
+
+	@Override
+	@Transactional
+	public World updateWorld(int worldId) {
+		var world = dbRepository.getWorld(worldId);
+		// Ensure that the new random number is not equal to the old one.
+		// That would cause the JPA-based implementation to avoid sending the
+		// UPDATE query to the database, which would violate the test
+		// requirements.
+
+		// Locally the records doesn't exist, maybe in the yours is ok but we need to
+		// make this check
+		if (world == null) {
+			return null;
+		}
+
+		int newRandomNumber;
+		do {
+			newRandomNumber = HelloController.randomWorldNumber();
+		} while (newRandomNumber == world.randomnumber);
+
+		return dbRepository.updateWorld(world, newRandomNumber);
+	}
+
+}

+ 10 - 21
frameworks/Java/spring/src/main/java/hello/controller/HelloController.java

@@ -13,6 +13,7 @@ import org.springframework.web.bind.annotation.ModelAttribute;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
+import hello.UpdateWorldService;
 import hello.model.Fortune;
 import hello.model.World;
 import hello.repository.DbRepository;
@@ -21,9 +22,13 @@ import hello.repository.DbRepository;
 public final class HelloController {
 
 	private DbRepository dbRepository;
+	private UpdateWorldService updateWorldService;
 
-	public HelloController(DbRepository dbRepository) {
+	public HelloController(
+			DbRepository dbRepository,
+			UpdateWorldService updateWorldService) {
 		this.dbRepository = dbRepository;
+		this.updateWorldService = updateWorldService;
 	}
 
 	@GetMapping(value = "/plaintext")
@@ -54,25 +59,9 @@ public final class HelloController {
 	@GetMapping("/updates")
 	World[] updates(HttpServletResponse response, @RequestParam(required = false) String queries) {
 		response.setContentType(MediaType.APPLICATION_JSON_VALUE);
-		return randomWorldNumbers().mapToObj(dbRepository::getWorld).map(world -> {
-			// Ensure that the new random number is not equal to the old one.
-			// That would cause the JPA-based implementation to avoid sending the
-			// UPDATE query to the database, which would violate the test
-			// requirements.
-
-			// Locally the records doesn't exist, maybe in the yours is ok but we need to
-			// make this check
-			if (world == null) {
-				return null;
-			}
-
-			int newRandomNumber;
-			do {
-				newRandomNumber = randomWorldNumber();
-			} while (newRandomNumber == world.randomnumber);
-
-			return dbRepository.updateWorld(world, newRandomNumber);
-		}).limit(parseQueryCount(queries)).toArray(World[]::new);
+		return randomWorldNumbers()
+				.mapToObj(id -> updateWorldService.updateWorld(id))
+				.limit(parseQueryCount(queries)).toArray(World[]::new);
 	}
 
 	@GetMapping("/fortunes")
@@ -89,7 +78,7 @@ public final class HelloController {
 	private static final int MIN_WORLD_NUMBER = 1;
 	private static final int MAX_WORLD_NUMBER_PLUS_ONE = 10_001;
 
-	private static int randomWorldNumber() {
+	public static int randomWorldNumber() {
 		return ThreadLocalRandom.current().nextInt(MIN_WORLD_NUMBER, MAX_WORLD_NUMBER_PLUS_ONE);
 	}
 

+ 5 - 1
frameworks/Java/spring/src/main/resources/application.yml

@@ -1,5 +1,10 @@
 ---
 spring:
+  threads:
+    virtual:
+      enabled: true
+  jpa:
+    open-in-view: false
   config:
     activate:
       on-profile: jdbc,jpa
@@ -22,7 +27,6 @@ spring:
       on-profile: jpa
   jpa:
     database-platform: org.hibernate.dialect.PostgreSQLDialect
-    open-in-view: false
 
 ---
 spring: