springboot配置mybatis的多数据源以及事务的处理

mac2025-07-23  5

前言

一、大部分情况下我们的数据库可能不止一个,比如sqlserver或者mysql等等,或者不在同一个服务器中,不在同一个端口等等,但是我们的一个项目又需要获取所有的数据库信息做后台,所以就用到了mybatis多数据源的配置

一、整体逻辑

二、配置

2.1、在application.yml中配置数据源(多个数据库的连接地址),列子如下:

spring: datasource: dspublicdbuat: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://91.150.232.255:3306/public_db_uat?useUnicode=true&characterEncoding=utf-8&useSSL=false username: ioplj password: jhjhujhj dsyygj: driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver url: jdbc:sqlserver://59.160.284.185:1433;DatabaseName=YYGJ username: dty password: disdusad

2.2、配置对应的数据源映射bean

package cn.gxm.test.config; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import javax.sql.DataSource; /** * @author GXM * @date 2019/10/9 */ @Configuration public class DataSourceConfig { /** * 主数据库 YYGJ 的数据源 * @return */ @Primary @Bean(name = "dsyygjDataSourceProperties") @ConfigurationProperties(prefix = "spring.datasource.dsyygj") public DataSourceProperties dsyygjDataSourceProperties() { return new DataSourceProperties(); } /** * 主数据库 * @param dataSourceProperties * @return */ @Primary @Bean(name = "dsyygjDataSource") public DataSource dsyygjDataSource( @Qualifier("dsyygjDataSourceProperties") DataSourceProperties dataSourceProperties) { return dataSourceProperties.initializeDataSourceBuilder().build(); } /** * 数据库 publicdbuat 的数据源 * @return */ @Bean(name = "dspublicdbuatDataSourceProperties") @ConfigurationProperties(prefix = "spring.datasource.dspublicdbuat") public DataSourceProperties dspublicdbuatDataSourceProperties() { return new DataSourceProperties(); } /** * 数据库 publicdbuat * @param dataSourceProperties * @return */ @Bean(name = "dspublicdbuatDataSource") public DataSource dspublicdbuatDataSource( @Qualifier("dspublicdbuatDataSourceProperties") DataSourceProperties dataSourceProperties) { return dataSourceProperties.initializeDataSourceBuilder().build(); } }

2.3、配置每一个数据源对应的注解(代替mybatis原生的@mapper注解)

每一个不同的注解都表明这个mapper使用那个数据源 连接YYGJ数据是mapper使用的注解

/** * @author GXM * @date 2019/10/9 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface YYGJAnno { String value() default ""; }

连接PublicDbUat数据是mapper使用的注解

/** * @author GXM * @date 2019/10/9 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface PublicDbUatAnno { String value() default ""; }

2.4、为每一个数据源配置一整套 manager,factory,template

2.4.1、配置 YYGJ数据库的manager,factory,template

package cn.gxm.test.config; import cn.gxm.test.annotation.YYGJAnno; import org.apache.ibatis.io.VFS; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.boot.autoconfigure.SpringBootVFS; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import javax.sql.DataSource; @Configuration @MapperScan(basePackages = { "cn.gxm.test.mapper" }, annotationClass = YYGJAnno.class, sqlSessionTemplateRef = "dsyygjSqlSessionTemplate") public class MybatisDSYYGJConfig { /** * 在cn.gxm.test.mapper包下的使用YYGJAnno注解的 mapper接口类,才使用该配置数据源(以及一整套 manager,factory,template) * 对应的mapper.xml文件 (PathMatchingResourcePatternResolver().getResources()) */ /** * 主数据源 YYGJ数据源 * @param dataSource * @return * @throws Exception */ @Primary @Bean("dsyygjSqlSessionFactory") public SqlSessionFactory dsyygjSqlSessionFactory(@Qualifier("dsyygjDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean(); sqlSessionFactory.setDataSource(dataSource); sqlSessionFactory.setMapperLocations( new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/yygj/*Mapper.xml")); sqlSessionFactory.setConfigLocation( new PathMatchingResourcePatternResolver().getResource("classpath:mapper/mybatis-config.xml")); sqlSessionFactory.setVfs(SpringBootVFS.class); VFS.addImplClass(SpringBootVFS.class); // 添加插件 sqlSessionFactory.setPlugins(new Interceptor[] { MybatisCommon.getPageHelper() }); // 扫描别名 // sqlSessionFactory.setTypeAliasesPackage(String.join(",", MybatisCommon.getPackages(typeAliasesPackage))); return sqlSessionFactory.getObject(); } @Primary @Bean(name = "dsyygjTransactionManager") public DataSourceTransactionManager dsyygjTransactionManager(@Qualifier("dsyygjDataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Primary @Bean(name = "dsyygjSqlSessionTemplate") public SqlSessionTemplate dsyygjSqlSessionTemplate( @Qualifier("dsyygjSqlSessionFactory") SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); } }

2.4.2、配置 publicdbuat数据库的manager,factory,template

package cn.gxm.test.config; import cn.gxm.test.annotation.PublicDbUatAnno; import org.apache.ibatis.io.VFS; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.boot.autoconfigure.SpringBootVFS; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import javax.sql.DataSource; @Configuration @MapperScan(basePackages = { "cn.gxm.test.mapper" }, annotationClass = PublicDbUatAnno.class, sqlSessionTemplateRef = "dspublicdbuatSqlSessionTemplate") public class MybatisPublicDBUatConfig { /** * publicdbuat * @param dataSource * @return * @throws Exception */ @Bean("dspublicdbuatSqlSessionFactory") public SqlSessionFactory dspublicdbuatSqlSessionFactory(@Qualifier("dspublicdbuatDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean(); sqlSessionFactory.setDataSource(dataSource); sqlSessionFactory.setMapperLocations( new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/public_db_uat/*Mapper.xml")); sqlSessionFactory.setConfigLocation( new PathMatchingResourcePatternResolver().getResource("classpath:mapper/mybatis-config.xml")); sqlSessionFactory.setVfs(SpringBootVFS.class); VFS.addImplClass(SpringBootVFS.class); // 添加插件 sqlSessionFactory.setPlugins(new Interceptor[] { MybatisCommon.getPageHelper() }); return sqlSessionFactory.getObject(); } @Bean(name = "dspublicdbuatTransactionManager") public DataSourceTransactionManager dspublicdbuatTransactionManager(@Qualifier("dspublicdbuatDataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Bean(name = "dspublicdbuatSqlSessionTemplate") public SqlSessionTemplate dspublicdbuatSqlSessionTemplate( @Qualifier("dspublicdbuatSqlSessionFactory") SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); } }

2.5、为每一个数据源配置对应的放置mapper接口的包名

需要与MybatisDSYYGJConfig中的basePackages配置一致 YYGJ数据库的mapper(注意各个接口上的注解不再是@mapper而是对应的数据源的自定义注解)

/** * @author GXM www.guokangjie.cn * @date 2019/8/29 */ @Component //@Mapper @YYGJAnno public interface BaseConfigMapper { BaseConfig selectOne(); int createTable(String tableName); BaseConfig selectByTempTable(String tableName); }

PublicDBUat数据库的mapper

/** * @author GXM www.guokangjie.cn * @date 2019/8/29 */ @Component //@Mapper @PublicDbUatAnno public interface DBConfigMapper { DbConfig selectOne(); }

2.6、为每一个数据源配置对应的防止mapperms.xml文件的包名

对应的xml文件的位置需要与之前java代码配置中写明的一致

new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/yygj/*Mapper.xml")); new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/public_db_uat/*Mapper.xml"));

DBConfigMapper.xml

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="cn.gxm.test.mapper.DBConfigMapper"> <select id="selectOne" resultType="DbConfig"> select * from db_config where id = 1 </select> </mapper>

BaseConfigMapper.xml

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="cn.gxm.test.mapper.BaseConfigMapper"> <select id="selectOne" resultType="BaseConfig"> select * from BaseConfig where ID = 1 </select> <!-- 创建零时表的时候 ## 表示全局临时表 #代表零时表 --> <update id="createTable" parameterType="string"> select * into ##TB from BaseConfig; </update> <select id="selectByTempTable" parameterType="string" resultType="BaseConfig"> select * from ##TB where id = 1 </select> </mapper>

全局的mybatis的config配置如下(mybatis-config.xml)

<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <setting name="cacheEnabled" value="true" /> <setting name="lazyLoadingEnabled" value="true" /> <setting name="multipleResultSetsEnabled" value="true" /> <setting name="useColumnLabel" value="true" /> <setting name="useGeneratedKeys" value="false" /> <setting name="autoMappingBehavior" value="PARTIAL" /> <setting name="defaultExecutorType" value="SIMPLE" /> <setting name="defaultStatementTimeout" value="250" /> <setting name="safeRowBoundsEnabled" value="false" /> <setting name="mapUnderscoreToCamelCase" value="false" /> <setting name="localCacheScope" value="SESSION" /> <setting name="jdbcTypeForNull" value="OTHER" /> <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString" /> <setting name="logImpl" value="SLF4J"/> </settings> <typeAliases> <!-- spring中的那个扫描器 --> <package name="cn.gxm.test.model" /> </typeAliases> </configuration>

最后整体的目录如下

三、多事务配置

本身springboot如果加入了数据库的连接查询,那么springboot就会默认会开启和当前数据库一致的事务处理,以前直接在类上放上注解就可以了,如下

@Transactional() @LogAnno(module = "公有模块-基础配置", operate = "添加") @PostMapping("/add") public CommonResult add(@RequestBody BaseConfig model) {

但是现在我们有多个数据源,如果还是这样写,它就会提示,不知道使用哪一个数据源的事务处理器,如下

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.transaction.PlatformTransactionManager' available: more than one 'primary' bean found among candidates: [dspublicdbuatTransactionManager, dsyygjTransactionManager]

错误信息告诉我们我们有很多数据源以及事务,所以我们在使用的时候需要指定哪一个事务管理器,如下

@Transactional(value = "dsyygjTransactionManager") @LogAnno(module = "公有模块-基础配置", operate = "添加") @PostMapping("/add") public CommonResult add(@RequestBody BaseConfig model) {

那么就ok了

3.1、解释说明: 数据库事务隔离级别-- 脏读、幻读、不可重复读(清晰解释) 3.2、详细配置事务(设置隔离级别等等) 简单使用,直接在注解上表明要是的隔离级别以及事务传播类型

//方法上注解属性会覆盖类注解上的相同属性   @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)   public void updateFoo(Foo foo) {

具体可以参考: SpringBoot设置事务隔离等级 3.3、 分布式事务 参考文章 【分布式事务】使用atomikos+jta解决分布式事务问题

最新回复(0)