Open
Description
Сергей Цыпанов opened DATAJPA-1370 and commented
Having dozens of interfaces extending JpaRepository and several hundreds of methods annotated with @Query
it takes application significant type to start up.
I think there could be an option to disable validation e.g. for tests.
Also SimpleJpaQuery::validateQuery can be improved: e.g. we should compute error message only if necessary
2 votes, 4 watchers
Activity
spring-projects-issues commentedon Jul 4, 2018
Oliver Drotbohm commented
Care to add some numbers to back "significantly slows down"? We have quite some extensive benchmarks in Spring Boot that indicate that Spring Data JPA only adds negligible bootstrap overhead compared e.g. to the native Hibernate bootstrap time.
I'll use this ticket to make sure we avoid the
String.format(…)
although I'd also doubt it has significant effect until I see some numbers 🙃spring-projects-issues commentedon Jul 6, 2018
Сергей Цыпанов commented
Sure, currently my metrics are approximate, right now I try to create sime reliable benchmark.
We have a project with Spring Data JPA under Spring Boot. Once someone add a dependency spring-boot-starter-data-ldap into a list of dependencies and we immediately spotted an increase in startup time. Debugging demonstrated that when Spring Boot initialized its context all repositories extending JpaRepository we treated as both JPA and LDAP repositories and query validation occurred. I've excluded spring-boot-starter-data-ldap from dependencies and turned off ldap repositories by adding spring.ldap.repositories.enabled: false into application.yml. Startup time reduction was about 15 seconds, I think this is the time wasted onto queries validation for ldap repos. Validation for JPA repos should cost us the same.
I understand this metrics is quite naive, but currently it's all I have
spring-projects-issues commentedon Jul 6, 2018
Сергей Цыпанов commented
P.S. On your project we have 93 repository interfaces extending JpaRepository and 118
@Query
methodsspring-projects-issues commentedon Jul 6, 2018
Oliver Drotbohm commented
Thanks, Сергей Цыпанов. That's very helpful information. What puzzles me a bit is that having two Spring Data modules on the classpath actually triggers a strict repository scanning mode which assigns a repository to a store by inspecting it closer (the repository base interface, annotations on the entity class managed etc.). I.e. it's kind of impossible that an interface is "assigned" to both stores, mostly because both store implementations would end up using the same bean name for the bean definitions registered and thus only one end up surviving as one would override the other.
I just checked Spring Data LDAP and it seems to have all things in place to take part in this more strict mode (ultimately it's these two methods in
LdapConfigurationExtension
). If you set the log level for Spring Data to debug you should actually see which store module a repository was assigned to. Also, the store implementations shouldn't even find queries to validate as the@Query
annotation is store specific.We have an example for using both MongoDB and JPA in our examples repository. You should be able to produce similar output in you case. Does that help diagnosing the issue?
spring-projects-issues commentedon Jul 7, 2018
Сергей Цыпанов commented
Oliver Drotbohm
I finally created a benchmark to measure how much does it cost to validate one single query. To my surprise it only takes 2 us to validate a simple HQL query. However, there are two circumstances making me think this issue requires deeper investigation:
Getting back to you last comment:
Those messages count is the same as number of interfaces inheriting JpaRepository, but unlike the multi-store example you provided log messages are coming only for LDAP, not for both LDAP and JPA, so the behaviour is slightly different.
I cannot attach sources of the project, but I'll try to build one for demonstration having exactly the same dependencies and configuration.
spring-projects-issues commentedon Jul 7, 2018
Сергей Цыпанов commented
Not sure, but it seems I've got high-level understanding of what happened.
In multi-store example both TreasureRepository and PersonRepository extend CrudRepository i.e. top-level interface in inheritance hierarchy. This is why both spring-boot-starter-data-mongodb and spring-boot-starter-data-jpa pick them up.
But in my case all our repositories extend JpaRepository and should be picket only by spring-boot-starter-data-jpa, not spring-boot-starter-data-ldap. What happens instead is that spring-boot-starter-data-ldap finds interfaces extending JpaRepository and tries to process them. As for me this is wrong and must be changed, as a child of JpaRepository by its nature is bound to JPA, not LDAP.
P.S. Demonstration example fails to start when TreasureRepository's parent is changed from CrudRepository to JpaRepository:
spring-projects-issues commentedon Aug 14, 2020
andrew-landsverk-win commented
I am also seeing weirdness with both data-cassandra and data-jpa on the classpath. I am strictly using JpaRepository and CassandraRepository as my base classes, yet spring-data-cassandra is insisting on checking the JpaRepository repositories and asking me to use cassandra specific interfaces even though these repositories are for Spring Data JPA. The application starts and loads fine, but these info messages are incorrect and misleading in my opinion, but maybe it's a setup issue on my end?
spring-projects-issues commentedon Aug 17, 2020
Oliver Drotbohm commented
The repository base interface is a quite unfortunate differentiator for the stores as we effectively cannot teach LDAP about all potential other Spring Data stores to ignore. To it, the repository is extending a Spring Data interface (as
JpaRepository
extendsCrudRepository
) and thus is just a potential candidate interface. Which means that each store will at least have to have an initial look at the interface and then decide what to do with it.In single store mode it will just use it straight away. In strict mode, i.e. if multiple stores are on the classpath, the repository should only be processed completely if it either implements the store specific repository or the domain type managed by the repository is annotated with a store specific annotation,
@Entry
in the case of LDAP. This is implemented inRepositoryConfigurationExtensionSupport.isStrictRepositoryCandidate(…)
which is called fromRCES.getRepositoryConfigurations(…)
before the configuration for the repository is added for instantiation.I guess it makes sense to add that if you see the message that a repository has been dropped by a particular store, no inspection of the repository or queries defined in it has been performed yet. That message is the result of a pure assignability / annotation presence check (again see
RCES.isStrictRepositoryCandidate(…)
). If the repository is making it into the query validation stage, it cannot be dropped anymore and every failure, invalid query or the like would result in an exception and a failure for the application to start.Сергей Цыпанов – I cannot follow your statements. The example linked to does not contain the mentioned repositories.
This is exactly what's expected if all your repositories are JPA repositories. LDAP would have to have a look at the JPA ones and signal that it's not gonna process them. JPA doesn't have to issue these logs as it's not dropping the repositories but actually taking them forward to eventually become Spring Beans.
If anyone of you can provide a minimal, reproducing project, that'd be helpful. Or modify one of the Spring Data examples projects to show the problem
amitlpande commentedon Dec 2, 2024
Is there any update on this work?
We too have a project with large number of JPA repository beans and the query validation is slowing down the application startup.
Is there any way to provide a custom SimpleJpaQuery (by extending this class) for testing purposes and see the difference?
https://github.com/spring-projects/spring-data-jpa/blob/main/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java
Thanks,
Amit