Manuel Jordan
Manuel Jordan

Reputation: 16271

Spring Data JPA: Work with Pageable but with a specific set of fields of the entity

I am working with Spring Data 2.0.6.RELEASE.

I am working about pagination for performance and presentation purposes. Here about performance I am talking about that if we have a lot of records is better show them through pages

I have the following and works fine:

interface PersonaDataJpaCrudRepository extends PagingAndSortingRepository<Persona, String> {
}

The @Controller works fine with:

@GetMapping(produces=MediaType.TEXT_HTML_VALUE)
public String findAll(Pageable pageable, Model model){

Through Thymeleaf I am able to apply pagination. Therefore until here the goal has been accomplished.

Note: The Persona class is annotated with JPA (@Entity, Id, etc)

Now I am concerned about the following: even when pagination works in Spring Data about the amount the records, what about of the content of each record?.

I mean: let's assume that Persona class contains 20 fields (consider any entity you want for your app), thus for a view based in html where a report only uses 4 fields (id, firstname, lastname, date), thus we have 16 unnecessary fields for each entity in memory

I have tried the following:

interface PersonaDataJpaCrudRepository extends PagingAndSortingRepository<Persona, String> {

    @Query("SELECT p.id, id.nombre, id.apellido, id.fecha FROM Persona p")
    @Override
    Page<Persona> findAll(Pageable pageable);

}

If I do a simple print in the @Controller it fails about the following:

java.lang.ClassCastException: 
[Ljava.lang.Object; cannot be cast to com.manuel.jordan.domain.Persona

If I avoid that the view fails with:

Caused by: 
org.springframework.expression.spel.SpelEvaluationException: 
EL1008E: 
Property or field 'id' cannot be found on object of type 
'java.lang.Object[]' - maybe not public or not valid?

I have read many posts in SO such as:

I understand the answer and I am agree about the Object[] return type because I am working with specific set of fields.

  1. Is mandatory work with the complete set of fields for each entity? Should I simply accept the cost of memory about the 16 fields in this case that never are used? It for each record retrieved?
  2. Is there a solution to work around with a specific set of fields or Object[] with the current API of Spring Data?

Upvotes: 0

Views: 3558

Answers (2)

Josh Sullivan
Josh Sullivan

Reputation: 209

Have a look at Spring data Projections. For example, interface-based projections may be used to expose certain attributes through specific getter methods.

Interface:

interface PersonaSubset {
    long getId();
    String getNombre();
    String getApellido();
    String getFecha();
}

Repository method:

Page<PersonaSubset> findAll(Pageable pageable);

Upvotes: 5

Piotr Podraza
Piotr Podraza

Reputation: 2029

If you only want to read a specific set of columns you don't need to fetch the whole entity. Create a class containing requested columns - for example:

public class PersonBasicData {
    private String firstName;
    private String lastName;

    public PersonBasicData(String firstName, String lastName) {
        this.firstName = fistName;
        this.lastName = lastName;
    }

    // getters and setters if needed
}

Then you can specify query using @Query annotation on repository method using constructor expression like this:

@Query("SELECT NEW some.package.PersonBasicData(p.firstName, p.lastName) FROM Person AS p")

You could also use Criteria API to get it done programatically:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<PersonBasicData> query = cb.createQuery(PersonBasicData.class);
Root<Person> person = query.from(Person.class);

query.multiselect(person.get("firstName"), person.get("lastName"));

List<PersonBasicData> results = entityManager.createQuery(query).getResultList();

Be aware that instance of PersonBasicData being created just for read purposes - you won't be able to make changes to it and persist those back in your database as the class is not marked as entity and thus your JPA provider will not work with it.

Upvotes: 3

Related Questions