CRUD REST services are the backbone of a microservice architecture. If we want to use microservices rather than monolithic applications, it’s essential that we can create a basic service with a minimum of effort. Spring Boot can be used to quickly create and deploy a new web service. Spring Data REST can be used to build out the REST interface based on a database entity model. Using both together allows us to create a running RESTful web service with zero custom Java code and no tricky XML.
This article describes how to build a RESTful web service as an executable JAR that provides CRUD operations against a single MySQL database table.
This demo can be downloaded from GitHub in the Spanners Demo Application version 4.0 (spanners-api module). You can run the working example as a docker-compose stack, along with the associated MySQL database and the Spring MVC web app that consumes the service (see the previous post on docker-compose for details on how to run this).
Spring Boot starters
The Spring Boot starter projects allow us to easily pull in the dependencies we need based on our broad requirements. It’s no longer necessary to explicitly define every library that we build on. Just pull in the Spring Boot starters for our application type and we’re good.
For a Spring Data REST project, we need the JPA starter (to configure our database mappings) and the Spring Data REST starter. We’ll most likely also want the test starter to allow us to write unit tests. The one dependency we need to explicitly define is our specific database driver (MySQL in my case).
If you dislike XML, this can be done with Gradle. I still prefer Maven so my pom.xml looks like this:
<?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"> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.5.RELEASE</version> </parent> <modelVersion>4.0.0</modelVersion> <groupId>spanners</groupId> <artifactId>spanners-api</artifactId> <version>4.0-SNAPSHOT</version> <packaging>jar</packaging> <name>Spanners REST API</name> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</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-test</artifactId> <scope>test</scope> </dependency> <!-- MySQL for production database connection --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
No need to specify version numbers for any of these. The Spring Boot parent manages all these. The Spring Boot Maven Plugin allows Maven to run Spring Boot applications directly, or to build an all in one executable jar so we can run our application from command line – no need to deploy to an application server.
Entity model and mapping
I have a database table that I want to model in my application:
CREATE TABLE `spanner` ( `id` int(11) NOT NULL auto_increment, `name` varchar(255) default NULL, `size` int(11) default NULL, `owner` varchar(255) default NULL )
This is mapped to a Java class using JPA annotations:
@Entity public class Spanner { @Id @GeneratedValue(strategy= GenerationType.AUTO) private Long id; private String name; private int size; private String owner; // Getters and setters go here }
Note that only the class and the id field are annotated. JPA is smart enough to correctly map the name, size and owner fields to the obvious database columns.
Data Repository
This is where it starts to get clever. A while back, if we needed to create a data repository (or Data Access Object / DAO), we’d define all the operations we want (create, read, update, delete) and then implement each operation as a method using Hibernate, SQL or whatever.
Now, the JpaRepository interface (with super interfaces PagingAndSortingRepository and CrudRepository) define methods for our standard CRUD operations. It’s a typed interface so we can define something like this:
public interface SpannerRepository extends JpaRepository<Spanner, Long> { }
Where Spanner is the type of object managed by the repository and Long is the type of unique identifier – essentially the database table primary key.
The JpaRepository interface defines 18 methods. This could result in a great deal of boilerplate code if we were to implement by hand. Fortunately, JPA will create the implementation of this interface automatically. Mechanisms exist to trigger this implementation using XML or Java based Spring config. This configuration is enabled by default with the spring-boot-starter-data-jpa module so we need do nothing further at all.
Expose a REST interface
We now want a RESTful API for our repository. Again, we could hand craft this, but Spring Data REST takes care of this for us with a single annotation on our repository definition:
@RepositoryRestResource public interface SpannerRepository extends JpaRepository<Spanner, Long> { }
This will expose a well-defined RESTful API with repository CRUD operations correctly mapped to HTTP methods (POST for create, GET for read etc) with API meta-data exposed using HAL.
Configuration
The only thing needing explicit configuration is the connection to a data source. This can be done in an application.properties file which can either be included inside the application (in src/main/resources) or provided at runtime. A connection to a MySQL database looks like this:
spring.datasource.url=jdbc:mysql://localhost:3306/spanners spring.datasource.username=spanners spring.datasource.password=password spring.datasource.driver-class-name=com.mysql.jdbc.Driver
And now, the boilerplate…
There had to be some boilerplate somewhere. In this case, it’s the main class that bootstraps into Spring Boot. You always need one and it usually looks the same. It looks like this:
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class); } }
That’s it.
Running the app
This could be built as a WAR and deployed to an application server. Spring Boot offers us an alternative though. It can build as an executable JAR containing all dependencies and an embedded web server. So we build the app (using Maven) in the usual way:
mvn clean package
and then run like this:
java -jar ./target/spanners-api-4.0.jar
We can verify that the app is running by entering http://localhost:8080/spanners/ in a browser. It should return a valid JSON response:
{ "_embedded" : { "spanners" : [ { "id" : 1, "name" : "Gertrude", "size" : 10, "owner" : "smith", "_links" : { "self" : { "href" : "http://localhost:8090/spanners/1" }, "spanner" : { "href" : "http://localhost:8090/spanners/1" } } }, { "id" : 2, "name" : "Samantha", "size" : 16, "owner" : "smith", "_links" : { "self" : { "href" : "http://localhost:8090/spanners/2" }, "spanner" : { "href" : "http://localhost:8090/spanners/2" } } }, { "id" : 3, "name" : "Susan", "size" : 20, "owner" : "jones", "_links" : { "self" : { "href" : "http://localhost:8090/spanners/3" }, "spanner" : { "href" : "http://localhost:8090/spanners/3" } } } ] }, "_links" : { "self" : { "href" : "http://localhost:8090/spanners" }, "profile" : { "href" : "http://localhost:8090/profile/spanners" } }, "page" : { "size" : 20, "totalElements" : 3, "totalPages" : 1, "number" : 0 } }
It shows the attributes of the three spanners currently in the database. It also shows paging information and a link to the service ALPS meta-data. That’s not at all bad for one annotated interface definition, a couple of annotated classes, a one line method implementation, some config and a build definition.
[…] item REST service, served by the findOne() method of a JpaRepository (see the previous post on No code REST services for details). Note that we have to describe the Spring context with the […]
clean & shinny
Very nice! Thank you.