控制器方法中的@Transactional不能与Hibernate Session一起使用

时间:2013-10-03 15:45:34

标签: json spring hibernate spring-mvc

我使用Spring MVC + Hibernate开发了一个webapp,并使用了三层,即Controller层,Service层和Dao层。

现在我想为我的webapp提供一个REST api。

由于我有一个GenericDao提供了像find(id),findAll(),findByProperty()这样的通用方法,我想我可以跳过Api控制器中的服务层并将daos注入Controller本身,否则我会有为我的每个域对象创建特定于类的方法,用于这些通用的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>

2 个答案:

答案 0 :(得分:1)

使用@Transactional注释您的控制器是个坏主意。见the Spring MVC documentation,17.3.2:

  

使用带注释的控制器类时常见的陷阱   在应用需要创建代理的功能时发生   控制器对象(例如@Transactional方法)。通常你会   为控制器引入一个接口,以便使用JDK动态   代理。要使其工作,您必须移动@RequestMapping   注释,以及任何其他类型和方法级注释   (例如@ModelAttribute,@ InitBinder)到接口以及   映射机制只能“看到”代理公开的接口。   或者,您可以在中激活proxy-target-class =“true”   应用于控制器的功能的配置(在我们的   交易场景)。这样做表明   应该使用基于CGLIB的子类代理而不是   基于接口的JDK代理。有关各种代理的更多信息   机制见第9.6节“代理机制”。

所以有一些解决方法,但听起来很痛苦。将@Transactional放在您的DAO上会更容易。

答案 1 :(得分:0)

  

根据定义,数据库事务必须是原子的,一致的,   隔离和耐用。[1]数据库从业者经常提到这些   使用首字母缩写词ACID。1

的数据库事务的属性

根据定义,交易是原子工作单位。它们具有all-or-nothing属性,这意味着事务操作已经提交或从未存在(回滚)。

作为所有MVC框架,Spring-mvc是UI的一部分。然后不需要是交易的。大多数J2EE应用程序中有3个基本层。首先是表示层,主要涉及MVC框架。其次是业务逻辑已完成的服务层。第三个是已完成数据库访问的数据层。

服务层是业务的好候选者,其中业务逻辑已经完成,但在我的想法中,在某些情况下,数据访问层可能需要是事务性的。因为事务单元是原子的,没有其他线程不能使用它。

最好使服务或dao类在您的情况下是事务性的。不要忘记在配置中添加适当的事务管理器。

另见

Database transaction

ACID

Transaction Management

Spring MVC