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"))
Related
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
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
I am new to spring-data-jdbc and just trying to port a small project, which currently uses JPA, for evaluation purposes.
My existing entities use a database schema which can easily be defined by JPAs #Table annotation on the entity level.
I saw, that a #Table annotation exists for spring-data-jpa, but no schema can be specified.
The only approach I found so far is to override the naming Strategy in the JdbcConfiguration:
#Bean
fun namingStrategy(): NamingStrategy {
return object : NamingStrategy {
override fun getSchema(): String {
return "my_schema"
}
}
}
I would rather prefer an approach, where the schema is specified directly at the entity, to be able to use the same configuration for different schemas.
Are there any other ways to specify the database schema for each aggregate separately?
The answer to my own question is rather trivial:
By using the annotation #Table(value = "my_schema.some_table") on the entity level the proper schema is used.
I am using spring-data for DB interaction. I want to see the jpa sql execution plan for a query written in repository. How can i do it.
https://vladmihalcea.com/execution-plan-oracle-hibernate-query-hints/ tells about using GATHER_PLAN_STATISTICS and COMMENT query hints. I added COMMENT hint but don't know how to add other one.
public interface StudentRepository extends JpaRepository<Student, Long>{
#QueryHints({
#QueryHint(name=org.hibernate.annotation.queryHints.COMMENT,
value="SQL_PLAN_STUDENT")
})
List<Student>findByStudentIDIn(List<Long> ids);
}
The #QueryHints annotation accepts an array constructor like list of #QueryHint items.
So you can add multiple QueryHints by addings them to a comma separated list. For example:
#QueryHints({
#QueryHint(name=org.hibernate.annotations.QueryHints.COMMENT, value="SQL_PLAN_STUDENT"),
#QueryHint(name="GATHER_PLAN_STATISTICS" , value="GATHER_PLAN_STATISTICS")
})
Unfortunately I don't have access to a running instance of a oracle dbms, so I can't check the result of the given hint.
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();