Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Easy Access to Data With Spring REST Data

DZone 's Guide to

Easy Access to Data With Spring REST Data

Explore easy access to data with Spring REST data.

· Database Zone ·
Free Resource

Spring Boot offers fantastic support for accessing data with JPA through the use of interfaces that extends to the repository. If we add to this the ease with which REST services are created, as explained in this entry, we can make an application that offers an API to access our preferred database very easily.

But if we want to implement HATEOAS in our project or if there are a lot of entry points in our API, we must write a lot of code. To solve this problem, Spring Boot offers the library Spring Data Rest. With it, we can write an API REST to access our database easily and without writing much code.

In the example that you have available on my GitHub page, we will see how to perform the maintenance of a customer table without writing a single line to define the different REST APIs.

Creating the Project

As always, we can go to https://start.spring.io to create our maven project. For the example project that we are going to create, we must include the following dependencies: Spring Data Rest, H2, JPA, and Lombok as seen in the screenshot below:

Image title

Once we have imported the project into our preferred IDE, we will have to modify the application.properties file to define certain parameters:

#H2
spring.h2.console.enabled=true
spring.h2.console.path=/h2
#JPA
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=create-drop
# Datasource
spring.datasource.url=jdbc:h2:file:~/test
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driver-class-name=org.h2.Driver
# Data Rest
spring.data.rest.base-path: /api

From this file, the only entry related to the Data Rest library is the last one. Here, we specify the address where REST calls should be implemented to access the different tables that our application have. In our case, it will be accessed through the URL: http://localhost: 8080/api

The other lines configure the H2 database that we will use, as well as certain JPA properties. Of course, we will let JPA create the structure of the database through the defined entities, thanks to the sentence:  spring.jpa.hibernate.ddl-auto = create-drop 

Project Structure

The project will have the following structure:Image title

As you can see, we will define two tables (entities), which are: City and Customer. We also define the corresponding CustomerRepository and CityRepository repositories.

The CityEntity.java class is the following:

@Entity
@Data
@RestResource(rel="customers", path="customer")
public class CustomerEntity {
  @Id
  long id;
  @Column
  String name;
  @Column
  String address;
  @Column
  String telephone;
      @Column @JsonIgnore
  String secret;
  @OneToOne
  CityEntity city;    
}

The particularities of this class are the following:

  1.  The @RestResource line where with the rel parameter we specify how the object should be called in the JSON output. The path parameter indicates where the request should be made.

  2. The annotation @JsonIgnore is applied to the secret column. With this label, that field will be ignored in such a way that neither will be shown in the outputs nor will it be updated, even if included in the requests.

If we did not put the tag @RestResource, Spring would present the resource to access the entity at http://localhost:8080/api/customerEntities because, by default, Spring use the name of the class, putting it in plural form for which it adds 'es'.
The repository of this entity is in CustomerRepository and contains only these lines:

public interface CustomerRepository  extends CrudRepository<CustomerEntity, Long>  {
    public List<CustomerEntity> findByNameIgnoreCaseContaining(@Param("name") String name);
}

The function findByNameIgnoreCaseContaining that I have defined will allow searching the clients, ignoring case, whose name contains the string sent. Later, I will explain how to make a query through this call with Spring Data Rest.

You have more documentation on how to create custom queries in Spring in this entry http://www.profesor-p.com/2018/08/25/jpa-hibernate-en-spring/ (in Spanish).

The CityEntity.java class contains the following lines:

@Entity
@Data
@RestResource(exported=false)
public class CityEntity {
  @Id 
  int id;
  @Column
  String name;
  @Column
  String province;
}

In this case, the @RestResource tag has the exported property set to false so that this entity is not treated by Data Rest and will not be accessible through any API.

Accessing the Data Rest API

The resources that are published will be available at the URL:  http://localhost:8080/api, as we have defined in the application.properties file. This will be the output in this project:

> curl -s http://localhost:8080/api/

{
  "_links" : {
    "customers" : {
      "href" : "http://localhost:8080/api/customer"
    },
    "profile" : {
      "href" : "http://localhost:8080/api/profile"
    }
  }
}

The part of profile refers to what is defined in RFC 6906, where details of the application are included, but I will not discuss it in this entry.

To access the only resource available in our project, which we have called customer, we will navigate to:  http://localhost: 8080/api/customer.

First, let's add a record. We will do this by making a POST request like the following:

> curl -s --request POST localhost:8080/api/customer -d '{"id": 1, "name":"nombre cliente 1","address":"direccion cliente 1","telephone":"telefono cliente 1", "secret": "no guardar"}' -H "Content-Type: application/json"
{
  "name" : "nombre cliente 1",
  "address" : "direccion cliente 1",
  "telephone" : "telefono cliente 1",
  "city" : null,
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/api/customer/1"
    },
    "customerEntity" : {
      "href" : "http://localhost:8080/api/customer/1"
    }
  }

We can verify that you have made the insertion thanks to the H2 database console. For this, we will go to the URL http://localhost: 8080/h2/ and press the Connect button. Once connected, if we perform a query on the CUSTOMER_ENTITY table, we will see the following output:
Image title

As we cannot access the city_entity table through our API as we have specified, we will take advantage of the fact that we are in the console and add a record in the table, which we will assign to the inserted client.

insert into city_entity values(1,'Logroño','La Rioja');
update customer_entity set city_id=1;

Image title

Now, if we make a GET request, we will obtain the following output:

> curl -s localhost:8080/api/customer
{
  "_embedded" : {
    "customers" : [ {
      "name" : "nombre cliente 1",
      "address" : "direccion cliente 1",
      "telephone" : "telefono cliente 1",
      "city" : {
        "name" : "Logroño",
        "province" : "La Rioja"
      },
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/api/customer/1"
        },
        "customerEntity" : {
          "href" : "http://localhost:8080/api/customer/1"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/api/customer"
    },
    "profile" : {
      "href" : "http://localhost:8080/api/profile/customer"
    },
    "search" : {
      "href" : "http://localhost:8080/api/customer/search"
    }
  }
}

Observe how HATEOAS shows the different links available automatically.

For example, we could directly consult the client with code 1 navigating to  http://localhost:8080/api/customer/1 

> curl -s localhost:8080/api/customer/1
{
  "name" : "nombre cliente 1",
  "address" : "direccion cliente 1",
  "telephone" : "telefono cliente 1",
  "city" : {
    "name" : "Logroño",
    "province" : "La Rioja"
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/api/customer/1"
    },
    "customerEntity" : {
      "href" : "http://localhost:8080/api/customer/1"
    }
  }
}

As I explained before, in the Customer repository, I defined the findByNameIgnoreCaseContaining function. To make a query using that function, we will use search links as:
http://localhost:8080/api/customer/search/findByNameIgnoreCaseContaining{?name} 

> curl -s http://localhost:8080/api/customer/search/findByNameIgnoreCaseContaining?name=Clien
{
  "_embedded" : {
    "customers" : [ {
      "name" : "nombre cliente 1",
      "address" : "direccion cliente 1",
      "telephone" : "telefono cliente 1",
      "city" : {
        "name" : "Logroño",
        "province" : "La Rioja"
      },
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/api/customer/1"
        },
        "customerEntity" : {
          "href" : "http://localhost:8080/api/customer/1"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/api/customer/search/findByNameIgnoreCaseContaining?name=Clien"
    }
  }
}

For not making the entry longer, I will not explain how to update a record with HTTP requests type PUT, delete records with HTTP requests DELETE type, or update part of a record with PATCH because I think it is obvious, and I will leave it as an exercise for the reader.

What is shown in this article is just an outline of the power of Spring Data Rest.
These are some of the many other features that it implements:

  • It allows you to add events in such a way that when you insert, modify, delete, or even consult a record, that event is triggered, and you can execute the desired code, which can even modify or cancel the request made. Of course, it supports navigation.

  • Supports navigation between the records consulted

  • Validation of the inserted data

  • The links can be totally personalized

And that is all. I'm hoping that this article has been interesting.

As always, any feedback will be appreciated, and remember that this article can be read in Spanish at http://www.profesor-p.com/2019/03/25/accediendo-facilmente-a-los-datos-con-spring-rest-data/.

Topics:
rest api ,rest ,spring boot ,data access ,java ,jpa ,hibernate ,database ,tutorial

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}