Spring Data map results - spring-data

I use Spring Data and I can't find a way to map a #Query results into a DTO. E.g on the query
#Query("select f.a f.b from Foo f")
List<FooStripped> find();
where
public class FooStripped {
String a;
String b;
...
}
I want all the results to be mapped to a list of FooStripped an Object[] is returned.

You can see an example for this in Is there a way to transform objects that spring data repositories return?
But I would not do that to be honest. I would advice you do to this manually in the service/controller level. You can use for this the following framework http://modelmapper.org/ or any other object mapping framework.

Related

Spring JPA "PersistentEntity must not be null" with aggregate DTO

My summary DTO is throwing PersistentEntity must not be null! I'm using a summary DTO to return sum() and count() aggregates. I added custom queries to the source repository interface.
public interface SettlementHistoryRepository extends JpaRepository<SettlementHistory, String> {
#Query("SELECT new com.x.service.report.dto.CategoryAmountSummary(h.organizationName, count(h.id), sum(h.rebateAmount)) FROM SettlementHistory h WHERE h.fundDate BETWEEN :rangeStart AND :rangeEnd GROUP BY h.organizationName ORDER BY h.organizationName")
List<CategoryAmountSummary> summarizeRebateAmountByOrganizationWhereFundDateIsBetweenOrderByFundDate(
#Param("rangeStart") #DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm'Z'") Date rangeStart,
#Param("rangeEnd") #DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm'Z'") Date rangeEnd
);
The method results in this error:
ERROR --- [nio-8080-exec-7] o.s.d.r.w.RepositoryRestExceptionHandler : PersistentEntity must not be null!
I would return the entity class if not for my need to have a count for each category. How else can I go about this?
In the end, Spring Data Rest didn't like the repository returning a different object type. To get around the problem I added a Rest Controller that, in turn, called the aggregation method on the repository. It was able to return the summary DTO without problems.

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:
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to
I understand the answer and I am agree about the Object[] return type because I am working with specific set of fields.
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?
Is there a solution to work around with a specific set of fields or Object[] with the current API of Spring Data?
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);
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.

JPA Criteria construct with non-entity parameter

I want to create a list of complex DTO objects with data from several Entities and one non-Entity-parameter. Let's say my DTO class has constructor:
public MyDto(String entityField, String someString) {...}
and I would like to use the CriteriaBuilder.construct method to create my list by doing like this:
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<MyDto> query = builder.createQuery(MyDto.class);
Root<MyEntity> root = query.from(MyEntity.class);
builder.construct(MyDto.class, root.get("entityField"), someString);
...
but I am not allowed to do it, because the construct method wants from me only javax.persistence.criteria.Selection arguments.
The question: is there is a way to do it similar to this (at one blow) with Criteria API? Or I need to load MyEntity objects first and go through them and create a list of DTOs (not so pretty)?
I use this approach each time I've a projection that collect fields from different entities or for privacy reason i must not return some data (for example passwords)
query.select(
builder.construct(
MyDto.class,
root.get("myfield"), // for field
cb.literal(1), // for number
cb.literal("blah blah") // for string
));

JPA eclipselink global batch fetch?

when joining I get one select per row. Solution is batch fetch but I dont want that annotation everywhere...
http://eclipse.org/eclipselink/documentation/2.4/jpa/extensions/a_batchfetch.htm
Why do I even need this? One select per row is awful... How can I set this globally? Cheers
Maybe not the ideal solution, but you may try to use JPA hints along with Java generics:
public <T> TypedQuery<T>
createBatchQuery(String ql, Class<T> clazz, String type, String size, String relation) {
return em.createQuery(jpql, clazz)
.setHint(QueryHints.BATCH_TYPE, type)
.setHint(QueryHints.BATCH_SIZE, size)
.setHint(QueryHints.BATCH, relation);
}
The above query may then be used globally and extended with concrete query implementations according to you needs, i.e.
String jpql = "SELECT c FROM Country c WHERE c.name = :name"; // or #NamedQuery
TypedQuery<Country> q = createBatchQuery(jpql, Country.class, "JOIN", "64", "c.cities");
q.setParameter("name", "Australia");
Country c = q.getSingleResult();
Articles on this topic:
Batch fetching - optimizing object graph loading
EclipseLink/Examples/JPA/QueryOptimization

JPA EntityManager TypedQuery -- Return a Different Class (ViewModel/DTO) than entity model?

Coming from a Entity Framework Background I can cast my ORM results to a class that contains a subset of the full back-end model's data.
I have a JAX-RS REST Service where I am usually returning something like
MyEntity result = em.createQuery(select e from MyEntity e ... blah blah blah).
I know I can do this:
Object result = em.createQuery(select e.Title, e.Version, e.Date from MyEntity e... blah blah blah).
But can I either a:
Cast my result to a separate class or B name my fields in my createquery such that they are named when returning my JSON?
For example in .Net Land I could do something like....
(select new {Title = e.Title, Version = e.Version})
and then cast this to another type. I tried using a typedquery and casting but I get a "Type X is incompatible with return type Y" type error.
My goal is to return a specific subset (view model/ DTO)of my information for consumption in a specific scenario.
E.g My model is huge and I don't want to return a large amount of the data every time.
Yes, creating non-entity types is possible, using JPA constructor expressions i.e. the NEW keyword:
List<DTO> dtos = em.createQuery("SELECT NEW com.example.DTO( o.title, o.version) FROM Entity o").getResultList();
The DTO must have a constructor with all the relevant fields.
There is no need for casting.