2013-10-03 74 views
0

我已經開發了一個使用Spring MVC + Hibernate的webapp,並使用三層,控制器層,服務層和道層。@Transactional控制器方法不工作與休眠會話

現在我想爲我的webapp提供一個REST api。因爲我有一個GenericDao,它提供了find(id),findAll(),findByProperty()這樣的泛型方法,我以爲我可以跳過Api控制器中的服務層,並將控制檯注入到控制器本身,否則我會必須爲這些通用find,findAll方法的每個域對象創建特定於類的方法,這在我只想預置原始數據時很麻煩。

我的第一個更通用的問題是關於這個架構決定。這是一個好的解決方案嗎?

我的第二個(也是主要的)問題是我在使用@Transactional註釋我的Controller方法時遇到問題,因此打開了一個hibernate會話。看起來它根本不工作。

我甚至在this question中說明了一個接口。

IApiController

@Controller 
public interface IApiController { 

    @ResponseBody 
    public String getStation(Long id); 
    @ResponseBody 
    public String getStations(); 


} 

ApiController

@Controller 
@RequestMapping("/api") 
public class ApiController extends BaseApiController implements IApiController { 

    @Autowired 
    private IStationDao stationDao; 


    @RequestMapping(value = "stations/{id}", produces = MediaType.APPLICATION_JSON_VALUE) 
    @ResponseBody 
    @Transactional(readOnly=true) 
    public String getStation(@PathVariable Long id){ 
     Station station = stationDao.findById(id); 
     return pack(station); 
    } 


    @Override 
    @RequestMapping(value = "stations", produces = MediaType.APPLICATION_JSON_VALUE) 
    @ResponseBody 
    @Transactional(readOnly=true) 
    public String getStations() { 
     List<Station> stations = stationDao.findAll(); 
     return pack(stations); 
    } 


} 

當我打電話api/stations我得到一個HibernateException: No Session found for current thread

語境配置

<context:component-scan base-package="my controller package" /> 
    <mvc:annotation-driven /> 
    <context:annotation-config /> 
    <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> 
     <property name="sessionFactory" ref="SessionFactory" /> 
    </bean> 
<bean id="SessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> 
     <property name="dataSource" ref="dataSource" /> 
     <property name="configLocation"> 
      <value>classpath:hibernate.cfg.xml</value> 
     </property> 
     <property name="mappingLocations"> 
      <list> 
       <value>classpath*:/hbm/*.hbm.xml</value> 
      </list> 
     </property> 
    </bean> 
+4

看看這個,我個人認爲移動DAO注射到服務層是更好比擁有它在控制器中,你會感謝你自己稍後再做。 http://stackoverflow.com/questions/1079114/spring-transactional-annotation-best-practice 爲交易,我認爲你必須配置交易http://stackoverflow.com/questions/14090547/spring-annotation-transaction -mangement – Zeus

+0

如果可以的話,我會給予@Zeus評論+10。事務管理和DAO注入屬於服務層,不屬於UI層一部分的控制器。 – Olaf

+0

你可以發佈你的spring數據源和事務配置嗎? – erencan

回答

1

用@Transactional註釋你的控制器是個不錯的主意。見the Spring MVC documentation,17.3.2:

帶有加註解的控制器類 施加需要創建代理用於 控制器對象(例如@Transactional方法)的功能,當發生工作時的一個常見缺陷。通常你會爲 介紹一個控制器的接口,以便使用JDK動態 代理。要做到這一點,您必須將@RequestMapping 註釋以及任何其他類型和方法級註釋 (例如@ModelAttribute,@InitBinder)移動到界面以及 映射機制中,只能「看見」界面由代理公開。 或者,您可以在 配置中激活proxy-target-class =「true」以應用於控制器的功能(在我們的 交易場景中)。這樣做表示 應該使用基於CGLIB的子類代理,而不是基於接口的JDK代理 。有關各種代理 機制的更多信息,請參見第9.6節「代理機制」。

所以有解決方法,但聽起來很痛苦。把@Transactional放在你的DAO上會更容易。

+0

DAO也不是交易的最佳場所。把@Transactional放在服務上最好。 – DraggonZ

+0

@DraggonZ:我同意。但是,當人們沒有任何業務邏輯來投入服務時,人們就會抵制這種方法。在這種情況下,OP特別希望避免創建服務。 –

0

根據定義,數據庫事務必須是原子性的,一致的,並且是持久的。[1]數據庫從業人員經常使用首字母縮略詞ACID引用數據庫事務的這些屬性。 1

根據定義,Transactions是原子的工作單位。它們具有全部或全部屬性,這意味着事務操作已被提交或從未存在(回滾)。

由於所有的MVC框架Spring-mvc都是UI的一部分。然後不需要是事務性的。大多數J2EE應用程序都有三個基本層。首先是主要涉及MVC框架的表示層。其次是業務邏輯已完成的服務層。第三是數據庫訪問已完成的數據層。

在業務邏輯已經完成的情況下,服務層是一個很好的候選人,但在我的想法中,在某些情況下,數據訪問層可能是事務性的。因爲事務單元是原子的,所以沒有其他線程不能使用它。

在您的情況下,最好讓服務或dao類成爲事務性的。不要忘記將正確的事務管理器添加到您的配置中。

又見

Database transaction

ACID

Transaction Management

Spring MVC