Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
77d1db2
Initial plan
Copilot Apr 18, 2026
f3f3c8b
Bump gradle-wrapper from 9.4.0 to 9.4.1 (#336)
dependabot[bot] Apr 18, 2026
67e71d3
Bump gradle/actions from 5 to 6 (#341)
dependabot[bot] Apr 18, 2026
cdb441e
Bump actions/configure-pages from 5 to 6 (#342)
dependabot[bot] Apr 18, 2026
01e2243
Bump actions/deploy-pages from 4 to 5 (#343)
dependabot[bot] Apr 18, 2026
2f6979e
feat: upgrade to Spring Boot 4.0.5 via OpenRewrite recipe
Copilot Apr 18, 2026
d2d2f6e
Add Spring Boot 4.0 modular test dependencies to subprojects
Copilot Apr 18, 2026
079f5ab
Update test slice annotation imports for Spring Boot 4.0
Copilot Apr 18, 2026
6b792cb
fix: resolve compilation issues after Spring Boot 4.0 migration
Copilot Apr 18, 2026
8001e57
Replace deprecated JobLauncherTestUtils with JobOperatorTestUtils
Copilot Apr 18, 2026
5a97b55
Replace deprecated StepBuilder.chunk(int, PlatformTransactionManager)
Copilot Apr 19, 2026
68901f0
Add spring-boot-restclient dependency to data-domain-events
Copilot Apr 19, 2026
cd2d071
Fix three failing test modules for Spring Boot 4.0 upgrade
Copilot Apr 19, 2026
81f38bf
Update Spring Boot version references from 3.5.6 to 4.0.5
Copilot Apr 19, 2026
9b10d9f
Remove duplicate test dependencies across modules
Copilot Apr 19, 2026
9505ef9
Add copilot-setup-steps.yml with Java 25 and Gradle setup
Copilot Apr 19, 2026
d9b3c63
Fix test failures in data-mongodb-tc-data-load, data-mongodb-transact…
Copilot Apr 19, 2026
bad1833
Fix test failures in data-rest-validation and data-rest-composite-id
Copilot Apr 19, 2026
2d757ae
Fix data-redis-cache flaky test: use synchronous Redis cache writes
Copilot Apr 19, 2026
309aad1
Plan: Fix test failures in batch-rest-repository, test-rest-assured, …
Copilot Apr 19, 2026
4815e9a
Fix test failures in batch-rest-repository, test-rest-assured, and te…
Copilot Apr 19, 2026
2b8969b
Plan: Fix Jackson2RepositoryPopulatorFactoryBean deprecation warning
Copilot Apr 19, 2026
938092f
Replace deprecated Jackson2RepositoryPopulatorFactoryBean with Jackso…
Copilot Apr 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/build-and-publish-antora.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
path: build/site

- name: Setup Pages
uses: actions/configure-pages@v5
uses: actions/configure-pages@v6

- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v4
Expand All @@ -63,4 +63,4 @@ jobs:

- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
uses: actions/deploy-pages@v5
30 changes: 30 additions & 0 deletions .github/workflows/copilot-setup-steps.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: "Copilot Setup Steps"

on:
workflow_dispatch:
push:
paths:
- .github/workflows/copilot-setup-steps.yml
pull_request:
paths:
- .github/workflows/copilot-setup-steps.yml

jobs:
copilot-setup-steps:
runs-on: ubuntu-latest

permissions:
contents: read

steps:
- name: Checkout code
uses: actions/checkout@v6

- name: Set up JDK 25
uses: actions/setup-java@v5
with:
java-version: "25"
distribution: "temurin"

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v6
6 changes: 3 additions & 3 deletions .github/workflows/gradle-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ jobs:
java-version: '25'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v5
uses: gradle/actions/setup-gradle@v6
with:
add-job-summary-as-pr-comment: on-failure
build-scan-publish: true
Expand Down Expand Up @@ -98,7 +98,7 @@ jobs:
distribution: 'temurin'
java-version: 25
- name: Generate and submit dependency graph
uses: gradle/actions/dependency-submission@v5
uses: gradle/actions/dependency-submission@v6
with:
build-scan-publish: true
build-scan-terms-of-use-url: "https://gradle.com/terms-of-service"
Expand All @@ -119,7 +119,7 @@ jobs:
java-version: '25'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v5
uses: gradle/actions/setup-gradle@v6
with:
add-job-summary-as-pr-comment: on-failure
build-scan-publish: true
Expand Down
4 changes: 2 additions & 2 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Spring Data, Spring Batch, Spring Cloud, Spring Security, Spring GraphQL, and Sp
with source code in individual submodules and can be found on {url-quickref}[GitHub].

image:https://img.shields.io/badge/Java_-25-blue?style=flat-square[Static Badge]
image:https://img.shields.io/badge/Spring_Boot-3.5.6-blue?style=flat-square&logo=springboot[Spring Boot version]
image:https://img.shields.io/badge/Spring_Boot-4.0.5-blue?style=flat-square&logo=springboot[Spring Boot version]
image:https://img.shields.io/github/actions/workflow/status/rashidi/spring-boot-tutorials/gradle-build.yml?style=flat-square&logo=githubactions&color=blue[Gradle Build]
image:https://img.shields.io/github/actions/workflow/status/rashidi/spring-boot-tutorials/build-and-publish-antora.yml?style=flat-square&logo=antora&label=Antora&color=blue[Antora Site Status, link={url-docs}]
image:https://img.shields.io/sonar/coverage/rashidi_spring-boot-tutorials?server=https%3A%2F%2Fsonarcloud.io&style=flat-square&color=blue[Sonar Coverage]
Expand All @@ -28,7 +28,7 @@ and always will be, my primary goal.

With the help of https://github.com/dependabot[Dependabot], each tutorial is also kept up-to-date with the latest
dependencies. Currently, we are using Java https://adoptium.net/en-GB/temurin/releases?version=25[Temurin 25]
with https://plugins.gradle.org/plugin/org.springframework.boot/3.5.6[Spring Boot 3.5.6].
with https://plugins.gradle.org/plugin/org.springframework.boot/4.0.5[Spring Boot 4.0.5].

== Documentation

Expand Down
4 changes: 2 additions & 2 deletions batch-rest-repository/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ class UserBatchJobTests {
private final static MongoDBContainer MONGO_DB_CONTAINER = new MongoDBContainer("mongo:latest");

@Autowired
private JobLauncherTestUtils launcher;
private JobOperatorTestUtils operator;

@Autowired
private MongoOperations mongoOperations;
Expand All @@ -125,7 +125,7 @@ class UserBatchJobTests {
void launch() {

await().atMost(ofSeconds(30)).untilAsserted(() -> {
var execution = launcher.launchJob();
var execution = operator.startJob();

assertThat(execution.getExitStatus()).isEqualTo(COMPLETED);
});
Expand Down
11 changes: 6 additions & 5 deletions batch-rest-repository/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
plugins {
java
id("org.springframework.boot") version "3.5.7"
id("org.springframework.boot") version "4.0.5"
id("io.spring.dependency-management") version "1.1.7"
}

Expand All @@ -20,14 +20,15 @@ repositories {
dependencies {
implementation("org.springframework.boot:spring-boot-starter-batch")
implementation("org.springframework.boot:spring-boot-starter-data-mongodb")
implementation("com.fasterxml.jackson.core:jackson-databind")
implementation("tools.jackson.core:jackson-databind")
runtimeOnly("com.mysql:mysql-connector-j")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.boot:spring-boot-starter-jdbc")
testImplementation("org.springframework.boot:spring-boot-testcontainers")
testImplementation("org.springframework.batch:spring-batch-test")
testImplementation("org.testcontainers:junit-jupiter")
testImplementation("org.testcontainers:mongodb")
testImplementation("org.testcontainers:mysql")
testImplementation("org.testcontainers:testcontainers-junit-jupiter")
testImplementation("org.testcontainers:testcontainers-mongodb")
testImplementation("org.testcontainers:testcontainers-mysql")
}

tasks.named<Test>("test") {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,41 +1,46 @@
package zin.rashidi.boot.batch.rest.user;

import java.net.MalformedURLException;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import tools.jackson.databind.DeserializationFeature;
import tools.jackson.databind.json.JsonMapper;
import org.springframework.batch.core.job.Job;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.Step;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.item.data.MongoItemWriter;
import org.springframework.batch.item.data.builder.MongoItemWriterBuilder;
import org.springframework.batch.item.json.JacksonJsonObjectReader;
import org.springframework.batch.item.json.JsonItemReader;
import org.springframework.batch.item.json.builder.JsonItemReaderBuilder;
import org.springframework.batch.infrastructure.item.data.MongoItemWriter;
import org.springframework.batch.infrastructure.item.data.builder.MongoItemWriterBuilder;
import org.springframework.batch.infrastructure.item.json.JacksonJsonObjectReader;
import org.springframework.batch.infrastructure.item.json.JsonItemReader;
import org.springframework.batch.infrastructure.item.json.builder.JsonItemReaderBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.UrlResource;
import org.springframework.core.io.Resource;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.transaction.PlatformTransactionManager;

import com.fasterxml.jackson.databind.ObjectMapper;

/**
* @author Rashidi Zin
*/
@Configuration
class UserJobConfiguration {

private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private static final JsonMapper OBJECT_MAPPER = JsonMapper.builder()
.disable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)
.build();

private final JobRepository jobRepository;
private final PlatformTransactionManager transactionManager;
private final MongoOperations mongo;
private final Resource usersResource;

UserJobConfiguration(JobRepository jobRepository, PlatformTransactionManager transactionManager, MongoOperations mongo) {
UserJobConfiguration(JobRepository jobRepository, PlatformTransactionManager transactionManager, MongoOperations mongo,
@Value("${batch.users.resource:https://jsonplaceholder.typicode.com/users}") Resource usersResource) {
this.jobRepository = jobRepository;
this.transactionManager = transactionManager;
this.mongo = mongo;
this.usersResource = usersResource;
}

@Bean
Expand All @@ -43,23 +48,24 @@
return new JobBuilder("userJob", jobRepository).start(step()).build();
}

private Step step() throws MalformedURLException {

Check warning on line 51 in batch-rest-repository/src/main/java/zin/rashidi/boot/batch/rest/user/UserJobConfiguration.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove the declaration of thrown exception 'java.net.MalformedURLException', as it cannot be thrown from method's body.

See more on https://sonarcloud.io/project/issues?id=rashidi_spring-boot-tutorials&issues=AZ2lIvqGWySueEBatWDc&open=AZ2lIvqGWySueEBatWDc&pullRequest=344
return new StepBuilder("userStep", jobRepository)
.<User, User>chunk(10, transactionManager)
.<User, User>chunk(10)
.transactionManager(transactionManager)
.reader(reader())
.writer(writer())
.build();
}

private JsonItemReader<User> reader() throws MalformedURLException {
private JsonItemReader<User> reader() {
JacksonJsonObjectReader<User> jsonObjectReader = new JacksonJsonObjectReader<>(User.class);

jsonObjectReader.setMapper(OBJECT_MAPPER);

return new JsonItemReaderBuilder<User>()
.name("userReader")
.jsonObjectReader(jsonObjectReader)
.resource(new UrlResource("https://jsonplaceholder.typicode.com/users"))
.resource(usersResource)
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
import com.mongodb.MongoClientSettings;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.batch.core.configuration.support.DefaultBatchConfiguration;
import org.springframework.batch.test.JobLauncherTestUtils;
import org.springframework.batch.core.configuration.support.JdbcDefaultBatchConfiguration;
import org.springframework.batch.core.job.JobExecution;

Check warning on line 8 in batch-rest-repository/src/test/java/zin/rashidi/boot/batch/rest/user/UserBatchJobTests.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this unused import 'org.springframework.batch.core.job.JobExecution'.

See more on https://sonarcloud.io/project/issues?id=rashidi_spring-boot-tutorials&issues=AZ2lIvp4WySueEBatWDb&open=AZ2lIvp4WySueEBatWDb&pullRequest=344
import org.springframework.batch.test.JobOperatorTestUtils;
import org.springframework.batch.test.context.SpringBatchTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.jdbc.DataSourceBuilder;
Expand All @@ -17,18 +18,18 @@
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.jdbc.support.JdbcTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.testcontainers.containers.MongoDBContainer;
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.mongodb.MongoDBContainer;
import org.testcontainers.mysql.MySQLContainer;

import javax.sql.DataSource;

import static java.time.Duration.ofSeconds;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.batch.core.ExitStatus.COMPLETED;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.NONE;
import static org.testcontainers.shaded.org.awaitility.Awaitility.await;
import static org.awaitility.Awaitility.await;
import static zin.rashidi.boot.batch.rest.user.UserBatchJobTests.BatchTestConfiguration;
import static zin.rashidi.boot.batch.rest.user.UserBatchJobTests.MongoTestConfiguration;

Expand All @@ -37,20 +38,21 @@
*/
@Testcontainers
@SpringBatchTest
@SpringBootTest(classes = { BatchTestConfiguration.class, MongoTestConfiguration.class, UserJobConfiguration.class }, webEnvironment = NONE)
@SpringBootTest(classes = { BatchTestConfiguration.class, MongoTestConfiguration.class, UserJobConfiguration.class }, webEnvironment = NONE,
properties = "batch.users.resource=classpath:users.json")
class UserBatchJobTests {

@Container
@ServiceConnection
private final static MySQLContainer<?> MYSQL_CONTAINER = new MySQLContainer<>("mysql:lts")
private final static MySQLContainer MYSQL_CONTAINER = new MySQLContainer("mysql:lts")

Check warning on line 47 in batch-rest-repository/src/test/java/zin/rashidi/boot/batch/rest/user/UserBatchJobTests.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Reorder the modifiers to comply with the Java Language Specification.

See more on https://sonarcloud.io/project/issues?id=rashidi_spring-boot-tutorials&issues=AZ2lIvp4WySueEBatWDa&open=AZ2lIvp4WySueEBatWDa&pullRequest=344
.withInitScript("org/springframework/batch/core/schema-mysql.sql");

@Container
@ServiceConnection
private final static MongoDBContainer MONGO_DB_CONTAINER = new MongoDBContainer("mongo:latest");

@Autowired
private JobLauncherTestUtils launcher;
private JobOperatorTestUtils operator;

@Autowired
private MongoOperations mongoOperations;
Expand All @@ -60,7 +62,7 @@
void launch() {

await().atMost(ofSeconds(30)).untilAsserted(() -> {
var execution = launcher.launchJob();
var execution = operator.startJob();

assertThat(execution.getExitStatus()).isEqualTo(COMPLETED);
});
Expand All @@ -71,7 +73,7 @@
}

@TestConfiguration
static class BatchTestConfiguration extends DefaultBatchConfiguration {
static class BatchTestConfiguration extends JdbcDefaultBatchConfiguration {

@Override
protected DataSource getDataSource() {
Expand Down
12 changes: 12 additions & 0 deletions batch-rest-repository/src/test/resources/users.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[
{"id": 1, "username": "Bret"},
{"id": 2, "username": "Antonette"},
{"id": 3, "username": "Samantha"},
{"id": 4, "username": "Karianne"},
{"id": 5, "username": "Kamren"},
{"id": 6, "username": "Leopoldo_Corkery"},
{"id": 7, "username": "Elwyn.Skiles"},
{"id": 8, "username": "Maxime_Nienow"},
{"id": 9, "username": "Delphine"},
{"id": 10, "username": "Moriah.Stiedemann"}
]
4 changes: 2 additions & 2 deletions batch-skip-step/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ class UserBatchJobTests {
private final static MySQLContainer<?> MYSQL_CONTAINER = new MySQLContainer<>("mysql:lts");

@Autowired
private JobLauncherTestUtils launcher;
private JobOperatorTestUtils operator;

@Autowired
private JdbcTemplate jdbc;
Expand All @@ -135,7 +135,7 @@ class UserBatchJobTests {
void findAll() {

await().atMost(10, SECONDS).untilAsserted(() -> {
var execution = launcher.launchJob();
var execution = operator.startJob();
assertThat(execution.getExitStatus()).isEqualTo(COMPLETED);
});

Expand Down
8 changes: 4 additions & 4 deletions batch-skip-step/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
plugins {
java
id("org.springframework.boot") version "3.5.7"
id("org.springframework.boot") version "4.0.5"
id("io.spring.dependency-management") version "1.1.7"
}

Expand All @@ -20,13 +20,13 @@ repositories {
dependencies {
implementation("org.springframework.boot:spring-boot-starter-batch")
implementation("org.springframework.boot:spring-boot-starter-data-jdbc")
implementation("com.fasterxml.jackson.core:jackson-databind")
implementation("tools.jackson.core:jackson-databind")
runtimeOnly("com.mysql:mysql-connector-j")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.boot:spring-boot-testcontainers")
testImplementation("org.springframework.batch:spring-batch-test")
testImplementation("org.testcontainers:junit-jupiter")
testImplementation("org.testcontainers:mysql")
testImplementation("org.testcontainers:testcontainers-junit-jupiter")
testImplementation("org.testcontainers:testcontainers-mysql")
}

tasks.named<Test>("test") {
Expand Down
Loading
Loading