【问题标题】:Proper usage of Spring mvc 3 with hibernate (Spring ORM)正确使用 Spring mvc 3 with hibernate (Spring ORM)
【发布时间】:2011-10-06 06:55:33
【问题描述】:

我正在开始一个新项目,这次尝试做正确的事情(所以不止一个问题),我可能需要一些帮助,我不确定我做错了什么:

  1. Spring context
  2. Controller
  3. Service Interface
  4. Service Implementation
  5. DAO interface
  6. DAO implementation

我想尽可能地利用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)

我做错了什么?任何人都可以对我的项目提出一些改进建议吗?

【问题讨论】:

  • 这不是一个问题,而是 5 或 10 个问题。抱歉,投票结束。

标签: java hibernate spring spring-mvc


【解决方案1】:

如何让@Transactional 处理会话打开/关闭?

您需要在 spring 上下文中使用 <tx:annotation-driven />(和 tx 命名空间)。

(见Using @Transactional

【讨论】:

    【解决方案2】:

    我会强烈建议在 Spring 中远离 @Transactional。对于您的使用,请坚持使用open-session-in-view 模式。当请求进来时,会打开一个会话并启动一个事务。如果遇到异常或其他错误,则回滚事务。否则提交。这样一来,Hibernate 就会为您处理所有繁重的工作。

    如果你沿着@Transactional 的道路前进,当你试图找出从哪里来的东西时,你将进入延迟加载异常和其他奇怪行为的阴暗领域。在最坏的情况下,您需要仔细跟踪调用方法的顺序(即严格注意您的堆栈),以确保您拥有正确的权限。

    最糟糕的是,如果你将 @Transactional 放在 Hibernate 一级或二级缓存中已经存在的东西上,你最终会得到大量的 BEGIN/END 事务进入你的数据库,而没有实际的查询被执行(因为它们在缓存中)。这会影响你的表现,而且几乎不可能被取消。

    会话中的事务发生故障后,您需要回滚。您可能需要重做会话,具体取决于围绕它的语义。第一个的修复很简单——在保存之前先检查实体是否已经存在。这将解决第二个问题。

    查看article comparing Hibernate/JPA and myBatis 了解更多评论。

    【讨论】:

    • open-session-in-view 模式不能替代声明性事务。 OSIVFilter 不启动事务。 Spring 的事务管理和它的 OSIVFilter 是相辅相成的,但各自发挥着非常特殊的作用。您所描述的问题是由于误解和/或滥用造成的。
    • 好的...你会如何描述他们的角色?您将如何解决这些担忧?我并不是说他们做同样的事情,我是说添加 Spring 事务管理(特别是对于 OP 描述的典型 Web 应用程序)非常复杂且令人头疼,没有特别的收获。
    • 我今天早些时候在另一篇文章中简要描述了answer 中的角色。 Spring 事务管理几乎是你能找到的最简单的,并且是专为这里讨论的应用程序类型而设计的。它是如此简单,我几乎可以在此评论的约束中给出一个教程。一个 bean:事务管理器,与您选择的持久性机制相关联;启用注释的一行 XML;以及服务方法的注释。完成。
    • 嗯,教程就是这么描述的。一旦你开始做任何复杂的事情,你就会回到相互冲突的事务注释的土地上,当你使用不同的设置访问不同的访问时,整个堆栈会产生不同的影响。琐碎的一次性方法请求不是问题。一旦您开始尝试跨越具有不同事务的方法的边界,您就完成了 - 只能通过仔细比较代码发出的事务并使用调试器遍历堆栈来调试(或理解)结果。
    【解决方案3】:

    我建议扩展 HibernateDaoSupport 并使用 HibernateTemplate 而不是在 DAO 代码中显式使用 SessionFactory(并创建事务)。

    【讨论】:

    • 那是老派。当前的最佳实践或多或少是因为他编写了 DAO:注入 SessionFactory 并使用 getCurrentSession()。
    • HibernateTemplate 等在 Spring 3.x 中不再推荐。见static.springsource.org/spring/docs/3.0.x/…
    • 有趣,我不知道这一点。尽管链接上的文档没有提到不再推荐 HibernateTemplate,但它似乎几乎没有提到它。就我个人而言,我更喜欢 HibernateTemplate 将 HibernateException 转换为 DataAccessException 系列
    • 您可以使用 @Repository 注释将 HibernateException 转换为 DataAccessException。
    【解决方案4】:

    组件扫描

    首先要做的事情是:您正在使用@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 字段。删除这些字段的设置器。他们不再需要了。组件扫描标签也会为你做自动装配。要使用 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,正如肖恩所说,你需要在你的spring上下文文件中添加一个元素:

    <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 中直接处理事务和会话的方式可能存在一些奇怪的相互作用。

    【讨论】:

    • 我认为“发生异常后不刷新会话”是因为他在 DAO 中关闭了会话。
    • 我考虑了一下,甚至稍微查看了 SessionImpl 的源代码,但我相当肯定 close() 不会导致 flush()。我可能是错的。如果有人能证明这一点,我会更新答案。
    • 参见docs.jboss.org/hibernate/core/3.3/reference/en/html/…,它描述了“Hibernate 抛出的异常意味着您必须回滚数据库事务并立即关闭会话”。一旦你有这个异常,你的会话就死了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-18
    • 2015-11-11
    • 1970-01-01
    • 2011-07-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多