Spring data jpa avoid in memory pagination and n+1 using specification - spring

I am trying to avoid in-memory pagination and N+1 while using Spring Data JPA Specification.
To be specific, I'm using the below method provided by the framework.
Page<T> findAll(#Nullable Specification<T> spec, Pageable pageable);
I tried to avoid N+1 by using #EntityGraph on the method (don't know if it's good or not) and after some research, I still don't know how to work around the in-memory pagination.
The database I'm using is Postgres if it matters
Are there any solutions to this problem?

The problem is that as soon as you fetch some kind of *-to-many association, Hibernate will do in-memory pagination, so #EntityGraph won't help. What you need is a special query that does pagination on the main/root entity and fetches associations in a second query.
I think this is a perfect use case for Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(User.class)
public interface UserDto {
#IdMapping
Long getId();
String getName();
Set<RoleDto> getRoles();
#EntityView(Role.class)
interface RoleDto {
#IdMapping
Long getId();
String getName();
}
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
UserDto a = entityViewManager.find(entityManager, UserDto.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
Page<UserDto> findAll(Specification spec, Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!
But you can even use it with plain entities if you like in which case this will also run more efficient queries as you can read about in the documentation: https://persistence.blazebit.com/documentation/core/manual/en_US/index.html#pagination

Related

Exclude byte field of an object

I'm trying to exclude byte field from my object query since there are several hundreds or thousand of reports and it takes a long time to query it from the database.
public class Reports
{
private int id;
private String reportName;
#Lob
#Basic(fetch= FetchType.LAZY)
private byte[] file;
private Date createdDate;
}
I tried setting up the hibernate byte enhancement for this How to setup Hibernate Gradle plugin for bytecode enhancement? but I'm still getting the file when I query all the reports. Did I missed something here?
In JPA, you can annotate a field with #Transient to indicate that it is not persistent.
Bytecode enhancement should help, but maybe you didn't configure it correctly or the Hibernate version you are using has a bug. I'd need to know details or see a reproducing test case to help you with that.
You could try to use java.sql.Blob instead which is guaranteed to be lazy and doesn't require byte code enhancement.
Apart from that, I would recommend you use DTO projections for actually fetching just the data that you need. I think this is a perfect use case for Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(Reports.class)
public interface ReportsDto {
#IdMapping
int getId();
String getReportName();
Date getCreatedDate();
Set<ReportRowDto> getRows();
#EntityView(ReportRows.class)
interface ReportRowDto {
#IdMapping
Long getId();
String getName();
}
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
ReportsDto a = entityViewManager.find(entityManager, ReportsDto.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
The DTO projection is validated against the entity model and it will only fetch what is necessary. Spring Data Projections falls back to "just" wrapping entities when the projection is too complex, whereas Blaze-Persistence Entity-Views will alter the query as if you had written it by hand :)

Spring Data JPA + Bytecode Enhancement

Is it possible to load #*ToOne attributes eagerly using JPA interface(Entity Graphs) which are set lazy using #LazyToOne , #LazyGroup in the parent entity class and enabled bytecode enhancement ? I am trying to load such attributes eagerly using entity graph but it is firing another query for such #*ToOne attributes when an parent entity is queried.
Trying to have another way to override static fetch type in entity classes including #LazyToOne which was added with bytecode enhancement.
Using Spring 5.1.3 , Spring JPA 2.2 , Hibernate 5.4.19
Update : Data JPA is working as expected and i could see joins for the attributes which i am trying to fetch eagerly but those lazy attributes are not being initialised with the join query response and hibernate causing each query on referencing attributes which were annotated with #LazyToOneOption.NO_PROXY and was already fetched eagerly using entity graph in my repository.
How can i avoid this second select which is not even required since i got the that data eagerly from entity graph in JPA respository ??
Any help would be highly appreciated.
Entity Graphs just like Hibernate fetch profiles apply regardless of what annotations you have on the association. If it does not, maybe there is a bug in Spring Data or maybe even Hibernate. It's probably best if you create a new JIRA issue with a test case reproducing the problem.
Having said that, I think this is the perfect use case for Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
An example DTO model could look like the following with Blaze-Persistence Entity-Views:
#EntityView(User.class)
public interface UserDto {
#IdMapping
Long getId();
String getName();
Set<RoleDto> getRoles();
#EntityView(Role.class)
interface RoleDto {
#IdMapping
Long getId();
String getName();
}
// Other mappings
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
UserDto a = entityViewManager.find(entityManager, UserDto.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

Can we make Spring JPA Specifications work with EntityGraph?

I am implementing a dynamic query logic using JPA specifications on an entity. The entity is having multiple relations with other entities. I am running into the issue of generating too many queries while executing JPA specifications.
Is there a way to combine JPA specifications findAll(specification, page) with EntityGraph so that we can one query generated while executing the same?
Yes it is absolutely possible to pass EntityGraph using EntityGraphJpaSpecificationExecutor
#Repository
public interface UserRepository
extends JpaRepository<EntityClassName, DatatypeOfPrimaryKey>, EntityGraphJpaSpecificationExecutor<EntityClassName> {
}
userRepository.findAll(specification, pageable, new NamedEntityGraph(EntityGraphType.FETCH, "graphName"))

Is there a way to use UPDATE query with dynamic attributes in Spring Framework?

I'm developing a REST server application using Spring Boot.
Just got a question while constructing an UPDATE query.
Currently my UPDATE query in UserRepository is like this;
#Modifying
#Transactional
#Query(value ="update User u set u.user_dob=:userDOB, u.user_lastname=:userLastName, u.user_firstname=:userFirstname, u.user_streetaddress=:userStreetAddress where d.driver_id=:driverId", nativeQuery = true)
void updateUser(#Param("userDOB") String userDOB, #Param("userLastName") String userLastName, #Param("userFirstName") String userFirstName, #Param("userStreetAddress") String userStreetAddress);
However, I don't like to list all the attributes of User in one UPDATE query.
Is there anyway to construct UPDATE query dynamically?
For example;
Update with
set u.user_dob=:userDOB, u.user_lastname=:userLastName, u.user_firstname=:userFirstname, u.user_streetaddress=:userStreetAddress
or
u.user_lastname=:userLastName, u.user_firstname=:userFirstname
using one update method.
If you are using Spring Data JPA (seems you do), your repository interface is probably extending JpaRepository interface.
In this case, you could simply use save method.
Here are some good examples:
http://www.springbyexample.org/examples/spring-data-jpa-repository.html
http://www.springbyexample.org/examples/spring-data-jpa-code-example.html

Spring Hibernate : Multiple resultset mapping

I want to understand the limitations of Spring's Data repository.
While querying the database, it seems that Spring repository can only return entities, or a collection of same type, like string/int etc. It makes sense because the Spring Repository is a function and a function can only return one result.
So what if I need to execute a complexe sql by using #Query annotation, and expect more than one result? like a collection of entityies and a number.
I don't think it is possible with Spring Repository, so if i'm wrong, please correct me.
And more importantly, how could I do that by using spring?
No, it's not possible that I know of for the Repository to work with queries, but the Repository is used by a Spring ServiceImpl anyway and you can Inject an EntityManager into the serviceImpl and use that. For example see Getting started with Spring Data JPA:
#PersistenceContext
private EntityManager em;
TypedQuery query = em.createQuery("select a from Account a where a.customer = ?1", Account.class);
query.setParameter(1, customer);
return query.getResultList();

Resources