Project_Data

Introduction

In recent years, Spring has become much more than just a dependancy injection container and an MVC web application framework. Nowadays, it’s the go-to for building enterprise solutions due to the fact it has a fantastic community built up around it, and it has a multitude of projects that makes every developer’s life that little bit easier! In this blog post, I’m going to briefly introduce Spring Data REST, and how we used it and an unknown feature called ‘projectionson a recent project. Note: This blog post is intended as a tutorial for how to use Spring Data REST. You can find the official reference documentation here, which are a great source of information. However, you won’t find any information about projections there, and hence why I wanted share our experience of using them with you all 🙂

What’s it all about?

According to the home page of the website, the primary goal of Spring Data REST is:

“[…]to provide a solid foundation on which to expose CRUD operations to your JPA Repository-managed entities using plain HTTP REST semantics.”

Ultimately, that means eliminating DTOs and DAOs, replaced instead by RESTful endpoints, which are automatically generated by the framework. This in turn leads to greatly decreased boilerplate code in applications that are not massive big enterprise monstrosities! For example, I will use our good old friend the Employee entity 🙂

@Entity
@Table(name = "Employee" )
public class Employee implements Serializable {
 @Id
 @GenericGenerator(name= "employeegen" , strategy="increment" )
 @GeneratedValue(generator= "employeegen")
 @Column(name= "EmployeeId")
 private Long id;

 @NotNull
 @Pattern(regexp = "^[A-Za-z\\s-]+$", message = "First name must contain letters, spaces or dashes")
 private String firstName;

 @Pattern(regexp = "^[A-Za-z\\s-]*$", message = "Surname must contain letters, spaces or dashes")
 private String lastName;

 @ManyToMany(fetch=FetchType.EAGER)
 @JoinTable(name = "EmployeesProjects",
 joinColumns = @JoinColumn(name = "EmployeeId" , referencedColumnName = "EmployeeId"),
 inverseJoinColumns = @JoinColumn(name = "ProjectId" , referencedColumnName = "ProjectId")
 )
 private Set<Project> projects;

 /* Getters-Setters are going here */
}

As we can clearly see from this snippet, the founders of Spring Data REST are obviously big advocates of the KISS principle! To expose the entity as a RESTful service, all we need to do is add the following annotation to the Spring repository:

@RepositoryRestResource (itemResourceRel="employee", collectionResourceRel = "employee", path = "employee")
public interface EmployeeRepository extends CrudRepository<Employee, Long>, JpaSpecificationExecutor {
}

Spring Data REST wires up all the repositories marked with the annotation @RepositoryRestResource, and creates a set of CRUD services for us. Under the hood, it’s using the HATEOAS representation schema. Thus, we can easily discover our beautifully auto-generated RESTful resources, like so:

>curl -v -H "Accept: application/x-spring-data-compact+json" http://localhost:8080/api/employee
...
{
  "links" : [
    {
      "rel" : "employee",
      "href" : "http://localhost:8080/api/employee/1"
    }, {
      "rel" : "employee",
      "href" : "http://localhost:8080/api/employee/2"
    }, {
      "rel" : "employee",
      "href" : "http://localhost:8080/api/employee/3"
    }, {
      "rel" : "employee",
      "href" : "http://localhost:8080/api/employee/4"
    } 
  ],
  "content" : [ ]
}
> curl -v -H "Accept: application/x-spring-data-compact+json" http://localhost:8080/api/employee/1
{
  "firstName" : "Bob",
  "lastName" : "Well",
  "links" : [ ],
  "content" : [ ],
  "links" : [
    {
      "rel" : "self",
      "href" : "http://localhost:8080/api/employee/1"
    }, 
    {
      "rel" : "projects",
      "href" : "http://localhost:8080/api/employee/1/projects"
    }
  ]
}

As you can clearly see, HATEOAS represents inner entities as href links. This is good because all linked objects are lazily loaded, and thus network overhead is reduced because we are not downloading unnecessary data. However, there is a problem with this – how do we customise this representation?

Projections

stock-footage-old-projector-showing-film That’s a simple use case when you need to expose an object using simple CRUD operations. But you also need different fields for different views. Using our Employee entity again as an example, the edit form might look something like this: Edit employee form Using the standard HATEOAS representation is not exactly convenient because we need to send additional network requests/queries to retrieve an employee’s project(s). This is where ‘projections’ comes in very handy 🙂 A projection is a special type of contract that you declare to describe what fields you want to include in a response object. In terms of Java, a projection is simply an interface that declares what fields should be sent to the client. When Spring Data REST is serializing an object it looks for the projection that was requested and uses its interface to determine what fields should be included. To add a projection is straight forward – all we need to do is add the annotation @Projection to our new interface, and give it a parameter name (e.g. “edit”) and specify the type (e.g. “Employee.class”) like so:

@Projection(name = "edit" , types = Employee.class)
public interface EditEmployeeProjection {
    String getFirstName();
    String getLastName();
    Set<Project> getProjects();
}

Let’s not forget to add a projection for our Project interface:

@Projection(name = "edit" , types = Project.class)
public interface EditProjectProjection {
    Long getId();
    String getProjectName();
}

Spring Data REST will automatically (as usual!) recognise all our projections, and generate an additional parameter for our RESTful services:

>curl -v -H "Accept: application/x-spring-data-compact+json" http://localhost:8080/api/employee
...
{
  "links" : [
  {
    "rel" : "employee",
    "href" : "http://localhost:8080/api/employee/1{?projection}"
  },
  ... 
  ],
  "content" : [ ]
}

Finally, after adding the name of the projection to the request:

> curl -v -H "Accept: application/json" http://localhost:8080/api/employee/1?projection=edit
{
  "projects" : [ 
  {
    "id" : 2,
    "projectName : "Christmas Turkey"
  },
  {
    "id" : 5,
    "projectName : "Chinese New Year Duck"
  } 
  ],
  "firstName" : "Bob",
  "lastName" : "Well",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/api/employee/1{?projection}",
      "templated" : true
    }
  }
}

And that’s it! Interestingly enough, projections are not mentioned in any official reference documentation. In saying that however, there is an existing ticket about the lack of official documentation in JIRA.

Limitations

tumblr_mboug8ln841qaobbko1_500 There are some points which you probably need to consider before you decide to use Spring Data REST in your project.

  • It’s not a replacement for business layer services. It may be difficult to integrate custom business logic with Spring Data REST. The framework provides listeners, but they are only useful for CRUD operations. You must separate your RESTful and business services layers. However, if you have a lot of business logic then you need to stop and ask yourself “should I really be using Spring Data REST?“.
  • You can’t update individual parts of an object. If you provide a RESTful interface to your object model, another limitation is that you can only update the complete object, and not individual fields. For example, in our Employee class:
    public class Employee implements Serializable {
        private Long id;
        private String firstName;
        private String lastName;
        private Set<Project> projects;
    }
    

    In this case, anyone who can update user can only update first name, last name and projects. When an user can update his names (for example, as part of a profile) then this model is not the best choice. So, be more careful with your data model when designing your application with REST.

That’s a Wrap!

In my opinion, Spring Data REST is really good choice for small to medium size applications where you don’t have a lot of custom business logic, and it could be driven by your data. Although, it has limitations like those that I’ve listed above, it really impressed us with its ease of use and simplicity.

11 comments

    1. Hi Dominik,

      You are absolutely right, it’s working good with controllers, but I meant the case when you don’t want to use controllers at all. Spring-Rest-MVC can’t offer much to insert your business logic.

      You can update individual fields, but you can’t prevent to not update some fields if you have security constraints. I think it’s moving us back to controllers where you can pass only fields that user can update.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s