mybatis架构
dataSource 数据源
-
sqlSessionFactory -----> org.mybatis.spring.SqlSessionFactoryBean
- dataSource
- configLocation
- mapperLocations
-
MapperScannerConfigurer(动态代理)
- 让spring将代理类注入到spring容器中,批量管理mapper
- basePackage ---> value 具体mapper接口的位置
- sqlSessionFactoryBeanName ---> value sqlSessionFactory
-
transactionManager 事务管理器 ---->
org.springframework.jdbc.datasource.DataSourceTransactionManager
- property 为
dataSource
- property 为
声明式事务
声明式事务分为注解式和Xml配置式
1.基于XML配置式
需要我们写有规律的方法名,xml定义如下
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 定义事务通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 定义方法的过滤规则 -->
<tx:attributes>
<tx:method name="create*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="insert*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="save*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="update*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="edit*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="delete*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="remove*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="*" propagation="REQUIRED" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 定义AOP配置 -->
<aop:config>
<!-- 定义一个切入点 -->
<aop:pointcut expression="execution(public * com.hxqc.data.gather.*.*.*.service.*.*(..))" id="txService"/>
<!-- 对切入点和事务的通知,进行适配 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txService"/>
</aop:config>
2.基于注解式
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 使用cglib的代理方式 -->
<!-- <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/> -->
说明:
transaction-manager
:指定事务管理器名字,默认为transactionManager,当使用其他名字时需要明确指定
proxy-target-class
:表示将使用的代码机制,默认false
表示使用JDK代理,如果为true
将使用CGLIB代理
order
:定义事务通知顺序,默认Ordered.LOWEST_PRECEDENCE
,表示将顺序决定权交给AOP来处理。
声明式事务的坑
这个坑也是比较严重的,当我在一次测试中时,发现service发生了异常,但是数据并没有回滚,因为数据库里的数据还是被修改了.后来发现是事务的动态代理压根就没有生效.找了很久原因,原来是这样:
在SSM整合中,有spring-context.xml
和springmvc.xml
这两个配置文件,需要加载,我们一般的做法是,让springmvc
只扫描带@Controller
注解的类,让spring-context
去扫描@Service
和@Component
等注解的类。
首先看我的工程目录(简易)
以下是我两个配置文件的扫包配置.
spring-context.xml
(对应工程文件中的applicationContext.xml
)
<!-- 配置扫描除controller注解(springmvc扫描的包)以外的包 -->
<context:component-scan base-package="com.zs.*">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>
springmvc.xml
<!-- 扫描注解包 -->
<context:component-scan base-package="com.zs.*">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>
如上,我原本以为,配置了include-filter
后,springmvc
就只会去扫描带controller
的类了,后来发现事务一直不生效,原来这里,他把@service
的类也给扫描了.
为什么springmvc扫描以后,事务就不生效了?
Spring MVC
启动时的配置文件,包含组件扫描、url映射以及设置freemarker参数,让spring不扫描带有@Service
注解的类。为什么要这样设置?因为springmvc.xml
与spring-context.xml
不是同时加载,如果不进行这样的设置,那么,springmvc
就会将所有带@Service
注解的类都扫描到容器中,等到加载spring-context.xml
的时候,会因为容器已经存在Service
类,使得cglib将不对Service进行代理,直接导致的结果就是在spring-context
中的事务配置不起作用,发生异常时,无法对数据进行回滚。
解决方法:
1.在springmvc.xml
的扫包处,排除@Service
,就是不扫@Service
的类
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
2.将扫包路径写的更详细一点,直接定位到web层(推荐这个方法)
<context:component-scan base-package="com.zs.web">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>