2017-08-07 63 views
0

異常,如果我用一個返回Stream Spring的數據存儲庫的方法,我總是得到以下異常:春數據JPA流查詢方法引起有關事務

org.springframework.dao.InvalidDataAccessApiUsageException: You're trying to execute a streaming query method without a surrounding transaction that keeps the connection open so that the Stream can actually be consumed. Make sure the code consuming the stream uses @Transactional or any other way of declaring a (read-only) transaction. 
    org.springframework.data.jpa.repository.query.JpaQueryExecution$StreamExecution.doExecute(JpaQueryExecution.java:338) 
    org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:85) 
    org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:116) 
    org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:106) 
    org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:483) 
    org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:461) 
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) 
    org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61) 
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) 
    org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) 
    org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282) 
    org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) 
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) 
    org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136) 
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) 
    org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:133) 
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) 
    org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) 
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) 
    org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:57) 
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) 
    org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) 
    com.sun.proxy.$Proxy201.findByPodcast(Unknown Source) 
    <my controller class>$$Lambda$118/2013513791.apply(Unknown Source) 

有問題的代碼真的應該到然而,在交易中執行。我有:

  • 使用的OpenSessionManagerInViewFilter
  • 啓用聲明式事務管理(@EnableTransactionManagement在我的根上下文的配置),並註明控制器類@Transactional

請求方法我已經還嘗試將代碼包裝在TransactionTemplate中,並將結果收集到List中以避免事務超出範圍,但這仍然無效。控制方法:

@RequestMapping ("/pod/{id}") 
@Transactional 
public Stream<RSSPodcastItem> podItems (@PathVariable("id") UUID id) 
{ 
    return pods.get (id).map (items::findByPodcast).orElseThrow (() -> new RuntimeException ("failed")); 
} 
@RequestMapping ("/podlist/{id}") 
@Transactional 
public List<RSSPodcastItem> podItemsList (@PathVariable("id") UUID id) 
{ 
    return tt.execute (ts -> 
     pods.get (id).map (items::findByPodcast).orElseThrow (() -> new RuntimeException ("failed")) 
     .collect (Collectors.toList())); 
} 

上下文根配置類:

@Configuration 
@ComponentScan (... my package names ...) 
@EnableTransactionManagement 
@EnableJpaRepositories(... package with repositories ...) 
public class SharedConfig 
{ 
    @Bean 
    public DataSource dataSource() 
    { 
     // .... snipped 
    } 

    @Bean 
    EntityManagerFactory entityManagerFactory() 
    { 
     LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean(); 
     entityManagerFactoryBean.setDataSource (dataSource()); 
     entityManagerFactoryBean.setJpaVendorAdapter (new HibernateJpaVendorAdapter()); 
     entityManagerFactoryBean.setPackagesToScan (... package with entities ...); 
     entityManagerFactoryBean.setJpaPropertyMap (hibernateProperties()); 
     entityManagerFactoryBean.afterPropertiesSet(); 
     return entityManagerFactoryBean.getObject(); 
    } 

    @Bean 
    JpaTransactionManager transactionManager() 
    { 
     JpaTransactionManager transactionManager = new JpaTransactionManager(); 
     transactionManager.setEntityManagerFactory (entityManagerFactory()); 

     return transactionManager; 
    } 

    @Bean 
    TransactionTemplate transactionTemplate (JpaTransactionManager tm) 
    { 
     return new TransactionTemplate (tm); 
    } 

    @Bean 
    Map<String, ?> hibernateProperties() 
    { 
     Map<String, Object> m = new HashMap<>(); 
     m.put ("hibernate.dialect", MySQL5Dialect.class); 
     m.put ("hibernate.dialect.storage_engine", "innodb"); 
     boolean devSystem = isDevSystem(); 
     m.put ("hibernate.hbm2ddl.auto", devSystem ? "update" : "create-only"); // will need to handle updates by hand on live system, but creation is OK. 
     m.put ("hibernate.show_sql", "" + devSystem); 
     m.put ("hibernate.cache.use_second_level_cache", "" + !devSystem); 
     return m; 
    } 

任何建議是怎麼回事錯在這裏?

+0

我的回答對你有幫助嗎?.. – Cepr0

回答

0

按照this post

庫客戶端可以使用......在try-與資源塊的方法調用的結果。

和Spring數據reference

甲流潛在地包數據存儲特定資源底層和使用後必須因此被關閉。您可以使用close()方法手動關閉流,也可以使用Java 7 try-with-resources塊。

所以我認爲你應該換你流去嘗試,與資源塊,作爲例外建議,設置只讀交易,這樣的事情:

@RequestMapping("/pod/{id}") 
@Transactional(readOnly = true) 
public Stream<RSSPodcastItem> podItems (@PathVariable("id") UUID id) { 
    try (Stream<RSSPodcastItem> items = repository.findByPodcast(...)) { 
     return items...; 
    } 
} 

附加信息:Spring Data - Java 8 examples

+0

如果MVC在返回之前關閉,它應該如何使用流? – Jules

+0

@Jules我認爲它在Spring 5中假設它 - https://stackoverflow.com/a/44509954(( – Cepr0