Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Data Rest projection for Embedded Entities

Lets assume I have the following entities:

@Entity
public class Registration {

    @ManyToOne
    private Student student;
    //getters, setters
}

@Entity
public class Student {

    private String id;
    private String userName;
    private String name;
    private String surname;
    //getters, setters
}

@Projection(name="minimal", types = {Registration.class, Student.class})
public interface RegistrationProjection {

    String getUserName();
    Student getStudent();

}

I'm trying to create the following JSON representation, So when I use http://localhost:8080/api/registrations?projection=minimal I dont need all the user data to come along:

{
  "_links": {
    "self": {
      "href": "http://localhost:8080/api/registrations{?page,size,sort,projection}",
    }
  },
  "_embedded": {
    "registrations": [
      {
        "student": {
          "userName": "user1"
        }
      }
    ],
    "_links": {
      "self": {
        "href": "http://localhost:8080/api/registrations/1{?projection}",
      }
    }
  }
}

However the Projection I have created I get an exception(it works without the getUserName() statement. Obviously I have defined the interface in the wrong way...but which is the correct way to do something like this?

EDIT: Exception is the following

Invalid property 'userName' of bean class [com.test.Registration]: 
Could not find field for property during fallback access! (through reference chain: org.springframework.hateoas.PagedResources["_embedded"]
->java.util.UnmodifiableMap["registrations"]->java.util.ArrayList[0]->org.springframework.data.rest.webmvc.json.["content"]
-&gt;$Proxy119[&quot;userName&quot;])</div></body></html>
like image 436
ChrisGeo Avatar asked May 15 '15 13:05

ChrisGeo


People also ask

What do spring Data REST projections do?

Spring data REST Projection supports projecting only a selected fields from an entity representation. To do that, we can define those specific fields into our @Projection interface. Let's create a custom view of our Student entity with first name and last name fields.

What is difference between JpaRepository and CrudRepository?

CrudRepository provides CRUD functions. PagingAndSortingRepository provides methods to do pagination and sort records. JpaRepository provides JPA related methods such as flushing the persistence context and delete records in a batch.

Why is Spring Data REST not recommended in real world applications?

Real-world applications should avoid using Spring Data REST because the entities are exposed as RESTful Services. The two most critical considerations in designing a RESTful service are the domain model and the consumers.

What is a DTO projection?

A DTO projection is a Java Object that contains the column values that were fetched by a given SQL projection query. The DTO projection can be a POJO (Plain Old Java Object), a JPA Tuple , or a Java Record, and we can fetch all those DTO projection types using Spring Data JPA.


1 Answers

As exception said userName is not a member of Registration entity. That why it failed. But I think you already understand that.

First at all if you just want to expose its projection for Registration you should first change the following line:

@Projection(name="minimal", types = {Registration.class, Student.class})

By setting types = {Registration.class, Student.class} you asked Spring Data Rest to apply projection on Registration.class and Student.class. And that can cause some issue because depending of the type you should not have the same member/methods. In practice types must share a common ancestor.

Otherwise for the main problem you should try virtual projections the following thing (I didn't try on your sample but it should work, I hope):

@Projection(name="minimal", types = {Registration.class})
public interface RegistrationProjection {

    @Value("#{target.getStudent().getUserName()}")
    String getUserName();
}

I don't know if you are familiar with SPeL but the previous code just create a getUserName like this.getStudent().getUserName() because target is bound on the instance object (see documentation).

like image 76
Kakawait Avatar answered Nov 08 '22 16:11

Kakawait