UF02-1845

Published

March 9, 2023

Subject

  • Create a Spring MVC Thyemeleaf with all CRUD.
  • Connect to a H2 JPA DB.
  • REST Controller (Optional).
  • Composition/Inherence0 (Optional).

Project

This Project has been developed during the Webb App Developement course. We are building an app that allows Board Game players to track their games collection, open game sessions where other players can join and save all the plays.


Model

On this section i’m only covering the part that i’ve developed, the class Boardgame, all the crud operations for the MVC model an the Rest controller.

BoardGame.java
@Data
@AllArgsConstructor
@Entity(name="BoardGame")
@Table(name="BOARDGAME_TABLE")

public class BoardGame {

    @Id
    @GenericGenerator(name="system-uuid", strategy="uuid")
    @Column(name="gameID", updatable=false)
    private String gameID;
    @Column(name="gameTitle")
    private String gameTitle;
    @Column(name="minPlayers")
    private int minPlayers;
    @Column(name="maxPlayers")
    private int maxPlayers;
    @Column(name="minPlayTime")
    private int minPlayTime;
    @Column(name="maxPlayTime")
    private int maxPlayTime;

    public BoardGame(String gameTitle){
        this.gameID = Helpers.generateUUID();
        this.gameTitle = gameTitle;
        this.minPlayers = 0;
        this.maxPlayers = 0;
        this.minPlayTime = 0;
        this.maxPlayTime = 0;
    }
    public BoardGame(String gameTitle, int minPlayers, int maxPlayers, int minPlayTime, int maxPlayTime){
        this.gameID = Helpers.generateUUID();
        this.gameTitle = gameTitle;
        this.minPlayers = minPlayers;
        this.maxPlayers = maxPlayers;
        this.minPlayTime = minPlayTime;
        this.maxPlayTime = maxPlayTime;
    }

    public BoardGame(){
        this.gameID = Helpers.generateUUID();
    }
}

Service

Service class with all the busines logic needed for CRUD operations.

BoardGameService.java
@@Service
public class BoardGameService {
    @Autowired
    BoardGameRepository boardGameRepository;

    public Iterable<BoardGame> getAllBoardGames() {

        return boardGameRepository.findAll();
    }

    public BoardGame createGame(BoardGame game){
        BoardGame newGame = boardGameRepository.save(game);
        return newGame;
    }


    public boolean addBoardGameToDB(BoardGame game){

        if (boardGameRepository.findById(game.getGameID()).isPresent()){
            return false;
        }
        boardGameRepository.save(game);
        return true;
    }
    
    public BoardGame getGameByID(String gameID) {
        Optional<BoardGame> gameFromDB = boardGameRepository.findByGameID(gameID);
        if (gameFromDB.isPresent()) {
            return gameFromDB.get();
        }

        return null;
    }

    public BoardGame getGameByGameTitle(String gameTitle){
        Optional<BoardGame> gameFromDB = boardGameRepository.findByGameTitle(gameTitle);
        if (gameFromDB.isPresent()){
            return gameFromDB.get();
        }
        return null;
    }

    public boolean deleteGameFromDB(BoardGame game){
        if (boardGameRepository.findById(game.getGameID()).isPresent()){
            boardGameRepository.delete(game);
            return true;
        }
        return false;
    }

    public boolean updateGameFromDB(BoardGame game){
        if(boardGameRepository.existsById(game.getGameID())){
            BoardGame gameFromDB = boardGameRepository.findById(game.getGameID()).get();

            if (gameFromDB.getGameTitle() != game.getGameTitle()){
                gameFromDB.setGameTitle(game.getGameTitle());
            }
            if (gameFromDB.getMinPlayers()!= game.getMinPlayers()){
                gameFromDB.setMinPlayers(game.getMinPlayers());
            }
            if (gameFromDB.getMaxPlayers()!= game.getMaxPlayers()){
                gameFromDB.setMaxPlayers(game.getMaxPlayers());
            }
            if (gameFromDB.getMinPlayTime()!= game.getMinPlayTime()){
                gameFromDB.setMinPlayTime(game.getMinPlayTime());
            }
            if (gameFromDB.getMaxPlayTime()!= game.getMaxPlayTime()){
                gameFromDB.setMaxPlayTime(game.getMaxPlayTime());
            }
            boardGameRepository.save(gameFromDB);
            return true;
        }
        return false;
    }
}

Controller

Here is the controllers and all the end-points needed to perform CRUD operations with thymeleaf.

BoardGameController.java
@Controller
@RequestMapping("/boardgames")
public class BoardGameController {
    @Autowired
    BoardGameService boardGameService;

    @RequestMapping({"/", ""})
    public String index(Model containerToView) {
        // Retrieve all available users
        containerToView.addAttribute("boardGamesFromController", boardGameService.getAllBoardGames());
        return "boardgames/index";
    }

    @GetMapping(value = {"/creategame", "/creategame/"})
    public String createBoardGame(Model containerToView) {
        containerToView.addAttribute("boardgame", new BoardGame());
        containerToView.addAttribute("operation", "creategame");
        return "boardgames/creategame.html";
    }

    @PostMapping(value = "/creategame/{id}")
    public String createBoardGame(@PathVariable("id") String gameTitle, Optional<BoardGame> game){
        if(boardGameService.getGameByGameTitle(gameTitle) != null){
            return "Already on DB";
        }
        if(game.isPresent()) {
            boardGameService.addBoardGameToDB(game.get());
            //TODO: add a confirmation message with redirection
        }
        return "redirect:/boardgames/creategame";
    }

    @GetMapping(value = "/id/{id}")
    public String getGameByID(@PathVariable("id") String id, Model containerToView) {
        BoardGame gameFromDB = boardGameService.getGameByID(id);
        containerToView.addAttribute("boardgame", gameFromDB);
        return "boardgames/gameDetails";
    }
    @GetMapping(value = "game/{gameTitle}")
    public String getByGameTitle(@PathVariable("gameTitle") String gameTitle, Model containerToView) {
        BoardGame gameFromDB = boardGameService.getGameByGameTitle(gameTitle);
        containerToView.addAttribute("boardgame", gameFromDB);
        return "boardgames/gameDetails";
    }

    @GetMapping("/deleteGame/{id}")
    public String deleteGame(@PathVariable("id") String id) {
        BoardGame toDelete = boardGameService.getGameByID(id);
        boardGameService.deleteGameFromDB(toDelete);
        return "redirect:/boardgames/";
    }

    @GetMapping(value = "/updategame/{id}")
    public String updateBoardGame(@PathVariable("id") String id, Model containerToView) {
        // Retrieve the user based on the provided ID
            BoardGame gameFromDB = boardGameService.getGameByID(id);
            containerToView.addAttribute("boardgame", gameFromDB);
            return "boardgames/updategame";
        }

    @PostMapping(value = "/updategame/{id}")
    public String updateBoardGame(@PathVariable("id") String id, Optional<BoardGame> updatedGame) {
        BoardGame gameToUpdate = boardGameService.getGameByID(id);

        if (updatedGame.isPresent()) {
            if (gameToUpdate != null  && updatedGame.get().getGameID().equals(gameToUpdate.getGameID())) {
                boardGameService.updateGameFromDB(updatedGame.get());
            }
        }
        // Redirect to the GET method
        return "redirect:/boardgames/updategame/" + id;
    }
}

Reposiory class

Code for the repository Interface. Implemented with the CrudRepository framework by Spring Boot.


BoardGameRepository.java
public interface BoardGameRepository extends CrudRepository<BoardGame, String> {
    Optional<BoardGame> findByGameTitle(String gameTitle);
    Optional<BoardGame> findByGameID(String gameID);
}

HTML templates

Here’s the templates used to implement the CRUD operations with TH implementation.

Create Board Game
Board Game Details
Update Board Game


API REST implementation

Here’s the class that implements the REST controller to acces DB and obtaining JSON files.

BoardGameRestController.java
@RestController
@RequestMapping("/api")
public class BoardGameRestController {

    @Autowired
    BoardGameService gameService;

    @GetMapping("")
    public String index(){
        return "Welcome to Meeple Match API!";
    }

    @GetMapping("/boardgames/")
    public Iterable<BoardGame> getAllGames(){
        return gameService.getAllBoardGames();
    }

    @GetMapping("/title/{gameTitle}")
    public BoardGame getGameByTitle(@PathVariable String gameTitle){
        Optional<BoardGame> game = Optional.ofNullable(gameService.getGameByGameTitle(gameTitle));
        if (game.isPresent()){
            return game.get();
        }
        return null;
    }

    @GetMapping("/id/{gameID}")
    public BoardGame getGameByID(@PathVariable String gameID){
        Optional<BoardGame> game = Optional.ofNullable(gameService.getGameByID(gameID));
        if (game.isPresent()){
            return game.get();
        }
        return null;
    }
    @PostMapping(path="createGame", consumes = "application/JSON")
    public BoardGame createGame(@RequestBody BoardGame game){
        BoardGame newGame = gameService.createGame(game);
        return newGame;
    }

    @DeleteMapping("/deleteGame")
    public ResponseEntity<BoardGame> deleteGame(@RequestParam("gameID") String gameID){
        HttpHeaders headers = new HttpHeaders();
        headers.add("operation", "deleteGame");
        headers.add("version", "api 1.0");

        Optional<BoardGame> gameFound = Optional.ofNullable(gameService.getGameByID(gameID));
        boolean game = gameFound.isPresent();
        if (game){
            gameService.deleteGameFromDB(gameFound.get());
            headers.add("operationStatus", "deleted");
            return ResponseEntity.accepted().headers(headers).body(gameFound.get());
        }
        return ResponseEntity.accepted().body(null);
    }

    @PutMapping("/updateGame/")
    public ResponseEntity<BoardGame> updateGame(@RequestBody BoardGame game){

        HttpHeaders headers = new HttpHeaders();
        headers.add("operation", "updateGame");
        headers.add("version", "api 1.0");
        Optional<BoardGame> gameFromDB= Optional.ofNullable(gameService.getGameByID(game.getGameID()));

        if (gameFromDB.isPresent()){
            gameService.updateGameFromDB(game);
            headers.add("operationStatus", "updated");
            return  ResponseEntity.accepted().headers(headers).body(gameService.getGameByID(game.getGameID()));
        }
        return ResponseEntity.accepted().headers(headers).body(null);
    }
}

GitHub

Source code to the project’s source code GitHub