├── .circleci
└── config.yml
├── .gitignore
├── README.md
├── pom.xml
├── renovate.json
└── src
├── main
├── java
│ └── pl
│ │ └── piomin
│ │ └── samples
│ │ └── springboot
│ │ └── tips
│ │ ├── TipsApp.java
│ │ ├── config
│ │ └── TipsAppProperties.java
│ │ ├── controller
│ │ ├── PersonController.java
│ │ └── TipController.java
│ │ ├── data
│ │ ├── model
│ │ │ ├── Address.java
│ │ │ ├── Contact.java
│ │ │ ├── Gender.java
│ │ │ ├── Person.java
│ │ │ ├── Person2.java
│ │ │ └── Tip.java
│ │ └── repository
│ │ │ ├── PersonRepository.java
│ │ │ └── TipRepository.java
│ │ ├── exception
│ │ └── handler
│ │ │ └── TipNotFoundHandler.java
│ │ ├── listener
│ │ └── ApplicationStartupListener.java
│ │ └── service
│ │ ├── PersonService.java
│ │ └── TipService.java
└── resources
│ ├── additional.yml
│ ├── app.properties
│ ├── application.yml
│ ├── data.sql
│ ├── db
│ └── changeLog.sql
│ └── global.properties
└── test
└── java
└── pl
└── piomin
└── samples
└── springboot
└── tips
├── AppTest.java
├── PersonsControllerTests.java
├── PersonsControllerViaRestClientTests.java
├── PersonsServiceTest.java
├── TipsAppTest.java
├── TipsControllerTest.java
└── TipsRepositoryTest.java
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2.1
2 |
3 | jobs:
4 | build:
5 | docker:
6 | - image: 'cimg/openjdk:21.0.6'
7 | steps:
8 | - checkout
9 | - run:
10 | name: Analyze on SonarCloud
11 | command: mvn verify sonar:sonar
12 |
13 | executors:
14 | jdk:
15 | docker:
16 | - image: 'cimg/openjdk:21.0.6'
17 |
18 | orbs:
19 | maven: circleci/maven@2.0.0
20 |
21 | workflows:
22 | maven_test:
23 | jobs:
24 | - maven/test:
25 | executor: jdk
26 | - build:
27 | context: SonarCloud
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Project exclude paths
2 | /target/
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Spring Boot Tips & Tricks Demo Project [](https://twitter.com/piotr_minkowski)
2 |
3 | [](https://circleci.com/gh/piomin/spring-boot-tips)
4 |
5 | [](https://sonarcloud.io/dashboard?id=piomin_spring-boot-tips)
6 | [](https://sonarcloud.io/dashboard?id=piomin_spring-boot-tips)
7 | [](https://sonarcloud.io/dashboard?id=piomin_spring-boot-tips)
8 | [](https://sonarcloud.io/dashboard?id=piomin_spring-boot-tips)
9 |
10 | This repository shows the most interesting tips and tricks with the Spring Boot framework.
11 |
12 | A detailed description can be found here: [Spring Boot Tips, Tricks and Techniques](https://piotrminkowski.com/2021/01/13/spring-boot-tips-tricks-and-techniques/)
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | org.springframework.boot
9 | spring-boot-starter-parent
10 | 3.5.0
11 |
12 |
13 | pl.piomin.samples
14 | spring-boot-tips
15 | 1.3-SNAPSHOT
16 |
17 |
18 | 21
19 | piomin_spring-boot-tips
20 | piomin
21 | https://sonarcloud.io
22 |
23 |
24 |
25 |
26 | org.springframework.boot
27 | spring-boot-starter-web
28 |
29 |
30 | org.springframework.boot
31 | spring-boot-starter-validation
32 |
33 |
34 | org.springframework.boot
35 | spring-boot-starter-actuator
36 |
37 |
38 | org.springframework.boot
39 | spring-boot-starter-data-jpa
40 |
41 |
42 | com.h2database
43 | h2
44 | runtime
45 |
46 |
47 | org.projectlombok
48 | lombok
49 |
50 |
51 | org.springframework.boot
52 | spring-boot-starter-test
53 | test
54 |
55 |
56 | org.instancio
57 | instancio-junit
58 | 5.4.1
59 |
60 |
61 | net.datafaker
62 | datafaker
63 | 2.4.3
64 |
65 |
66 | org.liquibase
67 | liquibase-core
68 |
69 |
70 |
71 |
72 |
73 |
74 | com.blazebit
75 | blaze-persistence-bom
76 | 1.6.15
77 | pom
78 | import
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | org.springframework.boot
87 | spring-boot-maven-plugin
88 |
89 |
90 |
91 | build-info
92 |
93 |
94 |
95 |
96 |
97 | org.jacoco
98 | jacoco-maven-plugin
99 | 0.8.13
100 |
101 |
102 |
103 | prepare-agent
104 |
105 |
106 |
107 | report
108 | test
109 |
110 | report
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "config:base",":dependencyDashboard"
5 | ],
6 | "packageRules": [
7 | {
8 | "matchUpdateTypes": ["minor", "patch", "pin", "digest"],
9 | "automerge": true
10 | }
11 | ],
12 | "prCreation": "not-pending"
13 | }
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/samples/springboot/tips/TipsApp.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.tips;
2 |
3 | import jakarta.annotation.PostConstruct;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.boot.SpringApplication;
9 | import org.springframework.boot.autoconfigure.SpringBootApplication;
10 | import org.springframework.boot.context.properties.EnableConfigurationProperties;
11 | import org.springframework.boot.info.BuildProperties;
12 | import org.springframework.boot.info.GitProperties;
13 | import pl.piomin.samples.springboot.tips.config.TipsAppProperties;
14 | import pl.piomin.samples.springboot.tips.controller.TipController;
15 | import pl.piomin.samples.springboot.tips.data.model.Person2;
16 |
17 | import java.util.Optional;
18 |
19 | @SpringBootApplication
20 | @EnableConfigurationProperties({TipsAppProperties.class, Person2.class})
21 | public class TipsApp {
22 |
23 | private static final Logger LOG = LoggerFactory.getLogger(TipsApp.class);
24 |
25 | public static void main(String[] args) {
26 | SpringApplication.run(TipsApp.class, args);
27 | }
28 |
29 | @Autowired
30 | private TipsAppProperties properties;
31 | @Autowired
32 | private Optional git;
33 | @Autowired
34 | private Optional buildProperties;
35 |
36 | @PostConstruct
37 | void init() {
38 | LOG.info("properties: {}", properties);
39 | if (git.isPresent()) {
40 | LOG.info("Git data: {} {}", git.get().getCommitId(), git.get().getCommitTime());
41 | }
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/samples/springboot/tips/config/TipsAppProperties.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.tips.config;
2 |
3 | import org.springframework.boot.context.properties.ConfigurationProperties;
4 |
5 | @ConfigurationProperties("app")
6 | public class TipsAppProperties {
7 |
8 | private final String name;
9 | private final String version;
10 |
11 | public TipsAppProperties(String name, String version) {
12 | this.name = name;
13 | this.version = version;
14 | }
15 |
16 | public String getName() {
17 | return name;
18 | }
19 |
20 | public String getVersion() {
21 | return version;
22 | }
23 |
24 | @Override
25 | public String toString() {
26 | return "TipsAppProperties{" +
27 | "name='" + name + '\'' +
28 | ", version='" + version + '\'' +
29 | '}';
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/samples/springboot/tips/controller/PersonController.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.tips.controller;
2 |
3 | import jakarta.validation.Valid;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 | import org.springframework.web.bind.annotation.*;
7 | import pl.piomin.samples.springboot.tips.data.model.Person;
8 | import pl.piomin.samples.springboot.tips.data.repository.PersonRepository;
9 |
10 | @RestController
11 | @RequestMapping("/persons")
12 | public class PersonController {
13 |
14 | private static final Logger LOG = LoggerFactory.getLogger(PersonController.class);
15 |
16 | private PersonRepository repository;
17 |
18 | public PersonController(PersonRepository repository) {
19 | this.repository = repository;
20 | }
21 |
22 | @PostMapping
23 | public Person add(@Valid @RequestBody Person person) {
24 | return repository.save(person);
25 | }
26 |
27 | @GetMapping("/{id}")
28 | public Person findById(@PathVariable("id") Long id, @RequestHeader("X-Version") String version) {
29 | LOG.info("FindById: id={}, version={}", id, version);
30 | return repository.findById(id).orElseThrow();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/samples/springboot/tips/controller/TipController.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.tips.controller;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 | import org.springframework.http.HttpStatus;
6 | import org.springframework.web.bind.annotation.GetMapping;
7 | import org.springframework.web.bind.annotation.PathVariable;
8 | import org.springframework.web.bind.annotation.RequestMapping;
9 | import org.springframework.web.bind.annotation.RestController;
10 | import org.springframework.web.server.ResponseStatusException;
11 | import pl.piomin.samples.springboot.tips.data.model.Tip;
12 | import pl.piomin.samples.springboot.tips.data.repository.TipRepository;
13 |
14 | import java.util.NoSuchElementException;
15 |
16 | @RestController
17 | @RequestMapping("/tips")
18 | public class TipController {
19 |
20 | private static final Logger LOG = LoggerFactory.getLogger(TipController.class);
21 | private TipRepository repository;
22 |
23 | public TipController(TipRepository repository) {
24 | this.repository = repository;
25 | }
26 |
27 | @GetMapping
28 | public Iterable findAll() {
29 | return repository.findAll();
30 | }
31 |
32 | @GetMapping("/{id}")
33 | public Tip findById(@PathVariable("id") Long id) {
34 | try {
35 | return repository.findById(id).orElseThrow();
36 | } catch (NoSuchElementException e) {
37 | LOG.error("Not found", e);
38 | throw new ResponseStatusException(HttpStatus.NO_CONTENT);
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/samples/springboot/tips/data/model/Address.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.tips.data.model;
2 |
3 | import jakarta.persistence.Embeddable;
4 |
5 | import java.util.Objects;
6 |
7 | @Embeddable
8 | public class Address {
9 | private String country;
10 | private String city;
11 | private String street;
12 | private int houseNumber;
13 | private int flatNumber;
14 |
15 | public Address() {
16 |
17 | }
18 |
19 | public Address(String country, String city, String street, int houseNumber, int flatNumber) {
20 | this.country = country;
21 | this.city = city;
22 | this.street = street;
23 | this.houseNumber = houseNumber;
24 | this.flatNumber = flatNumber;
25 | }
26 |
27 | public String getCountry() {
28 | return country;
29 | }
30 |
31 | public void setCountry(String country) {
32 | this.country = country;
33 | }
34 |
35 | public String getCity() {
36 | return city;
37 | }
38 |
39 | public void setCity(String city) {
40 | this.city = city;
41 | }
42 |
43 | public String getStreet() {
44 | return street;
45 | }
46 |
47 | public void setStreet(String street) {
48 | this.street = street;
49 | }
50 |
51 | public int getHouseNumber() {
52 | return houseNumber;
53 | }
54 |
55 | public void setHouseNumber(int houseNumber) {
56 | this.houseNumber = houseNumber;
57 | }
58 |
59 | public int getFlatNumber() {
60 | return flatNumber;
61 | }
62 |
63 | public void setFlatNumber(int flatNumber) {
64 | this.flatNumber = flatNumber;
65 | }
66 |
67 | @Override
68 | public String toString() {
69 | return "Address{" +
70 | "country='" + country + '\'' +
71 | ", city='" + city + '\'' +
72 | ", street='" + street + '\'' +
73 | ", houseNumber=" + houseNumber +
74 | ", flatNumber=" + flatNumber +
75 | '}';
76 | }
77 |
78 | @Override
79 | public boolean equals(Object o) {
80 | if (o == null || getClass() != o.getClass()) return false;
81 |
82 | Address address = (Address) o;
83 | return houseNumber == address.houseNumber && flatNumber == address.flatNumber && Objects.equals(country, address.country) && Objects.equals(city, address.city) && Objects.equals(street, address.street);
84 | }
85 |
86 | @Override
87 | public int hashCode() {
88 | int result = Objects.hashCode(country);
89 | result = 31 * result + Objects.hashCode(city);
90 | result = 31 * result + Objects.hashCode(street);
91 | result = 31 * result + houseNumber;
92 | result = 31 * result + flatNumber;
93 | return result;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/samples/springboot/tips/data/model/Contact.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.tips.data.model;
2 |
3 | import jakarta.annotation.Nullable;
4 | import jakarta.persistence.Embeddable;
5 | import jakarta.validation.constraints.Email;
6 | import jakarta.validation.constraints.Pattern;
7 |
8 | @Embeddable
9 | public class Contact {
10 |
11 | @Email
12 | private String email;
13 | @Pattern(regexp="^\\+\\d{1,3} \\d{3} \\d{3} \\d{3}$", message = "Invalid phone number format")
14 | @Nullable
15 | private String phoneNumber;
16 |
17 | public String getEmail() {
18 | return email;
19 | }
20 |
21 | public void setEmail(String email) {
22 | this.email = email;
23 | }
24 |
25 | public String getPhoneNumber() {
26 | return phoneNumber;
27 | }
28 |
29 | public void setPhoneNumber(String phoneNumber) {
30 | this.phoneNumber = phoneNumber;
31 | }
32 |
33 | @Override
34 | public String toString() {
35 | return "Contact{" +
36 | "email='" + email + '\'' +
37 | ", phoneNumber='" + phoneNumber + '\'' +
38 | '}';
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/samples/springboot/tips/data/model/Gender.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.tips.data.model;
2 |
3 | public enum Gender {
4 | MALE, FEMALE;
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/samples/springboot/tips/data/model/Person.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.tips.data.model;
2 |
3 | import jakarta.annotation.Nullable;
4 | import jakarta.persistence.Entity;
5 | import jakarta.persistence.GeneratedValue;
6 | import jakarta.persistence.GenerationType;
7 | import jakarta.persistence.Id;
8 | import jakarta.validation.Valid;
9 | import jakarta.validation.constraints.Email;
10 | import jakarta.validation.constraints.NotBlank;
11 | import jakarta.validation.constraints.NotNull;
12 | import jakarta.validation.constraints.Null;
13 | import lombok.AllArgsConstructor;
14 | import lombok.Data;
15 | import lombok.NoArgsConstructor;
16 |
17 | import java.util.Objects;
18 |
19 | @Entity
20 | @Data
21 | public class Person {
22 | @Id
23 | @GeneratedValue(strategy = GenerationType.IDENTITY)
24 | private Long id;
25 | private String name;
26 | private int age;
27 | private Gender gender;
28 | private Address address;
29 | @Valid
30 | private Contact contact;
31 |
32 | public Person() {
33 | }
34 |
35 | public Person(Long id, String name, int age, Gender gender, Address address, Contact contact) {
36 | this.id = id;
37 | this.name = name;
38 | this.age = age;
39 | this.gender = gender;
40 | this.address = address;
41 | this.contact = contact;
42 | }
43 |
44 | public Long getId() {
45 | return id;
46 | }
47 |
48 | public void setId(Long id) {
49 | this.id = id;
50 | }
51 |
52 | public String getName() {
53 | return name;
54 | }
55 |
56 | public void setName(String name) {
57 | this.name = name;
58 | }
59 |
60 | public int getAge() {
61 | return age;
62 | }
63 |
64 | public void setAge(int age) {
65 | this.age = age;
66 | }
67 |
68 | public Gender getGender() {
69 | return gender;
70 | }
71 |
72 | public void setGender(Gender gender) {
73 | this.gender = gender;
74 | }
75 |
76 | public Address getAddress() {
77 | return address;
78 | }
79 |
80 | public void setAddress(Address address) {
81 | this.address = address;
82 | }
83 |
84 | public Contact getContact() {
85 | return contact;
86 | }
87 |
88 | public void setContact(Contact contact) {
89 | this.contact = contact;
90 | }
91 |
92 | @Override
93 | public String toString() {
94 | return "Person{" +
95 | "id=" + id +
96 | ", name='" + name + '\'' +
97 | ", age=" + age +
98 | ", gender=" + gender +
99 | ", address=" + address +
100 | ", contact=" + contact +
101 | '}';
102 | }
103 |
104 | @Override
105 | public boolean equals(Object o) {
106 | if (o == null || getClass() != o.getClass()) return false;
107 |
108 | Person person = (Person) o;
109 | return Objects.equals(id, person.id);
110 | }
111 |
112 | @Override
113 | public int hashCode() {
114 | return Objects.hashCode(id);
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/samples/springboot/tips/data/model/Person2.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.tips.data.model;
2 |
3 | import org.springframework.boot.context.properties.ConfigurationProperties;
4 |
5 | @ConfigurationProperties(prefix = "person")
6 | public record Person2(String name, int age) { }
7 |
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/samples/springboot/tips/data/model/Tip.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.tips.data.model;
2 |
3 | import jakarta.persistence.Entity;
4 | import jakarta.persistence.GeneratedValue;
5 | import jakarta.persistence.GenerationType;
6 | import jakarta.persistence.Id;
7 |
8 |
9 | @Entity
10 | public class Tip {
11 | @Id
12 | @GeneratedValue(strategy = GenerationType.IDENTITY)
13 | private Long id;
14 | private String title;
15 | private String description;
16 |
17 | public Tip() {
18 | }
19 |
20 | public Tip(Long id, String title, String description) {
21 | this.id = id;
22 | this.title = title;
23 | this.description = description;
24 | }
25 |
26 | public Long getId() {
27 | return id;
28 | }
29 |
30 | public void setId(Long id) {
31 | this.id = id;
32 | }
33 |
34 | public String getTitle() {
35 | return title;
36 | }
37 |
38 | public void setTitle(String title) {
39 | this.title = title;
40 | }
41 |
42 | public String getDescription() {
43 | return description;
44 | }
45 |
46 | public void setDescription(String description) {
47 | this.description = description;
48 | }
49 |
50 | @Override
51 | public String toString() {
52 | return "Tip{" +
53 | "id=" + id +
54 | ", title='" + title + '\'' +
55 | ", description='" + description + '\'' +
56 | '}';
57 | }
58 |
59 | @Override
60 | public boolean equals(Object o) {
61 | if (o == null || getClass() != o.getClass()) return false;
62 |
63 | Tip tip = (Tip) o;
64 | return id.equals(tip.id);
65 | }
66 |
67 | @Override
68 | public int hashCode() {
69 | return id.hashCode();
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/samples/springboot/tips/data/repository/PersonRepository.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.tips.data.repository;
2 |
3 | import org.springframework.data.repository.CrudRepository;
4 | import pl.piomin.samples.springboot.tips.data.model.Person;
5 |
6 | import java.util.List;
7 |
8 | public interface PersonRepository extends CrudRepository {
9 |
10 | List findByAddressCity(String city);
11 | List findByAgeGreaterThan(int age);
12 |
13 | private void y() {
14 |
15 | }
16 |
17 | default void x() {
18 |
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/samples/springboot/tips/data/repository/TipRepository.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.tips.data.repository;
2 |
3 | import org.springframework.data.repository.CrudRepository;
4 | import pl.piomin.samples.springboot.tips.data.model.Tip;
5 |
6 | public interface TipRepository extends CrudRepository {
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/samples/springboot/tips/exception/handler/TipNotFoundHandler.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.tips.exception.handler;
2 |
3 | import org.springframework.http.HttpStatus;
4 | import org.springframework.web.bind.annotation.ControllerAdvice;
5 | import org.springframework.web.bind.annotation.ExceptionHandler;
6 | import org.springframework.web.bind.annotation.ResponseStatus;
7 | import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
8 |
9 | import java.util.NoSuchElementException;
10 |
11 | @ControllerAdvice
12 | public class TipNotFoundHandler {
13 |
14 | @ResponseStatus(HttpStatus.NO_CONTENT)
15 | @ExceptionHandler(NoSuchElementException.class)
16 | public void handleNotFound() {
17 |
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/samples/springboot/tips/listener/ApplicationStartupListener.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.tips.listener;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.boot.context.event.ApplicationReadyEvent;
5 | import org.springframework.context.ApplicationListener;
6 | import org.springframework.context.annotation.Profile;
7 | import org.springframework.stereotype.Component;
8 | import pl.piomin.samples.springboot.tips.data.model.Tip;
9 | import pl.piomin.samples.springboot.tips.data.repository.TipRepository;
10 |
11 | @Profile("demo")
12 | @Component
13 | public class ApplicationStartupListener implements ApplicationListener {
14 |
15 | @Autowired
16 | private TipRepository repository;
17 |
18 | public void onApplicationEvent(final ApplicationReadyEvent event) {
19 | repository.save(new Tip(null, "Test1", "Desc1"));
20 | repository.save(new Tip(null, "Test2", "Desc2"));
21 | repository.save(new Tip(null, "Test3", "Desc3"));
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/samples/springboot/tips/service/PersonService.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.tips.service;
2 |
3 | import org.springframework.stereotype.Service;
4 | import pl.piomin.samples.springboot.tips.data.model.Person;
5 | import pl.piomin.samples.springboot.tips.data.model.Tip;
6 | import pl.piomin.samples.springboot.tips.data.repository.PersonRepository;
7 | import pl.piomin.samples.springboot.tips.data.repository.TipRepository;
8 |
9 | import java.util.List;
10 |
11 | @Service
12 | public class PersonService {
13 |
14 | private PersonRepository repository;
15 |
16 | public PersonService(PersonRepository repository) {
17 | this.repository = repository;
18 | }
19 |
20 | public List findAll() {
21 | return (List) repository.findAll();
22 | }
23 |
24 | public Person addPerson(Person person) {
25 | return repository.save(person);
26 | }
27 |
28 | public List addPersons(List person) {
29 | return (List) repository.saveAll(person);
30 | }
31 |
32 | public Person findById(Long id) {
33 | return repository.findById(id).orElseThrow();
34 | }
35 |
36 | public List findByCity(String city) {
37 | return repository.findByAddressCity(city);
38 | }
39 |
40 | public List findAllGreaterThanAge(int age) {
41 | return repository.findByAgeGreaterThan(age);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/pl/piomin/samples/springboot/tips/service/TipService.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.tips.service;
2 |
3 | import org.springframework.stereotype.Service;
4 | import pl.piomin.samples.springboot.tips.data.model.Tip;
5 | import pl.piomin.samples.springboot.tips.data.repository.TipRepository;
6 |
7 | import java.util.List;
8 |
9 | @Service
10 | public class TipService {
11 |
12 | private TipRepository repository;
13 |
14 | public TipService(TipRepository repository) {
15 | this.repository = repository;
16 | }
17 |
18 | public List findAll() {
19 | return (List) repository.findAll();
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/resources/additional.yml:
--------------------------------------------------------------------------------
1 | app:
2 | name: tips-x
3 | version: 1.1
--------------------------------------------------------------------------------
/src/main/resources/app.properties:
--------------------------------------------------------------------------------
1 | property1=App specific property1
2 |
3 | person.name = Piotr Minkowski
4 | person.age = 18
5 |
6 | spring.liquibase.change-log = classpath:db/changeLog.sql
--------------------------------------------------------------------------------
/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | app:
2 | name: tips
3 | version: 1.0
4 |
5 | management:
6 | endpoints:
7 | web:
8 | exposure:
9 | include: "*"
10 |
11 | spring.liquibase.change-log: classpath:db/changeLog.sql
12 |
13 | spring:
14 | config:
15 | additional-location: additional2.yml
16 | location: additional2.yml
17 | main:
18 | lazy-initialization: true
--------------------------------------------------------------------------------
/src/main/resources/data.sql:
--------------------------------------------------------------------------------
1 | insert into tip(id, title, description) values (100, 'Test1', 'Desc1');
2 | insert into tip(id, title, description) values (200, 'Test2', 'Desc2');
3 | insert into tip(id, title, description) values (300, 'Test3', 'Desc3');
4 | insert into person(name, age) values ('Test1', 10);
5 | insert into person(name, age) values ('Test2', 20);
6 | insert into person(name, age) values ('Test3', 30);
7 | insert into person(name, age) values ('Test4', 40);
--------------------------------------------------------------------------------
/src/main/resources/db/changeLog.sql:
--------------------------------------------------------------------------------
1 | --liquibase formatted sql
2 |
3 | --changeset piomin:1
4 | create table tip (
5 | id int auto_increment,
6 | title varchar(255),
7 | description varchar(255)
8 | );
9 | create table person (
10 | id serial primary key,
11 | name varchar(255),
12 | age int,
13 | gender varchar(20),
14 | country varchar(50),
15 | city varchar(50),
16 | street varchar(50),
17 | house_number int,
18 | flat_number int,
19 | email varchar(50),
20 | phone_number varchar(20)
21 | );
--------------------------------------------------------------------------------
/src/main/resources/global.properties:
--------------------------------------------------------------------------------
1 | property1=Global property1
2 | property2=Global property2
--------------------------------------------------------------------------------
/src/test/java/pl/piomin/samples/springboot/tips/AppTest.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.tips;
2 |
3 | import org.junit.jupiter.api.Assertions;
4 | import org.junit.jupiter.api.Test;
5 | import org.springframework.boot.test.context.SpringBootTest;
6 | import org.springframework.boot.test.web.server.LocalServerPort;
7 |
8 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
9 | public class AppTest {
10 |
11 | @LocalServerPort
12 | private int port;
13 |
14 | @Test
15 | void test() {
16 | Assertions.assertTrue(port > 0);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/test/java/pl/piomin/samples/springboot/tips/PersonsControllerTests.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.tips;
2 |
3 | import net.datafaker.Faker;
4 | import org.junit.jupiter.api.Assertions;
5 | import org.junit.jupiter.api.Test;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.boot.test.context.SpringBootTest;
8 | import org.springframework.boot.test.web.client.TestRestTemplate;
9 | import pl.piomin.samples.springboot.tips.data.model.Address;
10 | import pl.piomin.samples.springboot.tips.data.model.Contact;
11 | import pl.piomin.samples.springboot.tips.data.model.Gender;
12 | import pl.piomin.samples.springboot.tips.data.model.Person;
13 |
14 | import java.util.Locale;
15 | import java.util.Random;
16 |
17 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
18 | public class PersonsControllerTests {
19 |
20 | @Autowired
21 | private TestRestTemplate restTemplate;
22 |
23 | @Test
24 | void add() {
25 | Faker faker = new Faker(Locale.of("pl"), new Random(0));
26 | Contact contact = new Contact();
27 | contact.setEmail(faker.internet().emailAddress());
28 | contact.setPhoneNumber(faker.phoneNumber().cellPhoneInternational());
29 | Address address = new Address();
30 | address.setCity(faker.address().city());
31 | address.setCountry(faker.address().country());
32 | address.setStreet(faker.address().streetName());
33 | int number = Integer.parseInt(faker.address().streetAddressNumber());
34 | address.setHouseNumber(number);
35 | number = Integer.parseInt(faker.address().buildingNumber());
36 | address.setFlatNumber(number);
37 | Person person = new Person();
38 | person.setName(faker.name().fullName());
39 | person.setContact(contact);
40 | person.setAddress(address);
41 | person.setGender(Gender.valueOf(faker.gender().binaryTypes().toUpperCase()));
42 | person.setAge(faker.number().numberBetween(18, 65));
43 | System.out.println(person);
44 |
45 | person = restTemplate.postForObject("/persons", person, Person.class);
46 | Assertions.assertNotNull(person);
47 | Assertions.assertNotNull(person.getId());
48 |
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/test/java/pl/piomin/samples/springboot/tips/PersonsControllerViaRestClientTests.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.tips;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.boot.test.context.SpringBootTest;
6 | import org.springframework.boot.test.context.TestConfiguration;
7 | import org.springframework.boot.test.web.server.LocalServerPort;
8 | import org.springframework.context.annotation.Bean;
9 | import org.springframework.web.client.RestClient;
10 | import pl.piomin.samples.springboot.tips.data.model.Person;
11 |
12 | import static org.junit.jupiter.api.Assertions.assertNotNull;
13 |
14 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
15 | public class PersonsControllerViaRestClientTests {
16 |
17 | @TestConfiguration
18 | public static class RestClientConfiguration {
19 | @LocalServerPort
20 | private Integer port;
21 |
22 | @Bean
23 | public RestClient restClient(RestClient.Builder builder) {
24 | return builder.baseUrl("http://localhost:" + port).build();
25 | }
26 | }
27 |
28 | @Autowired
29 | RestClient client;
30 |
31 | @Test
32 | void shouldGetPerson() {
33 | Person person = client.get()
34 | .uri("/persons/{id}", 1L)
35 | .header("X-Version", "v1")
36 | .retrieve()
37 | .body(Person.class);
38 | assertNotNull(person);
39 | assertNotNull(person.getId());
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/test/java/pl/piomin/samples/springboot/tips/PersonsServiceTest.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.tips;
2 |
3 | import org.instancio.Instancio;
4 | import org.instancio.Select;
5 | import org.junit.jupiter.api.Assertions;
6 | import org.junit.jupiter.api.Test;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 | import org.springframework.beans.factory.annotation.Autowired;
10 | import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
11 | import org.springframework.context.annotation.Import;
12 | import pl.piomin.samples.springboot.tips.data.model.Address;
13 | import pl.piomin.samples.springboot.tips.data.model.Contact;
14 | import pl.piomin.samples.springboot.tips.data.model.Person;
15 | import pl.piomin.samples.springboot.tips.service.PersonService;
16 | import pl.piomin.samples.springboot.tips.service.TipService;
17 |
18 | import java.util.List;
19 |
20 | @DataJpaTest(showSql = false)
21 | @Import({TipService.class, PersonService.class})
22 | public class PersonsServiceTest {
23 |
24 | private final static Logger LOG = LoggerFactory.getLogger(PersonsServiceTest.class);
25 |
26 | @Autowired
27 | private PersonService personService;
28 |
29 | @Test
30 | void testFindAll() {
31 | List persons = personService.findAll();
32 | Assertions.assertEquals(4, persons.size());
33 | }
34 |
35 | @Test
36 | void addAndGet() {
37 | Person person = Instancio.of(Person.class)
38 | .ignore(Select.field(Person::getId))
39 | .generate(Select.field(Contact::getPhoneNumber), gen -> gen.text().pattern("+#d#d #d#d#d #d#d#d #d#d#d"))
40 | .generate(Select.field(Contact::getEmail), gen -> gen.text().pattern("#c@#c.com"))
41 | .create();
42 | person = personService.addPerson(person);
43 | Assertions.assertNotNull(person.getId());
44 | person = personService.findById(person.getId());
45 | Assertions.assertNotNull(person);
46 | Assertions.assertNotNull(person.getAddress());
47 | }
48 |
49 | @Test
50 | void addListAndGet() {
51 | final int numberOfObjects = 5;
52 | final String city = "Warsaw";
53 | List persons = Instancio.ofList(Person.class)
54 | .size(numberOfObjects)
55 | .ignore(Select.field(Person::getId))
56 | .generate(Select.field(Contact::getPhoneNumber), gen -> gen.text().pattern("+#d#d #d#d#d #d#d#d #d#d#d"))
57 | .generate(Select.field(Contact::getEmail), gen -> gen.text().pattern("#c@#c.com"))
58 | .set(Select.field(Address::getCity), city)
59 | .create();
60 | personService.addPersons(persons);
61 | persons = personService.findByCity(city);
62 | Assertions.assertEquals(numberOfObjects, persons.size());
63 | }
64 |
65 | @Test
66 | void addGeneratorAndGet() {
67 | List persons = Instancio.ofList(Person.class)
68 | .size(100)
69 | .ignore(Select.field(Person::getId))
70 | .generate(Select.field(Person::getAge), gen -> gen.ints().range(18, 65))
71 | .generate(Select.field(Contact::getPhoneNumber), gen -> gen.text().pattern("+#d#d #d#d#d #d#d#d #d#d#d"))
72 | .generate(Select.field(Contact::getEmail), gen -> gen.text().pattern("#c@#c.com"))
73 | .create();
74 | personService.addPersons(persons);
75 | persons = personService.findAllGreaterThanAge(40);
76 | Assertions.assertTrue(persons.size() > 0);
77 | }
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/src/test/java/pl/piomin/samples/springboot/tips/TipsAppTest.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.tips;
2 |
3 | import org.junit.jupiter.api.Assertions;
4 | import org.junit.jupiter.api.Test;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.beans.factory.annotation.Value;
7 | import org.springframework.boot.test.context.SpringBootTest;
8 | import pl.piomin.samples.springboot.tips.data.model.Person2;
9 |
10 | @SpringBootTest(properties = {
11 | "spring.config.location=classpath:/global.properties,classpath:/app.properties"
12 | })
13 | public class TipsAppTest {
14 |
15 | @Value("${property1}")
16 | private String property1;
17 | @Value("${property2}")
18 | private String property2;
19 | @Autowired
20 | private Person2 person2;
21 |
22 | @Test
23 | void testProperties() {
24 | Assertions.assertEquals("App specific property1", property1);
25 | Assertions.assertEquals("Global property2", property2);
26 | }
27 |
28 | @Test
29 | void testConfigurationProperties() {
30 | Assertions.assertEquals("Piotr Minkowski", person2.name());
31 | Assertions.assertEquals(18, person2.age());
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/test/java/pl/piomin/samples/springboot/tips/TipsControllerTest.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.tips;
2 |
3 | import org.junit.jupiter.api.Assertions;
4 | import org.junit.jupiter.api.Test;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
7 | import org.springframework.context.annotation.Import;
8 | import pl.piomin.samples.springboot.tips.data.model.Tip;
9 | import pl.piomin.samples.springboot.tips.service.PersonService;
10 | import pl.piomin.samples.springboot.tips.service.TipService;
11 |
12 | import java.util.List;
13 |
14 | @DataJpaTest(showSql = false)
15 | @Import({TipService.class, PersonService.class})
16 | public class TipsControllerTest {
17 |
18 | @Autowired
19 | private TipService tipService;
20 |
21 | @Test
22 | void testFindAll() {
23 | List tips = tipService.findAll();
24 | Assertions.assertEquals(3, tips.size());
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/test/java/pl/piomin/samples/springboot/tips/TipsRepositoryTest.java:
--------------------------------------------------------------------------------
1 | package pl.piomin.samples.springboot.tips;
2 |
3 | import org.junit.jupiter.api.*;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.boot.test.context.SpringBootTest;
6 | import org.springframework.transaction.annotation.Transactional;
7 | import pl.piomin.samples.springboot.tips.data.model.Tip;
8 | import pl.piomin.samples.springboot.tips.data.repository.TipRepository;
9 |
10 | import java.util.List;
11 |
12 | @SpringBootTest
13 | @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
14 | @Transactional
15 | public class TipsRepositoryTest {
16 |
17 | @Autowired
18 | private TipRepository tipRepository;
19 |
20 | @Test
21 | @Order(1)
22 | public void testAdd() {
23 | Tip tip = tipRepository.save(new Tip(null, "Tip1", "Desc1"));
24 | Assertions.assertNotNull(tip);
25 | }
26 |
27 | @Test
28 | @Order(2)
29 | public void testFindAll() {
30 | Iterable tips = tipRepository.findAll();
31 | Assertions.assertEquals(3, ((List) tips).size());
32 | }
33 | }
34 |
--------------------------------------------------------------------------------