CRUD con Spring Boot REST JPA

Per realizzare dei servizi REST è possibile utilizzare il framework Spring Boot, che proprio per la sua definizione permette di avere velocemente a disposizione un’applicazione pronta per essere utilizzata in produzione. In questo articolo vedremo come semplicemente mettere in piedi un applicazione con Spring Boot che esponga un servizio REST recuperando informazioni dal database con JPA

Configurazione progetto

Per definire lo scheletro del nostro progetto Spring Boot REST JPA dobbiamo prima di tutto scrivere il pom dove riporteremo le varie dipendenze necessarie

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.javastaff</groupId>
    <artifactId>spring.boot.restjpa</artifactId>
    <version>0.0.1</version>
	<packaging>war</packaging>
	
	<name>Esempio Spring Boot REST JPA</name>
	
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-rest</artifactId>
		</dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
		</dependency>
    </dependencies>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Sono presenti diverse dipendenze relative a Spring Boot e una relativa al database H2 che utilizzeremo per memorizzare i dati. Il packaging del progetto è di tipo war perchè quello che vogliamo avere è qualcosa di cui potremo effettuare il deploy su un application server, ma durante lo sviluppo potremo lanciare l’applicazione utilizzando la classe ApplicationStarter che vediamo riportata qui di seguito

package com.javastaff.spring.boot.restjpa;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ApplicationStarter {

    public static void main(String[] args) {
        SpringApplication.run(ApplicationStarter.class, args);
    }
   
}

L’annotation SpringBootApplication racchiude le tre annotazioni @Configuration, @EnableAutoConfiguration e @ComponentScan. Utilizzando nella nostra classe questa annotazione riusciamo a far partire il contesto Spring e quindi a caricare tutte le cose di cui abbiamo bisogno.

 

Entity, Repository e REST

Partiamo prima di tutto dal modello dei dati che si vogliono gestire. Tanto per essere originali il nostro modello dati sarà un articolo, che vediamo nella classe entity riportata di seguito

package com.javastaff.spring.boot.restjpa;

import java.util.Date;
import java.util.List;

import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Articolo {

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

	private String titolo;
	private String testo;
	private Date dataPubblicazione;
	
	@ElementCollection(targetClass=String.class)
	private List<String> taglist;
	
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	public String getTitolo() {
		return titolo;
	}
	public void setTitolo(String titolo) {
		this.titolo = titolo;
	}
	public String getTesto() {
		return testo;
	}
	public void setTesto(String testo) {
		this.testo = testo;
	}
	public Date getDataPubblicazione() {
		return dataPubblicazione;
	}
	public void setDataPubblicazione(Date dataPubblicazione) {
		this.dataPubblicazione = dataPubblicazione;
	}
	public List<String> getTaglist() {
		return taglist;
	}
	public void setTaglist(List<String> taglist) {
		this.taglist = taglist;
	}
}

Ora grazie ad una singola annotazione potremo fare una magia, perchè definendo semplicemente il classico Repository Spring Data JPA e mettendoci sopra l’annotazione @RepositoryRestResource esporremo l’entità come risorsa REST, aggiungendo anche dei metodi search particolari

package com.javastaff.spring.boot.restjpa;

import java.util.List;

import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.data.rest.core.annotation.RestResource;

@RepositoryRestResource(collectionResourceRel = "articoli", path = "articoli")
public interface ArticoloRepository 
      extends PagingAndSortingRepository<Articolo, Long> {

	List<Articolo> findByTitolo(@Param("titolo") String titolo);
	List<Articolo> findByTaglistIn(@Param("tags") List<String> tags);
	
	@Override
	@RestResource(exported = false)
	void delete(Articolo articolo);
	
	@Override
	@RestResource(exported = false)
	void delete(Iterable<? extends Articolo> articoli);
	
	@Override
	@RestResource(exported = false)
	void delete(Long id);
	
	@Override
	@RestResource(exported = false)
	void deleteAll();

}

Oltre ad esporre la nostra entity come risorsa REST siamo andati a bloccare alcune operazioni. Infatti annotando con @RestResource(exported = false) un metodo evitiamo che questo sia richiamabile tramite REST. In questo modo possiamo ad esempio gestire delle informazioni che vengono alimentate da altri programmi e vogliamo esporre tramite REST soltanto per la consultazione.

 

Test dell’applicazione

Per richiamare la nostra applicazione dobbiamo utilizzare qualche client che ci permetta di effettuare chiamate REST. Potete utilizzare tool come RESTClient dentro Firefox o Postman. Tutte le chiamate dovranno specificare che il Content-Type è di tipo application/json. Come prima cosa facciamo degli inserimenti richiamando l’URL http://localhost:8080/articoli con il verbo HTTP POST, utilizzando un body del messaggio come quello riportato di seguito

{
"titolo" : "Il mio primo articolo",
"testo" : "Non so cosa scrivere",
"taglist" : ["primo","articolo"]
}

la risposta che riceviamo è la seguente

{
  "titolo" : "Il mio primo articolo",
  "testo" : "Non so cosa scrivere",
  "dataPubblicazione" : null,
  "taglist" : [ "primo", "articolo" ],
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/articoli/1"
    },
    "articolo" : {
      "href" : "http://localhost:8080/articoli/1"
    }
  }
}

Passiamo quindi a pubblicare il secondo articolo con i seguenti dati

{
"titolo" : "Il mio secondo articolo",
"testo" : "Ho il blocco dello scrittore...",
"taglist" : ["secondo","articolo"]
}

Ora se richiamiamo l’URL http://localhost:8080/articoli con il metodo GET otterremo la lista di tutti gli articoli attualmente salvati

{
  "_embedded" : {
    "articoli" : [ {
      "titolo" : "Il mio primo articolo",
      "testo" : "Non so cosa scrivere",
      "dataPubblicazione" : null,
      "taglist" : [ "primo", "articolo" ],
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/articoli/1"
        },
        "articolo" : {
          "href" : "http://localhost:8080/articoli/1"
        }
      }
    }, {
      "titolo" : "Il mio secondo articolo",
      "testo" : "Ho il blocco dello scrittore...",
      "dataPubblicazione" : null,
      "taglist" : [ "secondo", "articolo" ],
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/articoli/2"
        },
        "articolo" : {
          "href" : "http://localhost:8080/articoli/2"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/articoli"
    },
    "profile" : {
      "href" : "http://localhost:8080/profile/articoli"
    },
    "search" : {
      "href" : "http://localhost:8080/articoli/search"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 2,
    "totalPages" : 1,
    "number" : 0
  }
}

Se proviamo a cancellare un articolo utilizzando l’URL http://localhost:8080/articoli/1 e la DELETE riceviamo il seguente errore (corretto visto che l’abbiamo richiesto noi)

Status Code: 405
Allow: GET,HEAD,PUT,PATCH,OPTIONS

Se proviamo poi a richiamare in GET l’URL http://localhost:8080/articoli/search ci ritornano le ricerche che abbiamo definito nel repository

{
  "_links" : {
    "findByTitolo" : {
      "href" : "http://localhost:8080/articoli/search/findByTitolo{?titolo}",
      "templated" : true
    },
    "findByTaglistIn" : {
      "href" : "http://localhost:8080/articoli/search/findByTaglistIn{?tags}",
      "templated" : true
    },
    "self" : {
      "href" : "http://localhost:8080/articoli/search"
    }
  }
}

Provando ad esempio la ricerca per tag, con l’URL http://localhost:8080/articoli/search/findByTaglistIn?tags=primo otterremo nella risposta tutti gli articoli che hanno un tag con scritto “primo”. Buon proseguimento in compagni di Spring Boot 🙂

Federico Paparoni

Looking for a right "about me"...

Una risposta

  1. Marzo 28, 2017

    […] I also liked this Federico’s Italian-language post on supporting CRUD with Spring Data REST and Spring Data JPA […]