Spring项目中使用数据库的方式多种多样,注入MyBatis、JDBC、Hibernate等等都是支持的。之前的系统是基于MyBatis的。
关于如何Spring Boot和MyBatis网上有很多文章,可以参考。有时间我也会专门写一篇介绍集成的过程。本文讲的就是MyBatis移植到Spring Boot方面碰到的一些问题,主要是事务控制方面的。
1. Spring原始配置
在Spring中,之前是通过XML方式进行的事务配置。通过tx:advice
定义了事务的属性;然后通过aop:config
将事务织入业务代码。
1<!-- from the file 'context.xml' -->
2<?xml version="1.0" encoding="UTF-8"?>
3<beans xmlns="http://www.springframework.org/schema/beans"
4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5 xmlns:aop="http://www.springframework.org/schema/aop"
6 xmlns:tx="http://www.springframework.org/schema/tx"
7 xsi:schemaLocation="
8 http://www.springframework.org/schema/beans
9 http://www.springframework.org/schema/beans/spring-beans.xsd
10 http://www.springframework.org/schema/tx
11 http://www.springframework.org/schema/tx/spring-tx.xsd
12 http://www.springframework.org/schema/aop
13 http://www.springframework.org/schema/aop/spring-aop.xsd">
14
15 <!-- this is the service object that we want to make transactional -->
16 <bean id="fooService" class="x.y.service.DefaultFooService"/>
17
18 <!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean below) -->
19 <tx:advice id="txAdvice" transaction-manager="txManager">
20 <!-- the transactional semantics... -->
21 <tx:attributes>
22 <!-- all methods starting with 'get' are read-only -->
23 <tx:method name="get*" read-only="true"/>
24 <!-- other methods use the default transaction settings (see below) -->
25 <tx:method name="*"/>
26 </tx:attributes>
27 </tx:advice>
28
29 <!-- ensure that the above transactional advice runs for any execution
30 of an operation defined by the FooService interface -->
31 <aop:config>
32 <aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
33 <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
34 </aop:config>
35
36 <!-- don't forget the DataSource -->
37 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
38 <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
39 <property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>
40 <property name="username" value="scott"/>
41 <property name="password" value="tiger"/>
42 </bean>
43
44 <!-- similarly, don't forget the PlatformTransactionManager -->
45 <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
46 <property name="dataSource" ref="dataSource"/>
47 </bean>
48
49 <!-- other <bean/> definitions here -->
50
51</beans>
2. 迁移到SpringBoot
迁移到SpringBoot还是倾向于使用基于Java的配置,因此最初的想法是完全跑起xml配置文件。但是在实践中发现了一些限制:<aop:config>
标签无法完整准确的翻译成Java配置。
2.1 限制
通过上网搜索,发现解决方案并不完美:
The @Aspect model in AspectJ is designed for pure-Java configuration, and should be used in favor of aop:config when looking to create 100% java-based configuration using Spring’s @Configuration model. aop:config cannot properly be translated into the @Configuration model, largely because Java does not support method literals in the language. This leaves us resorting to String-based references to methods, which is not ideal. So the approach that users should consider are:
- Continue using aop:config by including the relevant XML snippet using @ImportResource
- Convert any existing aop:config elmements to use @Aspect style.
上面推荐的方案有两种:
- 继续使用xml配置,然后通过
@ImportResource
注解导入Java代码中; - 转换
<aop:config>
配置成@Aspect
注解。
2.2 最终方案
因为时间的原因,对@Aspect
形式的配置没有做深入研究。另外从StackOverflow上查到的说法是,tx-advice并没有合适的Java配置方法,使用xml配置还是最方便的。只是xml配置通过@Configuration
中的@ImportResource
注解引入即可,这样还是比较优雅,比较符合Java配置的规范性的。因此最终方案是使用了部分xml配置,通过@ImportResource
引入。
2.2.1 MasterDataSourceConfig
1@Configuration
2public class MasterDataSourceConfig {
3 static final String PACKAGE = "com.eveus.iot.shadow.mapper";
4 static final String MAPPER_LOCATION = "classpath:mybatis/mapper/**/*.xml";
5
6 @Bean
7 @Primary
8 @ConfigurationProperties("spring.datasource")
9 public DataSourceProperties firstDataSourceProperties() {
10 return new DataSourceProperties();
11 }
12
13 @Bean(name = "masterDataSource")
14 @Primary
15 @ConfigurationProperties(prefix = "spring.datasource")
16 public DataSource dataSource() {
17 return firstDataSourceProperties().initializeDataSourceBuilder().build();
18 }
19
20 @Bean(name = "masterTransactionManager")
21 public DataSourceTransactionManager transactionManager() {
22 return new DataSourceTransactionManager(dataSource());
23 }
24
25
26 @Bean(name = "masterSqlSessionFactory")
27 public SqlSessionFactory sqlSessionFactory(@Qualifier("masterDataSource") DataSource masterDataSource)
28 throws Exception {
29 final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
30 sessionFactory.setDataSource(masterDataSource);
31 sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
32 .getResources(MasterDataSourceConfig.MAPPER_LOCATION));
33 return sessionFactory.getObject();
34 }
35}
定义dataSource, transactionManager。
2.2.2 TransactionConfig
1...
2@Configuration
3@Aspect
4@ComponentScan("com.eveus.cloudauth")
5@ImportResource("classpath:/spring/uid-tx.xml")
6public class TransactionConfig {
7}
引入xml配置。
2.2.3 uid-tx.xml
1<?xml version="1.0" encoding="UTF-8"?>
2<beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xmlns:aop="http://www.springframework.org/schema/aop"
5 xmlns:tx="http://www.springframework.org/schema/tx"
6 xsi:schemaLocation="http://www.springframework.org/schema/beans
7 http://www.springframework.org/schema/beans/spring-beans.xsd
8
9 http://www.springframework.org/schema/aop
10 http://www.springframework.org/schema/aop/spring-aop.xsd
11 http://www.springframework.org/schema/tx
12 http://www.springframework.org/schema/tx/spring-tx.xsd">
13
14 <!-- AOP -->
15 <tx:advice id="merchantTransactionAdvice" transaction-manager="masterTransactionManager">
16 <tx:attributes>
17 <tx:method name="insert*" propagation="REQUIRED" />
18 <tx:method name="update*" propagation="REQUIRED" />
19 <tx:method name="delete*" propagation="REQUIRED" />
20 <tx:method name="lock*" propagation="REQUIRED" />
21 <tx:method name="unlock*" propagation="REQUIRED" />
22
23 <tx:method name="select*" propagation="SUPPORTS" />
24 <tx:method name="list*" propagation="SUPPORTS" />
25
26 <tx:method name="*" propagation="SUPPORTS" />
27 </tx:attributes>
28 </tx:advice>
29 <aop:config>
30 <aop:pointcut id="merchantTransactionPointcut" expression="execution(* com.eveus.cloudauth.dal.mybatis.MerchantDalImpl.*(..))" />
31 <aop:advisor pointcut-ref="merchantTransactionPointcut" advice-ref="merchantTransactionAdvice" />
32 </aop:config>
33 ...
34</beans>
如上配置中:transactionManager是在Java配置中定义的。其他的和原先的xml配置没有区别。
附录A. 参考资料
- Introduce @Configuration-based equivalent to aop:config XML element
- JavaConfig: Replacing aop:advisor and tx:advice
- Aspect Oriented Programming with Spring
- Spring事务管理(详解+实例)
- Spring Declarative Transaction Management Example
- Spring AOP : Replace XML with annotations for transaction management?
- 零xml配置Spring事务管理