2014-12-03 29 views
2

我在Tomcat 7中運行的Web應用程序中使用Spring 3.2與JPA和Hibernate 4.應用程序分爲控制器和服務DAO類。服務類在類和方法級別具有註釋的事務配置。 DAO是通過@PersistenceContext註釋注入實體管理器的普通JPA。通過多種方法進行Spring JPA事務

@Service("galleryService") 
@Transactional(propagation=Propagation.SUPPORTS, readOnly=true) 
public class GalleryServiceImpl implements GalleryService { 

    @Override 
    public Picture getPicture(Long pictureId) { 
    return pictureDao.find(pictureId); 
    } 

    @Override 
    public List<PictureComment> getComments(Picture picture) { 
    List<PictureComment> comments = commentDao.findVisibleByPicture(picture); 
    Collections.sort(comments, new Comment.ByCreatedOnComparator(Comment.ByCreatedOnComparator.SORT_DESCENDING)); 
    return comments; 
    } 
    ... 
} 

@Controller 
@RequestMapping("/gallery/displayPicture.html") 
public class DisplayPictureController extends AbstractGalleryController { 

    @RequestMapping(method = RequestMethod.GET) 
    public String doGet(ModelMap model, @RequestParam(REQUEST_PARAM_PICTURE_ID) Long pictureId) { 
    Picture picture = galleryService.getPicture(pictureId); 
    if (picture != null) { 
     model.addAttribute("picture", picture); 
     // Add comments 
     model.addAttribute("comments", galleryService.getComments(picture)); 
    } else { 
     LOGGER.warn(MessageFormat.format("Picture {0} not found.", pictureId)); 
     return ViewConstants.CONTENT_NOT_FOUND; 
    } 
    return ViewConstants.GALLERY_DISPLAY_PICTURE; 
    } 
    ... 
} 

我接通調試日誌記錄org.springframework.transaction,並注意到,該「創建新的交易」,「開闢了新的EntityManager」,「獲得......」,「關閉...」和「犯下事務「是爲我的服務類中每個方法的調用完成的。即使這些方法在我的控制器類中被一個單一方法調用。這是我的日誌輸出的一個例子:

2014-12-03 10:53:00,448 org.springframework.transaction.support.AbstractPlatformTransactionManager getTransaction 
DEBUG: Creating new transaction with name [de.domain.webapp.gallery.service.GalleryServiceImpl.getPicture]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly 
2014-12-03 10:53:00,448 org.springframework.orm.jpa.JpaTransactionManager doBegin 
DEBUG: Opened new EntityManager [[email protected]] for JPA transaction 
2014-12-03 10:53:00,468 org.springframework.orm.jpa.JpaTransactionManager doBegin 
DEBUG: Exposing JPA transaction as JDBC transaction [org.springframewo[email protected]5182c1b7] 
2014-12-03 10:53:00,468 org.springframework.transaction.interceptor.TransactionAspectSupport prepareTransactionInfo 
TRACE: Getting transaction for [de.domain.webapp.gallery.service.GalleryServiceImpl.getPicture] 
2014-12-03 10:53:00,489 org.springframework.transaction.interceptor.TransactionAspectSupport commitTransactionAfterReturning 
TRACE: Completing transaction for [de.domain.webapp.gallery.service.GalleryServiceImpl.getPicture] 
2014-12-03 10:53:00,489 org.springframework.transaction.support.AbstractPlatformTransactionManager processCommit 
DEBUG: Initiating transaction commit 
2014-12-03 10:53:00,489 org.springframework.orm.jpa.JpaTransactionManager doCommit 
DEBUG: Committing JPA transaction on EntityManager [[email protected]] 
2014-12-03 10:53:00,489 org.springframework.orm.jpa.JpaTransactionManager doCleanupAfterCompletion 
DEBUG: Closing JPA EntityManager [[email protected]33a72f] after transaction 
2014-12-03 10:53:00,489 org.springframework.orm.jpa.EntityManagerFactoryUtils closeEntityManager 
DEBUG: Closing JPA EntityManager 

我知道我可以使用OpenSessionInView持有一個完整的請求Hibernate的Session但有些人說,OSIV是一個反模式。相反,我使用SpringOpenEntityManagerInViewFilter。但沒有成功。我如何實現,Spring爲我的控制器的多個服務層方法調用使用單個事務?還是我誤解了任何東西?

這裏我的Spring配置的一部分:

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 
    <property name="dataSource" ref="dataSource" /> 
    <property name="jpaVendorAdapter"> 
     <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> 
      <property name="database" value="MYSQL" /> 
      <property name="showSql" value="false" /> 
     </bean> 
    </property> 
</bean> 

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> 
    <property name="jndiName" value="java:comp/env/jdbc/tikron" /> 
</bean> 

<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" /> 

<bean id="jpaTemplate" class="org.springframework.orm.jpa.JpaTemplate"> 
    <property name="entityManagerFactory" ref="entityManagerFactory" /> 
</bean> 

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> 
    <property name="entityManagerFactory" ref="entityManagerFactory" /> 
</bean> 

<tx:annotation-driven transaction-manager="transactionManager" /> 

<context:component-scan base-package="de.domain.webapp"> 
    <context:include-filter type="regex" expression=".*Service"/> 
</context:component-scan> 

我的持久化單元:

<persistence-unit name="tikron-data" transaction-type="RESOURCE_LOCAL"> 
    <!-- Entities located in external project tikron-data --> 
    <jar-file>/WEB-INF/lib/tikron-data-2.0.1-SNAPSHOT.jar</jar-file> 
    <!-- Enable JPA 2 second level cache --> 
    <shared-cache-mode>ALL</shared-cache-mode> 
    <properties> 
    <property name="hibernate.hbm2ddl.auto" value="validate" /> 
    <property name="hibernate.cache.use_second_level_cache" value="true" /> 
    <property name="hibernate.cache.use_query_cache" value="true" /> 
    <property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory"/> 
    <property name="hibernate.generate_statistics" value="false" /> 
    </properties> 
</persistence-unit> 

一些更從應用程序啓動日誌輸出:

2014-12-03 10:46:48,428 org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean createNativeEntityManagerFactory 
INFO: Building JPA container EntityManagerFactory for persistence unit 'tikron-data' 
2014-12-03 10:46:48,428 org.hibernate.ejb.HibernatePersistence logDeprecation 
WARN: HHH015016: Encountered a deprecated javax.persistence.spi.PersistenceProvider [org.hibernate.ejb.HibernatePersistence]; use [org.hibernate.jpa.HibernatePersistenceProvider] instead. 
2014-12-03 10:46:48,448 org.hibernate.jpa.internal.util.LogHelper logPersistenceUnitInformation 
INFO: HHH000204: Processing PersistenceUnitInfo [ 
    name: tikron-data 
    ...] 

... 

2014-12-03 10:46:51,101 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons 
INFO: Pre-instantiating singletons in org.s[email protected]6cccf90d: defining beans [propertyConfigurer,messageSource,entityManagerFactory,dataSource]; root of factory hierarchy 
2014-12-03 10:46:51,111 org.springframework.web.context.ContextLoader initWebApplicationContext 
INFO: Root WebApplicationContext: initialization completed in 3374 ms 

在此先感謝。

回答

7

您需要播放您的PROPAGATION_LEVEL並正確構建您的服務bean調用。如果你在你的服務類中使用@Transactional,你所描述的是正常的,因爲事務分界發生在公共方法級別上。因此,根據進入服務bean的公共方法時的傳播級別,事務將啓動,加入現有事務,拋出異常或以非事務方式執行。

要在一個事務中執行服務方法,它的傳播級別設置爲支持,就像您在GalleryService(假設您不在方法級別上覆蓋它)一樣,並調用這些方法來自另一種服務的單一方法,其被註釋爲@Transactional(propagation = Propagation.REQUIRED)。你打電話的重要性在於通過豆類例如(galleryService.getPicture而不是本地通話Getpicture中),因爲那注入的事務語義工作對一個包裝豆

@Service("exampleService") 
@Transactional(propagation=Propagation.REQUIRED) 
public class ExampleServiceImpl implements ExampleService { 

    @Autowired 
    private GalleryService galleryService; 

    @Override 
    public void singleTransaction() { 
    galleryService.getPicture 
    galleryService.getComments 
    } 

    ... 
} 

簡要PROPAGATION_LEVEL詞彙

MANDATORY 
      Support a current transaction, throw an exception if none exists. 
NESTED 
      Execute within a nested transaction if a current transaction exists, behave like PROPAGATION_REQUIRED else. 
NEVER 
      Execute non-transactionally, throw an exception if a transaction exists. 
NOT_SUPPORTED 
      Execute non-transactionally, suspend the current transaction if one exists. 
REQUIRED 
      Support a current transaction, create a new one if none exists. 
REQUIRES_NEW 
      Create a new transaction, suspend the current transaction if one exists. 
SUPPORTS 
      Support a current transaction, execute non-transactionally if none exists. 

更新代理方面關於評論

但是將服務方法調用組合成一個唯一的服務方法在單一交易中處理這些電話的方式?

不,但在我看來它是您的最佳選擇。考慮文章http://www.ibm.com/developerworks/java/library/j-ts2/index.html。我所描述的是文章中提到的API層策略。它定義爲

,當你有 粗粒度的方法,即充當主入口點到後端 功能API層事務策略中使用。 (打電話給他們服務,如果你想。)在這個 情況下,客戶端(無論是基於網絡的,基於Web的服務, 基於消息的,甚至桌面)作出後端一個調用 執行特定的請求。

現在在標準的三層體系結構中,您有了演示文稿,業務和持久層。簡而言之,您可以註釋您的控制器,服務或DAO。服務是持有邏輯工作單元的服務。如果你註釋你的控制器,它們是你的表示層的一部分,如果你的事務語義存在,並且你決定切換或添加一個非http客戶端(例如Swing客戶端),你必然會遷移或複製你的事務邏輯。 DAO層也不應該是事務的所有者,「DAO方法的粒度遠遠低於業務邏輯單元的粒度。我是從喜歡的最佳實踐等點限制,但如果你不確定,選擇你的業務(服務)爲您的API事務層:)

你有大量的帖子在各個方向上討論這個話題

why use @transactional with @service insted of with @controller Where should "@Transactional" be place Service Layer or DAO

很好的和有趣的閱讀,許多意見和非常依賴於上下文

+0

謝謝您的解釋!但是,將服務方法調用合併爲一個服務方法是在單個事務中處理這些調用的唯一方法?這將導致每個控制器的一種服務方法。我想,我的問題來自我的web應用程序中錯誤的Spring配置。 – Titus 2014-12-04 09:36:40

+0

編輯了一個答案,但它只是一般的評論,不僅僅是一個決定性的答案 – 2014-12-04 15:22:53

相關問題