---
# System prepended metadata

title: Components d'accés a dades. Spring Boot
tags: [UF4 Components d'accés a dades]

---

# **Components d'accés a dades. Spring Boot**

Spring és un framework obert basat en java el qual ens permet construir components com microserveis i webapps entre altres. 

Spring també és conegut per ser un framework que aplica la injecció de dependències (com a forma d'inversió de control), de tal forma que el contenidor de Spring (Spring core container) "injecta" objectes en altres objectes o "dependències". 

![image](https://hackmd.io/_uploads/B1iir5dQ0.png)
Font: [campusmvp: spring framework i spring boot](https://www.campusmvp.es/recursos/post/que-son-spring-framework-y-spring-boot-tu-primer-programa-java-con-este-framework.aspx)

El que farem tot seguit és mostrar en breus passos com construir components per accedir a dades amb Spring.

Un dels llocs webs que recomanem especialment és [Baeldung](https://www.baeldung.com/spring-boot) ja que conté molts tutorials i explicacions detallades per desenvolupar amb Spring.

### Creació del projecte de mostra botiga webapp

De forma senzilla accedirem a [Spring Initializr](https://start.spring.io/) i seleccionarem les opcions que més encaixin al tipus de component que volem construir:

![image](https://hackmd.io/_uploads/ryta25dX0.png)

Per fer el projecte de mostra marcarem:

* A la secció ***Project*** triem **Maven** (ja que volem que maven sigui el gestor de dependències).
* A la secció ***Spring Boot*** deixem la 3.2.5 tal i com ens suggereix. No necessàriament hem d'agafar les versions snapshot i ens decantarem per versions estables.
* Mirem a la dreta la secció ***Dependencies*** i cliquem el botó **ADD DEPENDENCIES**. Per fer aquest projecte de mostra triem les següents (veure a la imatge inferior com queda):

   * Per accedir a dades: **MariaDB driver** (pel SGDB on tenim la base de dades) i **Spring Data JPA** (pels repositoris).
   * Per utilitzar anotacions que ens permetin no codificar getters i setters: **Lombok**.
   * Per construir aplicacions web (inclós REST APIs) d'acord al ***patró MVC***: **Spring Web**.
   * Per últim **Thymeleaf** ens serà útil per construir les vistes de l'aplicació.


![image](https://hackmd.io/_uploads/Hy37bj_Q0.png)

Això el que farà és omplir-nos el fitxer pom.xml, el qual podrem editar posteriorment per afegir més dependències o treure'n.

A sota veurem la secció del project metadata. Aquí cal que hi fiquem el grup, l'artefacte,  i la descripció (opcional, però millor si hi és). Amb aquestes dades automàticament s'ompliran automàticament el name i el package name.

Per aquest projecte d'exemple:
* Group: com.accesadades
* Artifact: botiga
* Description: 

Pel que fa al packaging, triem jar, ja que war demanarà tenir instal·lat el servidor tomcat en l'equip en el qual volguem executar l'aplicació.

I la versió de java la que tingueu instal·lada als vostres equips.

Amb tot això premem al botó **GENERATE** i se'ns baixarà un .zip amb l'scafolding del projecte, el qual l'obrirem amb l'IDE amb el qual treballem (Visual Studio, Netbeans, etc.).

Procedirem a descomprimir-lo i obrir-lo amb l'IDE, en aquesta guia ho fem amb Visual Studio Code. Com a requeriment, cal disposar al visual studio code de l'extensió de **Maven** (sobre la qual ja vam parlar aquí [ORMs Hibernate](https://hackmd.io/MKJbS8UySTiuas78BWx0ZQ#Extensi%C3%B3-del-visual-studio-i-creaci%C3%B3-dun-nou-projecte)).

Un cop obert, l'estructura de projecte s'hauria de mostrar com segueix:

![image](https://hackmd.io/_uploads/B1mqGhO70.png)

### Configuració del servidor

Despleguem resources (dins src/main) i hi anirem tot seguit a l'application.properties:

```
spring.application.name=botiga

# Configuració per l'accés a la Base de Dades
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.globally_quoted_identifiers=true
 
# Port per on escolta el servidor un cop l'arrenquem
server.port=8080
 
# Dades de configuració amb la base de dades MariaDB:
spring.datasource.url=jdbc:mariadb://X.X.X.X:3306/botiga
spring.datasource.username=dam_app
spring.datasource.password=XXXX
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
```

On X.X.X.X serà la ip del servidor on estigui allotjada la base de dades o també el nom del servidor. Si la base de dades es troba a la nostra màquina, podem ficar **localhost**. I per password ficareu la que tingueu per aquest usuari en altres activitats pràctiques.

La propietat **spring.jpa.hibernate.ddl-auto** s' utilitza perquè la base de dades es generi automàticament en cada arrencada de l' aplicació. Això ens interessarà quan estem en desenvolupament però no quan vulguem desplegar-la en producció (com ja vam comentar també amb hibernate). Així els valors que pot prendre són:

* none: Per indicar que no volem que generi la base de dades.
* update: Si volem que la generi de nou en cada arrencada
* create: Si volem que la creu però que no la generi de nou si ja existeix

### Definició de la base de dades

Per aquesta guia utilitzarem la base de dades products de l'anterior activitat pràctica, però per simplicitat no inclourem les relacions amb altres entitats (formarà part de l'exercici pràctic). 

No obstant si volguèssim començar de zero, algunes consideracions:

* Cal crear la base de dades i gestionar els usuaris i com es connecten aquests a la base de dades (local o remot).
* Gràcies a la dependència Spring Data JPA, Spring està fent ús implícitament d'hibernate, de tal forma que podem treballar amb les nostres classes directament a la base de dades. Recordem que hibernate fa el mapeig entre les classes i taules de la BD.
* Si no volem que hibernate ens faci la creació de taules, podem procedir amb els CREATE TABLE com podria ser aquest:

```
CREATE TABLE `products` (
  `product_id` bigint(20) NOT NULL AUTO_INCREMENT,
  `company` varchar(255) DEFAULT NULL,
  `creation_at` datetime(6) DEFAULT NULL,
  `description` varchar(255) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  `price` float DEFAULT NULL,
  `subcategory` varchar(255) DEFAULT NULL,
  `units` bigint(20) DEFAULT NULL,
  `updated_at` datetime(6) DEFAULT NULL,
  PRIMARY KEY (`product_id`)
);
```

### Definició de les classes persistents

Com ja vam fer amb hibernate, és el torn de definir la classe product amb les diferents anotacions. Així, dins el directori com/accesadades/botiga crearem el directori Model (altres autors prefereixen anomenar-lo domini). Aquest directori serà el que emmagatzemarà les classes a persistir, com és el cas de product:

![image](https://hackmd.io/_uploads/HJSCKh_XC.png)

Ara continuem amb la implementació de la classe:

```
package com.accesadades.botiga.Model;

import lombok.*;
import jakarta.persistence.*;

import java.io.Serializable;
import java.time.LocalDateTime;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "products")
public class Product implements Serializable{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long product_id;
    @Column
    private String name;
    @Column
    private String description;
    @Column
    private String company;
    @Column
    private float price;
    @Column
    private long units;
    @Column(name = "creation_at")
    private LocalDateTime creationDate;
    @Column(name = "updated_at")
    private LocalDateTime updateDate;
    @Column
    private String subcategory;

/*     @ManyToOne(cascade=CascadeType.PERSIST)
    @JoinColumn(name="subcategory_id")
    private Subcategory subcategory;    */ 
}
```

Fixeu-vos que en aquest cas lombok aporta les anotacions @Data, @AllArgsConstructor, @NoArgsConstructor amb les quals ens estalviem implementar de forma explícita els constructors (amb i sense paràmetres) i els getters i els setters. A l'hora de cridar una instància de Product veurem que podem accedir als mètodes get i set de cada propietat.

### Repositori

D'acord al patró DAO, del qual ja vam parlar anteriorment, la següent peça és el repositori. El repositori és una interfície que ens permetra accedir a les dades. Com veureu no farem cap implementació en cap classe del repositori, ja que deixarem que ho faci el framework (inversió de control).

Sobre el directori com/accesadades/botiga crearem el directori Repository:

![image](https://hackmd.io/_uploads/r1FnTrqQR.png)

I crearem la classe ProductRepository amb el següent codi:

```
package com.accesadades.botiga.Repository;

import org.springframework.stereotype.Repository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.lang.NonNull;

import java.util.Set;
import com.accesadades.botiga.Model.Product;

@Repository
public interface ProductRepository extends CrudRepository<Product, Long> {

    @Override
    @NonNull
    Set<Product> findAll();
    Product findByName(String name);
    Set<Product> findByNameAndPrice(String name, float price);
}
```

Fixeu-vos que aquesta interfície estèn la interfície CrudRepository, la qual ja té una sèrie de mètodes de fàbrica força interessants:




| Modificador - Tipus | Nom del mètode | Descripció |
| -------- | -------- | -------- |
| void          | deleteAll()        | Elimina totes les entitats gestionades pel repositori         |
| void         | deleteAllById(Iterable<? extends ID> ids)  | Esborra totes les entitats del tipus T (com per exemple entitats vehicle, persona, etc.) amb les ids donades. Exemple d'aplicació: deleteAllById(ids) on ids representa ser una llista de ids pertanyents a les entitats que es vol esborrar         |
| booleà         | existsById(ID id)         | Retornarà si una entitat amb una determina id existeix a la base de dades         |
| Iterable<T>  | findAll()         | Retornarà totes les instàncies del tipus. Mireu l'exemple d'aplicació a la classe de dalt         |
| < S extends T > S         | save(S entity)         | Desa una entitat donada         |

Disposeu de més informació i la llista completa de mètodes a [Spring - Crud Repository](https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/CrudRepository.html)

### Implementació de la lògica de negoci: els serveis

Els serveis són aquells components que vehiculen la lògica de negoci. Com ja s'ha dit altres vegades, aquesta lògica de negoci pot suposar un accés a les dades (via els repositoris) o pot suposar uns altres mètodes diferents que no n'accedeixin.

Sobre el directori com/accesadades/botiga crearem el directori Service:

![image](https://hackmd.io/_uploads/Sy-YAH9mA.png)

On d'una banda crearem la interfície ProductService amb aquest codi:

```
package com.accesadades.botiga.Service;

import java.util.Set;
import com.accesadades.botiga.Model.Product;

public interface ProductService {
 
    Set<Product> findAllProducts();
    Product findProductsByName(String name);
    Set<Product> findAllProducts(String subcategory);
    void increasePrice(Product product);
}
```

I la seva implementació en la classe ProductServiceImpl:

```
package com.accesadades.botiga.Service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Set;
import com.accesadades.botiga.Model.Product;
import com.accesadades.botiga.Repository.ProductRepository;

@Service
public class ProductServiceImpl implements ProductService {

    @Autowired
    private ProductRepository productRepository;

    @Override
    public Set<Product> findAllProducts() {
        return productRepository.findAll();
    }

    @Override
    public Set<Product> findAllProducts(String subcategory) {
        return null;
    }

    @Override
    public Product findProductsByName(String name) {
        return productRepository.findByName(name);
    }

    @Override
    public void increasePrice(Product product) {

    }
}
```

Fixeu-vos que la implementació fa servir l'anotació de component **@Service**.

També destacar el paper que l'anotació @Autowired, la qual ens permet fer la injecció del bean repositori. Recordeu que **@Autowired** és una de les formes d'injectar dependències que utilitza Spring.

### Les vistes

Les vistes seran les interfícies que ens permetran mostrar la informació així com interaccionar amb les dades.

Dins el directori src/main/resources crearem el directori styles, on ficarem els arxius .css en cas de voler aplicar estils:

![image](https://hackmd.io/_uploads/S1CdkIcXA.png)

Dins la carpeta templates ficarem els arxius .html de les diferents vistes.

La idea és que **index** és la landing page, és a dir, la pàgina que apareixerà a l'arrencar el servidor i obrir l'explorador i les altres pàgines són accedides des de la landing page. En concret **catalog** mostra la llista sencera de productes i **search** ens permetrà la cerca d'un producte per nom.

El codi del .html/thymeleaf que proposem (es pot millorar) és:

**Index.html**
```
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Inici</title>
    <link th:href="@{/styles/style_index.css}" rel="stylesheet" />
</head>
<body>
    <h1>BENVINGUTS A LA BOTIGA DE DAM2</h1>
    <p>Ves al <a href="catalog">catàleg de productes</a></p>
    <p>Ves a la <a href="search">cerca de productes per nom</a></p>
</body>
</html>
```

**catalog.html**
```
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
    <head>
        <meta charset="UTF-8">
        <title>Catàleg de productes</title>
        <link th:href="@{/styles/style_catalog.css}" rel="stylesheet" />
    </head>
<body>
    <h1>Catàleg de productes</h1>
    <table border="1">
        <thead>
            <tr>
                <th>Id del producte</th>
                <th>Nom</th>
                <th>Descripció</th>
                <th>Subcategoria</th>
                <th>Unitats en estoc</th>
                <th>Preu unitat</th>
                <th>Fabricant</th>
            </tr>
        </thead>
        <tbody th:each="product : ${products}">
            <tr>
                <td th:text="${product.product_id}"></td>
                <td th:text="${product.name}"></td>
                <td th:text="${product.description}"></td>
                <td th:text="${product.subcategory}"></td>
                <td th:text="${product.units}"></td>
                <td th:text="${product.price}"></td>
                <td th:text="${product.company}"></td>
            </tr>
        </tbody>
    </table>
    <p><a class="nav-link" th:href="@{/}">Torna a la pàgina d'inici</a></p>
</body>
</html>
```

**search.html**
```
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Formulari de cerca</title>
    <link th:href="@{/styles/style_search.css}" rel="stylesheet" />
</head>
<body>
    <h1>Cerca de productes per nom</h1>
    <form action="#" th:action="@{/prodname}" th:object="${prodname}" th:method="post">
        <label for="name">Paràmetre:</label>
        <input type="text" id="name" name="name" required>
        <button type="submit">Cerca</button>
    </form>
    <div th:if="${product}">
        <table>
            <tr>
                <th>Id del producte</th>
                <th>Nom</th>
                <th>Descripció</th>
                <th>Subcategoria</th>
                <th>Unitats en estoc</th>
                <th>Preu unitat</th>
                <th>Fabricant</th>
            </tr>
            <tr>
                <td th:text="${product.product_id}"></td>
                <td th:text="${product.name}"></td>
                <td th:text="${product.description}"></td>
                <td th:text="${product.subcategory}"></td>
                <td th:text="${product.units}"></td>
                <td th:text="${product.price}"></td>
                <td th:text="${product.company}"></td>
            </tr>
        </table>
    </div>
    <p><a class="nav-link" th:href="@{/}">Torna a la pàgina d'inici</a></p>
</body>
</html>
```

I dels estils css que apliquen a cada html:

**style_index.css**
```
body {
    background-color: #f0f0f0;
    font-family: Arial, sans-serif;
    color: #333;
}

h1 {
    color: #007bff;
    text-align: center;
    margin-top: 50px;
}

p {
    text-align: center;
    font-size: 18px;
}

a {
    color: #007bff;
    text-decoration: none;
}

a:hover {
    text-decoration: underline;
}
```

**style_catalog.css**
```
body {
    background-color: #f0f0f0;
    font-family: Arial, sans-serif;
    color: #333;
}

h1 {
    color: #ff4633;
    text-align: center;
    margin-top: 50px;
}

table {
    width: 50%;
    margin: 20px auto;
    border-collapse: collapse;
}
th, td {
    padding: 10px;
    text-align: left;
}
th {
    background-color: #f2f2f2;
}
tr:nth-child(even) {
    background-color: #f9f9f9;
}
p {
    text-align: center;
    font-size: 18px;
}
```

**style_search.css**
```
body {
    font-family: Arial, sans-serif;
    margin: 0;
    padding: 20px;
    background-color: #f0f0f0;
}
h1 {
    text-align: center;
    color: #ff4633;
}
form {
    max-width: 400px;
    margin: 0 auto;
    background: white;
    padding: 20px;
    border-radius: 8px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    margin-bottom: 30px;
}
label, input, button {
    display: block;
    width: 90%;
    margin-bottom: 10px;
}
input, button {
    padding: 10px;
}
button {
    background: #007bff;
    color: white;
    border: none;
    cursor: pointer;
    padding: 10px 20px; 
}
table {
    border-collapse: collapse;
    width: 100%;
    padding: 10px;
}
th, td {
    border: 1px solid #ddd;
    padding: 8px;
    text-align: left;
}
th {
    background-color: #f2f2f2;
}
p {
    text-align: center;
    font-size: 18px;
}
```

### Els controladors

Aquesta és la peça fonamental, ja que és la que posa d'acord les vistes amb els components d'accés a dades.

Com hem fet abans, procedirem a crear sobre el directori com/accesadades/botiga el directori Controller:

![image](https://hackmd.io/_uploads/r1wmGIcQC.png)

En aquest cas implementem un únic controlador amb el següent codi:

```
package com.accesadades.botiga.Controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import com.accesadades.botiga.Model.Product;
import com.accesadades.botiga.Service.ProductService;
import java.util.Set;

@Controller
public class WebController {
 
    @Autowired
    private ProductService productService;
 
    @RequestMapping(value = "/")
    public String index(Model model) {
        return "index";
    }
 
    @RequestMapping(value = "/catalog")
    public String catalog(Model model) {
        Set<Product> products = productService.findAllProducts();
        model.addAttribute("products", products);
        return "catalog";
    }

    @RequestMapping(value = {"/search", "/prodname"}, method = {RequestMethod.GET, RequestMethod.POST})
    public String searchProductByName(@RequestParam(value = "name", required = false) String name, Model model) {
        if (name != null) {
            Product product = productService.findProductsByName(name);
            model.addAttribute("product", product);
        }
        return "search"; // Referencia a search.html en el directorio templates
    }
}
```

Cadascun dels mètodes interacciona amb cadascuna de les vistes definides anteriorment. Ho especifiquem en el paràmetre value.

El que fa cada mètode es invocar als mètodes del servei i retornar el seu resultat a cadascuna de les vistes.

### Arrenquem el servidor

Es pot fer de dues formes:

* Fent un mvn spring-boot:run des del terminal.
* Anant a la classe BotigaApplication.java i fent un run.

Un cop arrencat obrim un explorador i fem localhost:8080/. Si tot va bé hauríem de veure la pàgina index.







