├── .github └── workflows │ └── build.yml ├── .gitignore ├── LICENSE ├── README.md ├── pom.xml ├── spring-boot-actuator ├── README.md ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── amitph │ │ └── spring │ │ └── songs │ │ ├── Application.java │ │ ├── actuator │ │ ├── CustomRestActuator.java │ │ ├── ServerTimeActuator.java │ │ ├── ServiceAHealthIndicator.java │ │ └── ServiceBHealthIndicator.java │ │ ├── data │ │ ├── Song.java │ │ └── SongRepository.java │ │ ├── service │ │ ├── SongService.java │ │ └── SongTransformer.java │ │ └── web │ │ ├── SongController.java │ │ └── SongDto.java │ └── resources │ └── application.yaml ├── spring-boot-admin-server ├── README.md ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── amitph │ │ └── spring │ │ └── adminserver │ │ ├── Application.java │ │ └── SecurityConfiguration.java │ └── resources │ └── application.yaml ├── spring-boot-command-line ├── README.md ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── amitph │ └── spring │ └── nonweb │ ├── Application.java │ └── service │ └── NonWebService.java ├── spring-boot-data-jpa ├── .gitignore ├── LICENSE ├── README.md ├── pom.xml ├── spring-boot-data-jpa-h2 │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── amitph │ │ │ │ └── spring │ │ │ │ └── data │ │ │ │ ├── Application.java │ │ │ │ ├── repo │ │ │ │ ├── Student.java │ │ │ │ └── StudentRepository.java │ │ │ │ └── web │ │ │ │ ├── StudentController.java │ │ │ │ ├── StudentDto.java │ │ │ │ └── StudentNotFoundException.java │ │ └── resources │ │ │ └── application.yml │ │ └── test │ │ └── java │ │ └── com │ │ └── amitph │ │ └── spring │ │ └── data │ │ └── ApplicationTest.java ├── spring-boot-data-jpa-mssqlserver │ ├── docker-mssqlserver │ │ └── docker-compose.yml │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── amitph │ │ │ │ └── spring │ │ │ │ └── data │ │ │ │ ├── Application.java │ │ │ │ ├── repo │ │ │ │ ├── Student.java │ │ │ │ └── StudentRepository.java │ │ │ │ └── web │ │ │ │ ├── StudentController.java │ │ │ │ ├── StudentDto.java │ │ │ │ └── StudentNotFoundException.java │ │ └── resources │ │ │ ├── application.yml │ │ │ ├── data.sql │ │ │ └── schema.sql │ │ └── test │ │ └── java │ │ └── com │ │ └── amitph │ │ └── spring │ │ └── data │ │ └── ApplicationTest.java ├── spring-boot-data-jpa-mysql │ ├── docker-mysql │ │ └── docker-compose.yml │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── amitph │ │ │ │ └── spring │ │ │ │ └── data │ │ │ │ ├── Application.java │ │ │ │ ├── repo │ │ │ │ ├── Student.java │ │ │ │ └── StudentRepository.java │ │ │ │ └── web │ │ │ │ ├── StudentController.java │ │ │ │ ├── StudentDto.java │ │ │ │ └── StudentNotFoundException.java │ │ └── resources │ │ │ ├── application.yml │ │ │ ├── data.sql │ │ │ └── schema.sql │ │ └── test │ │ └── java │ │ └── com │ │ └── amitph │ │ └── spring │ │ └── data │ │ └── ApplicationTest.java ├── spring-boot-data-jpa-postgres │ ├── docker-postgres │ │ └── docker-compose.yml │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── amitph │ │ │ │ └── spring │ │ │ │ └── data │ │ │ │ ├── Application.java │ │ │ │ ├── repo │ │ │ │ ├── Student.java │ │ │ │ └── StudentRepository.java │ │ │ │ └── web │ │ │ │ ├── StudentController.java │ │ │ │ ├── StudentDto.java │ │ │ │ └── StudentNotFoundException.java │ │ └── resources │ │ │ ├── application.yml │ │ │ ├── data.sql │ │ │ └── schema.sql │ │ └── test │ │ └── java │ │ └── com │ │ └── amitph │ │ └── spring │ │ └── data │ │ └── ApplicationTest.java └── spring-data-jpa-embeddedid │ ├── README.md │ ├── pom.xml │ └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── amitph │ │ │ └── spring │ │ │ └── songs │ │ │ ├── Application.java │ │ │ ├── model │ │ │ └── SongDto.java │ │ │ ├── repo │ │ │ ├── LongKeySong.java │ │ │ ├── LongKeySongId.java │ │ │ ├── LongKeySongRepository.java │ │ │ ├── Song.java │ │ │ ├── SongId.java │ │ │ └── SongsRepository.java │ │ │ ├── service │ │ │ └── SongsService.java │ │ │ └── web │ │ │ ├── SongNotFoundException.java │ │ │ └── SongsController.java │ └── resources │ │ └── application.yml │ └── test │ ├── java │ └── com │ │ └── amitph │ │ └── spring │ │ └── songs │ │ └── repo │ │ └── SongsRepositoryTest.java │ └── resources │ ├── application-jpatest.yml │ └── data.sql ├── spring-boot-dog-service ├── README.md ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── amitph │ │ └── spring │ │ └── dogs │ │ ├── Application.java │ │ ├── model │ │ └── DogDto.java │ │ ├── repo │ │ ├── Dog.java │ │ └── MockDogProvider.java │ │ ├── service │ │ └── DogsService.java │ │ └── web │ │ └── DogsController.java │ └── resources │ └── application.yml ├── spring-boot-exit-codes ├── README.md ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── amitph │ │ └── spring │ │ └── exitcode │ │ ├── Application.java │ │ ├── ExitCodeListener.java │ │ ├── ShutdownWithStaticExitCode.java │ │ └── ValueTooSmallException.java │ └── resources │ └── application.yml ├── spring-boot-jpa-dog-service ├── README.md ├── docker │ └── docker-compose.yml ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── amitph │ │ │ └── spring │ │ │ └── dogs │ │ │ ├── Application.java │ │ │ ├── ApplicationRunnerImpl.java │ │ │ ├── CommandLineRunnerImpl.java │ │ │ ├── model │ │ │ └── DogDto.java │ │ │ ├── repo │ │ │ ├── Dog.java │ │ │ ├── DogsRepository.java │ │ │ ├── DogsRepositoryWildcardWithQuery.java │ │ │ └── DogsRepositoryWildcardWithQueryMethods.java │ │ │ ├── service │ │ │ ├── DogNotFoundException.java │ │ │ └── DogsService.java │ │ │ └── web │ │ │ └── DogsController.java │ └── resources │ │ ├── application.yml │ │ └── data.sql │ └── test │ ├── java │ └── com │ │ └── amitph │ │ └── spring │ │ └── dogs │ │ └── repo │ │ ├── DogsRepositoryWildcardWithQueryMethodsTest.java │ │ └── DogsRepositoryWildcardWithQueryTest.java │ └── resources │ └── application-jpatest.yml ├── spring-boot-read-properties ├── README.md ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── amitph │ │ └── spring │ │ ├── Application.java │ │ ├── PropertiesPrinter.java │ │ └── properties │ │ ├── ConstructorBasedLoginProperties.java │ │ ├── SetterBasedLoginProperties.java │ │ ├── SimpleProperties.java │ │ ├── list │ │ ├── ListOfMapProperties.java │ │ ├── ListOfObjectProperties.java │ │ └── PlainListProperties.java │ │ ├── map │ │ ├── MapProperties.java │ │ └── NestedMapProperties.java │ │ ├── nested │ │ ├── LoginHeaderProperties.java │ │ ├── LoginProperties.java │ │ ├── NestedClassesProperties.java │ │ ├── NestedProperties.java │ │ └── UserProperties.java │ │ ├── prefixed │ │ ├── DifferentlyNamedProperties.java │ │ ├── LoginServiceProperties.java │ │ └── UserServiceProperties.java │ │ ├── set │ │ └── SetOfObjectProperties.java │ │ └── validated │ │ └── ValidatedProperties.java │ └── resources │ └── application.yml ├── spring-boot-samples ├── README.md ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── amitph │ │ │ └── spring │ │ │ ├── Application.java │ │ │ ├── ApplicationConfig.java │ │ │ ├── filters │ │ │ ├── LoggingFilter.java │ │ │ ├── RequestStatsFilter.java │ │ │ └── V2LoggingFilter.java │ │ │ ├── headers │ │ │ └── RequestHeadersController.java │ │ │ ├── model │ │ │ └── Student.java │ │ │ ├── naming │ │ │ ├── Circle.java │ │ │ ├── Heart.java │ │ │ ├── Pentagon.java │ │ │ ├── Rectangle.java │ │ │ ├── Shape.java │ │ │ ├── ShapeService.java │ │ │ ├── Square.java │ │ │ └── Triangle.java │ │ │ ├── pathvariable │ │ │ └── PathVariableExampleController.java │ │ │ └── requestparam │ │ │ └── RequestParamExampleController.java │ └── resources │ │ └── application.yml │ └── test │ └── java │ └── com │ └── amitph │ └── spring │ ├── ApplicationTest.java │ ├── pathvariable │ └── PathVariableExampleControllerTest.java │ └── requestparam │ └── RequestParamExampleControllerTest.java ├── spring-boot-startup-metrics ├── README.md ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── amitph │ │ └── spring │ │ └── data │ │ ├── Application.java │ │ └── web │ │ └── StudentController.java │ └── resources │ └── application.yml ├── spring-data-jdbc ├── README.md ├── docker │ └── docker-compose.yml ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── amitph │ │ │ └── spring │ │ │ └── tutorials │ │ │ └── springdatajdbc │ │ │ ├── Application.java │ │ │ ├── repo │ │ │ ├── Student.java │ │ │ └── StudentRepository.java │ │ │ └── web │ │ │ └── StudentController.java │ └── resources │ │ ├── application.yml │ │ └── db │ │ ├── changelog │ │ └── 01-create-student.xml │ │ └── liquibase-changelog.xml │ └── test │ ├── java │ └── com │ │ └── amitph │ │ └── spring │ │ └── tutorials │ │ └── springdatajdbc │ │ ├── ApplicationTest.java │ │ └── repo │ │ └── StudentRepositoryTest.java │ └── resources │ └── application.yml ├── spring-data-rest ├── .gitignore ├── README.md ├── crud-example │ ├── .gitignore │ ├── README.md │ ├── pom.xml │ └── src │ │ └── main │ │ ├── java │ │ └── com │ │ │ └── amitph │ │ │ └── spring │ │ │ └── springdatarest │ │ │ ├── Application.java │ │ │ └── entity │ │ │ ├── Student.java │ │ │ └── StudentRepository.java │ │ └── resources │ │ ├── application.yaml │ │ └── schema.sql ├── pom.xml └── projection-excerpts │ ├── .gitignore │ ├── README.md │ ├── docker │ └── docker-compose.yml │ ├── pom.xml │ └── src │ └── main │ ├── java │ └── com │ │ └── amitph │ │ └── spring │ │ └── springdatarest │ │ ├── Application.java │ │ └── entity │ │ ├── Course.java │ │ ├── CourseRepository.java │ │ ├── Student.java │ │ ├── StudentRepository.java │ │ └── StudentView.java │ └── resources │ ├── application.yaml │ ├── data.sql │ └── schema.sql ├── spring-scheduled-tasks ├── README.md ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── amitph │ │ │ └── spring │ │ │ └── tutorials │ │ │ └── springbootdemo │ │ │ ├── Application.java │ │ │ └── scheduled │ │ │ └── ScheduledTask.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── com │ └── amitph │ └── spring │ └── tutorials │ └── springbootdemo │ └── SpringBootDemoApplicationTests.java └── spring-webflux-webclient ├── README.md ├── pom.xml └── src └── main ├── java └── com │ └── amitph │ └── spring │ └── webclients │ ├── Application.java │ ├── ApplicationConfig.java │ ├── ApplicationProperties.java │ ├── DogsFetcher.java │ ├── ExecuteWithFilters.java │ ├── FileDownloader.java │ ├── model │ ├── Dog.java │ └── OwnedDog.java │ └── service │ ├── DogsFetcherWebClientService.java │ ├── FileDownloaderWebClientService.java │ └── WebClientWithFilterService.java └── resources ├── application.yaml ├── banner.png └── banner.txt /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven 3 | 4 | name: spring-examples-build 5 | 6 | on: 7 | push: 8 | branches: [ main ] 9 | pull_request: 10 | branches: [ main ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Set up JDK 21 20 | uses: actions/setup-java@v3 21 | with: 22 | java-version: '21' 23 | distribution: 'adopt' 24 | cache: 'maven' 25 | - name: Build with Maven 26 | run: mvn -B package --file pom.xml 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | target/ 3 | *.iml 4 | .DS_Store 5 | 6 | # Compiled class file 7 | *.class 8 | 9 | # Log file 10 | *.log 11 | 12 | # BlueJ files 13 | *.ctxt 14 | 15 | # Mobile Tools for Java (J2ME) 16 | .mtj.tmp/ 17 | 18 | # Package Files # 19 | *.jar 20 | *.war 21 | *.nar 22 | *.ear 23 | *.zip 24 | *.tar.gz 25 | *.rar 26 | 27 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 28 | hs_err_pid* 29 | -------------------------------------------------------------------------------- /spring-boot-actuator/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot Actuator and Admin Server Client Example 2 | _An example of Spring Boot 2.x Actuator Endpoints_ 3 | 4 | This repository is part of [amitph.com](https://www.amitph.com/) tutorials. 5 | 6 | ## Respective Tutorials 7 | > - [Spring Boot Actuator with Spring Boot 2](https://www.amitph.com/spring-boot-actuator-spring-boot-2/) 8 | > - [Custom Health Check in Spring Boot Actuator](https://www.amitph.com/custom-health-check-spring-boot-actuator/) 9 | > - [Introduction to Spring Boot Admin Server with Example](https://www.amitph.com/spring-boot-admin-server/) 10 | > - [How to Secure Spring Boot Actuator Endpoints](https://www.amitph.com/how-to-secure-spring-boot-actuator-endpoints/) 11 | 12 | ## How to Use 13 | 14 | #### Clone this Git repository 15 | 16 | ``` 17 | git clone https://github.com/amitrp/spring-examples.git 18 | ``` 19 | 20 | #### Move to the module 21 | ``` 22 | cd spring-examples/spring-boot-actuator/ 23 | ``` 24 | 25 | #### Run the Application 26 | ``` 27 | mvn spring-boot:run 28 | ``` -------------------------------------------------------------------------------- /spring-boot-actuator/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | spring-examples 7 | com.amitph.spring 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | spring-boot-actuator 13 | 14 | 15 | 16 | org.springframework.boot 17 | spring-boot-starter-web 18 | 19 | 20 | org.springframework.boot 21 | spring-boot-starter-actuator 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-data-jpa 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-security 30 | 31 | 32 | de.codecentric 33 | spring-boot-admin-starter-client 34 | 2.7.2 35 | 36 | 37 | com.h2database 38 | h2 39 | 40 | 41 | 42 | 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-maven-plugin 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /spring-boot-actuator/src/main/java/com/amitph/spring/songs/Application.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.songs; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Application { 8 | public static void main(String[] args) { 9 | SpringApplication.run(Application.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /spring-boot-actuator/src/main/java/com/amitph/spring/songs/actuator/CustomRestActuator.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.songs.actuator; 2 | 3 | import java.time.LocalDate; 4 | import java.time.LocalTime; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint; 8 | import org.springframework.stereotype.Component; 9 | import org.springframework.web.bind.annotation.GetMapping; 10 | import org.springframework.web.bind.annotation.PostMapping; 11 | import org.springframework.web.bind.annotation.RequestBody; 12 | 13 | @Component 14 | @RestControllerEndpoint(id = "custom-rest-endpoint") 15 | public class CustomRestActuator { 16 | 17 | @GetMapping 18 | public Map get() { 19 | Map map = new HashMap<>(); 20 | map.put("server.date", LocalDate.now().toString()); 21 | map.put("server.time", LocalTime.now().toString()); 22 | return map; 23 | } 24 | 25 | @PostMapping 26 | public String post(@RequestBody String request) { 27 | return "We have received your request: " + request; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /spring-boot-actuator/src/main/java/com/amitph/spring/songs/actuator/ServerTimeActuator.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.songs.actuator; 2 | 3 | import java.time.LocalDate; 4 | import java.time.LocalTime; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation; 8 | import org.springframework.boot.actuate.endpoint.annotation.Endpoint; 9 | import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; 10 | import org.springframework.boot.actuate.endpoint.annotation.WriteOperation; 11 | import org.springframework.stereotype.Component; 12 | 13 | @Component 14 | @Endpoint(id = "custom-endpoint") 15 | public class ServerTimeActuator { 16 | 17 | @ReadOperation 18 | public Map readOperation() { 19 | Map map = new HashMap<>(); 20 | map.put("server.date", LocalDate.now().toString()); 21 | map.put("server.time", LocalTime.now().toString()); 22 | return map; 23 | } 24 | 25 | @WriteOperation 26 | public String writeOperation() { 27 | // Implementation skipped 28 | return ""; 29 | } 30 | 31 | @DeleteOperation 32 | public String deleteOperation() { 33 | // Implementation skipped 34 | return ""; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /spring-boot-actuator/src/main/java/com/amitph/spring/songs/actuator/ServiceAHealthIndicator.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.songs.actuator; 2 | 3 | import org.springframework.boot.actuate.health.Health; 4 | import org.springframework.boot.actuate.health.HealthIndicator; 5 | import org.springframework.stereotype.Component; 6 | 7 | @Component 8 | public class ServiceAHealthIndicator implements HealthIndicator { 9 | private final String message_key = "Service A"; 10 | 11 | @Override 12 | public Health health() { 13 | if (!isRunningServiceA()) { 14 | return Health.down().withDetail(message_key, "Not Available").build(); 15 | } 16 | return Health.up().withDetail(message_key, "Available").build(); 17 | } 18 | 19 | private Boolean isRunningServiceA() { 20 | Boolean isRunning = true; 21 | // Logic Skipped 22 | 23 | return isRunning; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /spring-boot-actuator/src/main/java/com/amitph/spring/songs/actuator/ServiceBHealthIndicator.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.songs.actuator; 2 | 3 | import org.springframework.boot.actuate.health.Health; 4 | import org.springframework.boot.actuate.health.HealthIndicator; 5 | import org.springframework.stereotype.Component; 6 | 7 | @Component 8 | public class ServiceBHealthIndicator implements HealthIndicator { 9 | private final String message_key = "Service B"; 10 | 11 | @Override 12 | public Health health() { 13 | if (!isRunningServiceB()) { 14 | return Health.down().withDetail(message_key, "Not Available").build(); 15 | } 16 | 17 | return Health.up().withDetail(message_key, "Available").build(); 18 | } 19 | 20 | private Boolean isRunningServiceB() { 21 | Boolean isRunning = true; 22 | // Logic Skipped 23 | 24 | return isRunning; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /spring-boot-actuator/src/main/java/com/amitph/spring/songs/data/Song.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.songs.data; 2 | 3 | import jakarta.persistence.Entity; 4 | import jakarta.persistence.GeneratedValue; 5 | import jakarta.persistence.Id; 6 | 7 | @Entity 8 | public class Song { 9 | @Id @GeneratedValue private Long songId; 10 | 11 | private String title; 12 | private String album; 13 | private String genre; 14 | private Integer year; 15 | 16 | public Long getSongId() { 17 | return songId; 18 | } 19 | 20 | public void setSongId(Long songId) { 21 | this.songId = songId; 22 | } 23 | 24 | public String getTitle() { 25 | return title; 26 | } 27 | 28 | public void setTitle(String title) { 29 | this.title = title; 30 | } 31 | 32 | public String getAlbum() { 33 | return album; 34 | } 35 | 36 | public void setAlbum(String album) { 37 | this.album = album; 38 | } 39 | 40 | public String getGenre() { 41 | return genre; 42 | } 43 | 44 | public void setGenre(String genre) { 45 | this.genre = genre; 46 | } 47 | 48 | public Integer getYear() { 49 | return year; 50 | } 51 | 52 | public void setYear(Integer year) { 53 | this.year = year; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /spring-boot-actuator/src/main/java/com/amitph/spring/songs/data/SongRepository.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.songs.data; 2 | 3 | import java.util.List; 4 | import org.springframework.data.repository.CrudRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface SongRepository extends CrudRepository { 9 | List findAll(); 10 | } 11 | -------------------------------------------------------------------------------- /spring-boot-actuator/src/main/java/com/amitph/spring/songs/service/SongService.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.songs.service; 2 | 3 | import com.amitph.spring.songs.data.SongRepository; 4 | import com.amitph.spring.songs.web.SongDto; 5 | import java.util.List; 6 | import java.util.stream.Collectors; 7 | import lombok.RequiredArgsConstructor; 8 | import org.springframework.stereotype.Component; 9 | 10 | @Component 11 | @RequiredArgsConstructor 12 | public class SongService { 13 | private final SongRepository repository; 14 | private final SongTransformer transformer; 15 | 16 | public List getAllSongs() { 17 | return repository.findAll().stream() 18 | .map(transformer::transform) 19 | .collect(Collectors.toList()); 20 | } 21 | 22 | public SongDto addNewSong(SongDto dto) { 23 | return transformer.transform(repository.save(transformer.transform(dto))); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /spring-boot-actuator/src/main/java/com/amitph/spring/songs/service/SongTransformer.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.songs.service; 2 | 3 | import com.amitph.spring.songs.data.Song; 4 | import com.amitph.spring.songs.web.SongDto; 5 | import org.springframework.stereotype.Component; 6 | 7 | @Component 8 | public class SongTransformer { 9 | public SongDto transform(Song song) { 10 | SongDto dto = new SongDto(); 11 | dto.setTitle(song.getTitle()); 12 | dto.setAlbum(song.getAlbum()); 13 | dto.setGenre(song.getGenre()); 14 | dto.setYear(song.getYear()); 15 | dto.setSongId(song.getSongId()); 16 | return dto; 17 | } 18 | 19 | public Song transform(SongDto dto) { 20 | Song song = new Song(); 21 | song.setAlbum(dto.getAlbum()); 22 | song.setGenre(dto.getGenre()); 23 | song.setTitle(dto.getTitle()); 24 | song.setYear(dto.getYear()); 25 | return song; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /spring-boot-actuator/src/main/java/com/amitph/spring/songs/web/SongController.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.songs.web; 2 | 3 | import com.amitph.spring.songs.service.SongService; 4 | import java.util.List; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.PostMapping; 8 | import org.springframework.web.bind.annotation.RequestBody; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | @RestController 13 | @RequestMapping("/songs") 14 | @RequiredArgsConstructor 15 | public class SongController { 16 | private final SongService songService; 17 | 18 | @GetMapping 19 | public List getAll() { 20 | return songService.getAllSongs(); 21 | } 22 | 23 | @PostMapping 24 | public SongDto addSong(@RequestBody SongDto dto) { 25 | return songService.addNewSong(dto); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /spring-boot-actuator/src/main/java/com/amitph/spring/songs/web/SongDto.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.songs.web; 2 | 3 | public class SongDto { 4 | private Long songId; 5 | private String title; 6 | private String album; 7 | private String genre; 8 | private Integer year; 9 | 10 | public Long getSongId() { 11 | return songId; 12 | } 13 | 14 | public void setSongId(Long songId) { 15 | this.songId = songId; 16 | } 17 | 18 | public String getTitle() { 19 | return title; 20 | } 21 | 22 | public void setTitle(String title) { 23 | this.title = title; 24 | } 25 | 26 | public String getAlbum() { 27 | return album; 28 | } 29 | 30 | public void setAlbum(String album) { 31 | this.album = album; 32 | } 33 | 34 | public String getGenre() { 35 | return genre; 36 | } 37 | 38 | public void setGenre(String genre) { 39 | this.genre = genre; 40 | } 41 | 42 | public Integer getYear() { 43 | return year; 44 | } 45 | 46 | public void setYear(Integer year) { 47 | this.year = year; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /spring-boot-actuator/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | 2 | management: 3 | endpoint: 4 | health: 5 | show-details: always 6 | endpoints: 7 | web: 8 | exposure: 9 | include: '*' 10 | spring: 11 | boot: 12 | admin: 13 | client: 14 | url: [http://localhost:8080] 15 | username: "admin" 16 | password: "admin123" 17 | instance: 18 | metadata: 19 | user.name: client 20 | user.password: clientPassword 21 | application: 22 | name: Songs Service 23 | security: 24 | user: 25 | name: client 26 | password: clientPassword 27 | 28 | server: 29 | port: 8081 30 | 31 | -------------------------------------------------------------------------------- /spring-boot-admin-server/README.md: -------------------------------------------------------------------------------- 1 | # Example of Spring Boot Admin Server 2 | _Spring Boot Admin Server - A Common place to monitor your other Spring Boot Applications_ 3 | 4 | 5 | This repository is part of [amitph.com](https://www.amitph.com/) tutorials. 6 | 7 | ## Referenced Tutorial 8 | - [Spring Boot Admin Server Example](https://www.amitph.com/spring-boot-admin-server/) 9 | 10 | ## How to Use 11 | 12 | #### Clone this Git repository 13 | ``` 14 | git clone https://github.com/amitrp/spring-examples.git 15 | ``` 16 | 17 | #### Move to the module 18 | ``` 19 | cd spring-examples/spring-boot-admin-server/ 20 | ``` 21 | 22 | #### Run the Application 23 | ``` 24 | mvn spring-boot:run 25 | ``` -------------------------------------------------------------------------------- /spring-boot-admin-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | spring-examples 9 | com.amitph.spring 10 | 1.0-SNAPSHOT 11 | 12 | 13 | spring-boot-admin-server 14 | 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter-web 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-security 23 | 24 | 25 | de.codecentric 26 | spring-boot-admin-starter-server 27 | 2.7.2 28 | 29 | 30 | de.codecentric 31 | spring-boot-admin-server-ui 32 | 2.7.2 33 | 34 | 35 | de.codecentric 36 | spring-boot-admin-server-ui-login 37 | 1.5.7 38 | 39 | 40 | 41 | 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-maven-plugin 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /spring-boot-admin-server/src/main/java/com/amitph/spring/adminserver/Application.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.adminserver; 2 | 3 | import de.codecentric.boot.admin.server.config.EnableAdminServer; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | 7 | @EnableAdminServer 8 | @SpringBootApplication 9 | public class Application { 10 | public static void main(String[] args) { 11 | SpringApplication.run(Application.class, args); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /spring-boot-admin-server/src/main/java/com/amitph/spring/adminserver/SecurityConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.adminserver; 2 | 3 | import de.codecentric.boot.admin.server.config.AdminServerProperties; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.security.config.Customizer; 7 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 8 | import org.springframework.security.web.SecurityFilterChain; 9 | import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; 10 | import org.springframework.security.web.csrf.CookieCsrfTokenRepository; 11 | 12 | @Configuration 13 | public class SecurityConfiguration { 14 | private final String adminContextPath; 15 | 16 | public SecurityConfiguration(AdminServerProperties adminServerProperties) { 17 | this.adminContextPath = adminServerProperties.getContextPath(); 18 | } 19 | 20 | @Bean 21 | protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception { 22 | SavedRequestAwareAuthenticationSuccessHandler successHandler = 23 | new SavedRequestAwareAuthenticationSuccessHandler(); 24 | successHandler.setTargetUrlParameter("redirectTo"); 25 | successHandler.setDefaultTargetUrl(adminContextPath + "/"); 26 | 27 | http.authorizeHttpRequests( 28 | req -> 29 | req.requestMatchers(adminContextPath + "/assets/**") 30 | .permitAll() 31 | .requestMatchers(adminContextPath + "/login") 32 | .permitAll() 33 | .anyRequest() 34 | .authenticated()) 35 | .formLogin( 36 | formLogin -> 37 | formLogin 38 | .loginPage(adminContextPath + "/login") 39 | .successHandler(successHandler)) 40 | .logout(logout -> logout.logoutUrl(adminContextPath + "/logout")) 41 | .httpBasic(Customizer.withDefaults()) 42 | .csrf( 43 | csrf -> 44 | csrf.csrfTokenRepository( 45 | CookieCsrfTokenRepository.withHttpOnlyFalse()) 46 | .ignoringRequestMatchers( 47 | adminContextPath + "/instances", 48 | adminContextPath + "/actuator/**")); 49 | return http.build(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /spring-boot-admin-server/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8080 3 | 4 | spring: 5 | security: 6 | user: 7 | name: "admin" 8 | password: "admin123" 9 | boot: 10 | admin: 11 | notify: 12 | slack: 13 | webhook-url: https://hooks.slack.com/services/ 14 | channel: test 15 | -------------------------------------------------------------------------------- /spring-boot-command-line/README.md: -------------------------------------------------------------------------------- 1 | #Spring Boot Command Line Application Example 2 | 3 | This is a Spring Boot Non Web Application is for the purpose of demonstration. 4 | 5 | This repository is part of http://www.amitph.com tutorials. 6 | > The spring-boot-command-line will be used as a Source Code example for [How to Write a non-web Application with Spring Boot](https://www.amitph.com/non-web-application-spring-boot/) at http://www.amitph.com. 7 | 8 | The source code may be incomplete or written within a limited scope of the tutorial, and some essential parts may have been ignored. 9 | 10 | ## How to use 11 | #### Checkout this project 12 | ``` 13 | git clone https://github.com/amitrp/spring-examples.git 14 | ``` 15 | 16 | #### Move to the spring-boot-command-line module 17 | ``` 18 | cd spring-examples/spring-boot-command-line 19 | ``` 20 | 21 | #### Start the application and pass a few arguments 22 | ``` 23 | mvn spring-boot:run -Dspring-boot.run.arguments="arg1 arg2" 24 | ``` -------------------------------------------------------------------------------- /spring-boot-command-line/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.amitph.spring 7 | spring-examples 8 | 1.0-SNAPSHOT 9 | 10 | 11 | spring-boot-command-line 12 | 1.0-SNAPSHOT 13 | spring-boot-command-line 14 | Demo project for Spring Boot - Command Line application 15 | 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-starter 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-test 25 | test 26 | 27 | 28 | 29 | 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-maven-plugin 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /spring-boot-command-line/src/main/java/com/amitph/spring/nonweb/Application.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.nonweb; 2 | 3 | import com.amitph.spring.nonweb.service.NonWebService; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.CommandLineRunner; 6 | import org.springframework.boot.SpringApplication; 7 | import org.springframework.boot.autoconfigure.SpringBootApplication; 8 | 9 | @SpringBootApplication 10 | public class Application implements CommandLineRunner { 11 | 12 | @Autowired NonWebService service; 13 | 14 | public static void main(String[] args) { 15 | SpringApplication.run(Application.class, args); 16 | } 17 | 18 | @Override 19 | public void run(String... args) throws Exception { 20 | service.printMessage(args); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /spring-boot-command-line/src/main/java/com/amitph/spring/nonweb/service/NonWebService.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.nonweb.service; 2 | 3 | import java.util.Arrays; 4 | import org.springframework.stereotype.Service; 5 | 6 | @Service 7 | public class NonWebService { 8 | public void printMessage(String[] arguments) { 9 | System.out.println("Inside NonWebService Class. Received below arguments"); 10 | Arrays.stream(arguments).forEach(System.out::println); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | **/target 3 | !.mvn/wrapper/maven-wrapper.jar 4 | .DS_Store 5 | **/logs/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | /build/ 29 | 30 | ### VS Code ### 31 | .vscode/ 32 | 33 | /logs/ -------------------------------------------------------------------------------- /spring-boot-data-jpa/README.md: -------------------------------------------------------------------------------- 1 | # spring-boot-data-jpa 2 | Examples of using Spring Boot + Spring Data JPA with different databases 3 | These examples are part of Spring Boot + Spring Data JPA Tutorials on https://www.amitph.com/ 4 | 5 | ## List of Tutorials 6 | > - [Spring Data REST CRUD Example](https://www.amitph.com/spring-data-rest-example/) 7 | > - [Spring Data REST Projections and Excerpts](https://www.amitph.com/spring-data-rest-projections-and-excerpts/) 8 | > - [Spring Boot - Spring Data JPA - MySQL Example](https://www.amitph.com/spring-boot-data-jpa-mysql/) 9 | > - [Spring Boot - Spring Data JPA - Postgres Example](https://www.amitph.com/spring-boot-data-jpa-postgres/) 10 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.amitph.spring 7 | spring-examples 8 | 1.0-SNAPSHOT 9 | 10 | spring-boot-data-jpa 11 | pom 12 | 1.0-SNAPSHOT 13 | 14 | spring-boot-data-jpa-mysql 15 | spring-boot-data-jpa-h2 16 | spring-boot-data-jpa-postgres 17 | spring-boot-data-jpa-mssqlserver 18 | spring-data-jpa-embeddedid 19 | 20 | 21 | UTF-8 22 | 21 23 | 24 | 25 | 26 | 27 | 28 | com.amitph.spring 29 | spring-boot-command-line 30 | 1.0-SNAPSHOT 31 | 32 | 33 | com.amitph.spring 34 | spring-boot-data-jpa-h2 35 | 1.0-SNAPSHOT 36 | 37 | 38 | com.amitph.spring 39 | spring-boot-data-jpa-mssqlserver 40 | 1.0-SNAPSHOT 41 | 42 | 43 | com.amitph.spring 44 | spring-boot-data-jpa-mysql 45 | 1.0-SNAPSHOT 46 | 47 | 48 | com.amitph.spring 49 | spring-boot-data-jpa-postgres 50 | 1.0-SNAPSHOT 51 | 52 | 53 | com.amitph.spring 54 | spring-data-jpa-embeddedid 55 | 1.0-SNAPSHOT 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-h2/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 4.0.0 6 | 7 | com.amitph.spring 8 | spring-boot-data-jpa 9 | 1.0-SNAPSHOT 10 | 11 | spring-boot-data-jpa-h2 12 | jar 13 | Spring Boot + Spring Data JPA + H2 Example 14 | 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter-data-jpa 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-web 23 | 24 | 25 | com.h2database 26 | h2 27 | runtime 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-test 32 | test 33 | 34 | 35 | 36 | 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-maven-plugin 41 | 42 | 43 | 44 | 45 | 46 | com.amitph.spring.data.Application 47 | 48 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-h2/src/main/java/com/amitph/spring/data/Application.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Application { 8 | public static void main(String[] args) { 9 | SpringApplication.run(Application.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-h2/src/main/java/com/amitph/spring/data/repo/Student.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data.repo; 2 | 3 | import jakarta.persistence.Entity; 4 | import jakarta.persistence.GeneratedValue; 5 | import jakarta.persistence.GenerationType; 6 | import jakarta.persistence.Id; 7 | import lombok.Data; 8 | 9 | @Data 10 | @Entity 11 | public class Student { 12 | @Id 13 | @GeneratedValue(strategy = GenerationType.IDENTITY) 14 | private Long student_id; 15 | 16 | private String firstName; 17 | private String lastName; 18 | private int year; 19 | } 20 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-h2/src/main/java/com/amitph/spring/data/repo/StudentRepository.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data.repo; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.stereotype.Repository; 5 | 6 | @Repository 7 | public interface StudentRepository extends JpaRepository {} 8 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-h2/src/main/java/com/amitph/spring/data/web/StudentController.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data.web; 2 | 3 | import com.amitph.spring.data.repo.Student; 4 | import com.amitph.spring.data.repo.StudentRepository; 5 | import java.util.List; 6 | import lombok.RequiredArgsConstructor; 7 | import org.springframework.util.StringUtils; 8 | import org.springframework.web.bind.annotation.DeleteMapping; 9 | import org.springframework.web.bind.annotation.GetMapping; 10 | import org.springframework.web.bind.annotation.PatchMapping; 11 | import org.springframework.web.bind.annotation.PathVariable; 12 | import org.springframework.web.bind.annotation.PostMapping; 13 | import org.springframework.web.bind.annotation.PutMapping; 14 | import org.springframework.web.bind.annotation.RequestBody; 15 | import org.springframework.web.bind.annotation.RestController; 16 | 17 | @RestController 18 | @RequiredArgsConstructor 19 | public class StudentController { 20 | private final StudentRepository studentRepository; 21 | 22 | @PostMapping("/students") 23 | public void postStudent(@RequestBody StudentDto studentDto) { 24 | Student student = new Student(); 25 | student.setFirstName(studentDto.getFirstName()); 26 | student.setLastName(studentDto.getLastName()); 27 | student.setYear(studentDto.getYear()); 28 | studentRepository.save(student); 29 | } 30 | 31 | @PutMapping("/students/{id}") 32 | public void putStudent(@PathVariable long id, @RequestBody StudentDto studentDto) { 33 | Student student = new Student(); 34 | student.setStudent_id(id); 35 | student.setFirstName(studentDto.getFirstName()); 36 | student.setLastName(studentDto.getLastName()); 37 | student.setYear(studentDto.getYear()); 38 | studentRepository.save(student); 39 | } 40 | 41 | @PatchMapping("/students/{id}") 42 | public void patchResource(@PathVariable long id, @RequestBody StudentDto studentDto) { 43 | 44 | Student student = studentRepository.findById(id).orElseThrow(StudentNotFoundException::new); 45 | 46 | boolean needUpdate = false; 47 | 48 | if (StringUtils.hasLength(studentDto.getFirstName())) { 49 | student.setFirstName(studentDto.getFirstName()); 50 | needUpdate = true; 51 | } 52 | 53 | if (StringUtils.hasLength(studentDto.getLastName())) { 54 | student.setLastName(studentDto.getLastName()); 55 | needUpdate = true; 56 | } 57 | 58 | if (studentDto.getYear() > 0) { 59 | student.setYear(studentDto.getYear()); 60 | needUpdate = true; 61 | } 62 | 63 | if (needUpdate) { 64 | studentRepository.save(student); 65 | } 66 | } 67 | 68 | @DeleteMapping("/students/{id}") 69 | public void deleteStudent(@PathVariable long id) { 70 | studentRepository.deleteById(id); 71 | } 72 | 73 | @GetMapping("/students/{id}") 74 | public Student getStudent(@PathVariable long id) { 75 | return studentRepository.findById(id).orElseThrow(StudentNotFoundException::new); 76 | } 77 | 78 | @GetMapping("/students") 79 | public List getStudents() { 80 | return studentRepository.findAll(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-h2/src/main/java/com/amitph/spring/data/web/StudentDto.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data.web; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class StudentDto { 7 | private Long student_id; 8 | private String firstName; 9 | private String lastName; 10 | private int year; 11 | } 12 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-h2/src/main/java/com/amitph/spring/data/web/StudentNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data.web; 2 | 3 | import static org.springframework.http.HttpStatus.NOT_FOUND; 4 | 5 | import org.springframework.web.bind.annotation.ResponseStatus; 6 | 7 | @ResponseStatus(NOT_FOUND) 8 | public class StudentNotFoundException extends RuntimeException {} 9 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-h2/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8082 3 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-h2/src/test/java/com/amitph/spring/data/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | class ApplicationTest { 6 | @Test 7 | void contextLoads() {} 8 | } 9 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-mssqlserver/docker-mssqlserver/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | database: 5 | restart: always 6 | image: mcr.microsoft.com/mssql/server 7 | container_name: ms-sql-server-latest 8 | environment: 9 | - ACCEPT_EULA=Y 10 | - SA_PASSWORD=password 11 | ports: 12 | - 14033:1433 -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-mssqlserver/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 4.0.0 6 | 7 | com.amitph.spring 8 | spring-boot-data-jpa 9 | 1.0-SNAPSHOT 10 | 11 | spring-boot-data-jpa-mssqlserver 12 | jar 13 | Spring Boot + Spring Data JPA + MS SQL Server Example 14 | 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter-data-jpa 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-web 23 | 24 | 25 | com.microsoft.sqlserver 26 | mssql-jdbc 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-test 31 | test 32 | 33 | 34 | 35 | 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-maven-plugin 40 | 41 | 42 | 43 | 44 | 45 | com.amitph.spring.data.Application 46 | 47 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-mssqlserver/src/main/java/com/amitph/spring/data/Application.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Application { 8 | public static void main(String[] args) { 9 | SpringApplication.run(Application.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-mssqlserver/src/main/java/com/amitph/spring/data/repo/Student.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data.repo; 2 | 3 | import jakarta.persistence.Entity; 4 | import jakarta.persistence.GeneratedValue; 5 | import jakarta.persistence.GenerationType; 6 | import jakarta.persistence.Id; 7 | import lombok.Data; 8 | 9 | @Data 10 | @Entity 11 | public class Student { 12 | @Id 13 | @GeneratedValue(strategy = GenerationType.IDENTITY) 14 | private Long student_id; 15 | 16 | private String firstName; 17 | private String lastName; 18 | private int year; 19 | } 20 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-mssqlserver/src/main/java/com/amitph/spring/data/repo/StudentRepository.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data.repo; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.stereotype.Repository; 5 | 6 | @Repository 7 | public interface StudentRepository extends JpaRepository {} 8 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-mssqlserver/src/main/java/com/amitph/spring/data/web/StudentController.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data.web; 2 | 3 | import com.amitph.spring.data.repo.Student; 4 | import com.amitph.spring.data.repo.StudentRepository; 5 | import java.util.List; 6 | import lombok.RequiredArgsConstructor; 7 | import org.springframework.web.bind.annotation.DeleteMapping; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.PathVariable; 10 | import org.springframework.web.bind.annotation.PostMapping; 11 | import org.springframework.web.bind.annotation.PutMapping; 12 | import org.springframework.web.bind.annotation.RequestBody; 13 | import org.springframework.web.bind.annotation.RestController; 14 | 15 | @RestController 16 | @RequiredArgsConstructor 17 | public class StudentController { 18 | private final StudentRepository studentRepository; 19 | 20 | @PostMapping("/students") 21 | public void postStudent(@RequestBody StudentDto studentDto) { 22 | Student student = new Student(); 23 | student.setFirstName(studentDto.getFirstName()); 24 | student.setLastName(studentDto.getLastName()); 25 | student.setYear(studentDto.getYear()); 26 | studentRepository.save(student); 27 | } 28 | 29 | @PutMapping("/students/{id}") 30 | public void putStudent(@PathVariable long id, @RequestBody StudentDto studentDto) { 31 | Student student = new Student(); 32 | student.setStudent_id(id); 33 | student.setFirstName(studentDto.getFirstName()); 34 | student.setLastName(studentDto.getLastName()); 35 | student.setYear(studentDto.getYear()); 36 | studentRepository.save(student); 37 | } 38 | 39 | @DeleteMapping("/students/{id}") 40 | public void deleteStudent(@PathVariable long id) { 41 | studentRepository.deleteById(id); 42 | } 43 | 44 | @GetMapping("/students/{id}") 45 | public Student getStudent(@PathVariable long id) { 46 | return studentRepository.findById(id).orElseThrow(StudentNotFoundException::new); 47 | } 48 | 49 | @GetMapping("/students") 50 | public List getStudents() { 51 | return studentRepository.findAll(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-mssqlserver/src/main/java/com/amitph/spring/data/web/StudentDto.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data.web; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class StudentDto { 7 | private Long student_id; 8 | private String firstName; 9 | private String lastName; 10 | private int year; 11 | } 12 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-mssqlserver/src/main/java/com/amitph/spring/data/web/StudentNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data.web; 2 | 3 | import static org.springframework.http.HttpStatus.NOT_FOUND; 4 | 5 | import org.springframework.web.bind.annotation.ResponseStatus; 6 | 7 | @ResponseStatus(NOT_FOUND) 8 | public class StudentNotFoundException extends RuntimeException {} 9 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-mssqlserver/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8084 3 | 4 | spring: 5 | datasource: 6 | username: "sa" 7 | password: "password" ##Enter your root password 8 | url: "jdbc:sqlserver://localhost:14033" 9 | driverClassName: "com.microsoft.sqlserver.jdbc.SQLServerDriver" 10 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-mssqlserver/src/main/resources/data.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO student (first_name, last_name, year) VALUES ('Jon', 'S', 2024); 2 | INSERT INTO student (first_name, last_name, year) VALUES ('Khal', 'D', 2025); 3 | INSERT INTO student (first_name, last_name, year) VALUES ('Belwas', 'S', 2029); 4 | INSERT INTO student (first_name, last_name, year) VALUES ('Petyr', 'L', 2023); -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-mssqlserver/src/main/resources/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE student 2 | ( 3 | student_id INTEGER NOT NULL IDENTITY PRIMARY KEY, 4 | first_name VARCHAR(50) NOT NULL, 5 | last_name VARCHAR(50), 6 | year INTEGER 7 | ); -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-mssqlserver/src/test/java/com/amitph/spring/data/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | class ApplicationTest { 6 | @Test 7 | void contextLoads() {} 8 | } 9 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-mysql/docker-mysql/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | mysql: 5 | restart: always 6 | image: mysql:latest 7 | container_name: my-sql-latest 8 | environment: 9 | - MYSQL_DATABASE=studentsdb 10 | - MYSQL_ROOT_PASSWORD=password 11 | ports: 12 | - 33080:3306 -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-mysql/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 4.0.0 6 | 7 | com.amitph.spring 8 | spring-boot-data-jpa 9 | 1.0-SNAPSHOT 10 | 11 | spring-boot-data-jpa-mysql 12 | jar 13 | Spring Boot + Spring Data JPA + MySQL Example 14 | 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter-data-jpa 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-web 23 | 24 | 25 | com.mysql 26 | mysql-connector-j 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-test 31 | test 32 | 33 | 34 | 35 | 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-maven-plugin 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-mysql/src/main/java/com/amitph/spring/data/Application.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Application { 8 | public static void main(String[] args) { 9 | SpringApplication.run(Application.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-mysql/src/main/java/com/amitph/spring/data/repo/Student.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data.repo; 2 | 3 | import jakarta.persistence.Entity; 4 | import jakarta.persistence.GeneratedValue; 5 | import jakarta.persistence.GenerationType; 6 | import jakarta.persistence.Id; 7 | import lombok.Data; 8 | 9 | @Data 10 | @Entity 11 | public class Student { 12 | @Id 13 | @GeneratedValue(strategy = GenerationType.IDENTITY) 14 | private Long student_id; 15 | 16 | private String firstName; 17 | private String lastName; 18 | private int year; 19 | } 20 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-mysql/src/main/java/com/amitph/spring/data/repo/StudentRepository.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data.repo; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.stereotype.Repository; 5 | 6 | @Repository 7 | public interface StudentRepository extends JpaRepository {} 8 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-mysql/src/main/java/com/amitph/spring/data/web/StudentController.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data.web; 2 | 3 | import com.amitph.spring.data.repo.Student; 4 | import com.amitph.spring.data.repo.StudentRepository; 5 | import java.util.List; 6 | import lombok.RequiredArgsConstructor; 7 | import org.springframework.web.bind.annotation.DeleteMapping; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.PathVariable; 10 | import org.springframework.web.bind.annotation.PostMapping; 11 | import org.springframework.web.bind.annotation.PutMapping; 12 | import org.springframework.web.bind.annotation.RequestBody; 13 | import org.springframework.web.bind.annotation.RestController; 14 | 15 | @RestController 16 | @RequiredArgsConstructor 17 | public class StudentController { 18 | private final StudentRepository studentRepository; 19 | 20 | @PostMapping("/students") 21 | public void postStudent(@RequestBody StudentDto studentDto) { 22 | Student student = new Student(); 23 | student.setFirstName(studentDto.getFirstName()); 24 | student.setLastName(studentDto.getLastName()); 25 | student.setYear(studentDto.getYear()); 26 | studentRepository.save(student); 27 | } 28 | 29 | @PutMapping("/students/{id}") 30 | public void putStudent(@PathVariable long id, @RequestBody StudentDto studentDto) { 31 | Student student = new Student(); 32 | student.setStudent_id(id); 33 | student.setFirstName(studentDto.getFirstName()); 34 | student.setLastName(studentDto.getLastName()); 35 | student.setYear(studentDto.getYear()); 36 | studentRepository.save(student); 37 | } 38 | 39 | @DeleteMapping("/students/{id}") 40 | public void deleteStudent(@PathVariable long id) { 41 | studentRepository.deleteById(id); 42 | } 43 | 44 | @GetMapping("/students/{id}") 45 | public Student getStudent(@PathVariable long id) { 46 | return studentRepository.findById(id).orElseThrow(StudentNotFoundException::new); 47 | } 48 | 49 | @GetMapping("/students") 50 | public List getStudents() { 51 | return studentRepository.findAll(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-mysql/src/main/java/com/amitph/spring/data/web/StudentDto.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data.web; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class StudentDto { 7 | private Long student_id; 8 | private String firstName; 9 | private String lastName; 10 | private int year; 11 | } 12 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-mysql/src/main/java/com/amitph/spring/data/web/StudentNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data.web; 2 | 3 | import static org.springframework.http.HttpStatus.NOT_FOUND; 4 | 5 | import org.springframework.web.bind.annotation.ResponseStatus; 6 | 7 | @ResponseStatus(NOT_FOUND) 8 | public class StudentNotFoundException extends RuntimeException {} 9 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-mysql/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8081 3 | 4 | spring: 5 | datasource: 6 | username: "root" 7 | password: "password" ##Enter your root password 8 | url: "jdbc:mysql://localhost:33080/studentsdb" 9 | driverClassName: "com.mysql.cj.jdbc.Driver" 10 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-mysql/src/main/resources/data.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO student (first_name, last_name, year) VALUES ('Jon', 'S', 2024); 2 | INSERT INTO student (first_name, last_name, year) VALUES ('Khal', 'D', 2025); 3 | INSERT INTO student (first_name, last_name, year) VALUES ('Belwas', 'S', 2029); 4 | INSERT INTO student (first_name, last_name, year) VALUES ('Petyr', 'L', 2023); -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-mysql/src/main/resources/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE student 2 | ( 3 | student_id INTEGER NOT NULL AUTO_INCREMENT, 4 | first_name VARCHAR(50) NOT NULL, 5 | last_name VARCHAR(50), 6 | year INTEGER, 7 | PRIMARY KEY (student_id) 8 | ); -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-mysql/src/test/java/com/amitph/spring/data/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | class ApplicationTest { 6 | @Test 7 | void contextLoads() {} 8 | } 9 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-postgres/docker-postgres/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | database: 5 | restart: always 6 | image: postgres:latest 7 | container_name: postgres-latest 8 | environment: 9 | - POSTGRES_DB=postgres 10 | - POSTGRES_USER=postgres 11 | - POSTGRES_PASSWORD=password 12 | ports: 13 | - 54032:5432 14 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-postgres/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 4.0.0 6 | 7 | com.amitph.spring 8 | spring-boot-data-jpa 9 | 1.0-SNAPSHOT 10 | 11 | spring-boot-data-jpa-postgres 12 | jar 13 | Spring Boot + Spring Data JPA + Postgres Example 14 | 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter-data-jpa 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-web 23 | 24 | 25 | org.postgresql 26 | postgresql 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-test 31 | test 32 | 33 | 34 | 35 | 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-maven-plugin 40 | 41 | 42 | 43 | 44 | 45 | com.amitph.spring.data.Application 46 | 47 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-postgres/src/main/java/com/amitph/spring/data/Application.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Application { 8 | public static void main(String[] args) { 9 | SpringApplication.run(Application.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-postgres/src/main/java/com/amitph/spring/data/repo/Student.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data.repo; 2 | 3 | import jakarta.persistence.Entity; 4 | import jakarta.persistence.GeneratedValue; 5 | import jakarta.persistence.GenerationType; 6 | import jakarta.persistence.Id; 7 | import lombok.Data; 8 | 9 | @Data 10 | @Entity 11 | public class Student { 12 | @Id 13 | @GeneratedValue(strategy = GenerationType.IDENTITY) 14 | private Long student_id; 15 | 16 | private String firstName; 17 | private String lastName; 18 | private int year; 19 | } 20 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-postgres/src/main/java/com/amitph/spring/data/repo/StudentRepository.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data.repo; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.stereotype.Repository; 5 | 6 | @Repository 7 | public interface StudentRepository extends JpaRepository {} 8 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-postgres/src/main/java/com/amitph/spring/data/web/StudentController.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data.web; 2 | 3 | import com.amitph.spring.data.repo.Student; 4 | import com.amitph.spring.data.repo.StudentRepository; 5 | import java.util.List; 6 | import lombok.RequiredArgsConstructor; 7 | import org.springframework.web.bind.annotation.DeleteMapping; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.PathVariable; 10 | import org.springframework.web.bind.annotation.PostMapping; 11 | import org.springframework.web.bind.annotation.PutMapping; 12 | import org.springframework.web.bind.annotation.RequestBody; 13 | import org.springframework.web.bind.annotation.RestController; 14 | 15 | @RestController 16 | @RequiredArgsConstructor 17 | public class StudentController { 18 | private final StudentRepository studentRepository; 19 | 20 | @PostMapping("/students") 21 | public void postStudent(@RequestBody StudentDto studentDto) { 22 | Student student = new Student(); 23 | student.setFirstName(studentDto.getFirstName()); 24 | student.setLastName(studentDto.getLastName()); 25 | student.setYear(studentDto.getYear()); 26 | studentRepository.save(student); 27 | } 28 | 29 | @PutMapping("/students/{id}") 30 | public void putStudent(@PathVariable long id, @RequestBody StudentDto studentDto) { 31 | Student student = new Student(); 32 | student.setStudent_id(id); 33 | student.setFirstName(studentDto.getFirstName()); 34 | student.setLastName(studentDto.getLastName()); 35 | student.setYear(studentDto.getYear()); 36 | studentRepository.save(student); 37 | } 38 | 39 | @DeleteMapping("/students/{id}") 40 | public void deleteStudent(@PathVariable long id) { 41 | studentRepository.deleteById(id); 42 | } 43 | 44 | @GetMapping("/students/{id}") 45 | public Student getStudent(@PathVariable long id) { 46 | return studentRepository.findById(id).orElseThrow(StudentNotFoundException::new); 47 | } 48 | 49 | @GetMapping("/students") 50 | public List getStudents() { 51 | return studentRepository.findAll(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-postgres/src/main/java/com/amitph/spring/data/web/StudentDto.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data.web; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class StudentDto { 7 | private Long student_id; 8 | private String firstName; 9 | private String lastName; 10 | private int year; 11 | } 12 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-postgres/src/main/java/com/amitph/spring/data/web/StudentNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data.web; 2 | 3 | import static org.springframework.http.HttpStatus.NOT_FOUND; 4 | 5 | import org.springframework.web.bind.annotation.ResponseStatus; 6 | 7 | @ResponseStatus(NOT_FOUND) 8 | public class StudentNotFoundException extends RuntimeException {} 9 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-postgres/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8083 3 | 4 | spring: 5 | datasource: 6 | driverClassName: "org.postgresql.Driver" 7 | url: "jdbc:postgresql://localhost:54032/postgres" 8 | username: "postgres" 9 | password: "password" ##Enter your root password 10 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-postgres/src/main/resources/data.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO student (first_name, last_name, year) VALUES ('Jon', 'S', 2024); 2 | INSERT INTO student (first_name, last_name, year) VALUES ('Khal', 'D', 2025); 3 | INSERT INTO student (first_name, last_name, year) VALUES ('Belwas', 'S', 2029); 4 | INSERT INTO student (first_name, last_name, year) VALUES ('Petyr', 'L', 2023); -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-postgres/src/main/resources/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE student 2 | ( 3 | student_id SERIAL NOT NULL, 4 | first_name VARCHAR(50) NOT NULL, 5 | last_name VARCHAR(50), 6 | year INTEGER, 7 | PRIMARY KEY (student_id) 8 | ); -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-boot-data-jpa-postgres/src/test/java/com/amitph/spring/data/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | class ApplicationTest { 6 | @Test 7 | void contextLoads() {} 8 | } 9 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-data-jpa-embeddedid/README.md: -------------------------------------------------------------------------------- 1 | # Composite Primary key using Spring Data JPA @EmbeddedId 2 | This is a Spring Boot Backed REST service examples of Spring Data @EmbeddedId 3 | 4 | ## This repository is part of http://www.amitph.com tutorials. 5 | > The Spring Data Jpa EmbeddedId repo will be used as a Source Code example for **[Spring Data JPA Composite Key with @EmbeddedId](https://www.amitph.com/spring-data-jpa-embeddedid/)** and **[Spring Data JPA find by @EmbeddedId Partially](http://www.amitph.com/spring-data-jpa-find-by-embeddedid-partially/)** at https://www.amitph.com. 6 | 7 | _The source code may be incomplete or written within a limited scope of the tutorial, and some essential parts may have been ignored._ 8 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-data-jpa-embeddedid/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 4.0.0 6 | 7 | com.amitph.spring 8 | spring-boot-data-jpa 9 | 1.0-SNAPSHOT 10 | 11 | 12 | spring-data-jpa-embeddedid 13 | jar 14 | Example of composite primary key using @EmbeddedId with Spring Data JPA 15 | 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-starter-web 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-data-jpa 24 | 25 | 26 | com.mysql 27 | mysql-connector-j 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-test 32 | test 33 | 34 | 35 | com.h2database 36 | h2 37 | test 38 | 39 | 40 | junit 41 | junit 42 | test 43 | 44 | 45 | 46 | 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-maven-plugin 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-data-jpa-embeddedid/src/main/java/com/amitph/spring/songs/Application.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.songs; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Application { 8 | public static void main(String[] args) { 9 | SpringApplication.run(Application.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-data-jpa-embeddedid/src/main/java/com/amitph/spring/songs/model/SongDto.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.songs.model; 2 | 3 | import java.time.LocalDateTime; 4 | 5 | public class SongDto { 6 | private String name; 7 | private String album; 8 | private String artist; 9 | private int duration; 10 | private String genre; 11 | private LocalDateTime releaseDate; 12 | int rating; 13 | private String downloadUrl; 14 | 15 | public String getName() { 16 | return name; 17 | } 18 | 19 | public void setName(String name) { 20 | this.name = name; 21 | } 22 | 23 | public String getAlbum() { 24 | return album; 25 | } 26 | 27 | public void setAlbum(String album) { 28 | this.album = album; 29 | } 30 | 31 | public String getArtist() { 32 | return artist; 33 | } 34 | 35 | public void setArtist(String artist) { 36 | this.artist = artist; 37 | } 38 | 39 | public int getDuration() { 40 | return duration; 41 | } 42 | 43 | public void setDuration(int duration) { 44 | this.duration = duration; 45 | } 46 | 47 | public String getGenre() { 48 | return genre; 49 | } 50 | 51 | public void setGenre(String genre) { 52 | this.genre = genre; 53 | } 54 | 55 | public LocalDateTime getReleaseDate() { 56 | return releaseDate; 57 | } 58 | 59 | public void setReleaseDate(LocalDateTime releaseDate) { 60 | this.releaseDate = releaseDate; 61 | } 62 | 63 | public int getRating() { 64 | return rating; 65 | } 66 | 67 | public void setRating(int rating) { 68 | this.rating = rating; 69 | } 70 | 71 | public String getDownloadUrl() { 72 | return downloadUrl; 73 | } 74 | 75 | public void setDownloadUrl(String downloadUrl) { 76 | this.downloadUrl = downloadUrl; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-data-jpa-embeddedid/src/main/java/com/amitph/spring/songs/repo/LongKeySong.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.songs.repo; 2 | 3 | import jakarta.persistence.EmbeddedId; 4 | import jakarta.persistence.Entity; 5 | import java.time.LocalDateTime; 6 | 7 | @Entity 8 | public class LongKeySong { 9 | @EmbeddedId private LongKeySongId id; 10 | private int duration; 11 | private String genre; 12 | private LocalDateTime releaseDate; 13 | int rating; 14 | private String downloadUrl; 15 | 16 | public LongKeySongId getId() { 17 | return id; 18 | } 19 | 20 | public void setId(LongKeySongId id) { 21 | this.id = id; 22 | } 23 | 24 | public int getDuration() { 25 | return duration; 26 | } 27 | 28 | public void setDuration(int duration) { 29 | this.duration = duration; 30 | } 31 | 32 | public String getGenre() { 33 | return genre; 34 | } 35 | 36 | public void setGenre(String genre) { 37 | this.genre = genre; 38 | } 39 | 40 | public LocalDateTime getReleaseDate() { 41 | return releaseDate; 42 | } 43 | 44 | public void setReleaseDate(LocalDateTime releaseDate) { 45 | this.releaseDate = releaseDate; 46 | } 47 | 48 | public int getRating() { 49 | return rating; 50 | } 51 | 52 | public void setRating(int rating) { 53 | this.rating = rating; 54 | } 55 | 56 | public String getDownloadUrl() { 57 | return downloadUrl; 58 | } 59 | 60 | public void setDownloadUrl(String downloadUrl) { 61 | this.downloadUrl = downloadUrl; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-data-jpa-embeddedid/src/main/java/com/amitph/spring/songs/repo/LongKeySongId.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.songs.repo; 2 | 3 | import jakarta.persistence.Embeddable; 4 | 5 | @Embeddable 6 | public class LongKeySongId { 7 | private String name; 8 | private String album; 9 | private String artist; 10 | private String coArtist; 11 | private String soundEngineer; 12 | private String recordingArtist; 13 | private String composer; 14 | private String producer; 15 | private String country; 16 | 17 | public String getCoArtist() { 18 | return coArtist; 19 | } 20 | 21 | public void setCoArtist(String coArtist) { 22 | this.coArtist = coArtist; 23 | } 24 | 25 | public String getSoundEngineer() { 26 | return soundEngineer; 27 | } 28 | 29 | public void setSoundEngineer(String soundEngineer) { 30 | this.soundEngineer = soundEngineer; 31 | } 32 | 33 | public String getRecordingArtist() { 34 | return recordingArtist; 35 | } 36 | 37 | public void setRecordingArtist(String recordingArtist) { 38 | this.recordingArtist = recordingArtist; 39 | } 40 | 41 | public String getComposer() { 42 | return composer; 43 | } 44 | 45 | public void setComposer(String composer) { 46 | this.composer = composer; 47 | } 48 | 49 | public String getProducer() { 50 | return producer; 51 | } 52 | 53 | public void setProducer(String producer) { 54 | this.producer = producer; 55 | } 56 | 57 | public String getCountry() { 58 | return country; 59 | } 60 | 61 | public void setCountry(String country) { 62 | this.country = country; 63 | } 64 | 65 | public String getName() { 66 | return name; 67 | } 68 | 69 | public void setName(String name) { 70 | this.name = name; 71 | } 72 | 73 | public String getAlbum() { 74 | return album; 75 | } 76 | 77 | public void setAlbum(String album) { 78 | this.album = album; 79 | } 80 | 81 | public String getArtist() { 82 | return artist; 83 | } 84 | 85 | public void setArtist(String artist) { 86 | this.artist = artist; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-data-jpa-embeddedid/src/main/java/com/amitph/spring/songs/repo/LongKeySongRepository.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.songs.repo; 2 | 3 | import java.util.List; 4 | import org.springframework.data.domain.Example; 5 | import org.springframework.data.repository.CrudRepository; 6 | import org.springframework.stereotype.Repository; 7 | 8 | @Repository 9 | public interface LongKeySongRepository extends CrudRepository { 10 | List findAll(Example song); 11 | 12 | // Method filters by 8 out of 9 Id keys 13 | List 14 | findByIdNameAndIdArtistAndIdAlbumAndIdCoArtistAndIdComposerAndIdSoundEngineerAndIdProducerAndIdRecordingArtist( 15 | String name, 16 | String artist, 17 | String album, 18 | String coArtist, 19 | String composer, 20 | String soundEngineer, 21 | String producer, 22 | String recordingArtist); 23 | } 24 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-data-jpa-embeddedid/src/main/java/com/amitph/spring/songs/repo/Song.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.songs.repo; 2 | 3 | import jakarta.persistence.EmbeddedId; 4 | import jakarta.persistence.Entity; 5 | import java.time.LocalDateTime; 6 | 7 | @Entity 8 | public class Song { 9 | @EmbeddedId private SongId id; 10 | private int duration; 11 | private String genre; 12 | private LocalDateTime releaseDate; 13 | int rating; 14 | private String downloadUrl; 15 | 16 | public Song( 17 | SongId id, 18 | int duration, 19 | String genre, 20 | LocalDateTime releaseDate, 21 | int rating, 22 | String downloadUrl) { 23 | this.id = id; 24 | this.duration = duration; 25 | this.genre = genre; 26 | this.releaseDate = releaseDate; 27 | this.rating = rating; 28 | this.downloadUrl = downloadUrl; 29 | } 30 | 31 | public Song() {} 32 | 33 | public SongId getId() { 34 | return id; 35 | } 36 | 37 | public void setId(SongId id) { 38 | this.id = id; 39 | } 40 | 41 | public int getDuration() { 42 | return duration; 43 | } 44 | 45 | public void setDuration(int duration) { 46 | this.duration = duration; 47 | } 48 | 49 | public String getGenre() { 50 | return genre; 51 | } 52 | 53 | public void setGenre(String genre) { 54 | this.genre = genre; 55 | } 56 | 57 | public LocalDateTime getReleaseDate() { 58 | return releaseDate; 59 | } 60 | 61 | public void setReleaseDate(LocalDateTime releaseDate) { 62 | this.releaseDate = releaseDate; 63 | } 64 | 65 | public int getRating() { 66 | return rating; 67 | } 68 | 69 | public void setRating(int rating) { 70 | this.rating = rating; 71 | } 72 | 73 | public String getDownloadUrl() { 74 | return downloadUrl; 75 | } 76 | 77 | public void setDownloadUrl(String downloadUrl) { 78 | this.downloadUrl = downloadUrl; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-data-jpa-embeddedid/src/main/java/com/amitph/spring/songs/repo/SongId.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.songs.repo; 2 | 3 | import jakarta.persistence.Embeddable; 4 | import java.io.Serializable; 5 | 6 | @Embeddable 7 | public class SongId implements Serializable { 8 | private String name; 9 | private String album; 10 | private String artist; 11 | 12 | public SongId(String name, String album, String artist) { 13 | this.name = name; 14 | this.album = album; 15 | this.artist = artist; 16 | } 17 | 18 | public SongId() {} 19 | 20 | public String getName() { 21 | return name; 22 | } 23 | 24 | public void setName(String name) { 25 | this.name = name; 26 | } 27 | 28 | public String getAlbum() { 29 | return album; 30 | } 31 | 32 | public void setAlbum(String album) { 33 | this.album = album; 34 | } 35 | 36 | public String getArtist() { 37 | return artist; 38 | } 39 | 40 | public void setArtist(String artist) { 41 | this.artist = artist; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-data-jpa-embeddedid/src/main/java/com/amitph/spring/songs/repo/SongsRepository.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.songs.repo; 2 | 3 | import java.util.List; 4 | import org.springframework.data.repository.CrudRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface SongsRepository extends CrudRepository { 9 | List findByIdNameAndIdArtist(String name, String artist); 10 | } 11 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-data-jpa-embeddedid/src/main/java/com/amitph/spring/songs/service/SongsService.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.songs.service; 2 | 3 | import com.amitph.spring.songs.model.SongDto; 4 | import com.amitph.spring.songs.repo.LongKeySong; 5 | import com.amitph.spring.songs.repo.LongKeySongId; 6 | import com.amitph.spring.songs.repo.LongKeySongRepository; 7 | import com.amitph.spring.songs.repo.Song; 8 | import com.amitph.spring.songs.repo.SongId; 9 | import com.amitph.spring.songs.repo.SongsRepository; 10 | import com.amitph.spring.songs.web.SongNotFoundException; 11 | import java.util.List; 12 | import lombok.RequiredArgsConstructor; 13 | import org.springframework.data.domain.Example; 14 | import org.springframework.stereotype.Component; 15 | 16 | @Component 17 | @RequiredArgsConstructor 18 | public class SongsService { 19 | private final SongsRepository repository; 20 | private final LongKeySongRepository longKeySongRepository; 21 | 22 | public Song addSong(SongDto dto) { 23 | return repository.save(dtoToSong(dto)); 24 | } 25 | 26 | public Song find(SongDto dto) { 27 | return repository.findById(dtoToSongId(dto)).orElseThrow(SongNotFoundException::new); 28 | } 29 | 30 | private Song dtoToSong(SongDto dto) { 31 | return new Song( 32 | dtoToSongId(dto), 33 | dto.getDuration(), 34 | dto.getGenre(), 35 | dto.getReleaseDate(), 36 | dto.getRating(), 37 | dto.getDownloadUrl()); 38 | } 39 | 40 | private SongId dtoToSongId(SongDto dto) { 41 | return new SongId(dto.getName(), dto.getAlbum(), dto.getArtist()); 42 | } 43 | 44 | public List findByIdPartially( 45 | String name, 46 | String artist, 47 | String album, 48 | String coArtist, 49 | String composer, 50 | String soundEngineer, 51 | String producer, 52 | String recordingArtist) { 53 | return longKeySongRepository 54 | .findByIdNameAndIdArtistAndIdAlbumAndIdCoArtistAndIdComposerAndIdSoundEngineerAndIdProducerAndIdRecordingArtist( 55 | name, 56 | artist, 57 | album, 58 | coArtist, 59 | composer, 60 | soundEngineer, 61 | producer, 62 | recordingArtist); 63 | } 64 | 65 | public List findByIdPartiallyWithExample( 66 | String name, 67 | String artist, 68 | String album, 69 | String coArtist, 70 | String composer, 71 | String soundEngineer, 72 | String producer, 73 | String recordingArtist) { 74 | LongKeySong longKeySong = new LongKeySong(); 75 | LongKeySongId longKeySongId = new LongKeySongId(); 76 | longKeySong.setId(longKeySongId); 77 | 78 | longKeySongId.setName(name); 79 | longKeySongId.setAlbum(album); 80 | longKeySongId.setArtist(artist); 81 | longKeySongId.setCoArtist(coArtist); 82 | longKeySongId.setComposer(composer); 83 | longKeySongId.setSoundEngineer(soundEngineer); 84 | longKeySongId.setProducer(producer); 85 | longKeySongId.setRecordingArtist(recordingArtist); 86 | 87 | Example songExample = Example.of(longKeySong); 88 | return longKeySongRepository.findAll(songExample); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-data-jpa-embeddedid/src/main/java/com/amitph/spring/songs/web/SongNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.songs.web; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | @ResponseStatus(HttpStatus.NOT_FOUND) 7 | public class SongNotFoundException extends RuntimeException {} 8 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-data-jpa-embeddedid/src/main/java/com/amitph/spring/songs/web/SongsController.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.songs.web; 2 | 3 | import com.amitph.spring.songs.model.SongDto; 4 | import com.amitph.spring.songs.repo.Song; 5 | import com.amitph.spring.songs.service.SongsService; 6 | import lombok.RequiredArgsConstructor; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.PathVariable; 9 | import org.springframework.web.bind.annotation.PostMapping; 10 | import org.springframework.web.bind.annotation.RequestBody; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.web.bind.annotation.RestController; 13 | 14 | @RestController 15 | @RequestMapping("/songs") 16 | @RequiredArgsConstructor 17 | public class SongsController { 18 | private final SongsService service; 19 | 20 | @GetMapping("/artist/{artist}/album/{album}/name/{name}") 21 | public Song getSong( 22 | @PathVariable String artist, @PathVariable String album, @PathVariable String name) { 23 | SongDto dto = new SongDto(); 24 | dto.setName(name); 25 | dto.setArtist(artist); 26 | dto.setAlbum(album); 27 | 28 | return service.find(dto); 29 | } 30 | 31 | @PostMapping 32 | public Song addSong(@RequestBody SongDto dto) { 33 | return service.addSong(dto); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-data-jpa-embeddedid/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | datasource: 3 | url: jdbc:mysql://localhost:33099/test 4 | password: password 5 | username: root 6 | driver-class-name: "com.mysql.cj.jdbc.Driver" 7 | jpa: 8 | database-platform: org.hibernate.dialect.MySQL5InnoDBDialect 9 | hibernate: 10 | ddl-auto: update -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-data-jpa-embeddedid/src/test/resources/application-jpatest.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | jpa: 3 | show-sql: true 4 | 5 | datasource: 6 | platform: test 7 | -------------------------------------------------------------------------------- /spring-boot-data-jpa/spring-data-jpa-embeddedid/src/test/resources/data.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE SONGS 2 | ( 3 | STUDENT_ID VARCHAR2(10) NOT NULL, 4 | FAMILY_NAME VARCHAR2(80), 5 | GIVEN_NAME VARCHAR2(40), 6 | OTHER_NAME VARCHAR2(40), 7 | PREFERRED_NAME VARCHAR2(40), 8 | TITLE VARCHAR2(6), 9 | NAME_EFF_DT DATE, 10 | DOB DATE, 11 | GENDER VARCHAR2(1), 12 | DECEASED_IND VARCHAR2(1), 13 | BIRTH_COUNTRY_CD VARCHAR2(4), 14 | HOME_LANG_CD VARCHAR2(4), 15 | ABOR_TSI_CD VARCHAR2(2), 16 | CONSOL_IND VARCHAR2(1), 17 | CONSOL_TO_STU_ID VARCHAR2(10), 18 | PREF_DISAB_INFO_IND VARCHAR2(1) 19 | --SAMS_MOD_DT DATE NOT NULL, 20 | --UPDATE_TS TIMESTAMP(6) NOT NULL, 21 | --UPDATE_BY VARCHAR2(64) NOT NULL, 22 | -- You can add these in when/if you need them 23 | --CONSTRAINT SAMS_STUDENT_FK2 FOREIGN KEY (BIRTH_COUNTRY_CD) REFERENCES SAMS_COUNTRY, 24 | --CONSTRAINT SAMS_STUDENT_FK1 FOREIGN KEY (ABOR_TSI_CD) REFERENCES SAMS_ABOR_TSI, 25 | --CONSTRAINT SAMS_STUDENT_FK3 FOREIGN KEY (HOME_LANG_CD) REFERENCES SAMS_HOME_LANG 26 | ); -------------------------------------------------------------------------------- /spring-boot-dog-service/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot Sample (Dogs Service) 2 | This is a Spring Boot backed Dogs Service which provides default CRUD operations for DOG. 3 | It uses a MockDataProvider to persist and retrieve Dog details. 4 | 5 | ## This repository is part of [amitph.com](https://www.amitph.com/) tutorials. 6 | > The Dogs-service is used as a Source Code example for [Spring Boot Rest Service](https://www.amitph.com/spring-boot-rest-service/) 7 | 8 | 9 | ## How to Run 10 | 11 | ### Git Checkout 12 | ``` 13 | ~ git clone https://github.com/amitrp/spring-examples.git 14 | ~ cd spring-examples/spring-boot-dog-service 15 | ``` 16 | 17 | ### Launch application 18 | ``` 19 | ~ mvn spring-boot:run 20 | ``` 21 | 22 | -------------------------------------------------------------------------------- /spring-boot-dog-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | spring-examples 9 | com.amitph.spring 10 | 1.0-SNAPSHOT 11 | 12 | 13 | spring-boot-dog-service 14 | 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter-web 19 | 20 | 21 | 22 | 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-maven-plugin 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /spring-boot-dog-service/src/main/java/com/amitph/spring/dogs/Application.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.dogs; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Application { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(Application.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /spring-boot-dog-service/src/main/java/com/amitph/spring/dogs/model/DogDto.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.dogs.model; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class DogDto { 7 | private long id; 8 | private String name; 9 | private int age; 10 | } 11 | -------------------------------------------------------------------------------- /spring-boot-dog-service/src/main/java/com/amitph/spring/dogs/repo/Dog.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.dogs.repo; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | @Data 7 | @AllArgsConstructor(staticName = "of") 8 | public class Dog { 9 | private long id; 10 | private String name; 11 | private int age; 12 | } 13 | -------------------------------------------------------------------------------- /spring-boot-dog-service/src/main/java/com/amitph/spring/dogs/repo/MockDogProvider.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.dogs.repo; 2 | 3 | import com.amitph.spring.dogs.model.DogDto; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | public class MockDogProvider { 10 | private List mockDogStore; 11 | 12 | public MockDogProvider() { 13 | mockDogStore = new ArrayList<>(); 14 | mockDogStore.add(Dog.of(1, "Benji", 10)); 15 | mockDogStore.add(Dog.of(2, "Baxter", 9)); 16 | mockDogStore.add(Dog.of(3, "Brinkley", 8)); 17 | mockDogStore.add(Dog.of(4, "Daisy", 10)); 18 | mockDogStore.add(Dog.of(5, "Cujo", 12)); 19 | } 20 | 21 | public List getDogs() { 22 | return mockDogStore; 23 | } 24 | 25 | public Dog findDogById(long id) { 26 | for (Dog dog : mockDogStore) { 27 | if (dog.getId() == id) { 28 | return dog; 29 | } 30 | } 31 | return null; 32 | } 33 | 34 | public void add(DogDto dto) { 35 | mockDogStore.add(Dog.of(dto.getId(), dto.getName(), dto.getAge())); 36 | } 37 | 38 | public void delete(long id) { 39 | int idxToDelete = -1; 40 | for (int idx = 0; idx < mockDogStore.size(); idx++) { 41 | if (mockDogStore.get(idx).getId() == id) { 42 | idxToDelete = idx; 43 | break; 44 | } 45 | } 46 | if (idxToDelete != -1) mockDogStore.remove(idxToDelete); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /spring-boot-dog-service/src/main/java/com/amitph/spring/dogs/service/DogsService.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.dogs.service; 2 | 3 | import com.amitph.spring.dogs.model.DogDto; 4 | import com.amitph.spring.dogs.repo.Dog; 5 | import com.amitph.spring.dogs.repo.MockDogProvider; 6 | import java.util.List; 7 | import lombok.RequiredArgsConstructor; 8 | import lombok.Setter; 9 | import org.springframework.stereotype.Component; 10 | 11 | @Component 12 | @RequiredArgsConstructor 13 | @Setter 14 | public class DogsService { 15 | private final MockDogProvider mockDogProvider; 16 | 17 | public void add(DogDto dto) { 18 | mockDogProvider.add(dto); 19 | } 20 | 21 | public void delete(long id) { 22 | mockDogProvider.delete(id); 23 | } 24 | 25 | public List getDogs() { 26 | return mockDogProvider.getDogs(); 27 | } 28 | 29 | public Dog getDogById(long id) { 30 | return mockDogProvider.findDogById(id); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /spring-boot-dog-service/src/main/java/com/amitph/spring/dogs/web/DogsController.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.dogs.web; 2 | 3 | import com.amitph.spring.dogs.model.DogDto; 4 | import com.amitph.spring.dogs.repo.Dog; 5 | import com.amitph.spring.dogs.service.DogsService; 6 | import java.util.List; 7 | import lombok.RequiredArgsConstructor; 8 | import lombok.Setter; 9 | import org.springframework.web.bind.annotation.DeleteMapping; 10 | import org.springframework.web.bind.annotation.GetMapping; 11 | import org.springframework.web.bind.annotation.PathVariable; 12 | import org.springframework.web.bind.annotation.PostMapping; 13 | import org.springframework.web.bind.annotation.RequestBody; 14 | import org.springframework.web.bind.annotation.RequestMapping; 15 | import org.springframework.web.bind.annotation.RestController; 16 | 17 | @RestController 18 | @RequestMapping("/dogs") 19 | @RequiredArgsConstructor 20 | @Setter 21 | public class DogsController { 22 | private final DogsService service; 23 | 24 | @GetMapping 25 | public List getDogs() { 26 | return service.getDogs(); 27 | } 28 | 29 | @PostMapping 30 | public void postDogs(@RequestBody DogDto dto) { 31 | service.add(dto); 32 | } 33 | 34 | @GetMapping("/{id}") 35 | public Dog getById(@PathVariable(required = true) long id) { 36 | return service.getDogById(id); 37 | } 38 | 39 | @DeleteMapping("/{id}") 40 | public void delete(@PathVariable(required = true) long id) { 41 | service.delete(id); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /spring-boot-dog-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amitrp/spring-examples/53a1272bccaaf52457c0fdd2cfbf441d12e76a91/spring-boot-dog-service/src/main/resources/application.yml -------------------------------------------------------------------------------- /spring-boot-exit-codes/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot Application Exit Codes Examples 2 | Example of customizing the exit codes returned by Spring Boot Application 3 | These examples are part of Spring Boot + Spring Data Tutorials on [amitph.com](https://www.amitph.com/) 4 | 5 | ## List of Tutorials 6 | - [Spring Boot Exit Codes Examples with Exception Mapping](https://www.amitph.com/spring-boot-exit-codes/) 7 | 8 | ## How to Use 9 | 10 | #### Clone this Git repository 11 | 12 | ``` 13 | git clone https://github.com/amitrp/spring-examples.git 14 | cd spring-example/spring-boot-exit-codes/ 15 | ``` 16 | 17 | #### Run the Application 18 | ``` 19 | mvn spring-boot:run 20 | ``` -------------------------------------------------------------------------------- /spring-boot-exit-codes/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | spring-examples 7 | com.amitph.spring 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | spring-boot-exit-codes 13 | 14 | 15 | 16 | org.springframework.boot 17 | spring-boot-starter-web 18 | 19 | 20 | 21 | 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-maven-plugin 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /spring-boot-exit-codes/src/main/java/com/amitph/spring/exitcode/Application.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.exitcode; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Application { 8 | public static void main(String[] args) { 9 | SpringApplication.run(Application.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /spring-boot-exit-codes/src/main/java/com/amitph/spring/exitcode/ExitCodeListener.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.exitcode; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.boot.ExitCodeEvent; 5 | import org.springframework.context.event.EventListener; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Slf4j 9 | @Component 10 | public class ExitCodeListener { 11 | 12 | @EventListener 13 | public void listenExitCodes(ExitCodeEvent event) { 14 | log.info("Exiting with code: {}", event.getExitCode()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /spring-boot-exit-codes/src/main/java/com/amitph/spring/exitcode/ShutdownWithStaticExitCode.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.exitcode; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.boot.ApplicationRunner; 5 | import org.springframework.boot.CommandLineRunner; 6 | import org.springframework.boot.ExitCodeExceptionMapper; 7 | import org.springframework.boot.SpringApplication; 8 | import org.springframework.context.ApplicationContext; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | 12 | @Configuration 13 | @RequiredArgsConstructor 14 | public class ShutdownWithStaticExitCode { 15 | private final ApplicationContext applicationContext; 16 | 17 | /* 18 | * Next two are the examples CommandLineRunner and ApplicationRunner 19 | * Comment either of them, and they work similarly 20 | */ 21 | @Bean 22 | public CommandLineRunner commandLineRunner() { 23 | return args -> { 24 | if (args.length == 0) { 25 | throw new IllegalArgumentException("Illegal argument received"); 26 | } 27 | 28 | long value = Long.parseLong(args[0]); 29 | if (value < 100) { 30 | throw new ValueTooSmallException("Value should be >= 100"); 31 | } 32 | 33 | System.exit(SpringApplication.exit(applicationContext, () -> 11)); 34 | }; 35 | } 36 | 37 | @Bean 38 | public ApplicationRunner applicationRunner() { 39 | return args -> { 40 | System.out.println("application runner"); 41 | if (args.getSourceArgs().length == 0) { 42 | throw new IllegalArgumentException("Illegal argument received"); 43 | } 44 | long value = Long.parseLong(args.getSourceArgs()[0]); 45 | 46 | if (value < 100) { 47 | throw new ValueTooSmallException("Value should be >= 100"); 48 | } 49 | 50 | System.exit(SpringApplication.exit(applicationContext, () -> 11)); 51 | }; 52 | } 53 | 54 | /* 55 | * If you are trying ValueTooSmallException, comment the next 56 | * ExitCodeExceptionMapper factory method 57 | */ 58 | @Bean 59 | public ExitCodeExceptionMapper exceptionBasedExitCode() { 60 | return exception -> { 61 | if (exception.getCause() instanceof NumberFormatException) { 62 | return 30; 63 | } 64 | if (exception.getCause() instanceof IllegalArgumentException) { 65 | return 20; 66 | } 67 | return 99; 68 | }; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /spring-boot-exit-codes/src/main/java/com/amitph/spring/exitcode/ValueTooSmallException.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.exitcode; 2 | 3 | import org.springframework.boot.ExitCodeGenerator; 4 | 5 | public class ValueTooSmallException extends RuntimeException implements ExitCodeGenerator { 6 | 7 | public ValueTooSmallException(String msg) { 8 | super(msg); 9 | } 10 | 11 | @Override 12 | public int getExitCode() { 13 | return 40; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /spring-boot-exit-codes/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | logging: 2 | level: 3 | root: INFO 4 | com: 5 | amitph: DEBUG 6 | pattern: 7 | console: "%-5p | [%thread] %logger{5}:%L - %msg%n" 8 | -------------------------------------------------------------------------------- /spring-boot-jpa-dog-service/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot + Spring Data JPA Sample (Dogs Service) 2 | This is a Spring Boot RESTful DogService for performing simple CRUD operations on Dog 3 | 4 | ## This repository is part of [amitph.com](https://www.amitph.com) tutorials. 5 | > The dogs-service-jpa will be used as a Source Code example for: 6 | > - **[Spring Data and JPA Tutorial](https://www.amitph.com/spring-data-and-jpa-tutorial/)** 7 | > - **[Wildcard Queries with Spring Data JPA](https://www.amitph.com/spring-data-and-jpa-tutorial/)** 8 | > - **[Spring Boot Runners – Application Runner and Command Line Runner](https://www.amitph.com/spring-boot-runners/)** 9 | 10 | ## Spring Data JPA Repository Implementations 11 | - [DogsRepository.java](src/main/java/com/amitph/spring/dogs/repo/DogsRepository.java): Simple CRUD Repository to perform basic operations on Dog table. 12 | - [DogsRepositoryWildcardWithQuery.java](src/main/java/com/amitph/spring/dogs/repo/DogsRepositoryWildcardWithQuery.java): Example of executing wildcard queries (LIKE, NOT LIKE, Starts With, Ends With) using @Query annotation. 13 | - [DogsRepositoryWildcardWithQueryMethods.java](src/main/java/com/amitph/spring/dogs/repo/DogsRepositoryWildcardWithQueryMethods.java): Example of performing wildcard searches (LIKE, NOT LIKE, Starts With, and Ends With) using Spring Data JPA Query methods. 14 | 15 | ## Spring Boot Runner Implementations 16 | - [ApplicationRunnerImpl.java](src/main/java/com/amitph/spring/dogs/ApplicationRunnerImpl.java) 17 | - [CommandLineRunnerImpl.java](src/main/java/com/amitph/spring/dogs/CommandLineRunnerImpl.java) 18 | 19 | ## How to Run 20 | 21 | ### Git Checkout 22 | ``` 23 | ~ git clone https://github.com/amitrp/spring-examples.git 24 | ~ cd spring-examples/spring-boot-jpa-dog-service 25 | ``` 26 | 27 | ### To launch a MySQL instance (Optional) 28 | ``` 29 | ~ docker-compose -f docker/docker-compose.yml up -d 30 | ``` 31 | 32 | ### Launch application 33 | ``` 34 | ~ mvn spring-boot:run 35 | ``` 36 | -------------------------------------------------------------------------------- /spring-boot-jpa-dog-service/docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | mysql: 5 | restart: always 6 | image: mysql:latest 7 | container_name: my-sql-latest 8 | environment: 9 | - MYSQL_DATABASE=dogs 10 | - MYSQL_ROOT_PASSWORD=password 11 | ports: 12 | - 33080:3306 -------------------------------------------------------------------------------- /spring-boot-jpa-dog-service/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | spring-examples 9 | com.amitph.spring 10 | 1.0-SNAPSHOT 11 | 12 | 13 | spring-boot-jpa-dog-service 14 | 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter-web 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-data-jpa 23 | 24 | 25 | com.mysql 26 | mysql-connector-j 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-test 31 | test 32 | 33 | 34 | com.h2database 35 | h2 36 | test 37 | 38 | 39 | org.testng 40 | testng 41 | 7.7.0 42 | test 43 | 44 | 45 | 46 | 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-maven-plugin 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /spring-boot-jpa-dog-service/src/main/java/com/amitph/spring/dogs/Application.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.dogs; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Application { 8 | public static void main(String[] args) { 9 | SpringApplication.run(Application.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /spring-boot-jpa-dog-service/src/main/java/com/amitph/spring/dogs/ApplicationRunnerImpl.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.dogs; 2 | 3 | import java.util.Arrays; 4 | import org.springframework.boot.ApplicationArguments; 5 | import org.springframework.boot.ApplicationRunner; 6 | import org.springframework.core.annotation.Order; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Order(2) 10 | @Component 11 | public class ApplicationRunnerImpl implements ApplicationRunner { 12 | @Override 13 | public void run(ApplicationArguments args) throws Exception { 14 | System.out.println("ApplicationRunner, printing all arguments..."); 15 | Arrays.stream(args.getSourceArgs()).forEach(System.out::println); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /spring-boot-jpa-dog-service/src/main/java/com/amitph/spring/dogs/CommandLineRunnerImpl.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.dogs; 2 | 3 | import java.util.Arrays; 4 | import org.springframework.boot.CommandLineRunner; 5 | import org.springframework.core.annotation.Order; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Order(1) 9 | @Component 10 | public class CommandLineRunnerImpl implements CommandLineRunner { 11 | @Override 12 | public void run(String... args) throws Exception { 13 | System.out.println("CommandLineRunner, printing all arguments..."); 14 | Arrays.stream(args).forEach(System.out::println); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /spring-boot-jpa-dog-service/src/main/java/com/amitph/spring/dogs/model/DogDto.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.dogs.model; 2 | 3 | public class DogDto { 4 | private long id; 5 | private String name; 6 | private int age; 7 | 8 | public long getId() { 9 | return id; 10 | } 11 | 12 | public void setId(long id) { 13 | this.id = id; 14 | } 15 | 16 | public String getName() { 17 | return name; 18 | } 19 | 20 | public void setName(String name) { 21 | this.name = name; 22 | } 23 | 24 | public int getAge() { 25 | return age; 26 | } 27 | 28 | public void setAge(int age) { 29 | this.age = age; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /spring-boot-jpa-dog-service/src/main/java/com/amitph/spring/dogs/repo/Dog.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.dogs.repo; 2 | 3 | import jakarta.persistence.Entity; 4 | import jakarta.persistence.GeneratedValue; 5 | import jakarta.persistence.GenerationType; 6 | import jakarta.persistence.Id; 7 | 8 | @Entity 9 | public class Dog { 10 | @Id 11 | @GeneratedValue(strategy = GenerationType.IDENTITY) 12 | private long id; 13 | 14 | private String name; 15 | private int age; 16 | 17 | public Dog() {} 18 | 19 | public Dog(String name, int age) { 20 | this.name = name; 21 | this.age = age; 22 | } 23 | 24 | public long getId() { 25 | return id; 26 | } 27 | 28 | public void setId(long id) { 29 | this.id = id; 30 | } 31 | 32 | public String getName() { 33 | return name; 34 | } 35 | 36 | public void setName(String name) { 37 | this.name = name; 38 | } 39 | 40 | public int getAge() { 41 | return age; 42 | } 43 | 44 | public void setAge(int age) { 45 | this.age = age; 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return "id: " + this.id + ", name: " + this.name + ", age: " + this.age; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /spring-boot-jpa-dog-service/src/main/java/com/amitph/spring/dogs/repo/DogsRepository.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.dogs.repo; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | import org.springframework.stereotype.Repository; 5 | 6 | @Repository 7 | public interface DogsRepository extends CrudRepository {} 8 | -------------------------------------------------------------------------------- /spring-boot-jpa-dog-service/src/main/java/com/amitph/spring/dogs/repo/DogsRepositoryWildcardWithQuery.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.dogs.repo; 2 | 3 | import java.util.List; 4 | import org.springframework.data.jpa.repository.Query; 5 | import org.springframework.data.repository.CrudRepository; 6 | import org.springframework.data.repository.query.Param; 7 | import org.springframework.stereotype.Repository; 8 | 9 | @Repository 10 | public interface DogsRepositoryWildcardWithQuery extends CrudRepository { 11 | 12 | @Query("FROM Dog d WHERE d.name LIKE %:name%") 13 | List findByNameLike(@Param("name") String name); 14 | 15 | @Query("FROM Dog d WHERE d.name NOT LIKE %:name%") 16 | List findByNameNotLike(@Param("name") String name); 17 | 18 | @Query("FROM Dog d WHERE d.name LIKE :name%") 19 | List findByNameStartsWith(@Param("name") String name); 20 | 21 | @Query("FROM Dog d WHERE d.name LIKE %:name") 22 | List findByNameEndsWith(@Param("name") String name); 23 | } 24 | -------------------------------------------------------------------------------- /spring-boot-jpa-dog-service/src/main/java/com/amitph/spring/dogs/repo/DogsRepositoryWildcardWithQueryMethods.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.dogs.repo; 2 | 3 | import java.util.List; 4 | import org.springframework.data.repository.CrudRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface DogsRepositoryWildcardWithQueryMethods extends CrudRepository { 9 | 10 | List findByNameContaining(String name); 11 | 12 | List findByNameNotContaining(String name); 13 | 14 | List findByNameStartsWith(String name); 15 | 16 | List findByNameEndsWith(String name); 17 | } 18 | -------------------------------------------------------------------------------- /spring-boot-jpa-dog-service/src/main/java/com/amitph/spring/dogs/service/DogNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.dogs.service; 2 | 3 | public class DogNotFoundException extends RuntimeException { 4 | public DogNotFoundException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /spring-boot-jpa-dog-service/src/main/java/com/amitph/spring/dogs/service/DogsService.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.dogs.service; 2 | 3 | import com.amitph.spring.dogs.model.DogDto; 4 | import com.amitph.spring.dogs.repo.Dog; 5 | import com.amitph.spring.dogs.repo.DogsRepository; 6 | import java.util.List; 7 | import java.util.Optional; 8 | import lombok.RequiredArgsConstructor; 9 | import org.springframework.stereotype.Component; 10 | 11 | @Component 12 | @RequiredArgsConstructor 13 | public class DogsService { 14 | private final DogsRepository repository; 15 | 16 | public void add(DogDto dto) { 17 | repository.save(toEntity(dto)); 18 | } 19 | 20 | public void delete(long id) { 21 | repository.deleteById(id); 22 | } 23 | 24 | public List getDogs() { 25 | return (List) repository.findAll(); 26 | } 27 | 28 | public Dog getDogById(long id) { 29 | Optional optionalDog = repository.findById(id); 30 | return optionalDog.orElseThrow( 31 | () -> new DogNotFoundException("Couldn't find a Dog with id: " + id)); 32 | } 33 | 34 | private Dog toEntity(DogDto dto) { 35 | Dog entity = new Dog(); 36 | entity.setName(dto.getName()); 37 | entity.setAge(dto.getAge()); 38 | return entity; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /spring-boot-jpa-dog-service/src/main/java/com/amitph/spring/dogs/web/DogsController.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.dogs.web; 2 | 3 | import com.amitph.spring.dogs.model.DogDto; 4 | import com.amitph.spring.dogs.repo.Dog; 5 | import com.amitph.spring.dogs.service.DogsService; 6 | import java.util.List; 7 | import lombok.RequiredArgsConstructor; 8 | import org.springframework.web.bind.annotation.DeleteMapping; 9 | import org.springframework.web.bind.annotation.GetMapping; 10 | import org.springframework.web.bind.annotation.PathVariable; 11 | import org.springframework.web.bind.annotation.PostMapping; 12 | import org.springframework.web.bind.annotation.RequestBody; 13 | import org.springframework.web.bind.annotation.RequestMapping; 14 | import org.springframework.web.bind.annotation.RestController; 15 | 16 | @RestController 17 | @RequestMapping("/dogs") 18 | @RequiredArgsConstructor 19 | public class DogsController { 20 | private final DogsService service; 21 | 22 | @GetMapping 23 | public List getDogs() { 24 | return service.getDogs(); 25 | } 26 | 27 | @PostMapping 28 | public void postDogs(@RequestBody DogDto dto) { 29 | service.add(dto); 30 | } 31 | 32 | @GetMapping("/{id}") 33 | public Dog getById(@PathVariable(required = true) long id) { 34 | return service.getDogById(id); 35 | } 36 | 37 | @DeleteMapping("/{id}") 38 | public void delete(@PathVariable(required = true) long id) { 39 | service.delete(id); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /spring-boot-jpa-dog-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | datasource: 3 | url: jdbc:mysql://localhost:33080/dogs 4 | password: password 5 | username: root 6 | driver-class-name: "com.mysql.cj.jdbc.Driver" 7 | jpa: 8 | database-platform: org.hibernate.dialect.MySQL5InnoDBDialect 9 | hibernate: 10 | ddl-auto: update -------------------------------------------------------------------------------- /spring-boot-jpa-dog-service/src/main/resources/data.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE DOG 2 | ( 3 | id NUMBER auto_increment, 4 | name VARCHAR(50), 5 | age NUMBER 6 | ); -------------------------------------------------------------------------------- /spring-boot-jpa-dog-service/src/test/resources/application-jpatest.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | jpa: 3 | show-sql: true 4 | generate-ddl: true 5 | hibernate: 6 | ddl-auto: update 7 | 8 | datasource: 9 | platform: test -------------------------------------------------------------------------------- /spring-boot-read-properties/README.md: -------------------------------------------------------------------------------- 1 | # Reading Properties in Spring Boot using @ConfigurationProperties 2 | 3 | _Covers a different examples of reading and binding properties file or yaml file configurations in a Java Class._ 4 | 5 | ## This repository is part of [amitph.com](https://www.amitph.com/) tutorials. 6 | 7 | > - [Using @ConfigurationProperties in Spring Boot](https://www.amitph.com/spring-boot-configuration-properties/) 8 | > - [Reading Nested Properties in Spring Boot](https://www.amitph.com/spring-boot-nested-configuration-properties/) 9 | > - [YAML to Map with Spring Boot](https://www.amitph.com/spring-boot-yaml-to-map/) 10 | > - [YAML to Java List of Objects in Spring Boot](https://www.amitph.com/spring-boot-yaml-to-list/) 11 | > - [Validations with @ConfigurationProperties in Spring Boot](https://www.amitph.com/spring-boot-configuration-properties-validation/) 12 | 13 | ## Examples 14 | 15 | #### Reading Simple Properties File 16 | 17 | - [SimpleProperties.java](src/main/java/com/amitph/spring/properties/SimpleProperties.java) 18 | 19 | #### Reading properties subsets using `prefix` attribute 20 | 21 | - [UserServiceProperties.java](src/main/java/com/amitph/spring/properties/prefixed/UserServiceProperties.java) 22 | - [LoginServiceProperties.java](src/main/java/com/amitph/spring/properties/prefixed/LoginServiceProperties.java) 23 | 24 | #### Binding properties of a different names 25 | 26 | - [DifferentlyNamedProperties.java](src/main/java/com/amitph/spring/properties/prefixed/DifferentlyNamedProperties.java) 27 | 28 | #### Setter Injected and Constructor Injected Properties 29 | 30 | - [SetterBasedLoginProperties.java](src/main/java/com/amitph/spring/properties/SetterBasedLoginProperties.java) 31 | - [ConstructorBasedLoginProperties.java](src/main/java/com/amitph/spring/properties/ConstructorBasedLoginProperties.java) 32 | 33 | #### Nested Properties 34 | 35 | - [NestedProperties.java](src/main/java/com/amitph/spring/properties/nested/NestedProperties.java) 36 | 37 | #### Nested Properties using Java Inner Classes 38 | 39 | - [NestedClassesProperties.java](src/main/java/com/amitph/spring/properties/nested/NestedClassesProperties.java) 40 | 41 | #### Reading YAML/Properties as Java HashMap 42 | 43 | - [MapProperties.java](src/main/java/com/amitph/spring/properties/map/MapProperties.java) 44 | 45 | #### YAML/Properties as Java Nested Hashmap or MultiValueMap 46 | 47 | - [NestedMapProperties.java](src/main/java/com/amitph/spring/properties/map/NestedMapProperties.java) 48 | 49 | #### YAML/Properties as Java plain List 50 | 51 | - [PlainListProperties.java](src/main/java/com/amitph/spring/properties/list/PlainListProperties.java) 52 | 53 | #### YAML/Properties as Java List of Map 54 | 55 | - [ListOfMapProperties.java](src/main/java/com/amitph/spring/properties/list/ListOfMapProperties.java) 56 | 57 | #### YAML/Properties as Java List of Objects 58 | 59 | - [ListOfObjectProperties.java](src/main/java/com/amitph/spring/properties/list/ListOfObjectProperties.java) 60 | 61 | #### Validating Configuration Properties 62 | 63 | - [ValidatedProperties.java](src/main/java/com/amitph/spring/properties/validated/ValidatedProperties.java) 64 | 65 | 66 | 67 | 68 | ## How to Run 69 | ``` 70 | ~ git clone https://github.com/amitrp/spring-examples.git 71 | ~ cd spring-examples/spring-boot-read-properties 72 | ``` 73 | 74 | ### Launch application 75 | ``` 76 | ~ mvn spring-boot:run 77 | ``` 78 | 79 | -------------------------------------------------------------------------------- /spring-boot-read-properties/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 4.0.0 7 | 8 | 9 | spring-examples 10 | com.amitph.spring 11 | 1.0-SNAPSHOT 12 | 13 | 14 | spring-boot-read-properties 15 | 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-starter-web 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-validation 24 | 25 | 26 | 27 | 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-maven-plugin 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /spring-boot-read-properties/src/main/java/com/amitph/spring/Application.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring; 2 | 3 | import com.amitph.spring.properties.ConstructorBasedLoginProperties; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | 7 | /** 8 | * Uncomment @EnableConfigurationProperties, to use constructor based properties binding in {@link 9 | * ConstructorBasedLoginProperties} 10 | */ 11 | // @EnableConfigurationProperties(ConstructorBasedLoginProperties.class) 12 | @SpringBootApplication 13 | public class Application { 14 | public static void main(String[] args) { 15 | SpringApplication.run(Application.class, args); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /spring-boot-read-properties/src/main/java/com/amitph/spring/properties/ConstructorBasedLoginProperties.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.properties; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | 5 | /* 6 | * To use constructor binding 7 | * 1. Uncomment next two lines 8 | * 2. Explicitly enable configuration class using 9 | * @EnableConfigurationProperties or with @ConfigurationPropertiesScan 10 | */ 11 | // @ConstructorBinding 12 | @ConfigurationProperties(prefix = "login-service") 13 | public class ConstructorBasedLoginProperties { 14 | private String loginUrl; 15 | private String username; 16 | private String password; 17 | 18 | public ConstructorBasedLoginProperties(String loginUrl, String username, String password) { 19 | this.loginUrl = loginUrl; 20 | this.username = username; 21 | this.password = password; 22 | } 23 | 24 | @Override 25 | public String toString() { 26 | return "loginUrl: " + loginUrl + ",\nusername: " + username + ",\npassword: " + password; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /spring-boot-read-properties/src/main/java/com/amitph/spring/properties/SetterBasedLoginProperties.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.properties; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | @Configuration 7 | @ConfigurationProperties(prefix = "login-service") 8 | public class SetterBasedLoginProperties { 9 | private String loginUrl; 10 | private String username; 11 | private String password; 12 | 13 | public SetterBasedLoginProperties() {} 14 | 15 | public void setLoginUrl(String loginUrl) { 16 | this.loginUrl = loginUrl; 17 | } 18 | 19 | public void setUsername(String username) { 20 | this.username = username; 21 | } 22 | 23 | public void setPassword(String password) { 24 | this.password = password; 25 | } 26 | 27 | @Override 28 | public String toString() { 29 | return "loginUrl: " + loginUrl + ",\nusername: " + username + ",\npassword: " + password; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /spring-boot-read-properties/src/main/java/com/amitph/spring/properties/SimpleProperties.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.properties; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | @Data 8 | @Configuration 9 | @ConfigurationProperties 10 | public class SimpleProperties { 11 | private String defaultUsername; 12 | private String defaultPassword; 13 | private int connectionTimeout; 14 | 15 | @Override 16 | public String toString() { 17 | return "defaultUsername: " 18 | + defaultUsername 19 | + ",\ndefaultPassword: " 20 | + defaultPassword 21 | + ",\nconnectionTimeout: " 22 | + connectionTimeout; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /spring-boot-read-properties/src/main/java/com/amitph/spring/properties/list/ListOfMapProperties.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.properties.list; 2 | 3 | import static java.util.stream.Collectors.joining; 4 | 5 | import java.util.List; 6 | import java.util.Map; 7 | import lombok.Data; 8 | import org.springframework.boot.context.properties.ConfigurationProperties; 9 | import org.springframework.context.annotation.Configuration; 10 | 11 | @Data 12 | @Configuration 13 | @ConfigurationProperties(prefix = "config") 14 | public class ListOfMapProperties { 15 | private List> miscellaneous; 16 | 17 | @Override 18 | public String toString() { 19 | return "miscellaneous: \n\t" 20 | + miscellaneous.stream().map(Object::toString).collect(joining("\n\t")); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /spring-boot-read-properties/src/main/java/com/amitph/spring/properties/list/ListOfObjectProperties.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.properties.list; 2 | 3 | import static java.util.stream.Collectors.joining; 4 | 5 | import java.util.List; 6 | import lombok.Data; 7 | import org.springframework.boot.context.properties.ConfigurationProperties; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | @Data 11 | @Configuration 12 | @ConfigurationProperties(prefix = "config") 13 | public class ListOfObjectProperties { 14 | private List services; 15 | 16 | @Data 17 | public static class Service { 18 | private String name; 19 | private String url; 20 | 21 | @Override 22 | public String toString() { 23 | return "name: " + name + ", url: " + url; 24 | } 25 | } 26 | 27 | @Override 28 | public String toString() { 29 | return "services: \n\t" + services.stream().map(Object::toString).collect(joining("\n\t")); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /spring-boot-read-properties/src/main/java/com/amitph/spring/properties/list/PlainListProperties.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.properties.list; 2 | 3 | import java.util.List; 4 | import lombok.Data; 5 | import org.springframework.boot.context.properties.ConfigurationProperties; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | @Data 9 | @Configuration 10 | @ConfigurationProperties(prefix = "config") 11 | public class PlainListProperties { 12 | private List env; 13 | 14 | @Override 15 | public String toString() { 16 | return "env: " + env; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /spring-boot-read-properties/src/main/java/com/amitph/spring/properties/map/MapProperties.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.properties.map; 2 | 3 | import static java.util.stream.Collectors.joining; 4 | 5 | import java.util.Map; 6 | import java.util.Objects; 7 | import lombok.Data; 8 | import org.springframework.boot.context.properties.ConfigurationProperties; 9 | import org.springframework.context.annotation.Configuration; 10 | 11 | @Data 12 | @Configuration 13 | @ConfigurationProperties(prefix = "spring") 14 | public class MapProperties { 15 | Map application; 16 | Map servlet; 17 | Map datasource; 18 | 19 | private static final String NEW_LINE_TAB = "\n\t"; 20 | 21 | @Override 22 | public String toString() { 23 | return "application:" 24 | + NEW_LINE_TAB 25 | + application.entrySet().stream() 26 | .map(Objects::toString) 27 | .collect(joining(NEW_LINE_TAB)) 28 | + "\nservlet:" 29 | + NEW_LINE_TAB 30 | + servlet.entrySet().stream().map(Objects::toString).collect(joining(NEW_LINE_TAB)) 31 | + "\ndatasource:" 32 | + NEW_LINE_TAB 33 | + datasource.entrySet().stream() 34 | .map(Objects::toString) 35 | .collect(joining(NEW_LINE_TAB)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /spring-boot-read-properties/src/main/java/com/amitph/spring/properties/map/NestedMapProperties.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.properties.map; 2 | 3 | import static java.util.stream.Collectors.joining; 4 | 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.Objects; 8 | import lombok.Data; 9 | import org.springframework.boot.context.properties.ConfigurationProperties; 10 | import org.springframework.context.annotation.Configuration; 11 | 12 | @Data 13 | @Configuration 14 | @ConfigurationProperties(prefix = "spring") 15 | public class NestedMapProperties { 16 | Map application; 17 | Map> servlet; 18 | Map datasource; 19 | Map> profiles; 20 | 21 | private static final String NEW_LINE_TAB = "\n\t"; 22 | 23 | @Override 24 | public String toString() { 25 | return "application:" 26 | + NEW_LINE_TAB 27 | + application.entrySet().stream() 28 | .map(Objects::toString) 29 | .collect(joining(NEW_LINE_TAB)) 30 | + "\nservlet:" 31 | + NEW_LINE_TAB 32 | + servlet.entrySet().stream().map(Objects::toString).collect(joining(NEW_LINE_TAB)) 33 | + "\ndatasource:" 34 | + NEW_LINE_TAB 35 | + datasource.entrySet().stream() 36 | .map(Objects::toString) 37 | .collect(joining(NEW_LINE_TAB)) 38 | + "\nprofiles:" 39 | + NEW_LINE_TAB 40 | + profiles.entrySet().stream() 41 | .map(Objects::toString) 42 | .collect(joining(NEW_LINE_TAB)); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /spring-boot-read-properties/src/main/java/com/amitph/spring/properties/nested/LoginHeaderProperties.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.properties.nested; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class LoginHeaderProperties { 7 | private String authToken; 8 | private String content; 9 | } 10 | -------------------------------------------------------------------------------- /spring-boot-read-properties/src/main/java/com/amitph/spring/properties/nested/LoginProperties.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.properties.nested; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class LoginProperties { 7 | private String loginUrl; 8 | private String username; 9 | private String password; 10 | private LoginHeaderProperties header; 11 | } 12 | -------------------------------------------------------------------------------- /spring-boot-read-properties/src/main/java/com/amitph/spring/properties/nested/NestedClassesProperties.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.properties.nested; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | @Data 8 | @Configuration 9 | @ConfigurationProperties 10 | public class NestedClassesProperties { 11 | private LoginProperties loginService; 12 | private UserProperties userService; 13 | 14 | @Data 15 | public static class LoginProperties { 16 | private String loginUrl; 17 | private String username; 18 | private String password; 19 | private LoginHeaderProperties header; 20 | 21 | @Data 22 | public static class LoginHeaderProperties { 23 | private String authToken; 24 | private String content; 25 | } 26 | } 27 | 28 | @Data 29 | public static class UserProperties { 30 | private String url; 31 | private String username; 32 | private String password; 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return "loginService: " 38 | + "\n\tloginUrl: " 39 | + loginService.getLoginUrl() 40 | + "\n\tusername: " 41 | + loginService.getUsername() 42 | + "\n\tpassword: " 43 | + loginService.getPassword() 44 | + "\n\theader: " 45 | + "\n\t\tauthToken: " 46 | + loginService.getHeader().getAuthToken() 47 | + "\n\t\tcontent: " 48 | + loginService.getHeader().getContent() 49 | + "\nuserService: " 50 | + "\n\turl: " 51 | + userService.getUrl() 52 | + "\n\tusername: " 53 | + userService.getUsername() 54 | + "\n\tpassword: " 55 | + userService.getPassword(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /spring-boot-read-properties/src/main/java/com/amitph/spring/properties/nested/NestedProperties.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.properties.nested; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | @Data 8 | @Configuration 9 | @ConfigurationProperties 10 | public class NestedProperties { 11 | private LoginProperties loginService; 12 | private UserProperties userService; 13 | 14 | @Override 15 | public String toString() { 16 | return "loginService: " 17 | + "\n\tloginUrl: " 18 | + loginService.getLoginUrl() 19 | + "\n\tusername: " 20 | + loginService.getUsername() 21 | + "\n\tpassword: " 22 | + loginService.getPassword() 23 | + "\n\theader: " 24 | + "\n\t\tauthToken: " 25 | + loginService.getHeader().getAuthToken() 26 | + "\n\t\tcontent: " 27 | + loginService.getHeader().getContent() 28 | + "\nuserService: " 29 | + "\n\turl: " 30 | + userService.getUrl() 31 | + "\n\tusername: " 32 | + userService.getUsername() 33 | + "\n\tpassword: " 34 | + userService.getPassword(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /spring-boot-read-properties/src/main/java/com/amitph/spring/properties/nested/UserProperties.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.properties.nested; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class UserProperties { 7 | private String url; 8 | private String username; 9 | private String password; 10 | } 11 | -------------------------------------------------------------------------------- /spring-boot-read-properties/src/main/java/com/amitph/spring/properties/prefixed/DifferentlyNamedProperties.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.properties.prefixed; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | @Configuration 7 | @ConfigurationProperties(prefix = "login-service") 8 | public class DifferentlyNamedProperties { 9 | private String url; 10 | 11 | public void setLoginUrl(String loginUrl) { 12 | this.url = loginUrl; 13 | } 14 | 15 | @Override 16 | public String toString() { 17 | return "url: " + url; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /spring-boot-read-properties/src/main/java/com/amitph/spring/properties/prefixed/LoginServiceProperties.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.properties.prefixed; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | @Data 8 | @Configuration 9 | @ConfigurationProperties(prefix = "login-service") 10 | public class LoginServiceProperties { 11 | private String loginUrl; 12 | private String username; 13 | private String password; 14 | 15 | @Override 16 | public String toString() { 17 | return "loginUrl: " + loginUrl + ",\nusername: " + username + ",\npassword: " + password; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /spring-boot-read-properties/src/main/java/com/amitph/spring/properties/prefixed/UserServiceProperties.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.properties.prefixed; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | @Data 8 | @Configuration 9 | @ConfigurationProperties(prefix = "user-service") 10 | public class UserServiceProperties { 11 | private String url; 12 | private String username; 13 | private String password; 14 | 15 | @Override 16 | public String toString() { 17 | return "url: " + url + ",\nusername: " + username + ",\npassword: " + password; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /spring-boot-read-properties/src/main/java/com/amitph/spring/properties/set/SetOfObjectProperties.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.properties.set; 2 | 3 | import static java.util.stream.Collectors.joining; 4 | 5 | import java.util.Set; 6 | import lombok.Data; 7 | import org.springframework.boot.context.properties.ConfigurationProperties; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | @Data 11 | @Configuration 12 | @ConfigurationProperties(prefix = "config") 13 | public class SetOfObjectProperties { 14 | private Set services; 15 | 16 | @Data 17 | public static class Service { 18 | private String name; 19 | private String url; 20 | 21 | @Override 22 | public String toString() { 23 | return "name: " + name + ", url: " + url; 24 | } 25 | } 26 | 27 | @Override 28 | public String toString() { 29 | return "services: \n\t" + services.stream().map(Object::toString).collect(joining("\n\t")); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /spring-boot-read-properties/src/main/java/com/amitph/spring/properties/validated/ValidatedProperties.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.properties.validated; 2 | 3 | import jakarta.validation.constraints.Email; 4 | import jakarta.validation.constraints.Max; 5 | import jakarta.validation.constraints.Min; 6 | import jakarta.validation.constraints.NotBlank; 7 | import jakarta.validation.constraints.Pattern; 8 | import jakarta.validation.constraints.PositiveOrZero; 9 | import lombok.Data; 10 | import org.springframework.boot.context.properties.ConfigurationProperties; 11 | import org.springframework.context.annotation.Configuration; 12 | import org.springframework.validation.annotation.Validated; 13 | 14 | @Data 15 | @Validated 16 | @Configuration 17 | @ConfigurationProperties(prefix = "connection") 18 | public class ValidatedProperties { 19 | 20 | @NotBlank private String host; 21 | 22 | @Min(1025) 23 | @Max(65536) 24 | private int port; 25 | 26 | @PositiveOrZero private long timeout; 27 | 28 | @Email private String notify; 29 | 30 | @Pattern(regexp = "([^\\s]+(\\.(?i)(log))$)") 31 | private String errorFile; 32 | 33 | @Override 34 | public String toString() { 35 | return "host: " 36 | + host 37 | + "\nport: " 38 | + port 39 | + "\ntimeout: " 40 | + timeout 41 | + "\nnotify: " 42 | + notify 43 | + "\nerrorFile: " 44 | + errorFile; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /spring-boot-read-properties/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | default-username: default_user 2 | default-password: password_default 3 | connection-timeout: 2000 4 | 5 | login-service: 6 | login-url: https://login.example.com 7 | username: login_user 8 | password: password123 9 | header: 10 | auth_token: TOKEN 11 | content: C_TYPE 12 | 13 | user-service: 14 | url: https://users.example.com 15 | username: user_name 16 | password: strong-password 17 | 18 | spring: 19 | application: 20 | name: data-service 21 | servlet: 22 | multipart: 23 | enabled: true 24 | datasource: 25 | driverClassName: com.mysql.cj.jdbc.Driver 26 | url: jdbc:mysql://localhost:3309/test 27 | password: password 28 | username: test_user 29 | profiles: 30 | active: 31 | - dev 32 | - qa 33 | - staging 34 | - PROD 35 | 36 | config: 37 | env: 38 | - dev 39 | - qa 40 | - prod 41 | miscellaneous: 42 | - 43 | poll-frequency: 20 44 | timeout: 10 45 | max-retry: 3 46 | - 47 | log-erros: true 48 | fail-on-errors: false 49 | - 50 | publish-metrics: true 51 | metrics-frequency: 30 52 | services: 53 | - 54 | name: login-service 55 | url: http://login.example.com 56 | - 57 | name: data-service 58 | url: http://data.example.com 59 | 60 | 61 | connection: 62 | host: 192.0.2.1 63 | port: 8181 64 | timeout: 30000 65 | notify: support@example.com 66 | error-file: logs/error.log 67 | -------------------------------------------------------------------------------- /spring-boot-samples/README.md: -------------------------------------------------------------------------------- 1 | # Spring Boot Examples 2 | Examples of using various features in Spring Boot 3 | These examples are part of Spring & Spring Boot Tutorials on [amitph.com](https://www.amitph.com/) 4 | 5 | 6 | - [Spring @RequestParam Annotation with Examples](https://www.amitph.com/spring-requestparam-annotation/) 7 | - [Reading HTTP Headers in Spring REST Controller](https://www.amitph.com/spring-rest-http-header/) 8 | - [Spring @PathVariable Examples](https://www.amitph.com/spring-pathvariable/) 9 | - Spring Boot Filters Example 10 | - Getting All Spring Managed Beans 11 | - Spring Autowiring by Name and Spring Bean's Custom Naming 12 | - How to disable Spring Boot Banner 13 | 14 | 15 | ## How to Use 16 | 17 | #### Clone this Git repository 18 | 19 | ``` 20 | ~ git clone https://github.com/amitrp/spring-examples.git 21 | ~ cd spring-boot-samples/ 22 | ``` 23 | 24 | #### Run the Application 25 | ``` 26 | ~ mvn spring-boot:run 27 | ``` 28 | -------------------------------------------------------------------------------- /spring-boot-samples/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | spring-examples 9 | com.amitph.spring 10 | 1.0-SNAPSHOT 11 | 12 | 13 | spring-boot-samples 14 | 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter-web 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-actuator 23 | 24 | 25 | com.google.code.gson 26 | gson 27 | 28 | 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-test 33 | test 34 | 35 | 36 | 37 | 38 | 39 | 40 | org.springframework.boot 41 | spring-boot-maven-plugin 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /spring-boot-samples/src/main/java/com/amitph/spring/Application.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring; 2 | 3 | import com.amitph.spring.filters.RequestStatsFilter; 4 | import jakarta.servlet.Filter; 5 | import java.util.Arrays; 6 | import java.util.Map; 7 | import org.springframework.boot.SpringApplication; 8 | import org.springframework.boot.autoconfigure.SpringBootApplication; 9 | import org.springframework.context.ApplicationContext; 10 | 11 | @SpringBootApplication 12 | public class Application { 13 | private static ApplicationContext context; 14 | 15 | public static void main(String[] args) { 16 | SpringApplication app = new SpringApplication(Application.class); 17 | 18 | // Turn off the startup Spring Boot Banner 19 | // app.setBannerMode(Banner.Mode.OFF); 20 | 21 | context = app.run(args); 22 | printBeanByName(); 23 | printBeanByType(); 24 | printBeanNames(); 25 | } 26 | 27 | private static void printBeanByName() { 28 | String beanName = "requestStatsFilter"; 29 | RequestStatsFilter requestStatsFilter = (RequestStatsFilter) context.getBean(beanName); 30 | 31 | System.out.println(beanName + " of type " + requestStatsFilter.getClass()); 32 | } 33 | 34 | private static void printBeanByType() { 35 | Map filterBeans = context.getBeansOfType(Filter.class); 36 | 37 | filterBeans.forEach((s, filter) -> System.out.println(s + " of type " + filter.getClass())); 38 | } 39 | 40 | private static void printBeanNames() { 41 | String[] beansNames = context.getBeanDefinitionNames(); 42 | 43 | Arrays.stream(beansNames) 44 | .forEach( 45 | beansName -> { 46 | Object bean = context.getBean(beansName); 47 | System.out.println(beansName + " of type " + bean.getClass()); 48 | }); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /spring-boot-samples/src/main/java/com/amitph/spring/ApplicationConfig.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring; 2 | 3 | import com.amitph.spring.filters.V2LoggingFilter; 4 | import com.amitph.spring.naming.Heart; 5 | import com.amitph.spring.naming.Pentagon; 6 | import com.amitph.spring.naming.Shape; 7 | import com.amitph.spring.naming.Square; 8 | import org.springframework.beans.factory.annotation.Qualifier; 9 | import org.springframework.boot.web.servlet.FilterRegistrationBean; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | 13 | @Configuration 14 | public class ApplicationConfig { 15 | 16 | @Bean 17 | public FilterRegistrationBean v2LoggingFilter() { 18 | FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); 19 | 20 | registrationBean.setFilter(new V2LoggingFilter()); 21 | registrationBean.addUrlPatterns("/v2/*"); 22 | registrationBean.setOrder(3); 23 | 24 | return registrationBean; 25 | } 26 | 27 | @Bean 28 | public Shape squareType() { 29 | return new Square(); 30 | } 31 | 32 | @Bean("pentagonShape") 33 | public Shape shape() { 34 | return new Pentagon(); 35 | } 36 | 37 | @Bean 38 | @Qualifier("heartBean") 39 | public Shape heartType() { 40 | return new Heart(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /spring-boot-samples/src/main/java/com/amitph/spring/filters/LoggingFilter.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.filters; 2 | 3 | import jakarta.servlet.Filter; 4 | import jakarta.servlet.FilterChain; 5 | import jakarta.servlet.ServletException; 6 | import jakarta.servlet.ServletRequest; 7 | import jakarta.servlet.ServletResponse; 8 | import jakarta.servlet.http.HttpServletRequest; 9 | import java.io.IOException; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.springframework.core.annotation.Order; 12 | import org.springframework.stereotype.Component; 13 | 14 | @Slf4j 15 | @Component 16 | @Order(2) 17 | public class LoggingFilter implements Filter { 18 | @Override 19 | public void doFilter( 20 | ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) 21 | throws IOException, ServletException { 22 | 23 | HttpServletRequest request = (HttpServletRequest) servletRequest; 24 | 25 | log.info("Logging request: request: {}", request.getRequestURI()); 26 | filterChain.doFilter(servletRequest, servletResponse); 27 | log.info("Logging response: response: {}", servletResponse.getContentType()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /spring-boot-samples/src/main/java/com/amitph/spring/filters/RequestStatsFilter.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.filters; 2 | 3 | import jakarta.servlet.*; 4 | import jakarta.servlet.http.HttpServletRequest; 5 | import java.io.IOException; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.core.annotation.Order; 8 | import org.springframework.stereotype.Component; 9 | 10 | @Slf4j 11 | @Component 12 | @Order(1) 13 | public class RequestStatsFilter implements Filter { 14 | 15 | @Override 16 | public void doFilter( 17 | ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) 18 | throws IOException, ServletException { 19 | HttpServletRequest request = (HttpServletRequest) servletRequest; 20 | 21 | log.info("Incrementing {} request counter", request.getMethod()); 22 | // Generate stats here 23 | filterChain.doFilter(servletRequest, servletResponse); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /spring-boot-samples/src/main/java/com/amitph/spring/filters/V2LoggingFilter.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.filters; 2 | 3 | import jakarta.servlet.Filter; 4 | import jakarta.servlet.FilterChain; 5 | import jakarta.servlet.ServletException; 6 | import jakarta.servlet.ServletRequest; 7 | import jakarta.servlet.ServletResponse; 8 | import jakarta.servlet.http.HttpServletRequest; 9 | import java.io.IOException; 10 | import lombok.extern.slf4j.Slf4j; 11 | 12 | @Slf4j 13 | public class V2LoggingFilter implements Filter { 14 | @Override 15 | public void doFilter( 16 | ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) 17 | throws IOException, ServletException { 18 | 19 | HttpServletRequest request = (HttpServletRequest) servletRequest; 20 | 21 | log.info("V2 Logging request: request: {}", request.getRequestURI()); 22 | filterChain.doFilter(servletRequest, servletResponse); 23 | log.info("V2 Logging response: response: {}", servletResponse.getContentType()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /spring-boot-samples/src/main/java/com/amitph/spring/headers/RequestHeadersController.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.headers; 2 | 3 | import com.amitph.spring.model.Student; 4 | import jakarta.servlet.http.HttpServletRequest; 5 | import java.util.Enumeration; 6 | import java.util.Map; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.http.HttpHeaders; 9 | import org.springframework.util.MultiValueMap; 10 | import org.springframework.web.bind.annotation.PostMapping; 11 | import org.springframework.web.bind.annotation.RequestBody; 12 | import org.springframework.web.bind.annotation.RequestHeader; 13 | import org.springframework.web.bind.annotation.RestController; 14 | 15 | @Slf4j 16 | @RestController 17 | public class RequestHeadersController { 18 | @PostMapping("v1/students") 19 | public void postStudent( 20 | @RequestHeader Map headers, @RequestBody Student student) { 21 | 22 | log.info("Header values: {}", headers); 23 | } 24 | 25 | @PostMapping("/v2/students") 26 | public void postStudent(@RequestHeader HttpHeaders headers, @RequestBody Student student) { 27 | 28 | log.info("Header values: {}", headers); 29 | } 30 | 31 | @PostMapping("/v3/students") 32 | public void postStudent( 33 | @RequestHeader MultiValueMap headers, @RequestBody Student student) { 34 | 35 | log.info("Header values: {}", headers); 36 | } 37 | 38 | @PostMapping("/v4/students") 39 | public void postStudent( 40 | @RequestHeader(value = "content-type", defaultValue = "application/json") 41 | String contentType, 42 | @RequestBody Student student) { 43 | 44 | log.info("Header value - Content-Type: {}", contentType); 45 | } 46 | 47 | @PostMapping("/v5/students") 48 | public void postStudent(HttpServletRequest request, @RequestBody Student student) { 49 | 50 | Enumeration headerNames = request.getHeaderNames(); 51 | while (headerNames.hasMoreElements()) { 52 | String key = headerNames.nextElement(); 53 | log.info(key + ": " + request.getHeader(key)); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /spring-boot-samples/src/main/java/com/amitph/spring/model/Student.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.model; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class Student { 7 | private String id; 8 | } 9 | -------------------------------------------------------------------------------- /spring-boot-samples/src/main/java/com/amitph/spring/naming/Circle.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.naming; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | @Component 6 | public class Circle extends Shape {} 7 | -------------------------------------------------------------------------------- /spring-boot-samples/src/main/java/com/amitph/spring/naming/Heart.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.naming; 2 | 3 | public class Heart extends Shape {} 4 | -------------------------------------------------------------------------------- /spring-boot-samples/src/main/java/com/amitph/spring/naming/Pentagon.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.naming; 2 | 3 | public class Pentagon extends Shape {} 4 | -------------------------------------------------------------------------------- /spring-boot-samples/src/main/java/com/amitph/spring/naming/Rectangle.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.naming; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | @Component 6 | public class Rectangle extends Shape {} 7 | -------------------------------------------------------------------------------- /spring-boot-samples/src/main/java/com/amitph/spring/naming/Shape.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.naming; 2 | 3 | public abstract class Shape {} 4 | -------------------------------------------------------------------------------- /spring-boot-samples/src/main/java/com/amitph/spring/naming/ShapeService.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.naming; 2 | 3 | import jakarta.annotation.PostConstruct; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.beans.factory.annotation.Qualifier; 6 | import org.springframework.stereotype.Service; 7 | 8 | @Service 9 | public class ShapeService { 10 | 11 | @Autowired private Shape circle; 12 | 13 | @Autowired private Shape rectangle; 14 | 15 | @Autowired private Shape squareType; 16 | 17 | @Autowired private Shape triangleShape; 18 | 19 | @Autowired private Shape pentagonShape; 20 | 21 | @Autowired 22 | @Qualifier("heartBean") 23 | private Shape heartShape; 24 | 25 | @PostConstruct 26 | public void afterStartup() { 27 | assert circle instanceof Circle; 28 | assert rectangle instanceof Rectangle; 29 | assert triangleShape instanceof Triangle; 30 | assert squareType instanceof Square; 31 | assert pentagonShape instanceof Pentagon; 32 | assert heartShape instanceof Heart; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /spring-boot-samples/src/main/java/com/amitph/spring/naming/Square.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.naming; 2 | 3 | public class Square extends Shape {} 4 | -------------------------------------------------------------------------------- /spring-boot-samples/src/main/java/com/amitph/spring/naming/Triangle.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.naming; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | @Component("triangleShape") 6 | public class Triangle extends Shape {} 7 | -------------------------------------------------------------------------------- /spring-boot-samples/src/main/java/com/amitph/spring/pathvariable/PathVariableExampleController.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.pathvariable; 2 | 3 | import java.util.Map; 4 | import java.util.Optional; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.PathVariable; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | @RestController 10 | public class PathVariableExampleController { 11 | 12 | @GetMapping("/v1/students/{id}") 13 | public String mapSimplePathVariable(@PathVariable String id) { 14 | return "id: " + id; 15 | } 16 | 17 | @GetMapping("/v2/students/{id}") 18 | public String mapPathVariableByName(@PathVariable("id") String studentId) { 19 | return "id: " + studentId; 20 | } 21 | 22 | @GetMapping("/v3/students/{id}/terms/{termId}") 23 | public String mapMultiplePathVariables(@PathVariable String id, @PathVariable String termId) { 24 | return "id: " + id + ", term id: " + termId; 25 | } 26 | 27 | @GetMapping("/v4/students/{id}/terms/{termId}") 28 | public String mapMultiplePathVariablesAsMap(@PathVariable Map pathVariables) { 29 | return pathVariables.toString(); 30 | } 31 | 32 | @GetMapping("/v5/students/{id}") 33 | public String mapPathVariableAsLong(@PathVariable("id") Long id) { 34 | return "id: " + id; 35 | } 36 | 37 | @GetMapping({"/v6/students/{id}/terms/{termId}", "/v6/students/{id}/terms/"}) 38 | public String mapNotRequiredPathVariables( 39 | @PathVariable String id, @PathVariable(required = false) String termId) { 40 | return "id: " + id + ", term id: " + termId; 41 | } 42 | 43 | @GetMapping({"/v7/students/{id}/terms/{termId}", "/v7/students/{id}/terms/"}) 44 | public String mapOptionalPathVariables( 45 | @PathVariable String id, @PathVariable Optional termId) { 46 | return "id: " + id + ", term id: " + termId.orElse("3"); 47 | } 48 | 49 | @GetMapping("/v8/students/{id}/terms/{termId}") 50 | public String mapPathVariableWithTermId(@PathVariable String id, @PathVariable String termId) { 51 | return "id: " + id + ", term id: " + termId; 52 | } 53 | 54 | @GetMapping("/v8/students/{id}/terms/") 55 | public String mapPathVariableWithoutTermId(@PathVariable String id) { 56 | return "id: " + id; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /spring-boot-samples/src/main/java/com/amitph/spring/requestparam/RequestParamExampleController.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.requestparam; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import java.util.Optional; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.RequestParam; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | @RestController 11 | public class RequestParamExampleController { 12 | 13 | @GetMapping("/data1") 14 | public String singleParam(@RequestParam String id) { 15 | return "id: " + id; 16 | } 17 | 18 | @GetMapping("/data2") 19 | public String multiParam(@RequestParam String id, @RequestParam String name) { 20 | 21 | return "id: " + id + ", name: " + name; 22 | } 23 | 24 | @GetMapping("/data3") 25 | public String typedParam(@RequestParam Long id) { 26 | return "id: " + id; 27 | } 28 | 29 | @GetMapping("/data4") 30 | public String multiValueParams(@RequestParam List id) { 31 | return "id: " + id; 32 | } 33 | 34 | @GetMapping("/data5") 35 | public String optionalParams(@RequestParam(required = false) Long id) { 36 | return "id: " + id; 37 | } 38 | 39 | @GetMapping("/data6") 40 | public String javaOptionalParams(@RequestParam Optional id) { 41 | return "id: " + id.orElseGet(() -> "Unknown"); 42 | } 43 | 44 | @GetMapping("/data7") 45 | public String defaultParams(@RequestParam(defaultValue = "Unknown") String id) { 46 | return "id: " + id; 47 | } 48 | 49 | @GetMapping("/data8") 50 | public String namedParams(@RequestParam(name = "id") String dataId) { 51 | return "dataId: " + dataId; 52 | } 53 | 54 | @GetMapping("/data9") 55 | public String mappedParams(@RequestParam Map dataQuery) { 56 | 57 | return dataQuery.toString(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /spring-boot-samples/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | logging: 2 | level: 3 | root: INFO 4 | pattern: 5 | console: "%d{HH:mm:ss} - %msg%n" 6 | 7 | management: 8 | endpoints: 9 | web: 10 | exposure: 11 | include: ["beans"] 12 | 13 | spring: 14 | main: 15 | banner-mode: console # console | off | log -------------------------------------------------------------------------------- /spring-boot-samples/src/test/java/com/amitph/spring/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class ApplicationTest { 8 | @Test 9 | public void contextLoads() {} 10 | } 11 | -------------------------------------------------------------------------------- /spring-boot-startup-metrics/README.md: -------------------------------------------------------------------------------- 1 | # Enable Spring Boot Startup Metrics 2 | 3 | [Example of enabling startup metrics in Spring Boot Application](https://www.amitph.com/spring-boot-startup-monitoring/) 4 | 5 | 6 | ## How to Use 7 | 8 | #### Clone this Git repository 9 | 10 | ``` 11 | git clone https://github.com/amitrp/spring-examples.git 12 | ``` 13 | 14 | #### Move to the module 15 | ``` 16 | cd spring-examples/spring-boot-startup-metrics/ 17 | ``` 18 | 19 | #### Run the Application 20 | ``` 21 | mvn spring-boot:run 22 | ``` -------------------------------------------------------------------------------- /spring-boot-startup-metrics/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 4.0.0 6 | 7 | com.amitph.spring 8 | spring-examples 9 | 1.0-SNAPSHOT 10 | 11 | spring-boot-startup-metrics 12 | jar 13 | Example of enabling Application Startup Metrics in Spring Boot 14 | 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter-web 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-actuator 23 | 24 | 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-maven-plugin 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /spring-boot-startup-metrics/src/main/java/com/amitph/spring/data/Application.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup; 6 | 7 | @SpringBootApplication 8 | public class Application { 9 | public static void main(String[] args) { 10 | SpringApplication application = new SpringApplication(Application.class); 11 | application.setApplicationStartup(new BufferingApplicationStartup(10000)); 12 | // application.setApplicationStartup(new FlightRecorderApplicationStartup()); 13 | application.run(args); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /spring-boot-startup-metrics/src/main/java/com/amitph/spring/data/web/StudentController.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.data.web; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | @RestController 8 | @RequiredArgsConstructor 9 | public class StudentController { 10 | @GetMapping("/") 11 | public String getStudents() { 12 | return "Hello!!"; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /spring-boot-startup-metrics/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | management: 2 | endpoints: 3 | web: 4 | exposure: 5 | include: 'startup' -------------------------------------------------------------------------------- /spring-data-jdbc/README.md: -------------------------------------------------------------------------------- 1 | # Spring Data JDBC Example 2 | Example of Building a Spring Data JDBC + Spring Boot REST Service 3 | These examples are part of Spring Boot + Spring Data Tutorials on https://www.amitph.com/ 4 | 5 | ## List of Tutorials 6 | - [Spring Data JDBC Tutorial with Examples](https://www.amitph.com/introduction-spring-data-jdbc/) 7 | - [Unit Tests for Spring Data JDBC Repositories](https://www.amitph.com/testing-spring-data-jdbc/) 8 | 9 | ## How to Use 10 | 11 | #### Clone this Git repository 12 | 13 | ``` 14 | git clone https://github.com/amitrp/spring-examples.git 15 | ``` 16 | 17 | #### Move to the spring-data-jdbc module 18 | ``` 19 | cd spring-example/spring-data-jdbc/ 20 | ``` 21 | 22 | #### Run MySQL Docker (Optional) 23 | ``` 24 | docker-compose -f docker/docker-compose.yml up -d 25 | ``` 26 | 27 | #### Run the Application 28 | ``` 29 | mvn spring-boot:run 30 | ``` -------------------------------------------------------------------------------- /spring-data-jdbc/docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | mysql: 5 | restart: always 6 | image: mysql:latest 7 | container_name: my-sql-latest 8 | environment: 9 | - MYSQL_DATABASE=testdb 10 | - MYSQL_ROOT_PASSWORD=password 11 | ports: 12 | - 33099:3306 -------------------------------------------------------------------------------- /spring-data-jdbc/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.amitph.spring 7 | spring-examples 8 | 1.0-SNAPSHOT 9 | 10 | 11 | spring-data-jdbc 12 | 1.0-SNAPSHOT 13 | spring-data-jdbc 14 | A demo Spring Data JDBC Project for tutorials on https://www.amitph.com/ 15 | 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-starter-data-jdbc 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-web 24 | 25 | 26 | com.mysql 27 | mysql-connector-j 28 | 29 | 30 | org.liquibase 31 | liquibase-core 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-starter-test 37 | test 38 | 39 | 40 | com.h2database 41 | h2 42 | test 43 | 44 | 45 | org.testng 46 | testng 47 | test 48 | 7.7.0 49 | 50 | 51 | junit 52 | junit 53 | test 54 | 55 | 56 | 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-maven-plugin 61 | 62 | 63 | 64 | org.projectlombok 65 | lombok 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /spring-data-jdbc/src/main/java/com/amitph/spring/tutorials/springdatajdbc/Application.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.tutorials.springdatajdbc; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Application { 8 | public static void main(String[] args) { 9 | SpringApplication.run(Application.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /spring-data-jdbc/src/main/java/com/amitph/spring/tutorials/springdatajdbc/repo/Student.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.tutorials.springdatajdbc.repo; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import org.springframework.data.annotation.Id; 6 | 7 | @Data 8 | @AllArgsConstructor 9 | public class Student { 10 | @Id private Long studentId; 11 | private String firstName; 12 | private String lastName; 13 | private Integer enrollmentYear; 14 | } 15 | -------------------------------------------------------------------------------- /spring-data-jdbc/src/main/java/com/amitph/spring/tutorials/springdatajdbc/repo/StudentRepository.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.tutorials.springdatajdbc.repo; 2 | 3 | import java.util.List; 4 | import org.springframework.data.repository.CrudRepository; 5 | import org.springframework.data.repository.query.Param; 6 | import org.springframework.stereotype.Repository; 7 | 8 | @Repository 9 | public interface StudentRepository extends CrudRepository { 10 | List findByLastName(@Param("lName") String lastName); 11 | 12 | List findByLastNameIgnoreCase(@Param("lName") String lastName); 13 | } 14 | -------------------------------------------------------------------------------- /spring-data-jdbc/src/main/java/com/amitph/spring/tutorials/springdatajdbc/web/StudentController.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.tutorials.springdatajdbc.web; 2 | 3 | import com.amitph.spring.tutorials.springdatajdbc.repo.Student; 4 | import com.amitph.spring.tutorials.springdatajdbc.repo.StudentRepository; 5 | import java.util.List; 6 | import lombok.AllArgsConstructor; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.util.StringUtils; 9 | import org.springframework.web.bind.annotation.GetMapping; 10 | import org.springframework.web.bind.annotation.PostMapping; 11 | import org.springframework.web.bind.annotation.RequestBody; 12 | import org.springframework.web.bind.annotation.RequestParam; 13 | import org.springframework.web.bind.annotation.RestController; 14 | 15 | @RestController 16 | @Slf4j 17 | @AllArgsConstructor 18 | public class StudentController { 19 | private final StudentRepository repository; 20 | 21 | @PostMapping("/students") 22 | public Student postStudent(@RequestBody Student student) { 23 | log.info("Request to create student: {}", student); 24 | 25 | return repository.save(student); 26 | } 27 | 28 | @GetMapping("/students") 29 | public List getStudents(@RequestParam(required = false) String lastName) { 30 | if (StringUtils.hasLength(lastName)) { 31 | return repository.findByLastName(lastName); 32 | } 33 | 34 | return (List) repository.findAll(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /spring-data-jdbc/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | liquibase: 3 | change-log: classpath:db/liquibase-changelog.xml 4 | datasource: 5 | username: "root" 6 | password: "password" 7 | url: "jdbc:mysql://localhost:33099/testdb" 8 | driverClassName: "com.mysql.cj.jdbc.Driver" 9 | 10 | logging: 11 | level: 12 | root: INFO 13 | liquibase: INFO 14 | com: 15 | amitph: DEBUG 16 | pattern: 17 | console: "%d{yyyy-MM-dd HH:mm:ss} | %-5p | %X{mdcData} | [%thread] %logger{5}:%L - %msg%n" 18 | -------------------------------------------------------------------------------- /spring-data-jdbc/src/main/resources/db/changelog/01-create-student.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | ANY 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | -------------------------------------------------------------------------------- /spring-data-jdbc/src/main/resources/db/liquibase-changelog.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /spring-data-jdbc/src/test/java/com/amitph/spring/tutorials/springdatajdbc/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.tutorials.springdatajdbc; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class ApplicationTest { 8 | 9 | @Test 10 | void contextLoads() {} 11 | } 12 | -------------------------------------------------------------------------------- /spring-data-jdbc/src/test/java/com/amitph/spring/tutorials/springdatajdbc/repo/StudentRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.tutorials.springdatajdbc.repo; 2 | 3 | import java.util.List; 4 | import java.util.stream.Collectors; 5 | import java.util.stream.Stream; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.autoconfigure.data.jdbc.DataJdbcTest; 11 | import org.springframework.test.context.ActiveProfiles; 12 | import org.springframework.test.context.junit4.SpringRunner; 13 | 14 | @RunWith(SpringRunner.class) 15 | @DataJdbcTest 16 | @ActiveProfiles("test") 17 | public class StudentRepositoryTest { 18 | 19 | @Autowired StudentRepository repository; 20 | 21 | @Test 22 | public void testFindByLastName() { 23 | List expectedList = 24 | entities() 25 | .map(repository::save) 26 | .filter(student -> student.getLastName().equals("Stark")) 27 | .collect(Collectors.toList()); 28 | 29 | List actualList = repository.findByLastName("Stark"); 30 | 31 | Assert.assertEquals(expectedList, actualList); 32 | } 33 | 34 | @Test 35 | public void testFindByLastNameIgnoreCase() { 36 | List expectedList = 37 | entities() 38 | .map(repository::save) 39 | .filter(student -> student.getLastName().equalsIgnoreCase("Stark")) 40 | .collect(Collectors.toList()); 41 | 42 | List actualList = repository.findByLastNameIgnoreCase("Stark"); 43 | Assert.assertEquals(expectedList, actualList); 44 | } 45 | 46 | private Stream entities() { 47 | return Stream.of( 48 | new Student(null, "Arya", "Stark", 2023), 49 | new Student(null, "Jon", "Snow", 2023), 50 | new Student(null, "Rob", "Stark", 2023), 51 | new Student(null, "Ned", "stark", 2023)); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /spring-data-jdbc/src/test/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | liquibase: 3 | change-log: classpath:db/liquibase-changelog.xml -------------------------------------------------------------------------------- /spring-data-rest/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /spring-data-rest/README.md: -------------------------------------------------------------------------------- 1 | # Spring Data REST Tutorials and Examples 2 | 3 | This repository contains source code examples used in [amitph.com](https://www.amitph.com/) tutorials. 4 | 5 | ## Respective Tutorials 6 | 7 | - [Spring Data REST CRUD Example](https://www.amitph.com/spring-data-rest-example/) 8 | - [Spring Data REST Projections and Excerpts](https://www.amitph.com/spring-data-rest-projections-and-excerpts/) 9 | -------------------------------------------------------------------------------- /spring-data-rest/crud-example/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /spring-data-rest/crud-example/README.md: -------------------------------------------------------------------------------- 1 | # Spring Data REST - CRUD Application Tutorial and Examples 2 | 3 | ## Referenced Tutorial 4 | 5 | - [Spring Data REST CRUD Example](https://www.amitph.com/spring-data-rest-example/) 6 | ## How to Run 7 | 8 | ### Git Checkout 9 | ``` 10 | ~ git clone https://github.com/amitrp/spring-examples.git 11 | ``` 12 | 13 | ### Move to the module & Launch application 14 | ``` 15 | ~ cd spring-examples/spring-data-rest/crud-example 16 | ~ mvn spring-boot:run 17 | ``` 18 | -------------------------------------------------------------------------------- /spring-data-rest/crud-example/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | com.amitph.spring 8 | spring-data-rest 9 | 1.0-SNAPSHOT 10 | 11 | 12 | crud-example 13 | 1.0-SNAPSHOT 14 | crud-example 15 | Demo Application for Spring Data REST Framework CRUD Example Service 16 | 17 | 18 | 19 | org.springframework.boot 20 | spring-boot-starter-data-rest 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-data-jpa 25 | 26 | 27 | com.h2database 28 | h2 29 | runtime 30 | 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-test 35 | test 36 | 37 | 38 | 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-maven-plugin 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /spring-data-rest/crud-example/src/main/java/com/amitph/spring/springdatarest/Application.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.springdatarest; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Application { 8 | public static void main(String[] args) { 9 | SpringApplication.run(Application.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /spring-data-rest/crud-example/src/main/java/com/amitph/spring/springdatarest/entity/Student.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.springdatarest.entity; 2 | 3 | import jakarta.persistence.Entity; 4 | import jakarta.persistence.GeneratedValue; 5 | import jakarta.persistence.GenerationType; 6 | import jakarta.persistence.Id; 7 | import lombok.Data; 8 | 9 | @Data 10 | @Entity 11 | public class Student { 12 | @Id 13 | @GeneratedValue(strategy = GenerationType.IDENTITY) 14 | private Long id; 15 | 16 | private String name; 17 | } 18 | -------------------------------------------------------------------------------- /spring-data-rest/crud-example/src/main/java/com/amitph/spring/springdatarest/entity/StudentRepository.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.springdatarest.entity; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.data.rest.core.annotation.RepositoryRestResource; 5 | 6 | @RepositoryRestResource 7 | public interface StudentRepository extends JpaRepository {} 8 | -------------------------------------------------------------------------------- /spring-data-rest/crud-example/src/main/resources/application.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amitrp/spring-examples/53a1272bccaaf52457c0fdd2cfbf441d12e76a91/spring-data-rest/crud-example/src/main/resources/application.yaml -------------------------------------------------------------------------------- /spring-data-rest/crud-example/src/main/resources/schema.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS student; 2 | 3 | CREATE TABLE student 4 | ( 5 | id INT AUTO_INCREMENT PRIMARY KEY, 6 | name VARCHAR(250) NOT NULL 7 | ); -------------------------------------------------------------------------------- /spring-data-rest/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | com.amitph.spring 8 | spring-examples 9 | 1.0-SNAPSHOT 10 | 11 | 12 | spring-data-rest 13 | 1.0-SNAPSHOT 14 | pom 15 | spring-data-rest 16 | Demo Application for Spring Data REST Framework 17 | 18 | 19 | projection-excerpts 20 | crud-example 21 | 22 | 23 | 24 | 25 | 26 | com.amitph.spring 27 | crud-example 28 | 1.0-SNAPSHOT 29 | 30 | 31 | com.amitph.spring 32 | projection-excerpts 33 | 1.0-SNAPSHOT 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /spring-data-rest/projection-excerpts/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /spring-data-rest/projection-excerpts/README.md: -------------------------------------------------------------------------------- 1 | # Spring Data REST - Projections and Excerpts Tutorial and Examples 2 | 3 | ## Referenced Tutorial 4 | 5 | - [Spring Data REST Projections and Excerpts](https://www.amitph.com/spring-data-rest-projections-and-excerpts/) 6 | 7 | ## How to Run 8 | 9 | ### Git Checkout 10 | ``` 11 | ~ git clone https://github.com/amitrp/spring-examples.git 12 | ~ cd spring-examples/spring-data-rest/projection-excerpts 13 | ``` 14 | 15 | ### To launch a MySQL instance (Optional) 16 | ``` 17 | ~ docker-compose -f docker/docker-compose.yml up -d 18 | ``` 19 | 20 | ### Launch application 21 | ``` 22 | ~ mvn spring-boot:run 23 | ``` 24 | -------------------------------------------------------------------------------- /spring-data-rest/projection-excerpts/docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | mysql: 5 | restart: always 6 | image: mysql:latest 7 | container_name: my-sql-latest 8 | environment: 9 | - MYSQL_DATABASE=testdb 10 | - MYSQL_ROOT_PASSWORD=password 11 | ports: 12 | - 33099:3306 -------------------------------------------------------------------------------- /spring-data-rest/projection-excerpts/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | com.amitph.spring 8 | spring-data-rest 9 | 1.0-SNAPSHOT 10 | 11 | 12 | projection-excerpts 13 | 1.0-SNAPSHOT 14 | projection-excerpts 15 | Demo Application for Spring Data REST Framework Projection Excerpts 16 | 17 | 18 | 19 | org.springframework.boot 20 | spring-boot-starter-data-rest 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-data-jpa 25 | 26 | 27 | com.mysql 28 | mysql-connector-j 29 | 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-test 34 | test 35 | 36 | 37 | com.h2database 38 | h2 39 | test 40 | 41 | 42 | 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-maven-plugin 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /spring-data-rest/projection-excerpts/src/main/java/com/amitph/spring/springdatarest/Application.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.springdatarest; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Application { 8 | public static void main(String[] args) { 9 | SpringApplication.run(Application.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /spring-data-rest/projection-excerpts/src/main/java/com/amitph/spring/springdatarest/entity/Course.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.springdatarest.entity; 2 | 3 | import jakarta.persistence.CascadeType; 4 | import jakarta.persistence.Entity; 5 | import jakarta.persistence.GeneratedValue; 6 | import jakarta.persistence.GenerationType; 7 | import jakarta.persistence.Id; 8 | import jakarta.persistence.JoinColumn; 9 | import jakarta.persistence.JoinTable; 10 | import jakarta.persistence.ManyToMany; 11 | import java.util.List; 12 | import lombok.Data; 13 | 14 | @Data 15 | @Entity 16 | public class Course { 17 | @Id 18 | @GeneratedValue(strategy = GenerationType.IDENTITY) 19 | private Long id; 20 | 21 | private String name; 22 | 23 | @ManyToMany(cascade = CascadeType.ALL) 24 | @JoinTable( 25 | name = "enrolment", 26 | inverseJoinColumns = @JoinColumn(name = "student_id", referencedColumnName = "id"), 27 | joinColumns = @JoinColumn(name = "course_id", referencedColumnName = "id")) 28 | private List students; 29 | } 30 | -------------------------------------------------------------------------------- /spring-data-rest/projection-excerpts/src/main/java/com/amitph/spring/springdatarest/entity/CourseRepository.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.springdatarest.entity; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | import org.springframework.data.rest.core.annotation.RepositoryRestResource; 5 | 6 | @RepositoryRestResource 7 | public interface CourseRepository extends CrudRepository {} 8 | -------------------------------------------------------------------------------- /spring-data-rest/projection-excerpts/src/main/java/com/amitph/spring/springdatarest/entity/Student.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.springdatarest.entity; 2 | 3 | import jakarta.persistence.Entity; 4 | import jakarta.persistence.GeneratedValue; 5 | import jakarta.persistence.GenerationType; 6 | import jakarta.persistence.Id; 7 | import jakarta.persistence.ManyToMany; 8 | import java.util.List; 9 | import lombok.Data; 10 | 11 | @Data 12 | @Entity 13 | public class Student { 14 | @Id 15 | @GeneratedValue(strategy = GenerationType.IDENTITY) 16 | private Long id; 17 | 18 | private String firstName; 19 | private String lastName; 20 | private Integer year; 21 | 22 | @ManyToMany(mappedBy = "students") 23 | public List courses; 24 | } 25 | -------------------------------------------------------------------------------- /spring-data-rest/projection-excerpts/src/main/java/com/amitph/spring/springdatarest/entity/StudentRepository.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.springdatarest.entity; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | import org.springframework.data.rest.core.annotation.RepositoryRestResource; 5 | 6 | @RepositoryRestResource 7 | public interface StudentRepository extends CrudRepository {} 8 | -------------------------------------------------------------------------------- /spring-data-rest/projection-excerpts/src/main/java/com/amitph/spring/springdatarest/entity/StudentView.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.springdatarest.entity; 2 | 3 | import java.util.List; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.data.rest.core.config.Projection; 6 | 7 | @Projection(name = "studentView", types = Student.class) 8 | public interface StudentView { 9 | Long getId(); 10 | 11 | String getFirstName(); 12 | 13 | String getLastName(); 14 | 15 | @Value("#{target.lastName}, #{target.firstName}") 16 | String getDisplayName(); 17 | 18 | List getCourses(); 19 | } 20 | -------------------------------------------------------------------------------- /spring-data-rest/projection-excerpts/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | spring: 2 | datasource: 3 | username: "root" 4 | password: "password" 5 | url: "jdbc:mysql://localhost:33099/testdb" 6 | driverClassName: "com.mysql.cj.jdbc.Driver" 7 | jpa: 8 | database-platform: org.hibernate.dialect.MySQL5InnoDBDialect 9 | show-sql: false -------------------------------------------------------------------------------- /spring-data-rest/projection-excerpts/src/main/resources/data.sql: -------------------------------------------------------------------------------- 1 | insert into student (first_name, last_name, year) values ('Eddard', 'Stark', 2020); 2 | insert into student (first_name, last_name, year) values ('Jorah', 'Morment', 2021); 3 | insert into student (first_name, last_name, year) values ('Gregor', 'Clegane', 2021); 4 | insert into student (first_name, last_name, year) values ('Tomund', 'Giantsbane', 2020); 5 | insert into student (first_name, last_name, year) values ('Loras', 'Tyrell', 2026); 6 | insert into student (first_name, last_name, year) values ('Renly', 'Baratheon', 2023); 7 | insert into student (first_name, last_name, year) values ('Ellaria', 'Sand', 2027); 8 | insert into student (first_name, last_name, year) values ('Daario', 'Naharis', 2025); 9 | 10 | insert into course (name) values('Spring Boot Introduction'); 11 | insert into course (name) values('Spring Data REST Guide'); 12 | 13 | insert into enrolment(student_id, course_id) values (1, 1); 14 | insert into enrolment(student_id, course_id) values (1, 2); 15 | insert into enrolment(student_id, course_id) values (2, 2); 16 | insert into enrolment(student_id, course_id) values (3, 1); 17 | insert into enrolment(student_id, course_id) values (4, 2); 18 | insert into enrolment(student_id, course_id) values (5, 1); 19 | insert into enrolment(student_id, course_id) values (5, 2); 20 | insert into enrolment(student_id, course_id) values (6, 2); 21 | insert into enrolment(student_id, course_id) values (7, 1); 22 | insert into enrolment(student_id, course_id) values (8, 1); 23 | insert into enrolment(student_id, course_id) values (8, 2); 24 | 25 | -------------------------------------------------------------------------------- /spring-data-rest/projection-excerpts/src/main/resources/schema.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS enrolment; 2 | DROP TABLE IF EXISTS student; 3 | DROP TABLE IF EXISTS course; 4 | 5 | CREATE TABLE student 6 | ( 7 | id INTEGER PRIMARY KEY AUTO_INCREMENT, 8 | first_name VARCHAR(50), 9 | last_name VARCHAR(50), 10 | year INTEGER 11 | ); 12 | 13 | CREATE TABLE course 14 | ( 15 | id INTEGER PRIMARY KEY AUTO_INCREMENT, 16 | name VARCHAR(50) 17 | ); 18 | 19 | CREATE TABLE enrolment 20 | ( 21 | id INTEGER PRIMARY KEY AUTO_INCREMENT, 22 | student_id INTEGER, 23 | course_id INTEGER, 24 | UNIQUE (student_id, course_id), 25 | FOREIGN KEY (student_id) REFERENCES student (id), 26 | FOREIGN KEY (course_id) REFERENCES course (id) 27 | ); -------------------------------------------------------------------------------- /spring-scheduled-tasks/README.md: -------------------------------------------------------------------------------- 1 | # Using @Scheduled annotation in a Spring Boot Application 2 | 3 | ## This repository is part of https://www.amitph.com tutorials. 4 | > - **[Scheduled Tasks in Spring with @Scheduled](https://www.amitph.com/scheduled-tasks-in-spring/)** 5 | 6 | 7 | ## How to Use 8 | 9 | #### Clone this Git repository 10 | 11 | ``` 12 | git clone https://github.com/amitrp/spring-examples.git 13 | ``` 14 | 15 | #### Move to the module 16 | ``` 17 | cd spring-scheduled-tasks/ 18 | ``` 19 | 20 | #### Run the Application 21 | ``` 22 | mvn spring-boot:run 23 | ``` -------------------------------------------------------------------------------- /spring-scheduled-tasks/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.amitph.spring 7 | spring-examples 8 | 1.0-SNAPSHOT 9 | 10 | 11 | spring-boot-demo 12 | spring-scheduled-tasks 13 | 1.0-SNAPSHOT 14 | Examples of using @Scheduled annotation in Spring 15 | 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-starter-web 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-test 24 | test 25 | 26 | 27 | 28 | 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-maven-plugin 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /spring-scheduled-tasks/src/main/java/com/amitph/spring/tutorials/springbootdemo/Application.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.tutorials.springbootdemo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.scheduling.annotation.EnableScheduling; 6 | 7 | @EnableScheduling 8 | @SpringBootApplication 9 | public class Application { 10 | public static void main(String[] args) { 11 | SpringApplication.run(Application.class, args); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /spring-scheduled-tasks/src/main/java/com/amitph/spring/tutorials/springbootdemo/scheduled/ScheduledTask.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.tutorials.springbootdemo.scheduled; 2 | 3 | import static java.util.concurrent.TimeUnit.MILLISECONDS; 4 | 5 | import java.time.LocalTime; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.scheduling.annotation.Async; 9 | import org.springframework.scheduling.annotation.EnableAsync; 10 | import org.springframework.scheduling.annotation.Scheduled; 11 | import org.springframework.stereotype.Component; 12 | 13 | @Component 14 | @EnableAsync 15 | public class ScheduledTask { 16 | private final Logger logger = LoggerFactory.getLogger(ScheduledTask.class); 17 | 18 | @Scheduled(fixedDelayString = "${schedule.fixedDelay}") 19 | public void taskWithFixedDelay() { 20 | logger.info("Task with Fixed Delay, " + (LocalTime.now().getSecond())); 21 | } 22 | 23 | @Scheduled(fixedRateString = "${schedule.fixedRate}") 24 | @Async 25 | public void taskWithFixedRate() throws InterruptedException { 26 | logger.info("Task with Fixed Rate, " + (LocalTime.now().getSecond())); 27 | MILLISECONDS.sleep(4000L); 28 | } 29 | 30 | @Scheduled( 31 | fixedRateString = "${schedule.fixedRate}", 32 | initialDelayString = "${schedule.initialDelay}") 33 | public void taskWithFixedRateAndInitialDelay() { 34 | logger.info("Task with Fixed Rate and Initial Delay, " + (LocalTime.now().getSecond())); 35 | } 36 | 37 | @Scheduled(cron = "${schedule.cron}", zone = "${schedule.timezone}") 38 | public void taskWithCronExpression() { 39 | logger.info("Task with Cron Expression, " + (LocalTime.now().getSecond())); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /spring-scheduled-tasks/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | schedule.fixedDelay=2000 2 | schedule.fixedRate=2000 3 | schedule.initialDelay=10000 4 | schedule.cron=0 45 1,2,3 * * ? 5 | schedule.timezone=Europe/London -------------------------------------------------------------------------------- /spring-scheduled-tasks/src/test/java/com/amitph/spring/tutorials/springbootdemo/SpringBootDemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.tutorials.springbootdemo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class SpringBootDemoApplicationTests { 8 | 9 | @Test 10 | void contextLoads() {} 11 | } 12 | -------------------------------------------------------------------------------- /spring-webflux-webclient/README.md: -------------------------------------------------------------------------------- 1 | # Spring WebFlux WebClient Examples 2 | Examples of using Spring WebClient 3 | These examples are part of Spring & Spring Boot Tutorials on [amitph.com](https://www.amitph.com/) 4 | 5 | - [Introduction to Spring WebClient](https://www.amitph.com/introduction-to-spring-webclient/) 6 | - [Configure timeout for Spring WebFlux WebClient](https://www.amitph.com/spring-webflux-timeouts/) 7 | - [How to Read JSON Data with WebClient](https://www.amitph.com/spring-webclient-read-json-data/) 8 | - [Add URI Parameters to Spring WebClient Requests](https://www.amitph.com/spring-webclient-request-parameters/) 9 | - [Downloading Large Files using Spring WebClient](https://www.amitph.com/spring-webclient-large-file-download/) 10 | - [Parallel Requests with Spring WebClient](https://www.amitph.com/spring-webclient-concurrent-calls/) 11 | - [Custom Banners with Spring Boot](https://www.amitph.com/spring-boot-custom-banner/) 12 | - Spring WebClient Filters Examples 13 | 14 | 15 | ## How to Use 16 | 17 | #### Clone this Git repository 18 | 19 | ``` 20 | ~ git clone https://github.com/amitrp/spring-examples.git 21 | ~ cd spring-webflux-webclient/ 22 | ``` 23 | 24 | #### Run the Application 25 | ``` 26 | ~ mvn spring-boot:run 27 | ``` -------------------------------------------------------------------------------- /spring-webflux-webclient/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | spring-examples 7 | com.amitph.spring 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | spring-webflux-webclient 13 | 14 | 15 | 16 | org.springframework.boot 17 | spring-boot-starter-webflux 18 | 19 | 20 | -------------------------------------------------------------------------------- /spring-webflux-webclient/src/main/java/com/amitph/spring/webclients/Application.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.webclients; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.scheduling.annotation.EnableScheduling; 6 | 7 | @EnableScheduling 8 | @SpringBootApplication 9 | public class Application { 10 | public static void main(String[] args) { 11 | SpringApplication.run(Application.class, args); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /spring-webflux-webclient/src/main/java/com/amitph/spring/webclients/ApplicationProperties.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.webclients; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | @Data 8 | @Configuration 9 | @ConfigurationProperties 10 | public class ApplicationProperties { 11 | private String fileServerUrl; 12 | private String outputPath; 13 | } 14 | -------------------------------------------------------------------------------- /spring-webflux-webclient/src/main/java/com/amitph/spring/webclients/DogsFetcher.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.webclients; 2 | 3 | import com.amitph.spring.webclients.service.DogsFetcherWebClientService; 4 | import jakarta.annotation.PostConstruct; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Slf4j 10 | @Component 11 | @RequiredArgsConstructor 12 | public class DogsFetcher { 13 | private final DogsFetcherWebClientService webClientService; 14 | 15 | @PostConstruct 16 | public void fetchStudents() { 17 | log.info("Beginning to fetch"); 18 | 19 | log.info("Fetching dogs sequentially"); 20 | webClientService.getInSequence(1L, 3L, 4L, 2L); 21 | 22 | log.info("Fetching dogs in parallel using Java Streams"); 23 | webClientService.getWithParallelStream(1L, 3L, 4L, 2L); 24 | 25 | log.info("Fetching dogs in parallel using Flux"); 26 | webClientService.getWithParallelFlux(1L, 3L, 4L, 2L); 27 | 28 | log.info("Fetching dogs in parallel using Flux and Thread Scheduler"); 29 | webClientService.getWithParallelFluxAndScheduler(1L, 3L, 4L, 2L); 30 | 31 | log.info("Fetching dogs in parallel from different services using Flux"); 32 | webClientService.getFromMultipleServicesUsingParallelFlux(1L, 3L, 4L, 2L); 33 | 34 | log.info("Fetching different resources in parallel from different services using Flux"); 35 | webClientService.getDifferentObjectsUsingParallelFlux(1L, 3L, 4L, 2L); 36 | 37 | log.info("Finished fetching"); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /spring-webflux-webclient/src/main/java/com/amitph/spring/webclients/ExecuteWithFilters.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.webclients; 2 | 3 | import com.amitph.spring.webclients.service.WebClientWithFilterService; 4 | import jakarta.annotation.PostConstruct; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | @RequiredArgsConstructor 10 | public class ExecuteWithFilters { 11 | private final WebClientWithFilterService service; 12 | 13 | @PostConstruct 14 | public void executeFilteredWebClient() { 15 | service.fetchResource(3L); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /spring-webflux-webclient/src/main/java/com/amitph/spring/webclients/FileDownloader.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.webclients; 2 | 3 | import com.amitph.spring.webclients.service.FileDownloaderWebClientService; 4 | import jakarta.annotation.PostConstruct; 5 | import java.io.IOException; 6 | import java.nio.file.Path; 7 | import java.nio.file.Paths; 8 | import lombok.RequiredArgsConstructor; 9 | import org.springframework.stereotype.Component; 10 | 11 | @Component 12 | @RequiredArgsConstructor 13 | public class FileDownloader { 14 | private final FileDownloaderWebClientService webClientService; 15 | private final ApplicationProperties props; 16 | 17 | @PostConstruct 18 | public void onApplicationEvent() throws IOException { 19 | Path output = Paths.get(props.getOutputPath()); 20 | // webClientService.downloadUsingByteArray(output); 21 | // webClientService.downloadUsingMono(output); 22 | webClientService.downloadUsingFlux(output); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /spring-webflux-webclient/src/main/java/com/amitph/spring/webclients/model/Dog.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.webclients.model; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class Dog { 7 | private long id; 8 | private String name; 9 | private int age; 10 | } 11 | -------------------------------------------------------------------------------- /spring-webflux-webclient/src/main/java/com/amitph/spring/webclients/model/OwnedDog.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.webclients.model; 2 | 3 | import lombok.ToString; 4 | 5 | @ToString 6 | public class OwnedDog { 7 | private long id; 8 | private String name; 9 | private int age; 10 | private String owner; 11 | 12 | public OwnedDog(Dog d, String owner) { 13 | this.id = d.getId(); 14 | this.name = d.getName(); 15 | this.age = d.getAge(); 16 | this.owner = owner; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /spring-webflux-webclient/src/main/java/com/amitph/spring/webclients/service/FileDownloaderWebClientService.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.webclients.service; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | import java.nio.file.Path; 6 | import java.nio.file.StandardOpenOption; 7 | import java.util.Objects; 8 | import lombok.RequiredArgsConstructor; 9 | import org.springframework.core.io.buffer.DataBuffer; 10 | import org.springframework.core.io.buffer.DataBufferUtils; 11 | import org.springframework.stereotype.Service; 12 | import org.springframework.web.reactive.function.client.WebClient; 13 | import reactor.core.publisher.Flux; 14 | import reactor.core.publisher.Mono; 15 | 16 | @Service 17 | @RequiredArgsConstructor 18 | public class FileDownloaderWebClientService { 19 | private final WebClient webClient; 20 | 21 | /** Reads the complete file in-memory. Thus, only useful for very large file */ 22 | public void downloadUsingByteArray(Path destination) throws IOException { 23 | Mono monoContents = 24 | webClient.get().uri("/largefiles/1").retrieve().bodyToMono(byte[].class); 25 | 26 | Files.write( 27 | destination, 28 | Objects.requireNonNull(monoContents.share().block()), 29 | StandardOpenOption.CREATE); 30 | } 31 | 32 | /** 33 | * Reading file using Mono will try to fit the entire file into the DataBuffer. Results in 34 | * exception when the file is larger than the DataBuffer capacity. 35 | */ 36 | public void downloadUsingMono(Path destination) { 37 | Mono dataBuffer = 38 | webClient.get().uri("/largefiles/1").retrieve().bodyToMono(DataBuffer.class); 39 | 40 | DataBufferUtils.write(dataBuffer, destination, StandardOpenOption.CREATE).share().block(); 41 | } 42 | 43 | /** 44 | * Having using Flux we can download files of any size safely. Optionally, we can configure 45 | * DataBuffer capacity for better memory utilization. 46 | */ 47 | public void downloadUsingFlux(Path destination) { 48 | Flux dataBuffer = 49 | webClient.get().uri("/largefiles/1").retrieve().bodyToFlux(DataBuffer.class); 50 | 51 | DataBufferUtils.write(dataBuffer, destination, StandardOpenOption.CREATE).share().block(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /spring-webflux-webclient/src/main/java/com/amitph/spring/webclients/service/WebClientWithFilterService.java: -------------------------------------------------------------------------------- 1 | package com.amitph.spring.webclients.service; 2 | 3 | import com.amitph.spring.webclients.model.Dog; 4 | import lombok.RequiredArgsConstructor; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.stereotype.Service; 7 | import org.springframework.web.reactive.function.client.WebClient; 8 | 9 | @Slf4j 10 | @Service 11 | @RequiredArgsConstructor 12 | public class WebClientWithFilterService { 13 | private final WebClient webClientWithFilter; 14 | 15 | public void fetchResource(Long id) { 16 | Dog dog = 17 | webClientWithFilter 18 | .get() 19 | .uri("/dogs/{id}", id) 20 | .retrieve() 21 | .bodyToMono(Dog.class) 22 | .share() 23 | .block(); 24 | log.info("Retrieved Dog: {}", dog); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /spring-webflux-webclient/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | file-server-url: "http://localhost:8182" 2 | output-path: "~/Downloads/large2.json" 3 | 4 | server: 5 | port: 8183 6 | 7 | logging: 8 | level: 9 | root: INFO 10 | com: 11 | amitph: DEBUG 12 | pattern: 13 | console: "%d{HH:mm:ss} - %msg%n" 14 | spring: 15 | banner: 16 | image: 17 | width: 50 18 | height: 15 19 | margin: 10 20 | invert: true -------------------------------------------------------------------------------- /spring-webflux-webclient/src/main/resources/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amitrp/spring-examples/53a1272bccaaf52457c0fdd2cfbf441d12e76a91/spring-webflux-webclient/src/main/resources/banner.png -------------------------------------------------------------------------------- /spring-webflux-webclient/src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | . . 2 | ,@@@@@@@@ ****** /@@@@# &@@@@@@@% 3 | %@@@@#((( */ .@@@@@ /@@@@# (((@@@@@ 4 | ,@@@@( *#***@**#* *****,*#**@*.& @ *&%*** ...... .@@@@ (**** # */(* ( /@@@@# &**@(*(* ,@@@@& 5 | ,@@@@( @@@@@@@@@@@@@@ &@@@@@@@@@@@@@@#@@@@@@@@@@@& //**/*/@@@@@@@@@@@ @@@@@@@@@@@@@@@, /@@@@@@@@@@@@@@@( ,@@@@& 6 | ,@@@@( .# #@@@@@ &@@@@@, @@@@@@& @@@@@@ //**/* .@@@@@ @@@@@/ &@@@@@# /@@@@@@ #@@@@& ,@@@@& 7 | &@@@@@@ ,%@@@@@@@@@@@& &@@@@ *@@@@( .@@@@# //**/* .@@@@@ @@@@@ /@@@@# /@@@@#. .@@@@, *@@@@@@ 8 | ,@@@@@@/ (@@@@@@/////@@@@& &@@@@# *@@@@( @@@@# //**/* .@@@@@ @@@@@ /@@@@# /@@@@# .@@@@, %@@@@@@& 9 | ,@@@@( @@@@. ,(@@@@& &@@@@# *@@@@( @@@@# //*//* .@@@@@ , @@@@@/* .#@@@@@# /@@@@# .@@@@, ,@@@@& 10 | ,@@@@( (@@@@@@@@@@@@@@& &@@@@# *@@@@( @@@@# //**/* #@@@@@@@@& @@@@@@@@@@@@@@@, /@@@@# .@@@@, ,@@@@& 11 | ,@@@@( % @ , # @@@@ @ & ,@@@@& 12 | %@@@@@@%%# @@@@ ,%%@@@@@( 13 | @@@@@@( @@@@ /@@@@@@%@ --------------------------------------------------------------------------------