我正在开始一个新项目,这次尝试做正确的事情(所以不止一个问题),我可能需要一些帮助,我不确定我做错了什么:
我想尽可能多地利用spring MVC,如何通过@Transactional处理会话开启/关闭?
如何捕获异常(即非现有记录或数据库失败)(如果有)。即我的数据库不接受像这样的重复条目:
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry
我怎么能抓住这个?
对于我提出的每个下一个请求,我都会遇到此异常:
org.hibernate.AssertionFailure: null id in com.test.spring.ws.service.impl.TestObject entry (don't flush the Session after an exception occurs)
我做错了什么?有人可以建议我的项目有一些改进吗?
答案 0 :(得分:12)
组件扫描
首先要做的事情是:你正在使用@ Controller,@ Service,@ Repository和@Autowired,但是你没有对它们做任何事情。我建议使用classpath scanning。从spring上下文文件中删除“testServiceDAO”和“testService”bean,改为使用:
<context:component-scan base-package="com.test.spring.ws"/>
这将通过注释找到并创建这些bean,而不是要求您在XML中声明它们。将@Autowired添加到服务中的testServiceDAO
字段以及DAO中的sessionFactory
字段。删除这些字段的setter。他们不再需要了。组件扫描标签也将为您进行自动装配。要使用context
命名空间,您需要将其添加到根元素。例如:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
交易管理
对于use @Transactional,正如肖恩所说,你需要在弹簧上下文文件中添加一个元素:
<tx:annotation-driven/>
由于您的事务管理器bean名为“transactionManager”,因此它会自动找到它。您还需要将“tx”命名空间添加到根元素中,因此它应该类似于:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
要使其有机会工作,您需要从DAO方法中删除session.beginTransaction()
和session.close()
。以这种方式打开自己的事务是混合程序化和声明式事务划分,声明方式通常更好。此外,您永远不应该永远关闭真实项目中DAO中的会话。这会让你遇到各种各样的麻烦。
异常处理
你的MySQLIntegrityConstraintViolationException
是一个特定于数据库的例外,它将被Hibernate捕获并包含在ConstraintViolationException中,这是你DAO的结果;但是,既然你的DAO现在是一个@Repository,你可以从Spring的exception translation中受益。有了这个,Hibernate异常将被Spring捕获并转换为DataIntegrityViolationException。数据库异常处理总是很有趣!
会话管理
您使用的是OpenSessionInViewFilter还是OpenSessionInViewInterceptor?如果是,则在首次接收请求时打开Hibernate会话,并在写入响应后关闭。如果没有,则会话直到事务开始(在@Transactional方法)才开始,并且在该事务完成时关闭。使用过滤器/拦截器,您可以在需要回调数据库的“视图”层中执行操作 - 特别是当您具有渲染视图所需的延迟关系或延迟加载对象时。如果会话不可用 - 因为它不是仅存在于事务服务方法的长度 - 你不能在视图中执行这些操作而你会得到臭名昭着的LazyInitializationException
至于“在发生异常后不刷新会话”错误,我得到的错误,我没有立即看到任何会让我觉得应该发生的事情。您的Web层Spring环境中的某些内容可能是配置错误,或者您在DAO中直接处理事务和会话时可能存在一些奇怪的相互作用。
答案 1 :(得分:2)
答案 2 :(得分:1)
我建议扩展HibernateDaoSupport
并使用HibernateTemplate
而不是在DAO代码中明确使用SessionFactory(和创建事务)。
答案 3 :(得分:-1)
我强烈建议远离春天的@Transactional。对于您的使用,请坚持使用open-session-in-view模式。当请求进入时,会话打开并启动事务。如果遇到异常或其他错误,请回滚事务。否则提交。这样,Hibernate就会为你解决所有繁重的工作。
如果沿着@Transactional的路走下去,当你试图理清从哪里来的时候,你将进入一个懒惰加载异常和其他奇怪行为的阴暗境界。在最坏的情况下,您需要仔细跟踪调用方法的顺序(即严格关注您的堆栈),以确保您拥有正确的权限。
最糟糕的是,如果你把@Transactional放在已经存在于Hibernate一级或二级缓存中的东西上,你将会得到很多很多BEGIN / END事务进入你的数据库而没有执行实际的查询(因为它们在缓存中。这可能会破坏你的表现,而且几乎不可能撤销。
会话中的事务爆发后,您需要回滚。您可能需要重做会话,具体取决于周围的语义。第一个修复很简单 - 在执行保存之前首先检查实体是否已存在。这将解决第二个问题。
查看此article comparing Hibernate/JPA and myBatis以获取更多评论。