Java - TP5

Spring Boot Application
Author

Ludovic Deneuville

Ideas of Questions:

  • Use Stream
  • Check if age >=0
  • Use an Enum for gender

Objectives

The aim is to develop a Spring Boot application:

  • using layered programming
  • creating new functionalities
  • coding some API endpoints

The principle will be the same as what was done in IT project 2A.

1 Before you start

When you’ve finished, don’t forget to delete your service.

After each part, create a commit and push your code to the remote repository.

1.1 Launch service VSCode Java

We’re developing an API for public Internet access, which requires us to expose our application through a specific port (5000).

    • It looks like : https://user-<sspcloud_username>-<6digits_number>-user<optional: -5000>.user.lab.sspcloud.fr/
Warning

If your service authorises the opening of another port (ex: 5000) on the Internet, you will need to use the same port to launch your Spring Boot application (application.yml file).

1.2 Update your repository

Perhaps the repository used as a template has been updated since the last practical session.

1.3 Open Folder

In VSCode :

    • or using: code-server ~/work/ENSAI-2A-Java-TP/tp5/

tp5 must be the root folder in your explorer.

1.4 When you’ve finished

1.5 Compile and run

Use this command to run your application:

  • mvn spring-boot:run
  • CTRL + C to stop

After launching the Spring Boot application, it will be deployed to something like:

  • https://user-<sspcloud_username>-<6digits_number>-user<optional: -5000>.user.lab.sspcloud.fr/
Warning

If you meet that error: Web server failed to start. Port 5000 was already in use.

  • ps -aux | grep java
  • kill processes (ask for help!)

Sometimes mvn clean could be usefull.

2 Exercice

You’ll be working on an application that manages registrations for running races.

Features already implemented:

  • List of all Athletes
  • Add an Athlete
  • Delete an Athlete
Caution

To make it easier to use, you’ll be using:

  • Thymeleaf to provide an interface
  • An embedded, in-memory H2 database

However, bear in mind that in modern applications, Java is not used for GUIs. For example, the standard stack at INSEE is:

  • database: PostgreSQL
  • backend: Java (API that exposes endpoints)
  • frontend: React JS (uses API data for display)

2.1 Project structure

If you expand all the folders, you’ll notice that there are a lot of files.

Let’s focus on a few key points:

  • src/main/java/fr/ensai/running/: the java classes of your Layered Spring Boot application
    • Layers: controller, service, repository, model…
    • Application.java: the Main class to run your application
  • src/main/resources/
    • templates/: html pages
    • application.yml: Spring Boot properties of your application
    • data.sql: Used to create and pop your database
  • pom.xml: Maven config
  • /config: To setup your app (security…)
  • LoggerAspect.java: Used to log before and after each method call

2.2 For students in IT sections

Let’s use a PostgreSQL database insted of an embedded H2:

    • search on internet for the necessary configuration
    • If all goes well, CloudBeaver will automatically connect to your database
    • Otherwise you need to create a new connection entering Host, Database, Username and Password

2.3 Workflow example

  • You want to display all Athletes
  • You click on button Athletes on the navigation bar
    • This takes you to the /athlete endpoint (check the navbar.html file if necessary)
  • In the Controller, it match with method findAllAthletes()
    • This method calls method findAll() from the service
      • This method calls method findAll() from the repository
        • But if you check class AthleteRepository.java, there is no method findAll()
        • In fact, it’s a native method of CrudRepository, so it is not necessary to implement it
        • This method retrieves all athletes from the database
    • Once the data has been retrieved, the method returns the string ‘allAthletes’
    • Which means that you will be redirected to the allAthletes.html page.
    athlete

sequenceDiagram
    participant IHM
    participant AthleteController
    participant AthleteService
    participant CrudRepository

    IHM->>AthleteController: Request findAllAthletes()
    AthleteController->>AthleteService: Call findAll()
    AthleteService->>CrudRepository: Call findAll()
    CrudRepository->>CrudRepository: Execute findAll()
    CrudRepository-->>AthleteService: Return list of athletes
    AthleteService-->>AthleteController: Return list of athletes
    AthleteController-->>IHM: Return allAthletes

2.4 API Rest

Before playing with the GUI, you’re going to create a few endpoints for your API.

In file ApiRestController.java, add the following endpoints:

If you haven’t any http client installed (Insomnia, Bruno, Postman…) on your machine you can use these bash commands to test your endpoints:

curl -X POST https://<your_url>.user.lab.sspcloud.fr/api/athlete \
     -H "Content-Type: application/json" \
     -d '{
           "firstName": "Floria",
           "lastName": "Guei",
           "age": 34,
           "gender": "Female"
         }'

curl -X DELETE https://<your_url>.user.lab.sspcloud.fr/api/athlete/1
Caution

The ApiRestController class is annotated with @RestController, and is therefore used to create endpoints for a REST API.

Next, we’re going to use simple Controllers to interact with the GUI.

If you prefer, you can continue to develop REST API endpoints without using the GUI.

2.5 Create competitions

Note

The goal is to display all competitions.

In your database there is a table called competition (loaded at start-up via the data.sql file).

CREATE TABLE competition (
    id_competition SERIAL PRIMARY KEY,
    designation    VARCHAR(255),
    city           VARCHAR(255),
    event_date     DATE,
    distance       FLOAT,
    max_athletes   INTEGER
);
    • To store dates, use LocalDate from java.time.LocalDate
WarningNaming conventions

Please respect the naming conventions inherent in the languages (Java: camelCase, SQL: snake_case).

    • list all competitions
    • find a competition from its id
    • delete a competition
    • /competition: display all competitions (allCompetitions.html)
      • In the Model, add an attribute named “competitions”
    • /competition/delete/{id}: to delete a competition using its id

2.6 Binding Athletes and Competitions

We have Athletes.

We have competitions.

Now we want to register athletes for competitions.

----------------------------------------------
-- Registration
----------------------------------------------

DROP TABLE IF EXISTS registration;

CREATE TABLE registration (
    id_registration       SERIAL PRIMARY KEY,
    id_athlete            BIGINT REFERENCES athlete(id_athlete),
    id_competition        BIGINT REFERENCES competition(id_competition),
    registration_date     DATE
);

INSERT INTO registration (id_registration, id_athlete, id_competition, registration_date) VALUES
(77770, 99990, 88880, '2024-03-01'),
(77771, 99991, 88881, '2024-03-02'),
(77772, 99992, 88882, '2024-03-03'),
(77773, 99993, 88883, '2024-03-04'),
(77774, 99994, 88884, '2024-03-05'),
(77775, 99995, 88880, '2024-03-06'),
(77776, 99996, 88881, '2024-03-07'),
(77777, 99997, 88882, '2024-03-08'),
(77778, 99998, 88883, '2024-03-09'),
(77779, 99999, 88884, '2024-03-10'),
(77780, 99990, 88881, '2024-03-11'),
(77781, 99991, 88882, '2024-03-12'),
(77782, 99992, 88883, '2024-03-13'),
(77783, 99993, 88884, '2024-03-14'),
(77784, 99994, 88880, '2024-03-15'),
(77785, 99995, 88882, '2024-03-16'),
(77786, 99996, 88883, '2024-03-17'),
(77787, 99997, 88884, '2024-03-18'),
(77788, 99998, 88880, '2024-03-19'),
(77789, 99999, 88881, '2024-03-20'),
(77790, 99990, 88882, '2024-03-21'),
(77791, 99991, 88883, '2024-03-22'),
(77792, 99992, 88884, '2024-03-23'),
(77793, 99993, 88880, '2024-03-24'),
(77794, 99994, 88881, '2024-03-25'),
(77795, 99995, 88883, '2024-03-26'),
(77796, 99996, 88884, '2024-03-27'),
(77797, 99997, 88880, '2024-03-28'),
(77798, 99998, 88881, '2024-03-29'),
(77799, 99999, 88882, '2024-03-30');
SELECT r.athlete.id
  FROM Registration r
 WHERE r.competition.id = :idCompetition

Now let’s add some methods in CompetitionService:

Try to delete a Competition. What’s wrong?

    • to allow you to force deletion

2.7 Register an Athlete

Difficulty :

On the screen listing the athletes in a competition:

In Java classes:

    • to create the Registration
    • call the suitable repository method

If that was too easy, have fun creating other features.