4

我有一个问题,Spring 将 DAO 对象的代理注入到服务中,但是这个服务被注入到控制器中,它是具体的类。这不允许我使用服务范围的事务并分别为每个 DAO 调用启动事务。这是我所期望的。

配置:

Controller 是带有 @Controller 注释和构造函数 DI 的类。

服务:

@零件
@Transactional
公共类 UserServiceImpl 实现 UserService { ...}

道:

@零件
@Transactional
公共类 UserDaoImpl 实现 UserDao {

JPA 配置:

<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>

<bean id="entityManagerFactory"
      class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" >
    <property name="dataSource" ref="dataSource"/>
    <property name="persistenceUnitName" value="xxxPersistenceUnit"/>
    <property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml"/>
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
        </bean>
    </property>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.dialect">${hibernate.dialect}</prop>
            <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
            <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
            <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
        </props>
    </property>
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

<tx:annotation-driven />

有人知道为什么会这样吗?

4

4 回答 4

5

您很可能是错误地在 servlet 上下文UserServiceImpl中创建的- 请检查 表达式以检查其中是否仅 包含类。context:component-scanController

有关组件扫描过滤器的示例,请参阅@Service 构造两次

例如,如果事务管理器 bean 和<tx:annotation-driven>根 Web 应用程序上下文中声明,那么事务代理将仅为根应用程序上下文中的 bean 创建(来自Spring 文档):

BeanPostProcessor 接口的范围是每个容器。这仅在您使用容器层次结构时才相关。如果您在一个容器中定义一个 BeanPostProcessor,它只会在该容器中的 bean 上工作。在一个容器中定义的 Bean 不会由另一个容器中的 BeanPostProcessor 进行后处理,即使两个容器是同一层次结构的一部分。

不太可能将用户服务的事务配置配置为使用另一个事务管理器(或另一个默认传播),但在这种情况下,TransactionInterceptor 调用将出现在 DAO 方法的堆栈跟踪中。

如果您了解自己在做什么,那么在 Spring 中使用 DAO 类是绝对可以@Transactional的 - 存储库或 DAO 无法打开事务的想法来自黑暗时期,当时您必须创建样板代码来打开事务并且很难管理事务实例(你不能确定事务是如何管理的)。但是当您使用声明式配置时,事情并没有那么糟糕。Spring 提倡约定优于配置的风格,其中大多数方法使用Propagation.REQUIRED事务模式。在 SpringPropagation.REQUIRED中,当您装饰方法时,默认模式是使用@Transactional(此传播被硬编码为@Transactional注解声明),这意味着新的逻辑事务被映射到相同的物理事务,因此使用@Transactional是无害的。

有关Spring 中事务传播的参考,请参阅http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/transaction.html#tx-propagation

例如,在 Spring Data JPA(我很确定他们知道自己在做什么)中,存储库实例上的 CRUD 方法默认是事务性的。这在某些情况下可能很有用,该机制与 Hibernate 允许您在Session 不声明显式事务的情况下从用于显示的 get() 一些任意对象时相同(当然这并不意味着框架以某种方式设法在没有事务的情况下运行 -在这种情况下是隐含的)。

于 2013-01-29T20:05:18.467 回答
2

我在理解您在说什么时遇到了一些麻烦,但您似乎很惊讶每次 DAO 调用都会获得新事务,而不仅仅是服务调用。不幸的是,这正是您通过在 DAO 类上放置“@Transactional”所指定的。你的 DAO 不应该是事务性的,至少如果你遵循通常的模式。如果我对您的理解正确,您应该删除 DAO 类上的 @Transactional 注释。

于 2013-01-29T16:22:53.473 回答
1

其他响应者是正确的,因为您不应该将 DAO 注释为@Transactional,但要真正了解正在发生的事情,您应该参考Spring 参考手册中的 Transaction Propagation 部分。使用时的默认传播@TransactionalREQUIRES_PROPAGATION,因此请具体查看。

你的问题不是那么具体,所以我不确定你在寻找什么。

编辑:重新阅读您的问题后,您的组件扫描可能存在问题。检查以确保<tx:annotation-driven />您在组件扫描服务类的同一应用程序上下文中。

于 2013-01-29T18:46:44.930 回答
0

你不应该在你的 DAO 对象中使用那个“@Transactional”注解。您在您的服务中定义它,这将允许您在服务方法中调用的所有 DAO 方法在同一个事务中执行,这似乎正是您想要的,当您说“服务范围的事务”时,对?

此外,如建议的那样,您可能希望在 UserServiceImpl 中将注释从“@Component”更改为“@Service”,在 UserDaoImpl 中更改为“@Repository”。

最好的祝福。

于 2013-01-29T16:40:34.003 回答