【问题标题】:Transactional annotation does not save on exit事务注释在退出时不保存
【发布时间】:2015-06-02 20:39:33
【问题描述】:

我已经配置了带有注释驱动的 spring jpa。我希望以下代码在方法存在时将更改持久保存到数据库。

@Transactional
public Foo changeValue(int id){
    final Foo foo = fooRepository.findOne(id);
    if(foo != null){
       foo.setValue("new value");
       //fooRepository.save(foo);
    }
}

FooRepositoryJPARepository 并且 foo 对象正在被获取,因此它被管理。根据我读到的关于@Transactional 我希望,即使没有fooRepository.save(foo) 调用,数据库中foo 的值列中的更改也会在方法存在时保持不变。但是,只有当我取消注释对 fooRepository.save(foo) 的调用时才会发生这种情况

不抛出异常,jpa 数据源的配置如下。

  <bean id="entityManagerFactory"  class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 
   ...
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
    p:entityManagerFactory-ref="entityManagerFactory" />

<tx:annotation-driven transaction-manager="transactionManager" />

<context:component-scan
    base-package="com.example.package.data" />

<jpa:repositories
    base-package="com.example.package.data.repository"
    entity-manager-factory-ref="entityManagerFactory"
    transaction-manager-ref="transactionManager" />

有什么想法吗?

更新

我有一个ContextLoaderListener 和一个DispatcherServlet,但我没有对两者进行组件扫描。

    <servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
    </init-param>
    </servlet>
    ....
    <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

更新

    <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring/root-context.xml</param-value>

    </context-param>

我做到了

    <context:component-scan base-package="com.example.package">
 <context:exclude-filter type="regex" 
        expression="com.example.package.data.*" />
 <context:exclude-filter type="regex" 
        expression="com.example.package.web.controller.*" />    
 <context:exclude-filter type="regex" 
        expression="com.example.package.web.service.*" />       
</context:component-scan>

servlet-context

<context:component-scan base-package="com.example.package.web" />

【问题讨论】:

  • 我严重怀疑 Spring 是否会自动保存您的实体,因为您在事务中修改了附加的实体。你能给我们参考任何说明这一点的文件吗?如果您查看this Spring Documentation,尤其是该页面上的 2.16 示例,那么您可以看到代码在事务性addRoleToAllUsers 方法中显式调用了存储库save 方法。
  • @DuncanKinnear Spring 在任何情况下都不会保存实体,这是 Hibernate 的工作。
  • 您的非工作示例看起来完全像这样吗?或者这是一个示范性的简短例子?
  • 您是否同时拥有ContextLoaderListenerDispatcherServlet?在&lt;context:component-scan /&gt;?如果是,您有 2 个相同 bean 的实例 1 个事务性的,另一个没有,最后一个将被使用。
  • @DraganBozanovic 是的,非工作示例看起来像这样。我只是更改了变量名并省略了根据我的调试未达到的错误处理。

标签: spring hibernate jpa transactions


【解决方案1】:

您的一般方法是正确的。更改应在方法退出时保存并提交。虽然这种方法在工作时效果很好,但调试起来很麻烦。

您的配置对我来说看起来不错,所以我会仔细检查一下,您实际上有 Spring Beans 供您使用,而不是您通过调用构造函数创建的某个实例。

要验证这一点,只需在调用带注释的方法的代码点放置一个断点。带有注解方法的 bean 不应该属于您编写的类,而是一些 ProxySomething 类。

本文还提供了一些提示,说明您的设置可能存在哪些问题。

【讨论】:

    【解决方案2】:

    如果您同时拥有ContextLoaderListenerDispatcherServlet,它们都会加载配置文件。

    从您的配置来看,服务由DispatcherServlet 加载,而您的&lt;tx:annotation-driven /&gt;ContextLoaderListener 加载。它们都创建了ApplicationContext,并且由于 AOP 仅应用于相同上下文中的 bean,因此您的服务将不是事务性的(事务管理是通过使用 AOP 完成的)。

    根据经验,您的ContextLoaderListener 应包含所有共享或全局资源(如服务、daos、数据源等),而您的DispatcherServlet 应仅包含与 Web 相关的 bean,如控制器、视图解析器等。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-22
      • 2014-06-12
      • 2016-12-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多