Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# ![RealWorld Example App](logo.png)

> ### **Java 21 + Spring Boot 3** codebase containing real-world examples (CRUD, authentication, advanced patterns, etc.) that adheres to the [RealWorld](https://github.com/gothinkster/realworld) specification and API.
> ### **Java 25 + Spring Boot 4** codebase containing real-world examples (CRUD, authentication, advanced patterns, etc.) that adheres to the [RealWorld](https://github.com/gothinkster/realworld) specification and API.

### [Demo](https://demo.realworld.io/)      [RealWorld](https://github.com/gothinkster/realworld)

This codebase demonstrates a fully-fledged fullstack application built with **Java 21 + Spring Boot 3**, including CRUD operations, authentication, routing, pagination, and more.
This codebase demonstrates a fully-fledged fullstack application built with **Java 25 + Spring Boot 4**, including CRUD operations, authentication, routing, pagination, and more.

If you want to see the API documentation related to this, please refer to https://1chz.github.io/realworld-java21-springboot3

Expand Down Expand Up @@ -65,9 +65,9 @@ This application provides the following key features:

### Project Structure

The project is implemented using Java 21 and Spring Boot 3, leveraging technologies such as Spring MVC, Spring Data JPA, and Spring Security. It uses H2 DB (in-memory, MySQL mode) as the database and JUnit 5 for testing.
The project is implemented using Java 25 and Spring Boot 4, leveraging technologies such as Spring MVC, Spring Data JPA, and Spring Security. It uses H2 DB (in-memory, MySQL mode) as the database and JUnit 5 for testing.

To run the project, ensure that JDK (or JRE) 21 is installed. Then, execute the following command in the project root directory:
To run the project, ensure that JDK (or JRE) 25 is installed. Then, execute the following command in the project root directory:

```shell
./gradlew realworld:bootRun
Expand Down Expand Up @@ -110,7 +110,7 @@ Many developers using JPA tend to use `Long` as the ID type. However, consider w

## Getting Started

> **Note:** Ensure JDK 21 is installed.
> **Note:** Ensure JDK 25 is installed.
>
> **Note:** If you encounter a permission denied error when running Gradle tasks, run `chmod +x gradlew` to grant execution permission.

Expand Down
3 changes: 2 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ allprojects {

spotless {
java {
palantirJavaFormat().formatJavadoc(true)
// https://github.com/palantir/palantir-java-format/releases/tag/2.71.0
palantirJavaFormat("2.71.0").formatJavadoc(true)

formatAnnotations()
removeUnusedImports()
Expand Down
10 changes: 4 additions & 6 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
[versions]
java = "21"
spotless = "7.0.3"
java = "25"
spotless = "7.2.1"

spring-boot = "3.3.0"
spring-dependency-management = "1.1.5"
spring-boot-p6spy = "1.9.0"
spring-boot = "4.0.0-M2"
spring-dependency-management = "1.1.7"

[libraries]
lombok = { group = "org.projectlombok", name = "lombok" }
Expand All @@ -19,7 +18,6 @@ spring-boot-starter-web = { group = "org.springframework.boot", name = "spring-b
spring-boot-starter-cache = { group = "org.springframework.boot", name = "spring-boot-starter-cache" }
spring-boot-starter-data-jpa = { group = "org.springframework.boot", name = "spring-boot-starter-data-jpa" }
spring-boot-starter-oauth2-resource-server = { group = "org.springframework.boot", name = "spring-boot-starter-oauth2-resource-server" }
spring-boot-starter-p6spy = { group = "com.github.gavlyukovskiy", name = "p6spy-spring-boot-starter", version.ref = "spring-boot-p6spy" }

cache-caffeine = { group = "com.github.ben-manes.caffeine", name = "caffeine" }

Expand Down
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
44 changes: 26 additions & 18 deletions gradlew

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 14 additions & 12 deletions gradlew.bat

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion module/core/src/main/java/io/zhc1/realworld/model/Tag.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.zhc1.realworld.model;

import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.Objects;

import jakarta.persistence.Column;
Expand All @@ -17,7 +18,7 @@
@Table(name = "tag")
@SuppressWarnings("JpaDataSourceORMInspection")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Tag {
public class Tag implements Comparable<Tag> {
@Id
@Column(length = 20)
private String name;
Expand All @@ -42,4 +43,9 @@ public boolean equals(Object o) {
public int hashCode() {
return Objects.hash(this.getName());
}

@Override
public int compareTo(Tag other) {
return Comparator.comparing(Tag::getName).compare(this, other);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ public List<ArticleDetails> getFeeds(User user, ArticleFacets facets) {
.map(UserFollow::getFollowing)
.toList();

if (following.isEmpty()) {
return List.of();
}

return articleRepository.findByAuthors(following, facets).stream()
.map(article -> articleRepository.findArticleDetails(user, article))
.toList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,6 @@ void whenGetFeedsWithEmptyFollowings_thenShouldReturnEmptyList() {
User user = new User("email", "username", "password");
ArticleFacets facets = new ArticleFacets(1, 10);
when(userRelationshipRepository.findByFollower(user)).thenReturn(List.of());
when(articleRepository.findByAuthors(List.of(), facets)).thenReturn(List.of());

// when
List<ArticleDetails> actualArticleDetailsList = sut.getFeeds(user, facets);
Expand Down
1 change: 0 additions & 1 deletion module/persistence/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ dependencies {

implementation(libs.spring.boot.starter.data.jpa)
implementation(libs.spring.boot.starter.cache)
implementation(libs.spring.boot.starter.p6spy)

implementation(libs.cache.caffeine)
}
Loading