TECNOLOGIE UTILIZZATE:
- html/css per il la pagina web
- javascript per la gestione delle rest lato client e la renderizzazione della pagina
- java per la gestione del backend e delle rest lato server
- mysql per la gestione dei dati su db
per la creazione del progetto uso spring una piattaforma che mi permette di avere un ambiente preimpostato che permette di niettare in odo effieciete le dipendenze e l'esposizione dei vari servizi
REST tramite l'annotazione @RestController, semplificando la creazione di end-point per la comunicazione front-end-backend
Per la connesione al db aggiungo la dipendenza MySQL Driver e SPING DATA JPA che mi semplifica, viste le tempistiche, l'accesso ai dati e la gestione delle query
Tipologia di progetto spring:
- Maven in linguaggio java
- SpeingBoot 3.5.3 con packaging WAR e java 17
Per l'apertura delle porte MYSQL e Tomcat userò XAMPP
Per testare le chiamte REST prima di intergrarle nel frontend farò uso di POSTMAN , in modo da validare LE RICHIESTE IN modo rapido e controllato
Credo di mantenere, in quanto ottimamle l'architettua a tre livelli
- Interface layer (controller) espone gli endpoint REST e gestisce lo scambio dai con il front end tramite DTO
- Business layer (service) Contiene le logiche applicative
- DATABAse layer (repository) accede ai dati tramite JPA collegato al Db Mysql
'-----------------------------------------------------------------------------------------------------
PER PRIMA COSA CREO LA CONNESSIONE E IL MIO DATABASE biblioteca utilizzando il programma Dbeaver nella sua versione portable
Creo tabelle a mano anche se JPA mi consentirebbe di mapparle attraverso le entity del mio progetto Java, questo per non rischiare di sovrascrivere o modifcare colonne in corso d'opera (sempre in relazione alle tempistiche)
Prima Tabella book
id INT chiave primaria, autoincrement not null
title Varchar(100) NOT NULL
author Varchar 100 NOT NULL
publish_year Int NOT NULL
iduser INT null
Voglio mantenere il campo iduser di tipo INT e lasciarlo NULL, senza definire una foreign key, perché gestisco la relazione con la tabella User
direttamente tramite le annotazioni JPA @ManyToOne (in Book) e @OneToMany (in User).
Nell'entità Book sarà presente una proprietà di tipo User, e questo approccio mi semplifica la gestione delle associazioni:
mi è meno laborioso aggiornare la relazione utente-libro lavorando direttamente sull'oggetto User invece che sul campo iduser.
Seconda Tabella user
id INT chiave primaria, autoincrement not null
firstname Varchar(100) NOT NULL
lastname Varchar 100 NOT NULL
PREDISPONDO L'ambiente java
'----------------------------------------------------------------
Nota: Per ragioni di tempo, non sono stati inclusi i getter e setter, ma in un contesto reale verrebbero aggiunti per garantire una gestione corretta delle entità.
'-------------------------------------------------------------
DIR Controller – Espone gli endpoint REST al frontend
--> BookControlle.java - annotation @Entities - @autowired BookService @autowired UserService
DIR Dtos – Trasferisce dati tra client e server in modo sicuro
--> BookDto.java --> Proprietà + costruttore BookDto vuoto + costruttore BookDto parametri + + costruttore BookDto (Book)
--> BookRequestDto.java --> Proprietà
--> UserDto.java Proprietà + costruttore UserDto vuoto + costruttore UserDto parametri + + costruttore BoUserDtookDto (User)
--> UserRequestDto.java
DIR Entities – Mappano le tabelle del database
--> Book.java annotation @Entities contierne ha un campo User user @ManyToOne e @JoinColumn (iduser) che restituiesce uno user
---> User.java annotation @Entities annotation @OneToMany mappato su "user" che restituisce una lista di libri
DIR Repositories – Gestiscono l’accesso al database
--> BookRepository.java - public Interface extended Jpa Repository annotation @Repository --> opera sulla tabella book
--> UserRepository.java - public Interface extended Jpa Repository annotation @Repository--> opera sulla tabella libri
DIR Services – Contiene la logica di business
--> BookService.java annotation @Service @autowired BookRepository
--> UserService.java annotation @Service @autowired UserRepository
'-------------------------PRIMO PUNTO
Creo l'index.html (/resource/static/) skin base html (uso bootstrap, linkato da cdn che conosco bene e uso abitualmente) e un /static/assets/css/custom.css per le personalizzazioni. Imposto già la navigazione per le varie pagine
Creo i file html che mi servono
- listBook.html
- addbook.html
Per ciascun file creo il file js (vuoto) per le xmlhhtprequest che dovrò effettuare
NB avrei preferito usare fetch (piu bello e moderno) ma il poco tempo mi fa optare per l'xmlhhtprequest che riesco a debaggare al momnto con piu facilità. E' piu prolisso e verboso ma per quasto meno ostico da testare,
cosi come userò DOM classico (createElement, appendChild, ecc.) per la creazione dinamica dell'html piuttosto che innerHTML con template string oppure framework/librerie come React o Vue.
'-------------------------PRIMO PUNTO LISTA libri
Html -->
Tabella con col di bootstrap
JS -->
onload--> GetBooks (end point "get_books") fa richiesta GET al controller della lista libri, la riceve, la formatta GetListBooks() e la pubblica costruendo le righe in maniera dinamica MakeHtml()
backend
CController
@GetMapping sull’endpoint get-books → GetListBooks() riceve la richiesta, controlla la congruità dei dati e la restituisce al client dopo averla fatta elaborare al service.
Service
GetListBooks() → Chiede al repository, attraverso una funzione findAll() di JPA, una lista di entità Book.
Crea dall'entità una lista di DTO e la passa al controller.
'-------------------------SECONDO PUNTO ADD book
Html (addbook.html) -->
Form con campi input per l'inserimento
Button che chiama la funzione Addbook nel js (addbook.js)
Div nascoste per messaggi di errore o conferma
JS
onload → rende invisibili le div di risposta.
AddBook() → assegna i valori degli input alle variabili, controlla i campi obbligatori,
crea l’oggetto book.
Chiama l'endpoint add-book con un POST, inviando al controller un JSON con l’oggetto.
Se il libro viene inserito correttamente, riceve una risposta positiva (Created: 201)
→ valorizza e rende visibile il messaggio di OK sull’HTML.
Se il libro non viene inserito, riceve un messaggio di errore (HttpServerError specifico)
→ rende visibile il messaggio di errore sull’HTML.
BACKEND
Controller
@PostMapping sull’endpoint add-book → AddBook()
→ risponde con una ResponseEntity> per poter restituire valori diversi.
Riceve la richiesta annotata con @RequestBody in forma di BookRequestDto.
Controlla la congruità dei dati; se non rispettata, restituisce una BadRequest.
Crea un BookDto e lo passa al service per l’inserimento.
Nel blocco try-catch:
se la creazione va a buon fine → ritorna al client l’oggetto creato.
se fallisce → ritorna uno status Server Error con messaggio di errore.
Service
AddBook() → riceve il BookDto, lo trasforma in entità Book
e lo salva con save() del repository.
Restituisce il nuovo oggetto creato (in DTO).
'-------------------------TERZO PUNTO lIBRO IN PRESTITO
Html (CHECKOUTBOOK.html) -->
Form con campi input per l'inserimento di nome e cognome
Select dove viene pubblicata la lista dei libri disponibili
button che chiama CheckOutBook()
Div nascoste per messaggi di errore o conferma
JS
onload → rende invisibili le div di risposta, chiama GetListAvaiableBook
GetListAvaiableBook() (end point "get-avaiable-books") fa richiesta GET al controller della lista libri non in prestito (iduser=0 o null), la riceve, la formatta e la pubblica costruendo le righe in maniera dinamica le option della select(MakeSelectOption)
CheckOutBook() , setta e valorizza le variabili degli input, controlla la congruita dei dati, chiama l'endpoint checkout-book con un POST, inviando al controller un JSON con l’oggetto formato da (id del libro, nome e gognome dell'utente).
Se il libro viene datao in prestip correttamente, riceve una risposta positiva (Created: 201)
→ valorizza e rende visibile il messaggio di OK sull’HTML.
Se il libro non viene dato in prestito, riceve un messaggio di errore (HttpServerError specifico)
→ rende visibile il messaggio di errore sull’HTML.
BACKEND
Controller
@GetMapping sull'end-point get-avaiable-books
GetListAvaiableBook chiede al service una lista di libri con iduser 0 o null
ottiene una lista BookDto disponibili
@PostMapping sull’endpoint check-out-book → CheckOutBook()
→ risponde con una ResponseEntity> per poter restituire valori diversi.
Riceve la richiesta annotata con @RequestBody in forma di BookRequestDto.
Controlla la congruità dei dati; se non rispettata, restituisce una BadRequest.
Chiama il service (GetBook) passandogliid del libro ottiente uno UserDto
se questo passaggio va a buon fine
Chiama il service (FindUserByName) passandogli nomne e cognome dell.utente e ottiente uno UserDto
Se questo passaggio va a buon fine
chiama di il service passandogli BookDto e UserDto ed ottiene il bookdto aggiornato con il nuovo iduser
** tutte queste chiamate aòl service avvengono dentro un try/catch
Service Bookservice
GetListAvaiableBook() --> restituisce una lista di BookDto disponibili interrogando il repository con una query nativa (iduser=0 or null)
GetBook() → Cerca il libro attraverso jpa findById()
Restituisce il nuovo oggetto trovato o creato (come UserDTO).
CheckOutBook(). riceve uno UserDto e un BookDto
converte i dto in entità
Aggiorna il Book con il nuovo user
Lo ritrasforma in dto e lo restituisce al controller
Service UserService
GetUserByName() → Cerca l'utente interrogando il repository con una query nativa coi parametri @Param("firstname") String firstamane, @Param("lastname") String lastname
Se non lo trova lo crea
<nav class="navbar navbar-dark navbar-expand-lg bg-dark fixed-top">
<div class="container-fluid">
<a class="navbar-brand">La mia biblioteca on line</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mynavbar">
<span class="navbar-toggler-icon"></span></button>
<div class="collapse navbar-collapse justify-content-center" id="mynavbar">
<div class="navbar-nav">
<a class="nav-link active" href="/index.html"> Home Page</a>
<a class="nav-link active" href="/ListBooks.html"> Lista libri</a>
<a class="nav-link active" href="/AddBook.html"> Aggiungi libri</a>
<a class="nav-link active" href="/CheckOutBook.html"> Prestito libri</a>
<a class="nav-link active" href="/CheckInBook.html"> Restituzione libri</a>
</div>
</div>
</div>
</nav>
html, body {
height: 100%;
}
.centerdata {
display: flex;
align-items: center;
justify-content: center;
}
.title-row {
color: #fff;
background-color: blue;
font-weight: bold;
}
.wrapper {
background-color: antiquewhite;
border: 1px solid #060606;
border-radius: 18px;
padding: 2em;
max-width: 600px;
width: 100%;
box-shadow: 0 0 15px rgba(0,0,0,0.1);
}
#ContainerError {
background-color: lightpink;
color: red;
padding: 15px;
border-radius: 18px;
}
#ContainerOk {
background-color: lightgreen;
color: green;
padding: 15px;
border-radius: 18px;
}
let url = `get-user-books?firstname=${encodeURIComponent(firstname)}&lastname=${encodeURIComponent(lastname)}`
public ResponseEntity> GetUserBooksList(@RequestParam("firstname") String firstname, @RequestParam("lastname") String lastname) {
spring.jpa.properties.hibernate.format_sql=true
spring.datasource.url=jdbc:mysql://localhost:3306/mylibrary
spring.datasource.username=root
spring.datasource.password=
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
server.error.include-message=always
server.error.include-stacktrace=always
spring.jpa.show-sql=true
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql=TRACE