1. Exercise: Spring Boot Introduction
-
Start a project using http://start.spring.io
-
to open the project in IntelliJ
-
Describe the contents of
-
the Maven pom
-
the spring-boot-starters in the Maven pom
-
-
Start the Tomcat server
-
Know how to start a Java app in IntelliJ
1.1. Run Introducing Spring Boot
Action: go to start.spring.io and start the project based on the instructions above
1.2. Testing
The server should be running on port 8080
1.3. Takeaway
In this exercise, you have started your first Spring Boot application
2. Exercise: Hello world!
-
Run a Hello world controller to smoke test the application
2.1. Steps: Hello world!
-
Copy the code from the demo (in the slides) into your project
-
Re run the server
-
Open http://localhost:8080/api/helloworld in your browser
-
A Hello world message should be rendered
2.2. Takeaway
In this exercise, we smoke tested our app to get started.
3. Exercise: Hitting the Database
-
Implement the connection from a Spring Boot application to a database in general and espcially for MySQL
-
Know how to start a container
-
Know how to bash into a container
-
Know how to stop a container
3.1. Exercise Steps: Hitting the Database
docker container run -d -it --name my-local-mysql-server -e MYSQL_ROOT_PASSWORD=... -p 3306:3306 mysql
| Remember the root password for later |
mysql -h localhost -P 3306 --protocol=tcp -u root -p
docker container exec -it my-local-mysql-server bash
mysql -h localhost -P 3306 --protocol=tcp -u root -p
create database carDb;
| Below fill in a username and password on the 'username' and 'password' locations |
create user 'username'@'%' identified by 'password';
grant all privileges on carDb.* to 'username'@'%';
flush privileges;
exit;
| Below again, fill in the username and password as the value (after the '=' sign) |
spring.datasource.url=jdbc:mysql://localhost:3306/carDb
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=theUsername
spring.datasource.password=thePassword
# Recreate or updte the database after stopping and starting ??? =>
## options for spring.jpa.hibernate.ddl-auto: none, validate, update, create, create-drop
spring.jpa.hibernate.ddl-auto=update
# log sql queries to console or not
spring.jpa.show-sql=true
3.2. Testing
If all is well you should be able to run the server without errors during started and you should see the following part of the log
... o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
... com.acme.cars.CarsApplication : Started CarsApplication in 4.018 seconds (JVM running for 4.464)
3.3. Takeaway
In this exercise, you learned how to make connection to a MySQL database in your Spring Boot application
4. Exercise: JPA
-
Create an entity
-
Use and describe the annotations @Entity, @Id and @GeneratedValue
4.1. Steps: JPA
-
Create entity Car with properties
-
licensePlate: String
-
mileage: int
-
brand: String
-
id: long (mandatory)
-
-
Generate getters and setters for all but leave out the setter for id
| An entity must have a no-arg constructor. |
-
@Id
-
@GeneratedValue(strategy=GenerationType.IDENTITY)
-
Start the server
-
Validate there is no error in the Spring Boot logging
-
-
Validate that there is now a table car in the carDb with columns 'id, license_plate, mileage and brand'
| JPA will change the camelcase to snake_case e.g. licensePlate in the entity goes to license_plate in the database |
4.2. Takeaway
In this exercise, you set up the first entity for our project and learned some basic regarding JPA
5. Exercise: Dependency Injection I
-
Describe and know the @Component and specialities of it
-
Open the @Component class
-
Browse through the subtypes. Which are?
-
Know that this will become more fine grainer during the upcoming modules Repository and RestController
6. Exercise: Repository
-
Describe and Use the CrudRepository and JPARepository to persist to the database
-
Describe the differences between these two
-
Have the server running without errors
6.1. Steps: Repository
-
Create interface CarRepository
-
extends JpaRepository
-
set the correct Generic Type
-
-
Start the server
-
The server should start without error messages
| We will be using this repo in the next module RestController |
6.2. Takeaway
In this exercise, you created the repository for saving objects of type Car
7. Exercise: RestController
-
Create and describe the semantics of the RestControllers and the REST principles and ..Identify the various annotations related to RestControler in Spring Boot
-
@RestController
-
@RequestMapping(method=…) or shorter
-
@GetMapping for getting a List or an instance
-
Omitting - or using the {id} path variable e.g. * @GetMapping("{id}")
-
-
@PostMapping for creating
-
@PutMapping for updating
-
@DeleteMapping for deleting
-
-
@RequestBody for reading an object from the request
-
@PathVariable for reading a scalar from the url of the request
-
@CrossOrigin for setting the REST controller open to specific or all visitors
7.1. Steps: RestController
-
Create a class RestController for the Car entity alike the BeerController shown in the slides
-
After starting the server
-
Use Postman to get an empty list, which is rendered as [] using GET:http://localhost:8080/api/cars
-
Use Postman to post a Car using POST:http://localhost:8080/api/cars
-
Use Postman to update a Car using PUT:http://localhost:8080/api/cars/{id}
-
Use Postman to delete some car
7.2. Takeaway
In this exercise, you created the RestController with the injected Repository and used the GET,POST,PUT and DELETE request for crudding Cars
8. Exercise: Rest with Statuscodes
-
Describe and implement the statuscodes of the REST responses
8.1. Steps: Rest with Statuscodes
-
Implement your in the previous exercise created CarController to use HTTP Status code alike the in the slides shown BeerController
-
After starting the server successfully
-
Use Postman to create a new Car which should result in 200 OK
-
Use Postman to read an existing Car by id which should result in 200 OK
-
Use Postman to delete an existing Car by id which should result in 204 No Content
-
Use Postman to delete a NON existing Car by id which should result in 404 Not found
8.2. Bonus Exercise
Formally, when creating an entity, we should return a 201 status code. Please build that in! Use the API of the ResponseEntity class for that and use a bit your imagination - or ask the trainer. Be aware that Postman shows the created URL in the Location header of the Response. See the links in this module under 'Further Reading' for more clarification
8.3. Takeaway
In this exercise, you learned working with some most important HTTP status codes and rendered them using Spring Boot
9. Exercise: Service
-
Implement a Service for the Car
9.1. Steps: Service
-
Create a class CarService
-
Annotate with @Service
-
Move the repository from the controller to the service
-
Use the intellij Refactor ⇒ Delegate methods to have the methods from the repo in the service
-
Inject the service into the RestController
9.2. Testing
-
Do all the steps for the Repository again with Postman
9.3. Takeaway
In this exercise, you set up a basic service
10. Exercise: Dependency Injection II
-
Implement a Spring Bean using the following annotations
-
@Configuration
-
@Bean
-
10.1. Steps: Dependency Injection II
-
Create a Bean of the created RestTemplate in the CountryService
-
Validate that the CountryService still works
11. Exercise: Transactions
-
For starters, describe and implement transactions using the @Transactional annotation
-
Validate that a transaction is rolled back when the server crashes during a saving
-
There are two types of saving in this context
-
In the Service
-
In the DB
-
The difference is that while in the service the data is NOT persisted yet but a new id might be set to an about-to-be-saved Car!
-
11.1. Steps: Transactions
-
Add @Transactional annotation to methods which are imperative e.g. save and update
public class CarService {
@Transactional
public Car save(Car car) {
Car savedCar = this.carRepository.save(car);
// this is to demo the consequences of rolling back a Transaction
if ("rollback".equals(savedCar.getBrand())) {
System.out.println(3 / 0); // results in an ArithmeticException
// The car should not be saved/updated now!!!
}
return savedCar;
}
}
-
Start the server in debug mode
-
Add breakpoints on the above location on the line with the if statement
-
Run Postman and start to create a Car using POST and check the flow using the breakpoints
-
The first one with brand:'BMW' which should be saved successfully in the Service::save method
-
The second one with brand:'rollback' which should be saved in the Service::save method before the division by zero
-
Validate that the car while being saved gets an id from the underlying database
-
-
-
Validate that the car with an other brand (so not 'rollback') is sucessfully saved to the database
-
Validate that the in step four saved car with brand:'rollback' is NOT saved in the DB ⇒ the Transaction is rolled back
-
Inspect the id of a successfully saved car after a rolledback saving. What happened to the failed id?
| Remember this: Be sure that saving here is saving in the Spring Boot service::save method. Be clear that saving here to the DB is really done after the save method is committed and NOT rolled back |
11.2. Takeaway
In this exercise, you implemented and tested the usage of the @Transactional annotation
12. Exercise: Query methods
-
Describe and Implement query methods in our Repository
-
Have a good understanding of the syntax of the query methods
12.1. Steps: Query methods
-
find the car with a specific licensePlate
-
find all cars with a specific brand
-
find all cars with a mileage > 10000
| Use the link in the topic to make the query methods |
-
Use Postman to validate the result
12.2. Takeaway
In this exercise, you should have learned how to create your own query methods and use them with Postman
12.3. Bonus Exercise
-
find all cars from a specific and order them by mileage
13. Exercise: Named queries
-
Create Named queries
-
JPQL Queries
-
Native queries using native SQL
-
13.1. Steps: Named queries
-
Make the previous @Query method native by supplying a native SQL statement
-
Cryptic ⇒ See the other attribute of @Query - change the value so that a native query is executed
-
Again, use Postman to validate your results
13.2. Bonus Exercise
-
Create a query which finds all cars with an invalid licensePlate - which contain an invalid character (see the KentekenValidator as a hint in the code below)
class KentekenValidator {
private static final String REGEXP_KENTEKEN_FORMAT;
// so NOT the characters mentioned below (^ = NOT in regexp in a range)
private static final String VALID_LETTERS = "[^AaEeIiOoCcQqUu0-9]";
// A valid licensePlate in the NL matches the following regular expression
static {
REGEXP_KENTEKEN_FORMAT = String.format("\\d-%1$s{2}-\\d{3}|\\d-%1$s{3}-\\d{2}|"
+ "\\d{2}-\\d{2}-%1$s{2}|\\d{2}-%1$s{2}-\\d{2}|\\d{2}-%1$s{2}-%1$s{2}|\\d{2}-%1$s{3}-\\d|"
+ "\\d{3}-%1$s{2}-\\d|%1$s-\\d{2}-%1$s{3}|%1$s-\\d{3}-%1$s{2}|%1$s{2}-\\d{2}-\\d{2}|%1$s{2}-\\d{2}-%1$s{2}|"
+ "%1$s{2}-\\d{3}-%1$s|%1$s{2}-%1$s{2}-\\d{2}|%1$s{3}-\\d{2}-%1$s", VALID_LETTERS);
}
}
-
Again, use Postman to validate your results
13.3. Takeaway
In this exercise, you learned to make JPQL named queries
14. Exercise: Profiles and Properties
-
Have a full understanding of creating and using a profile
14.1. Roadmap: Profiles and Properties
-
Create a Profile development and move the dataSource properties from application.properties therein
-
Start your app with the profile development
14.2. Steps: Profiles and Properties
-
Add a file application-development.properties
-
Open CarsApplication
-
IntelliJ: Run as ⇒ Edit configuration ⇒ Set environment variables 'spring.profiles.active=development'
-
Run the application
-
Validate that your application.properties file does NOT CONTAIN DB specific keys as url, username and password!
-
After starting use Postman and validate that the data is still persisted in the same database as before
14.3. Takeaway
In this exercise, you set up a newly development profile and started the Spring Boot application using that profile
14.4. (Optional) Uninstalling Profiles and Properties
If you do not like working with the newly created development profile you migh move back to the original application.properties file
15. Exercise: Minor fixes
-
Symptom: The CarApplicationTests is now an @SpringBootTest which is during the test phase. That should be during integration-test phase
-
Cause: The test should end with IT
-
Solution: Rename the CarsApplicationTests class to CarsApplicationIT
-
Testing: During mvn clean test the test is NOT executed
-
Symptom: After the change above the test will not run during mvn clean verify
-
Cause: The maven-failsafe-plugin is NOT added to the pom.xml
-
Solution: Add the maven-failsafe-plugin as plugin in the build section of the pom.xml (see below)
-
Testing: During mvn clean verify the test is executed
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
| The version of the maven-failsafe-plugin can be omitted since that is already settled for us in the Spring Boot parent project |
-
Symtom: the version is 0.0.1.-SNAPSHOT which indicated that we are creating a bugfix for the 0.0 version
-
Cause: The version number is strange
-
Solution: Bump it to 0.1.0-SNAPSHOT
mvn versions:set -DgenerateBackupPoms=false -DnewVersion=0.1.0-SNAPSHOT
-
During mvn clean test the version 0.1.0-SNAPSHOT is printed
16. Exercise: Testing
-
Describe and implement Unittests and Integration test
-
Learn how to make use of the Junit5 library and the Spring Boot Testing library
16.1. Setup Testing
| There is also a handy referal github repo for this on this Github repo |
16.2. Steps: Testing
|
Step 1: Do the exercises on the site of testing
This site is used before and may contains some error. I omitted to fix the errors since at this point they might
be fixable for yourself at this moment during this training.
|
-
You should have run unittests and integration test on http://testing.carpago.nl using the BankAccount paradigm
When time permits
Exercise: Bean Validation
-
Install and use bean validation for your Spring Boot application
Steps: Bean Validation
Use the text from the demo and implement the validation on your app
Testing
You might write some integration testing for it! In fact that is a very good practise!
Takeaway
In this exercise, you set up validation and have learned how to add validation to your app
Exercise: RestTemplate
-
Invoke a REST api using a Spring Boot app
-
Return the values of the REST api
Setup RestTemplate
We will be using the countries REST api for this exercise
Steps: RestTemplate
-
Create a CountryController
-
Add a GET http://localhost:8080/api/countries/${country}/cities mapping in that controller
-
Use the RestTemplate as shown in the code above for calling this method
-
Using Postman, invoke GET http://localhost:8080/api/countries/Netherlands/cities
-
Validate that all the cities in the Netherlands are displayed
Takeaway
In this exercise, you learned how to invoke a REST api from within your Spring Boot app using RestTemplate
Exercise: JDBC Template
-
Have a little understanding of the JDBC Template
Setup JDBC Template
Code the code from the slides, Customer and SpringBootJdbcApplication to your workspace
Steps: JDBC Template
-
After adding the code from the step above (Setup)
-
Run the code
-
There should be printed that Customers are created and changed
Takeaway
In this exercise, you learned some very basic regarding JDBC Templating
Future
Exercise: WebFlux
-
Run an async app with Spring Boot and verify that you have seen async result in the browser window
Setup WebFlux
docker container run -dit --name some-mongo -p 27017:27017 mongo:latest
-
Open the project on this Github repository and run it
Steps: WebFlux
-
On this tutorial
-
Browse to '5: Demo' on that article
-
Open the link above in What and How and run the POST, PUT and GET methods on http://localhost:8080
-
The POST and PUT with Postman
-
The GET with some Internet Browser which should show that the response is getting asynchronously
-
Testing
Be sure that you have seen the responses of the users asynchronousely in the final part of the exercise which validates that the app is asynchronousely responding
Bonus Exercise
-
WebClient is the future asynchronous and perhaps replacement of RestTemplate
-
Follow this tutorial from Baeldung for introducing the future WebClient
(Optional) Uninstalling WebFlux
You might remove the project or keep the project if you like, that is entirely up to you!