背景:
项目中需要从两个不同的数据库查询数据,之前实现方法是:springboot配置连接一个数据源,另一个使用jdbc代码连接。
为了改进,现在使用SpringBoot配置连接两个数据源。
实现效果:
一个SpringBoot项目,同时连接两个数据库。
注意:连接什么数据库,要引入对应数据库的包
实现
实现多数据源均需要使用到mybatis plus。这里也引入了druid连接池。
双数据源中需要将不同数据源的mapper和对应xml文件放在不同的包中。
先准备两个数据源。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| create schema mybatisdubboone; create table user ( id int auto_increment primary key, username varchar(50) default '' not null, mobile varchar(15) default '' null, create_time datetime default CURRENT_TIMESTAMP not null );
create schema mybatisdubbotwo; create table mybatisdubbotwo.order_info ( id int auto_increment primary key, order_id varchar(20) default '' not null, user_id int default 0 not null, create_time datetime default CURRENT_TIMESTAMP not null );
|
静态双数据源
静态数据源需要对不同的数据源单独配置,同时需要关闭掉springboot和druid的数据源自动配置。
1 2 3 4 5 6 7 8 9 10 11 12 13
| spring: application: name: staticmybatisdubbomysql datasourceone: jdbc-url: jdbc:mysql://localhost:3306/mybatisdubboone?serverTimezone=GMT driver-class-name: com.mysql.cj.jdbc.Driver username: root password: 1234 datasourcetwo: jdbc-url: jdbc:mysql://localhost:3306/mybatisdubbotwo?serverTimezone=GMT driver-class-name: com.mysql.cj.jdbc.Driver username: root password: 1234
|
数据源1配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| @Configuration @MapperScan(basePackages = "com.lin.mybatisdubbomysql.dal.mapper.databaseone", sqlSessionTemplateRef = "SqlSessionTemplateOne") @EnableConfigurationProperties(DatabaseOneProperties.class) public class DatabaseOneConfig {
@Resource DatabaseOneProperties databaseOneProperties;
@Bean("dataSourceOne") @Primary public DataSource dataSource() { DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setUrl(databaseOneProperties.getJdbcUrl()); druidDataSource.setUsername(databaseOneProperties.getUsername()); druidDataSource.setPassword(databaseOneProperties.getPassword()); druidDataSource.setDriverClassName(databaseOneProperties.getDriverClassName()); return druidDataSource; }
@Bean("sqlSessionFactoryOne") @Primary public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSourceOne") DataSource dataSource) throws Exception { MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); sqlSessionFactoryBean.setMapperLocations( new PathMatchingResourcePatternResolver().getResources("classpath:mapper/databaseone/*.xml")); return sqlSessionFactoryBean.getObject(); }
@Bean("SqlSessionTemplateOne") @Primary public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactoryOne")SqlSessionFactory sqlSessionFactory){ return new SqlSessionTemplate(sqlSessionFactory); }
@Bean("Txone") @Primary public DataSourceTransactionManager dataSourceTransactionManager(@Qualifier("dataSourceOne") DataSource dataSource){ return new DataSourceTransactionManager(dataSource); } }
|
数据源2配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| @Configuration @MapperScan(basePackages = "com.lin.mybatisdubbomysql.dal.mapper.databasetwo", sqlSessionTemplateRef = "SqlSessionTemplateTwo") @EnableConfigurationProperties(DatabaseTwoProperties.class) public class DatabaseTwoConfig {
@Resource DatabaseTwoProperties databaseTwoProperties;
@Bean("dataSourceTwo") public DataSource dataSource() { DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setUrl(databaseTwoProperties.getJdbcUrl()); druidDataSource.setUsername(databaseTwoProperties.getUsername()); druidDataSource.setPassword(databaseTwoProperties.getPassword()); druidDataSource.setDriverClassName(databaseTwoProperties.getDriverClassName()); return druidDataSource; }
@Bean("sqlSessionFactoryTwo") public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSourceTwo") DataSource dataSource) throws Exception { MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); sqlSessionFactoryBean.setMapperLocations( new PathMatchingResourcePatternResolver().getResources("classpath:mapper/databasetwo/*.xml")); return sqlSessionFactoryBean.getObject(); }
@Bean("SqlSessionTemplateTwo") public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactoryTwo") SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); }
@Bean("Txtwo") public DataSourceTransactionManager dataSourceTransactionManager(@Qualifier("dataSourceTwo") DataSource dataSource){ return new DataSourceTransactionManager(dataSource); }
}
|
开启druid监控:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| @Configuration public class DruidConfig {
@Bean public ServletRegistrationBean<StatViewServlet> druidServlet() { ServletRegistrationBean<StatViewServlet> servletRegistrationBean = new ServletRegistrationBean<>( new StatViewServlet(), "/druid/*"); servletRegistrationBean.addInitParameter("allow", "127.0.0.1"); servletRegistrationBean.addInitParameter("deny", "192.168.1.110"); servletRegistrationBean.addInitParameter("loginUsername", "admin"); servletRegistrationBean.addInitParameter("loginPassword", "admin"); servletRegistrationBean.addInitParameter("resetEnable", "false"); return servletRegistrationBean; }
@Bean public FilterRegistrationBean<WebStatFilter> filterRegistrationBean() { FilterRegistrationBean<WebStatFilter> filterRegistrationBean = new FilterRegistrationBean<>(); filterRegistrationBean.setFilter(new WebStatFilter()); filterRegistrationBean.addUrlPatterns("/*"); filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.css,/druid/*"); return filterRegistrationBean; }
}
|
启动类配置:
1 2 3 4 5 6 7 8 9 10
| @EnableTransactionManagement @ComponentScan(basePackages = "com.lin.mybatisdubbomysql.*") @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, DruidDataSourceAutoConfigure.class}) public class StaticMybatisDubboMysqlApplication {
public static void main(String[] args) { SpringApplication.run(StaticMybatisDubboMysqlApplication.class, args); }
}
|
动态双数据源
动态数据源,通过MybatisPlus提供的AbstractRoutingDataSource的路由能力,配合AOP在操作数据库前进行数据源切换。同样也需要关闭SpringBoot和druid的自动数据源配置。
数据源配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| @Configuration @MapperScan(basePackages = "com.lin.mybatisdubbomysql.dal.mapper",sqlSessionFactoryRef = "sqlSessionFactory") public class DBConfig {
@Bean @ConfigurationProperties(prefix = "spring.datasourceone") public DataSource dataSourceOne() { return new DruidDataSource(); }
@Bean @ConfigurationProperties(prefix = "spring.datasourcetwo") public DataSource dataSourceTwo() { return new DruidDataSource(); }
@Bean @Primary public DynamicDataSource dataSource(DataSource dataSourceOne,DataSource dataSourceTwo){ Map<Object, Object> targetDataSources = new HashMap<>(8); targetDataSources.put(DateBaseEnum.DataBaseOne.getValue(),dataSourceOne); targetDataSources.put(DateBaseEnum.DataBaseTwo.getValue(), dataSourceTwo); return new DynamicDataSource(dataSourceOne, targetDataSources); }
@Bean public SqlSessionFactory sqlSessionFactory(DynamicDataSource dynamicDataSource) throws Exception { MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dynamicDataSource); return sqlSessionFactoryBean.getObject(); }
}
|
动态数据路由配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) { super.setDefaultTargetDataSource(defaultTargetDataSource); super.setTargetDataSources(targetDataSources); super.afterPropertiesSet(); }
@Override protected Object determineCurrentLookupKey() { return getDataSource(); }
public static void setDataSource(String dataSource) { CONTEXT_HOLDER.set(dataSource); }
public static String getDataSource() { return CONTEXT_HOLDER.get(); }
public static void clearDataSource() { CONTEXT_HOLDER.remove(); } }
|
数据类型枚举:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @AllArgsConstructor @Getter public enum DateBaseEnum {
DataBaseOne("one", "one"),
DataBaseTwo("two", "two");
private final String name; private final String value; }
|
通过AOP在执行前切换数据源:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Component @Aspect public class DatasourceAspect {
@Pointcut("execution(public * com.lin.mybatisdubbomysql.dal.mapper.databasetwo..*.*(..))") public void dataSourcePointCut() {
}
@Around("dataSourcePointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { DynamicDataSource.setDataSource(DateBaseEnum.DataBaseTwo.getValue()); try { return point.proceed(); } finally { DynamicDataSource.clearDataSource(); } } }
|
mybatisplus官方动态双数据源
官方的动态数据源配置最为简单。
yml文件的配置与其他的不同,使用mybatisplus的配置,yml文件配置后,使用自动注入的数据源即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| spring: application: name: plusmybatisdubbomysql datasource: dynamic: primary: master strict: false datasource: master: url: jdbc:mysql://localhost:3306/mybatisdubboone?serverTimezone=GMT username: root password: 1234 driver-class-name: com.mysql.cj.jdbc.Driver slave: url: jdbc:mysql://localhost:3306/mybatisdubbotwo?serverTimezone=GMT username: root password: 1234 driver-class-name: com.mysql.cj.jdbc.Driver
|
官方的切换方式,采用@DS("")
注解,指定类、方法、mapper(官方不推荐)进行数据源的切换。这里注解中的值要和yml文件配置的一样。
1 2 3 4 5
| @Service @DS("master") public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
|