mirror of
https://github.com/spring-projects/spring-petclinic.git
synced 2025-12-27 17:37:27 +00:00
Merge pull request #2016 from mhalbritter
* pr/2016: Upgrade to Spring Boot 4.0.0-M3 Add NullAway and JSpecify annotations Upgrade to Spring Boot 4.0.0-M2 Closes gh-2016
This commit is contained in:
commit
684adfe2ba
32 changed files with 195 additions and 114 deletions
2
.github/workflows/gradle-build.yml
vendored
2
.github/workflows/gradle-build.yml
vendored
|
|
@ -15,7 +15,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
java: [ '17' ]
|
java: [ '24' ]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
|
||||||
2
.github/workflows/maven-build.yml
vendored
2
.github/workflows/maven-build.yml
vendored
|
|
@ -15,7 +15,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
java: [ '17' ]
|
java: [ '24' ]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
|
||||||
10
.mvn/jvm.config
Normal file
10
.mvn/jvm.config
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
|
||||||
|
--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED
|
||||||
|
--add-exports jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED
|
||||||
|
--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED
|
||||||
|
--add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED
|
||||||
|
--add-exports jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED
|
||||||
|
--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
|
||||||
|
--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
|
||||||
|
--add-opens jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
|
||||||
|
--add-opens jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED
|
||||||
|
|
@ -13,7 +13,8 @@ See the presentation here:
|
||||||
|
|
||||||
## Run Petclinic locally
|
## Run Petclinic locally
|
||||||
|
|
||||||
Spring Petclinic is a [Spring Boot](https://spring.io/guides/gs/spring-boot) application built using [Maven](https://spring.io/guides/gs/maven/) or [Gradle](https://spring.io/guides/gs/gradle/). You can build a JAR file and run it from the command line (it should work just as well with Java 17 or newer):
|
Spring Petclinic is a [Spring Boot](https://spring.io/guides/gs/spring-boot) application built using [Maven](https://spring.io/guides/gs/maven/) or [Gradle](https://spring.io/guides/gs/gradle/).
|
||||||
|
Java 24 or later is required for the build, but the application can run with Java 17 or newer:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/spring-projects/spring-petclinic.git
|
git clone https://github.com/spring-projects/spring-petclinic.git
|
||||||
|
|
@ -97,7 +98,7 @@ There is a `petclinic.css` in `src/main/resources/static/resources/css`. It was
|
||||||
|
|
||||||
The following items should be installed in your system:
|
The following items should be installed in your system:
|
||||||
|
|
||||||
- Java 17 or newer (full JDK, not a JRE)
|
- Java 24 or newer (full JDK, not a JRE)
|
||||||
- [Git command line tool](https://help.github.com/articles/set-up-git)
|
- [Git command line tool](https://help.github.com/articles/set-up-git)
|
||||||
- Your preferred IDE
|
- Your preferred IDE
|
||||||
- Eclipse with the m2e plugin. Note: when m2e is available, there is an m2 icon in `Help -> About` dialog. If m2e is
|
- Eclipse with the m2e plugin. Note: when m2e is available, there is an m2 icon in `Help -> About` dialog. If m2e is
|
||||||
|
|
|
||||||
27
build.gradle
27
build.gradle
|
|
@ -1,22 +1,23 @@
|
||||||
plugins {
|
plugins {
|
||||||
id 'java'
|
id 'java'
|
||||||
id 'checkstyle'
|
id 'checkstyle'
|
||||||
id 'org.springframework.boot' version '3.5.6'
|
id 'org.springframework.boot' version '4.0.0-M3'
|
||||||
id 'io.spring.dependency-management' version '1.1.7'
|
id 'io.spring.dependency-management' version '1.1.7'
|
||||||
id 'org.graalvm.buildtools.native' version '0.11.1'
|
id 'org.graalvm.buildtools.native' version '0.11.1'
|
||||||
id 'org.cyclonedx.bom' version '3.0.0'
|
id 'org.cyclonedx.bom' version '3.0.0'
|
||||||
id 'io.spring.javaformat' version '0.0.47'
|
id 'io.spring.javaformat' version '0.0.47'
|
||||||
id "io.spring.nohttp" version "0.0.11"
|
id "io.spring.nohttp" version "0.0.11"
|
||||||
|
id 'net.ltgt.errorprone' version '4.3.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
gradle.startParameter.excludedTaskNames += [ "checkFormatAot", "checkFormatAotTest" ]
|
gradle.startParameter.excludedTaskNames += [ "checkFormatAot", "checkFormatAotTest" ]
|
||||||
|
|
||||||
group = 'org.springframework.samples'
|
group = 'org.springframework.samples'
|
||||||
version = '3.5.0'
|
version = '4.0.0-SNAPSHOT'
|
||||||
|
|
||||||
java {
|
java {
|
||||||
toolchain {
|
toolchain {
|
||||||
languageVersion = JavaLanguageVersion.of(17)
|
languageVersion = JavaLanguageVersion.of(24)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -29,6 +30,8 @@ ext.springJavaformatCheckstyleVersion = "0.0.47"
|
||||||
ext.webjarsLocatorLiteVersion = "1.1.1"
|
ext.webjarsLocatorLiteVersion = "1.1.1"
|
||||||
ext.webjarsFontawesomeVersion = "4.7.0"
|
ext.webjarsFontawesomeVersion = "4.7.0"
|
||||||
ext.webjarsBootstrapVersion = "5.3.8"
|
ext.webjarsBootstrapVersion = "5.3.8"
|
||||||
|
ext.errorProneVersion = "2.42.0"
|
||||||
|
ext.nullAwayVersion = "0.12.10"
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'org.springframework.boot:spring-boot-starter-cache'
|
implementation 'org.springframework.boot:spring-boot-starter-cache'
|
||||||
|
|
@ -48,12 +51,15 @@ dependencies {
|
||||||
runtimeOnly 'org.postgresql:postgresql'
|
runtimeOnly 'org.postgresql:postgresql'
|
||||||
developmentOnly 'org.springframework.boot:spring-boot-devtools'
|
developmentOnly 'org.springframework.boot:spring-boot-devtools'
|
||||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||||
|
testImplementation 'org.springframework.boot:spring-boot-starter-restclient'
|
||||||
testImplementation 'org.springframework.boot:spring-boot-testcontainers'
|
testImplementation 'org.springframework.boot:spring-boot-testcontainers'
|
||||||
testImplementation 'org.springframework.boot:spring-boot-docker-compose'
|
testImplementation 'org.springframework.boot:spring-boot-docker-compose'
|
||||||
testImplementation 'org.testcontainers:junit-jupiter'
|
testImplementation 'org.testcontainers:junit-jupiter'
|
||||||
testImplementation 'org.testcontainers:mysql'
|
testImplementation 'org.testcontainers:mysql'
|
||||||
checkstyle "io.spring.javaformat:spring-javaformat-checkstyle:${springJavaformatCheckstyleVersion}"
|
checkstyle "io.spring.javaformat:spring-javaformat-checkstyle:${springJavaformatCheckstyleVersion}"
|
||||||
checkstyle "com.puppycrawl.tools:checkstyle:${checkstyleVersion}"
|
checkstyle "com.puppycrawl.tools:checkstyle:${checkstyleVersion}"
|
||||||
|
errorprone "com.google.errorprone:error_prone_core:${errorProneVersion}"
|
||||||
|
errorprone "com.uber.nullaway:nullaway:${nullAwayVersion}"
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named('test') {
|
tasks.named('test') {
|
||||||
|
|
@ -70,6 +76,21 @@ checkstyleNohttp {
|
||||||
configFile = file('src/checkstyle/nohttp-checkstyle.xml')
|
configFile = file('src/checkstyle/nohttp-checkstyle.xml')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.withType(JavaCompile).configureEach {
|
||||||
|
options.release = 17
|
||||||
|
options.errorprone {
|
||||||
|
disableAllChecks = true
|
||||||
|
}
|
||||||
|
if (name.equals("compileJava")) {
|
||||||
|
options.errorprone {
|
||||||
|
error("NullAway")
|
||||||
|
option("NullAway:OnlyNullMarked", "true")
|
||||||
|
option("NullAway:CustomContractAnnotations", "org.springframework.lang.Contract")
|
||||||
|
option("NullAway:JSpecifyMode", "true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tasks.named("formatMain").configure { dependsOn("checkstyleMain") }
|
tasks.named("formatMain").configure { dependsOn("checkstyleMain") }
|
||||||
tasks.named("formatMain").configure { dependsOn("checkstyleNohttp") }
|
tasks.named("formatMain").configure { dependsOn("checkstyleNohttp") }
|
||||||
|
|
||||||
|
|
|
||||||
84
pom.xml
84
pom.xml
|
|
@ -5,20 +5,21 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-parent</artifactId>
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
<version>3.5.6</version>
|
<version>4.0.0-M3</version>
|
||||||
<relativePath></relativePath>
|
<relativePath></relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<groupId>org.springframework.samples</groupId>
|
<groupId>org.springframework.samples</groupId>
|
||||||
<artifactId>spring-petclinic</artifactId>
|
<artifactId>spring-petclinic</artifactId>
|
||||||
<version>3.5.0-SNAPSHOT</version>
|
<version>4.0.0-SNAPSHOT</version>
|
||||||
|
|
||||||
<name>petclinic</name>
|
<name>petclinic</name>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
|
||||||
<!-- Generic properties -->
|
<!-- Generic properties -->
|
||||||
<java.version>17</java.version>
|
<java.version>24</java.version>
|
||||||
|
<maven.compiler.release>17</maven.compiler.release>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
<!-- Important for reproducible builds. Update using e.g. ./mvnw versions:set -DnewVersion=... -->
|
<!-- Important for reproducible builds. Update using e.g. ./mvnw versions:set -DnewVersion=... -->
|
||||||
|
|
@ -30,13 +31,14 @@
|
||||||
<webjars-font-awesome.version>4.7.0</webjars-font-awesome.version>
|
<webjars-font-awesome.version>4.7.0</webjars-font-awesome.version>
|
||||||
|
|
||||||
<checkstyle.version>11.1.0</checkstyle.version>
|
<checkstyle.version>11.1.0</checkstyle.version>
|
||||||
|
<error-prone.version>2.42.0</error-prone.version>
|
||||||
<jacoco.version>0.8.13</jacoco.version>
|
<jacoco.version>0.8.13</jacoco.version>
|
||||||
<libsass.version>0.3.4</libsass.version>
|
<libsass.version>0.3.4</libsass.version>
|
||||||
<lifecycle-mapping>1.0.0</lifecycle-mapping>
|
<lifecycle-mapping>1.0.0</lifecycle-mapping>
|
||||||
<maven-checkstyle.version>3.6.0</maven-checkstyle.version>
|
<maven-checkstyle.version>3.6.0</maven-checkstyle.version>
|
||||||
<nohttp-checkstyle.version>0.0.11</nohttp-checkstyle.version>
|
<nohttp-checkstyle.version>0.0.11</nohttp-checkstyle.version>
|
||||||
|
<nullaway.version>0.12.10</nullaway.version>
|
||||||
<spring-format.version>0.0.47</spring-format.version>
|
<spring-format.version>0.0.47</spring-format.version>
|
||||||
|
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
@ -70,6 +72,11 @@
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-restclient</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Databases - Uses H2 by default -->
|
<!-- Databases - Uses H2 by default -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
@ -271,6 +278,38 @@
|
||||||
<failOnUnableToExtractRepoInfo>false</failOnUnableToExtractRepoInfo>
|
<failOnUnableToExtractRepoInfo>false</failOnUnableToExtractRepoInfo>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>default-compile</id>
|
||||||
|
<phase>compile</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>compile</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<compilerArgs>
|
||||||
|
<arg>-XDcompilePolicy=simple</arg>
|
||||||
|
<arg>--should-stop=ifError=FLOW</arg>
|
||||||
|
<arg>-Xplugin:ErrorProne -XepDisableAllChecks -Xep:NullAway:ERROR -XepOpt:NullAway:OnlyNullMarked=true -XepOpt:NullAway:CustomContractAnnotations=org.springframework.lang.Contract -XepOpt:NullAway:JSpecifyMode=true</arg>
|
||||||
|
</compilerArgs>
|
||||||
|
<annotationProcessorPaths>
|
||||||
|
<path>
|
||||||
|
<groupId>com.google.errorprone</groupId>
|
||||||
|
<artifactId>error_prone_core</artifactId>
|
||||||
|
<version>${error-prone.version}</version>
|
||||||
|
</path>
|
||||||
|
<path>
|
||||||
|
<groupId>com.uber.nullaway</groupId>
|
||||||
|
<artifactId>nullaway</artifactId>
|
||||||
|
<version>${nullaway.version}</version>
|
||||||
|
</path>
|
||||||
|
</annotationProcessorPaths>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
<!-- Spring Boot Actuator displays sbom-related information if a CycloneDX SBOM file is
|
<!-- Spring Boot Actuator displays sbom-related information if a CycloneDX SBOM file is
|
||||||
present at the classpath -->
|
present at the classpath -->
|
||||||
<plugin>
|
<plugin>
|
||||||
|
|
@ -288,43 +327,6 @@
|
||||||
</license>
|
</license>
|
||||||
</licenses>
|
</licenses>
|
||||||
|
|
||||||
<repositories>
|
|
||||||
<repository>
|
|
||||||
<snapshots>
|
|
||||||
<enabled>true</enabled>
|
|
||||||
</snapshots>
|
|
||||||
<id>spring-snapshots</id>
|
|
||||||
<name>Spring Snapshots</name>
|
|
||||||
<url>https://repo.spring.io/snapshot</url>
|
|
||||||
</repository>
|
|
||||||
<repository>
|
|
||||||
<snapshots>
|
|
||||||
<enabled>false</enabled>
|
|
||||||
</snapshots>
|
|
||||||
<id>spring-milestones</id>
|
|
||||||
<name>Spring Milestones</name>
|
|
||||||
<url>https://repo.spring.io/milestone</url>
|
|
||||||
</repository>
|
|
||||||
</repositories>
|
|
||||||
<pluginRepositories>
|
|
||||||
<pluginRepository>
|
|
||||||
<snapshots>
|
|
||||||
<enabled>true</enabled>
|
|
||||||
</snapshots>
|
|
||||||
<id>spring-snapshots</id>
|
|
||||||
<name>Spring Snapshots</name>
|
|
||||||
<url>https://repo.spring.io/snapshot</url>
|
|
||||||
</pluginRepository>
|
|
||||||
<pluginRepository>
|
|
||||||
<snapshots>
|
|
||||||
<enabled>false</enabled>
|
|
||||||
</snapshots>
|
|
||||||
<id>spring-milestones</id>
|
|
||||||
<name>Spring Milestones</name>
|
|
||||||
<url>https://repo.spring.io/milestone</url>
|
|
||||||
</pluginRepository>
|
|
||||||
</pluginRepositories>
|
|
||||||
|
|
||||||
<profiles>
|
<profiles>
|
||||||
<profile>
|
<profile>
|
||||||
<id>css</id>
|
<id>css</id>
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package org.springframework.samples.petclinic;
|
package org.springframework.samples.petclinic;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.aot.hint.RuntimeHints;
|
import org.springframework.aot.hint.RuntimeHints;
|
||||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||||
import org.springframework.samples.petclinic.model.BaseEntity;
|
import org.springframework.samples.petclinic.model.BaseEntity;
|
||||||
|
|
@ -25,7 +27,7 @@ import org.springframework.samples.petclinic.vet.Vet;
|
||||||
public class PetClinicRuntimeHints implements RuntimeHintsRegistrar {
|
public class PetClinicRuntimeHints implements RuntimeHintsRegistrar {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||||
hints.resources().registerPattern("db/*"); // https://github.com/spring-projects/spring-boot/issues/32654
|
hints.resources().registerPattern("db/*"); // https://github.com/spring-projects/spring-boot/issues/32654
|
||||||
hints.resources().registerPattern("messages/*");
|
hints.resources().registerPattern("messages/*");
|
||||||
hints.resources().registerPattern("mysql-default-conf");
|
hints.resources().registerPattern("mysql-default-conf");
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import jakarta.persistence.GeneratedValue;
|
||||||
import jakarta.persistence.GenerationType;
|
import jakarta.persistence.GenerationType;
|
||||||
import jakarta.persistence.Id;
|
import jakarta.persistence.Id;
|
||||||
import jakarta.persistence.MappedSuperclass;
|
import jakarta.persistence.MappedSuperclass;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple JavaBean domain object with an id property. Used as a base class for objects
|
* Simple JavaBean domain object with an id property. Used as a base class for objects
|
||||||
|
|
@ -34,13 +35,13 @@ public class BaseEntity implements Serializable {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
private Integer id;
|
private @Nullable Integer id;
|
||||||
|
|
||||||
public Integer getId() {
|
public @Nullable Integer getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setId(Integer id) {
|
public void setId(@Nullable Integer id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package org.springframework.samples.petclinic.model;
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.MappedSuperclass;
|
import jakarta.persistence.MappedSuperclass;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple JavaBean domain object adds a name property to <code>BaseEntity</code>. Used as
|
* Simple JavaBean domain object adds a name property to <code>BaseEntity</code>. Used as
|
||||||
|
|
@ -32,19 +33,20 @@ public class NamedEntity extends BaseEntity {
|
||||||
|
|
||||||
@Column(name = "name")
|
@Column(name = "name")
|
||||||
@NotBlank
|
@NotBlank
|
||||||
private String name;
|
private @Nullable String name;
|
||||||
|
|
||||||
public String getName() {
|
public @Nullable String getName() {
|
||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setName(String name) {
|
public void setName(@Nullable String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return this.getName();
|
String name = this.getName();
|
||||||
|
return (name != null) ? name : "<null>";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package org.springframework.samples.petclinic.model;
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.MappedSuperclass;
|
import jakarta.persistence.MappedSuperclass;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple JavaBean domain object representing an person.
|
* Simple JavaBean domain object representing an person.
|
||||||
|
|
@ -29,25 +30,25 @@ public class Person extends BaseEntity {
|
||||||
|
|
||||||
@Column(name = "first_name")
|
@Column(name = "first_name")
|
||||||
@NotBlank
|
@NotBlank
|
||||||
private String firstName;
|
private @Nullable String firstName;
|
||||||
|
|
||||||
@Column(name = "last_name")
|
@Column(name = "last_name")
|
||||||
@NotBlank
|
@NotBlank
|
||||||
private String lastName;
|
private @Nullable String lastName;
|
||||||
|
|
||||||
public String getFirstName() {
|
public @Nullable String getFirstName() {
|
||||||
return this.firstName;
|
return this.firstName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFirstName(String firstName) {
|
public void setFirstName(@Nullable String firstName) {
|
||||||
this.firstName = firstName;
|
this.firstName = firstName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getLastName() {
|
public @Nullable String getLastName() {
|
||||||
return this.lastName;
|
return this.lastName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLastName(String lastName) {
|
public void setLastName(@Nullable String lastName) {
|
||||||
this.lastName = lastName;
|
this.lastName = lastName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,4 +17,7 @@
|
||||||
/**
|
/**
|
||||||
* The classes in this package represent utilities used by the domain.
|
* The classes in this package represent utilities used by the domain.
|
||||||
*/
|
*/
|
||||||
|
@NullMarked
|
||||||
package org.springframework.samples.petclinic.model;
|
package org.springframework.samples.petclinic.model;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import org.springframework.core.style.ToStringCreator;
|
import org.springframework.core.style.ToStringCreator;
|
||||||
import org.springframework.samples.petclinic.model.Person;
|
import org.springframework.samples.petclinic.model.Person;
|
||||||
|
|
@ -32,6 +33,7 @@ import jakarta.persistence.OrderBy;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
import jakarta.validation.constraints.Pattern;
|
import jakarta.validation.constraints.Pattern;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple JavaBean domain object representing an owner.
|
* Simple JavaBean domain object representing an owner.
|
||||||
|
|
@ -49,43 +51,43 @@ public class Owner extends Person {
|
||||||
|
|
||||||
@Column(name = "address")
|
@Column(name = "address")
|
||||||
@NotBlank
|
@NotBlank
|
||||||
private String address;
|
private @Nullable String address;
|
||||||
|
|
||||||
@Column(name = "city")
|
@Column(name = "city")
|
||||||
@NotBlank
|
@NotBlank
|
||||||
private String city;
|
private @Nullable String city;
|
||||||
|
|
||||||
@Column(name = "telephone")
|
@Column(name = "telephone")
|
||||||
@NotBlank
|
@NotBlank
|
||||||
@Pattern(regexp = "\\d{10}", message = "{telephone.invalid}")
|
@Pattern(regexp = "\\d{10}", message = "{telephone.invalid}")
|
||||||
private String telephone;
|
private @Nullable String telephone;
|
||||||
|
|
||||||
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||||
@JoinColumn(name = "owner_id")
|
@JoinColumn(name = "owner_id")
|
||||||
@OrderBy("name")
|
@OrderBy("name")
|
||||||
private final List<Pet> pets = new ArrayList<>();
|
private final List<Pet> pets = new ArrayList<>();
|
||||||
|
|
||||||
public String getAddress() {
|
public @Nullable String getAddress() {
|
||||||
return this.address;
|
return this.address;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAddress(String address) {
|
public void setAddress(@Nullable String address) {
|
||||||
this.address = address;
|
this.address = address;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCity() {
|
public @Nullable String getCity() {
|
||||||
return this.city;
|
return this.city;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCity(String city) {
|
public void setCity(@Nullable String city) {
|
||||||
this.city = city;
|
this.city = city;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTelephone() {
|
public @Nullable String getTelephone() {
|
||||||
return this.telephone;
|
return this.telephone;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTelephone(String telephone) {
|
public void setTelephone(@Nullable String telephone) {
|
||||||
this.telephone = telephone;
|
this.telephone = telephone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -104,7 +106,7 @@ public class Owner extends Person {
|
||||||
* @param name to test
|
* @param name to test
|
||||||
* @return the Pet with the given name, or null if no such Pet exists for this Owner
|
* @return the Pet with the given name, or null if no such Pet exists for this Owner
|
||||||
*/
|
*/
|
||||||
public Pet getPet(String name) {
|
public @Nullable Pet getPet(String name) {
|
||||||
return getPet(name, false);
|
return getPet(name, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,11 +115,11 @@ public class Owner extends Person {
|
||||||
* @param id to test
|
* @param id to test
|
||||||
* @return the Pet with the given id, or null if no such Pet exists for this Owner
|
* @return the Pet with the given id, or null if no such Pet exists for this Owner
|
||||||
*/
|
*/
|
||||||
public Pet getPet(Integer id) {
|
public @Nullable Pet getPet(Integer id) {
|
||||||
for (Pet pet : getPets()) {
|
for (Pet pet : getPets()) {
|
||||||
if (!pet.isNew()) {
|
if (!pet.isNew()) {
|
||||||
Integer compId = pet.getId();
|
Integer compId = pet.getId();
|
||||||
if (compId.equals(id)) {
|
if (Objects.equals(compId, id)) {
|
||||||
return pet;
|
return pet;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -131,7 +133,7 @@ public class Owner extends Person {
|
||||||
* @param ignoreNew whether to ignore new pets (pets that are not saved yet)
|
* @param ignoreNew whether to ignore new pets (pets that are not saved yet)
|
||||||
* @return the Pet with the given name, or null if no such Pet exists for this Owner
|
* @return the Pet with the given name, or null if no such Pet exists for this Owner
|
||||||
*/
|
*/
|
||||||
public Pet getPet(String name, boolean ignoreNew) {
|
public @Nullable Pet getPet(String name, boolean ignoreNew) {
|
||||||
for (Pet pet : getPets()) {
|
for (Pet pet : getPets()) {
|
||||||
String compName = pet.getName();
|
String compName = pet.getName();
|
||||||
if (compName != null && compName.equalsIgnoreCase(name)) {
|
if (compName != null && compName.equalsIgnoreCase(name)) {
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
package org.springframework.samples.petclinic.owner;
|
package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
|
|
@ -34,6 +35,8 @@ import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -60,7 +63,7 @@ class OwnerController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ModelAttribute("owner")
|
@ModelAttribute("owner")
|
||||||
public Owner findOwner(@PathVariable(name = "ownerId", required = false) Integer ownerId) {
|
public Owner findOwner(@PathVariable(name = "ownerId", required = false) @Nullable Integer ownerId) {
|
||||||
return ownerId == null ? new Owner()
|
return ownerId == null ? new Owner()
|
||||||
: this.owners.findById(ownerId)
|
: this.owners.findById(ownerId)
|
||||||
.orElseThrow(() -> new IllegalArgumentException("Owner not found with id: " + ownerId
|
.orElseThrow(() -> new IllegalArgumentException("Owner not found with id: " + ownerId
|
||||||
|
|
@ -93,12 +96,13 @@ class OwnerController {
|
||||||
public String processFindForm(@RequestParam(defaultValue = "1") int page, Owner owner, BindingResult result,
|
public String processFindForm(@RequestParam(defaultValue = "1") int page, Owner owner, BindingResult result,
|
||||||
Model model) {
|
Model model) {
|
||||||
// allow parameterless GET request for /owners to return all records
|
// allow parameterless GET request for /owners to return all records
|
||||||
if (owner.getLastName() == null) {
|
String lastName = owner.getLastName();
|
||||||
owner.setLastName(""); // empty string signifies broadest possible search
|
if (lastName == null) {
|
||||||
|
lastName = ""; // empty string signifies broadest possible search
|
||||||
}
|
}
|
||||||
|
|
||||||
// find owners by last name
|
// find owners by last name
|
||||||
Page<Owner> ownersResults = findPaginatedForOwnersLastName(page, owner.getLastName());
|
Page<Owner> ownersResults = findPaginatedForOwnersLastName(page, lastName);
|
||||||
if (ownersResults.isEmpty()) {
|
if (ownersResults.isEmpty()) {
|
||||||
// no owners found
|
// no owners found
|
||||||
result.rejectValue("lastName", "notFound", "not found");
|
result.rejectValue("lastName", "notFound", "not found");
|
||||||
|
|
@ -143,7 +147,7 @@ class OwnerController {
|
||||||
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (owner.getId() != ownerId) {
|
if (!Objects.equals(owner.getId(), ownerId)) {
|
||||||
result.rejectValue("id", "mismatch", "The owner ID in the form does not match the URL.");
|
result.rejectValue("id", "mismatch", "The owner ID in the form does not match the URL.");
|
||||||
redirectAttributes.addFlashAttribute("error", "Owner ID mismatch. Please try again.");
|
redirectAttributes.addFlashAttribute("error", "Owner ID mismatch. Please try again.");
|
||||||
return "redirect:/owners/{ownerId}/edit";
|
return "redirect:/owners/{ownerId}/edit";
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,6 @@ public interface OwnerRepository extends JpaRepository<Owner, Integer> {
|
||||||
* @throws IllegalArgumentException if the id is null (assuming null is not a valid
|
* @throws IllegalArgumentException if the id is null (assuming null is not a valid
|
||||||
* input for id)
|
* input for id)
|
||||||
*/
|
*/
|
||||||
Optional<Owner> findById(@Nonnull Integer id);
|
Optional<Owner> findById(Integer id);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import jakarta.persistence.ManyToOne;
|
||||||
import jakarta.persistence.OneToMany;
|
import jakarta.persistence.OneToMany;
|
||||||
import jakarta.persistence.OrderBy;
|
import jakarta.persistence.OrderBy;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple business object representing a pet.
|
* Simple business object representing a pet.
|
||||||
|
|
@ -47,30 +48,30 @@ public class Pet extends NamedEntity {
|
||||||
|
|
||||||
@Column(name = "birth_date")
|
@Column(name = "birth_date")
|
||||||
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||||
private LocalDate birthDate;
|
private @Nullable LocalDate birthDate;
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne
|
||||||
@JoinColumn(name = "type_id")
|
@JoinColumn(name = "type_id")
|
||||||
private PetType type;
|
private @Nullable PetType type;
|
||||||
|
|
||||||
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||||
@JoinColumn(name = "pet_id")
|
@JoinColumn(name = "pet_id")
|
||||||
@OrderBy("date ASC")
|
@OrderBy("date ASC")
|
||||||
private final Set<Visit> visits = new LinkedHashSet<>();
|
private final Set<Visit> visits = new LinkedHashSet<>();
|
||||||
|
|
||||||
public void setBirthDate(LocalDate birthDate) {
|
public void setBirthDate(@Nullable LocalDate birthDate) {
|
||||||
this.birthDate = birthDate;
|
this.birthDate = birthDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalDate getBirthDate() {
|
public @Nullable LocalDate getBirthDate() {
|
||||||
return this.birthDate;
|
return this.birthDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PetType getType() {
|
public @Nullable PetType getType() {
|
||||||
return this.type;
|
return this.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setType(PetType type) {
|
public void setType(@Nullable PetType type) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,12 @@ package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.ModelMap;
|
import org.springframework.ui.ModelMap;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.validation.BindingResult;
|
import org.springframework.validation.BindingResult;
|
||||||
import org.springframework.web.bind.WebDataBinder;
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
|
|
@ -32,6 +34,8 @@ import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -69,8 +73,8 @@ class PetController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ModelAttribute("pet")
|
@ModelAttribute("pet")
|
||||||
public Pet findPet(@PathVariable("ownerId") int ownerId,
|
public @Nullable Pet findPet(@PathVariable("ownerId") int ownerId,
|
||||||
@PathVariable(name = "petId", required = false) Integer petId) {
|
@PathVariable(name = "petId", required = false) @Nullable Integer petId) {
|
||||||
|
|
||||||
if (petId == null) {
|
if (petId == null) {
|
||||||
return new Pet();
|
return new Pet();
|
||||||
|
|
@ -135,7 +139,7 @@ class PetController {
|
||||||
// checking if the pet name already exists for the owner
|
// checking if the pet name already exists for the owner
|
||||||
if (StringUtils.hasText(petName)) {
|
if (StringUtils.hasText(petName)) {
|
||||||
Pet existingPet = owner.getPet(petName, false);
|
Pet existingPet = owner.getPet(petName, false);
|
||||||
if (existingPet != null && !existingPet.getId().equals(pet.getId())) {
|
if (existingPet != null && !Objects.equals(existingPet.getId(), pet.getId())) {
|
||||||
result.rejectValue("name", "duplicate", "already exists");
|
result.rejectValue("name", "duplicate", "already exists");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -160,7 +164,9 @@ class PetController {
|
||||||
* @param pet The pet with updated details
|
* @param pet The pet with updated details
|
||||||
*/
|
*/
|
||||||
private void updatePetDetails(Owner owner, Pet pet) {
|
private void updatePetDetails(Owner owner, Pet pet) {
|
||||||
Pet existingPet = owner.getPet(pet.getId());
|
Integer id = pet.getId();
|
||||||
|
Assert.state(id != null, "'pet.getId()' must not be null");
|
||||||
|
Pet existingPet = owner.getPet(id);
|
||||||
if (existingPet != null) {
|
if (existingPet != null) {
|
||||||
// Update existing pet's properties
|
// Update existing pet's properties
|
||||||
existingPet.setName(pet.getName());
|
existingPet.setName(pet.getName());
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import org.springframework.stereotype.Component;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instructs Spring MVC on how to parse and print elements of type 'PetType'. Starting
|
* Instructs Spring MVC on how to parse and print elements of type 'PetType'. Starting
|
||||||
|
|
@ -43,14 +44,15 @@ public class PetTypeFormatter implements Formatter<PetType> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String print(PetType petType, Locale locale) {
|
public String print(PetType petType, Locale locale) {
|
||||||
return petType.getName();
|
String name = petType.getName();
|
||||||
|
return (name != null) ? name : "<null>";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PetType parse(String text, Locale locale) throws ParseException {
|
public PetType parse(String text, Locale locale) throws ParseException {
|
||||||
Collection<PetType> findPetTypes = this.types.findPetTypes();
|
Collection<PetType> findPetTypes = this.types.findPetTypes();
|
||||||
for (PetType type : findPetTypes) {
|
for (PetType type : findPetTypes) {
|
||||||
if (type.getName().equals(text)) {
|
if (Objects.equals(type.getName(), text)) {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple JavaBean domain object representing a visit.
|
* Simple JavaBean domain object representing a visit.
|
||||||
|
|
@ -37,10 +38,10 @@ public class Visit extends BaseEntity {
|
||||||
|
|
||||||
@Column(name = "visit_date")
|
@Column(name = "visit_date")
|
||||||
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||||
private LocalDate date;
|
private @Nullable LocalDate date;
|
||||||
|
|
||||||
@NotBlank
|
@NotBlank
|
||||||
private String description;
|
private @Nullable String description;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of Visit for the current date
|
* Creates a new instance of Visit for the current date
|
||||||
|
|
@ -49,19 +50,19 @@ public class Visit extends BaseEntity {
|
||||||
this.date = LocalDate.now();
|
this.date = LocalDate.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalDate getDate() {
|
public @Nullable LocalDate getDate() {
|
||||||
return this.date;
|
return this.date;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDate(LocalDate date) {
|
public void setDate(@Nullable LocalDate date) {
|
||||||
this.date = date;
|
this.date = date;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDescription() {
|
public @Nullable String getDescription() {
|
||||||
return this.description;
|
return this.description;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDescription(String description) {
|
public void setDescription(@Nullable String description) {
|
||||||
this.description = description;
|
this.description = description;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,10 @@ class VisitController {
|
||||||
"Owner not found with id: " + ownerId + ". Please ensure the ID is correct "));
|
"Owner not found with id: " + ownerId + ". Please ensure the ID is correct "));
|
||||||
|
|
||||||
Pet pet = owner.getPet(petId);
|
Pet pet = owner.getPet(petId);
|
||||||
|
if (pet == null) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Pet with id " + petId + " not found for owner with id " + ownerId + ".");
|
||||||
|
}
|
||||||
model.put("pet", pet);
|
model.put("pet", pet);
|
||||||
model.put("owner", owner);
|
model.put("owner", owner);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
@NullMarked
|
||||||
|
package org.springframework.samples.petclinic.owner;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
@NullMarked
|
||||||
|
package org.springframework.samples.petclinic;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
package org.springframework.samples.petclinic.system;
|
package org.springframework.samples.petclinic.system;
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer;
|
import org.springframework.boot.cache.autoconfigure.JCacheManagerCustomizer;
|
||||||
import org.springframework.cache.annotation.EnableCaching;
|
import org.springframework.cache.annotation.EnableCaching;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
@NullMarked
|
||||||
|
package org.springframework.samples.petclinic.system;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
||||||
|
|
@ -31,6 +31,7 @@ import jakarta.persistence.JoinTable;
|
||||||
import jakarta.persistence.ManyToMany;
|
import jakarta.persistence.ManyToMany;
|
||||||
import jakarta.persistence.Table;
|
import jakarta.persistence.Table;
|
||||||
import jakarta.xml.bind.annotation.XmlElement;
|
import jakarta.xml.bind.annotation.XmlElement;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple JavaBean domain object representing a veterinarian.
|
* Simple JavaBean domain object representing a veterinarian.
|
||||||
|
|
@ -47,7 +48,7 @@ public class Vet extends Person {
|
||||||
@ManyToMany(fetch = FetchType.EAGER)
|
@ManyToMany(fetch = FetchType.EAGER)
|
||||||
@JoinTable(name = "vet_specialties", joinColumns = @JoinColumn(name = "vet_id"),
|
@JoinTable(name = "vet_specialties", joinColumns = @JoinColumn(name = "vet_id"),
|
||||||
inverseJoinColumns = @JoinColumn(name = "specialty_id"))
|
inverseJoinColumns = @JoinColumn(name = "specialty_id"))
|
||||||
private Set<Specialty> specialties;
|
private @Nullable Set<Specialty> specialties;
|
||||||
|
|
||||||
protected Set<Specialty> getSpecialtiesInternal() {
|
protected Set<Specialty> getSpecialtiesInternal() {
|
||||||
if (this.specialties == null) {
|
if (this.specialties == null) {
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import java.util.List;
|
||||||
|
|
||||||
import jakarta.xml.bind.annotation.XmlElement;
|
import jakarta.xml.bind.annotation.XmlElement;
|
||||||
import jakarta.xml.bind.annotation.XmlRootElement;
|
import jakarta.xml.bind.annotation.XmlRootElement;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple domain object representing a list of veterinarians. Mostly here to be used for
|
* Simple domain object representing a list of veterinarians. Mostly here to be used for
|
||||||
|
|
@ -30,7 +31,7 @@ import jakarta.xml.bind.annotation.XmlRootElement;
|
||||||
@XmlRootElement
|
@XmlRootElement
|
||||||
public class Vets {
|
public class Vets {
|
||||||
|
|
||||||
private List<Vet> vets;
|
private @Nullable List<Vet> vets;
|
||||||
|
|
||||||
@XmlElement
|
@XmlElement
|
||||||
public List<Vet> getVetList() {
|
public List<Vet> getVetList() {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
@NullMarked
|
||||||
|
package org.springframework.samples.petclinic.vet;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
||||||
|
|
@ -21,11 +21,11 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.condition.DisabledInNativeImage;
|
import org.junit.jupiter.api.condition.DisabledInNativeImage;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.restclient.RestTemplateBuilder;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
|
||||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
import org.springframework.boot.web.server.test.LocalServerPort;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.RequestEntity;
|
import org.springframework.http.RequestEntity;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,10 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.restclient.RestTemplateBuilder;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
import org.springframework.boot.web.server.test.LocalServerPort;
|
||||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.RequestEntity;
|
import org.springframework.http.RequestEntity;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
|
|
||||||
|
|
@ -32,10 +32,10 @@ import org.junit.jupiter.api.condition.DisabledInNativeImage;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||||
import org.springframework.boot.context.event.ApplicationPreparedEvent;
|
import org.springframework.boot.context.event.ApplicationPreparedEvent;
|
||||||
|
import org.springframework.boot.restclient.RestTemplateBuilder;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
import org.springframework.boot.web.server.test.LocalServerPort;
|
||||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
|
||||||
import org.springframework.context.ApplicationListener;
|
import org.springframework.context.ApplicationListener;
|
||||||
import org.springframework.core.env.ConfigurableEnvironment;
|
import org.springframework.core.env.ConfigurableEnvironment;
|
||||||
import org.springframework.core.env.EnumerablePropertySource;
|
import org.springframework.core.env.EnumerablePropertySource;
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ class ClinicServiceTests {
|
||||||
@Autowired
|
@Autowired
|
||||||
protected VetRepository vets;
|
protected VetRepository vets;
|
||||||
|
|
||||||
Pageable pageable;
|
private final Pageable pageable = Pageable.unpaged();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldFindOwnersByLastName() {
|
void shouldFindOwnersByLastName() {
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ public abstract class EntityUtils {
|
||||||
public static <T extends BaseEntity> T getById(Collection<T> entities, Class<T> entityClass, int entityId)
|
public static <T extends BaseEntity> T getById(Collection<T> entities, Class<T> entityClass, int entityId)
|
||||||
throws ObjectRetrievalFailureException {
|
throws ObjectRetrievalFailureException {
|
||||||
for (T entity : entities) {
|
for (T entity : entities) {
|
||||||
if (entity.getId() == entityId && entityClass.isInstance(entity)) {
|
if (entity.getId() != null && entity.getId() == entityId && entityClass.isInstance(entity)) {
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,11 +26,11 @@ import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
import org.springframework.boot.hibernate.autoconfigure.HibernateJpaAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
|
import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
|
import org.springframework.boot.jdbc.autoconfigure.DataSourceTransactionManagerAutoConfiguration;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
import org.springframework.boot.web.server.test.client.TestRestTemplate;
|
||||||
import org.springframework.core.ParameterizedTypeReference;
|
import org.springframework.core.ParameterizedTypeReference;
|
||||||
import org.springframework.http.HttpEntity;
|
import org.springframework.http.HttpEntity;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue