spring-projects / spring-data-jpa

Simplifies the development of creating a JPA-based data access layer.
https://spring.io/projects/spring-data-jpa/
Apache License 2.0
2.97k stars 1.41k forks source link

Support for @QueryHints on CRUD methods [DATAJPA-173] #590

Closed spring-projects-issues closed 10 years ago

spring-projects-issues commented 12 years ago

Pascal Heus opened DATAJPA-173 and commented

Add support for @QueryHints annotations for CRUD methods such as findAll(…), findOne(…), etc. For complex hierarchical structure, having the option to fine tune joins would be highly beneficial in terms of performance


Reference URL: http://forum.springsource.org/showthread.php?113519-Query-Caching

Issue Links:

Referenced from: commits https://github.com/spring-projects/spring-data-jpa/commit/b71cd3e2f999d200ea02c17acfd24b1cded78ba2, https://github.com/spring-projects/spring-data-jpa/commit/5438c44c8f922cfb07f30ff7f5902631e1266224

9 votes, 10 watchers

spring-projects-issues commented 10 years ago

pascal gehl commented

Adding query hints for caching on CRUD methods (findAll, findOne...) seems to be a minimum feature, specially with ORM's second level caches. Do you have any timeline for this feature ?

spring-projects-issues commented 10 years ago

Dmitry Angelov commented

IMHO at least its worth to move its priority one level up

spring-projects-issues commented 10 years ago

Jan Goyvaerts commented

Is there a straightforward way to work around this issue ? I have massive amounts of find-by-id queries only producing cache misses. The second level cache is filled but is never used.

Because findOne is not cached ?

spring-projects-issues commented 10 years ago

Martin Maier-Moessner commented

You could write your own base repository class and override findOne() or create a new method findOneCached(). In your custom implementation you then have direct access to the javax.persistence.Query instance, which has the setHint()-method you want to use.

For details see http://docs.spring.io/spring-data/jpa/docs/1.5.0.RELEASE/reference/htmlsingle/#repositories.custom-behaviour-for-all-repositories

spring-projects-issues commented 10 years ago

Jan Goyvaerts commented

I've added:

@Transactional(readOnly = true, propagation = Propagation.MANDATORY, rollbackFor = Throwable.class)
public T findById(@NotNull final Long id) {
  return em.find(clazz, id, QUERY_HINTS);
}

Where clazz is the domain class of the constructor and QUERY_HINTS a static immutable map containing two Hibernate hints.

  private static final Map<String, Object> QUERY_HINTS;

  static {
    final Map<String, Object> map = new HashMap<String, Object>();
// The official JPA2 hints don't seem to work for Hibernate 4.2.8.Final
//  map.put("javax.persistence.cache.retrieveMode", CacheRetrieveMode.USE);
//  map.put("javax.persistence.cache.storeMode", CacheStoreMode.REFRESH);
    map.put("org.hibernate.cacheable", true);
    map.put("org.hibernate.cacheMode", CacheMode.NORMAL);
    QUERY_HINTS = Collections.unmodifiableMap(map);
  }

Making a query each each call is way too slow for me. Hence the usage of EntityManager.find()

spring-projects-issues commented 10 years ago

Oliver Drotbohm commented

That's in place for the 1.6.0.BUILD-SNAPSHOTS. Feel free to give this a spin. I extended the previously existing infrastructure to detect @Lock annotations to detect @QueryHints as well.

I also added some caching of the metadata to make sure we don't waste too much time for reflection based annotation lookup for every method invocation.

We're shooting for an M1 by some time next week, so it'd be incredibly helpful to get some feedback about this

spring-projects-issues commented 10 years ago

Jan Goyvaerts commented

I'll have a go today - I have other cats to skin, but I will be the last to discourage such speedy support ! :-)

spring-projects-issues commented 10 years ago

Jan Goyvaerts commented

A bit of a hassle to upgrade from 1.4

=> new methods => relocated classes => wrong version of Data Common => errors against the usage of JsonProperty of Jackson.

But here we are !

I'm getting NPE at SimpleJpaRepository.applyRepositoryMethodMetadata(), line 528. Variable "crudMethodMetadata" is null.

Anything I can try ?

====================================================

java.lang.NullPointerException at org.springframework.data.jpa.repository.support.SimpleJpaRepository.applyRepositoryMethodMetadata(SimpleJpaRepository.java:528) at org.springframework.data.jpa.repository.support.SimpleJpaRepository.getQuery(SimpleJpaRepository.java:475) at org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAll(SimpleJpaRepository.java:268) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:358) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:343) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:96) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:260) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodIntercceptor.invoke(CrudMethodMetadataPostProcessor.java:111) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:91) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204) at com.sun.proxy.$Proxy126.findAll(Unknown Source) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:198) at com.sun.proxy.$Proxy127.findAll(Unknown Source) at com.noesis.optimus.services.ProjectServiceImpl.findAll(ProjectServiceImpl.java:65) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) etc...

spring-projects-issues commented 10 years ago

Oliver Drotbohm commented

Which versions are you using exactly?

spring-projects-issues commented 10 years ago

Jan Goyvaerts commented

spring-data-commons-1.8.0.BUILD-20140311.113611-24.jar

and

spring-data-jpa-1.6.0.BUILD-20140312.143908-17.jar

Btw, there is no source jar for the Data Commons artifact

spring-projects-issues commented 10 years ago

Oliver Drotbohm commented

Hm, there clearly is… see the repo.

How do you instantiate the repository? If you manually create them via JpaRepositoryFactory you need to call setRepositoryMethodMetadata(…) manually. Is it a CDI context maybe?

spring-projects-issues commented 10 years ago

Jan Goyvaerts commented

Because I have specialized repositories I had to make a repository factory bean - as described in the documentation. I didn't know about the method meta data.

Do you have documentation about what kind of data it expects ?