Spring | 事务没有生效导致数据没有持久化的探究
今天遇到一个很诡异的问题,在使用 Hibernate
持久化数据时,数据没有写入到数据库,其他正常的查询数据都是没有问题的。
1 事务配置
spring-context.xml
中事务的配置
<!-- 配置事务管理器 -->
<bean name="transactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!-- 基于 <tx> 和 <aop> 命名空间的声明式事务管理 -->
<tx:advice id="transactionAdvice">
<tx:attributes>
<tx:method name="add*" rollback-for="Exception" propagation="REQUIRED"/>
<tx:method name="save*" rollback-for="Exception" propagation="REQUIRED"/>
<tx:method name="insert*" rollback-for="Exception" propagation="REQUIRED"/>
<tx:method name="update*" rollback-for="Exception" propagation="REQUIRED"/>
<tx:method name="edit*" rollback-for="Exception" propagation="REQUIRED"/>
<tx:method name="modify*" rollback-for="Exception" propagation="REQUIRED"/>
<tx:method name="delete*" rollback-for="Exception" propagation="REQUIRED"/>
<tx:method name="remove*" rollback-for="Exception" propagation="REQUIRED"/>
<tx:method name="get*" propagation="NOT_SUPPORTED" read-only="true"/>
<tx:method name="find*" propagation="NOT_SUPPORTED" read-only="true"/>
<tx:method name="list*" propagation="NOT_SUPPORTED" read-only="true"/>
<tx:method name="select*" propagation="NOT_SUPPORTED" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 开启注解式事务的支持,即使用注解 Transactional -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 配置事务切面 -->
<aop:config>
<aop:pointcut id="transactionPointcut"
expression="execution(* com.fairy.springmvc.apps.*.service.*.*(..))"/>
<aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice"/>
</aop:config>
配置中同时使用了两种风格的事务支持,一种是切面式的,一种是注解式的。
经过仔细的 Debug
和排查,发现 Service
中的持久化操作没有被包裹在事务中执行。至于其他的异常和数据库的原因统统被排除掉。经过一步步的调试,发现是配置的问题(spring-servlet.xml
)。
2 spring-servlet.xml 的配置
原来的配置:
<!--扫描 controller 包,并将其生命周期纳入 Spring 管理-->
<!--如果需要扫描多个位置,可以指定多路径 例如 -->
<!--<context:component-scan base-package="x.y.z.service, x.y.z.controller, x.y.z.dao" />-->
<context:component-scan base-package="com.fairy.springmvc">
<!--只扫描 @Controller 注解的类-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.RestController"/>
</context:component-scan>
3 原因探究
其实核心问题还是 spring-context.xml
和 spring-servlet.xml
的关系没有完全理清导致出现的问题。
因为 Spring
应用中 spring-context.xml
和 spring-servlet.xml
对应的容器是父子容器,spring-context.xml
对应的是父容器,spring-servlet.xml
对应的是子容器。
子容器配置扫描的 Controller
进行扫描装配 Service
时装配了 @Service
注解的 Bean
(没有被事务包裹),因为 spring-servlet.xml
优先于 spring-context.xml
,导致子容器此时得到的是原样的Service
(没有经过事务加强处理,故而没有事务处理能力)。
其实正常情况下 Bean
应该由父容器进行初始化以保证事务的增强处理(因为事务的配置在 spring-context.xml
中)。
关系理清之后,问题的解决方式其实很简单,核心就一条,保证 spring-servlet.xml
在扫描的时候尽量只扫描控制器,如果是单个 Servlet
内部使用的 bean
,可以根据业务自行调整。
所以下文简单列举了几种修改方式。类推,不仅仅是事务包裹的 Service
,其他类似的业务也是如此。
下面列举几种修改方式,配置修改成下面的情形都可正常持久化
第一种情况:排除使用 Service
注解的类
重点关注
context:exclude-filter
标签
<context:component-scan base-package="com.fairy.springmvc">
<!--只扫描 Controller 注解的类-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.RestController"/>
<!--排除 Service 注解的类-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
第二种情况:只专注扫描 controller
包
<context:component-scan base-package="com.fairy.springmvc.apps.*.controller">
<!--只扫描 Controller 注解的类-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.RestController"/>
</context:component-scan>
第三种情况:设置 use-default-filters="false"
注意
use-default-filters="false"
这个属性:默认为true
,会扫描包含Service
,Component
,Repository
,Controller
注解修饰的类,配置成false
,只扫描指定的注解。
<context:component-scan base-package="com.fairy.springmvc" use-default-filters="false">
<!--只扫描 Controller 注解的类-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.RestController"/>
</context:component-scan>
转载自:https://juejin.cn/post/7132713117263331365