1

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

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

因为我有一个 GenericDao,它提供了像 find(id)、findAll()、findByProperty() 这样的通用方法,我想我可以跳过 Api 控制器中的服务层并将 daos 注入控制器本身,否则我将不得不创建类- 这些通用查找的每个域对象的特定方法,findAll 方法,当我只想预设原始数据时,这是一件很痛苦的事情。

我的第一个更通用的问题是关于这个架构决策。这是一个好的解决方案吗?

我的第二个(也是主要)问题是我在使用@Transactional休眠会话打开时注释我的控制器方法时遇到了麻烦。似乎它根本不起作用。

我什至创建了这个问题中所说的界面。

IApi控制器

@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

2 回答 2

2

用 @Transactional 注释你的控制器是个坏主意。请参阅Spring MVC 文档,17.3.2:

当应用需要为控制器对象创建代理的功能(例如@Transactional 方法)时,使用带注释的控制器类时会发生一个常见的陷阱。通常您会为控制器引入一个接口,以便使用 JDK 动态代理。要完成这项工作,您必须将@RequestMapping 注释以及任何其他类型和方法级别的注释(例如@ModelAttribute、@InitBinder)移动到接口以及映射机制只能“看到”由代理人。或者,您可以在配置中为应用于控制器的功能激活 proxy-target-class="true"(在我们的事务场景中)。这样做表明应该使用基于 CGLIB 的子类代理而不是基于接口的 JDK 代理。

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

于 2013-10-03T15:52:01.043 回答
-1

根据定义,数据库事务必须是原子的、一致的、隔离的和持久的。 [1] 数据库从业人员经常使用缩写词 ACID 来指代数据库事务的这些属性。1

根据定义,事务是一个原子工作单元。它们具有全有或全无属性,这意味着事务操作已提交或从未存在(回滚)。

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

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

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

也可以看看

数据库事务

事务管理

春季MVC

于 2013-10-03T16:47:10.570 回答