我使用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>
答案 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类在您的情况下是事务性的。不要忘记在配置中添加适当的事务管理器。
另见