GESTIONE LIBRERIA: SPRING BOOT CON CRUD

Me lo avete chiesto in tanti ed eccovi accontentati. Oggi inizieremo a mettere giù un progetto Spring Boot con connessione ad un database PostgreSQL e relativi metodi CRUD, ossia le classiche operazioni di aggiunta, rimozione ed aggiornamento sui relativi oggetti in base dati.

L'idea è quella di fornire un esempio completo con tanto di interfaccia grafica che consenta all'utente di gestire una propria libreria personale, effettuare delle ricerche e catalogare nuovi libri, il tutto condito da una moltitudine di articoli che andranno ad arricchire, di settimana in settimana, il software con nuove funzionalità ed improvement computazionali.

Il punto di partenza è l'aggiunta delle dipendenze all'interno del pom.xml:

<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>org.projectlombok</groupId>
		<artifactId>lombok</artifactId>
		<version>1.18.26</version>
		<scope>provided</scope>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-thymeleaf</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-jpa</artifactId>
	</dependency>
	<dependency>
		<groupId>org.postgresql</groupId>
		<artifactId>postgresql</artifactId>
		<scope>runtime</scope>
	</dependency>
</dependencies>

A questo punto, è d'obbligo creare una classe Java che astragga l'entità 'Libro' e modelli lo stesso alle nostre esigenze. Per semplicità i metodi getters e setters, nonchè ToString, sono stati omessi ma si consiglia l'utilizzo della libreria Lombok per accelerare il processo di sviluppo e snellire la verbosità delle classi:

public class Book {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    private String title;
    private String publisher;
    private int year;
    private String author;

    // standard getters and setters

}

Nel package dao, inseriamo lo strato software al quale è demandato il compito di interagire con la base dati avvalendoci della libreria Spring Data, il quale consente di omettere l'implementazione delle queries e relativi metodi fornendoci sottobanco una lista di operazioni ricavate automaticamente dalla classe sulla quale il dao opera. 

@Repository
public interface BookRepository extends JpaRepository<Book> {

    List<Book> findByTitle(String title);
    List<Book> findByAuthor(String name);
 
}

La vera e propria logica di business è, invece, inserita all'interno delle classi sotto il package service. La classe BookService riceve i parametri inviati dal client ed elaborandoli, interroga il db per persistere le informazioni.

@Repository
@Service
public class BookService {
	
	private final BookRepository bookRepository;
	
	public BookService(BookRepository bookRepository) {
		this.bookRepository = bookRepository;
	}
	
	public List<Book> findAll() {
		return bookRepository.findAll();
	}
	
	public void add(Book book) {
		//book.setId(bookRepository.getNextSeriesId());
		bookRepository.save(book);
	}
	
	public Book findById(Long id) {
		return bookRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("Invalid book Id:" + id));
	}
	
	public void delete(Long id) {
		Book book = this.findById(id);
		bookRepository.delete(book);
	}

}

Il punto di ingresso della nostra applicazione sarà invece il controller REST contenente tutti gli endpoint che un qualsiasi client potrà invocare per operare sui nostri amati libri. In questa prima fase del progetto non separeremo le chiamate che agiscono sulle risorse da quelle che invece si occupano del rendering delle pagine web.


package com.soa.libreriamo.controller;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.soa.libreriamo.model.Book;
import com.soa.libreriamo.service.BookService;

@Controller
@RequestMapping("/books")
public class BookController {
    
    private final BookService bookService;

    @Autowired
    public BookController(BookService bookService) {
        this.bookService = bookService;
    }
    
    @GetMapping
    public String getBooks(Model model) {
        model.addAttribute("books", bookService.findAll());
        return "index";
    }
    
    @GetMapping("/signup")
    public String showSignUpForm(Book book) {
        return "addBook";
    }
    
    @PostMapping("/addbook")
    public String addUser(@Valid Book user, BindingResult result, Model model) {
        if (result.hasErrors()) {
            return "addBook";
        }
        
        bookService.add(user);
        return "redirect:/index";
    }
    
    @GetMapping("/edit/{id}")
    public String showUpdateForm(@PathVariable("id") long id, Model model) {
        Book book = bookService.findById(id);
        model.addAttribute("user", book);
        
        return "update-book";
    }
    
    @PostMapping("/update/{id}")
    public String updateUser(@PathVariable("id") long id, @Valid Book book, BindingResult result, Model model) {
        if (result.hasErrors()) {
            book.setId(id);
            return "update-book";
        }
        
        bookService.add(book);

        return "redirect:/index";
    }
    
    @GetMapping("/delete/{id}")
    public String deleteUser(@PathVariable("id") long id, Model model) {
        bookService.delete(id);
        return "redirect:/index";
    }
}

Per quanto riguarda il frontend, il punto di ingresso della nostra applicazione sarà l'index.html. Da essa si sviluppa l'intero progetto che trasformeremo di volta in volta in una "single page application".

All'interno della folder resources, oltre alle pagine html - contenute nella sotto-cartella templates, troviamo anche la folder fragments, al cui interno sono presenti altre due pagine html rappresentati l'intestazione e il footer del layout che verranno  embeddate per l'appunto come frammenti nel seguente modo:

<div th:replace="fragments/header :: header"></div>
<div th:replace="fragments/footer :: footer"></div>

Il codice sorgente del progetto è disponibile sul mio github personale al seguente link:

https://github.com/pierpaolo1989/Libreriamo


 


Comments

Popular posts from this blog

AGGIORNAMENTO INVESTIMENTI 2° TRIMESTRE 2024

AGGIORNAMENTO INVESTIMENTI 1° TRIMESTRE 2024

HO COMPRATO UNO XIOAMI 14 ULTRA