一、Springboot配置双数据源
在Spring Boot应用程序中配置多个数据源是很常见的需求。由于Spring Boot的自动配置机制,使得我们可以轻松优雅的实现多数据源配置和切换。 若要配置双数据源,则需要使用Spring Boot的自动装配机制执行以下操作:
- 确保我们的应用程序中依赖了Spring Boot的MyBatis Starter,这将为MyBatis集成提供必要的依赖关系和自动配置。在pom.xml文件中添加以下依赖:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
- 在application.properties中配置双数据源的各项信息:
# primary datasource
spring.datasource.primary.url=jdbc:mysql://localhost:3306/primary_db
spring.datasource.primary.username=dbuser
spring.datasource.primary.password=dbpassword
spring.datasource.primary.driver-class-name=com.mysql.jdbc.Driver
# secondary datasource
spring.datasource.secondary.url=jdbc:mysql://localhost:3306/secondary_db
spring.datasource.secondary.username=dbuser
spring.datasource.secondary.password=dbpassword
spring.datasource.secondary.driver-class-name=com.mysql.jdbc.Driver
- 创建主数据源和次数据源的数据源类:
@Configuration
@MapperScan(basePackages = "com.example.demo.mapper.primary", sqlSessionTemplateRef = "primarySqlSessionTemplate")
public class PrimaryDataSourceConfig {
@Autowired
private DataSourceProperties dataSourceProperties;
@Bean(name = "primaryDataSource")
@Primary
public DataSource primaryDataSource() {
return dataSourceProperties.initializeDataSourceBuilder().build();
}
@Bean(name = "primarySqlSessionFactory")
@Primary
public SqlSessionFactory primarySqlSessionFactory(@Qualifier("primaryDataSource") DataSource primaryDataSource) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(primaryDataSource);
return sessionFactory.getObject();
}
@Bean(name = "primaryTransactionManager")
@Primary
public DataSourceTransactionManager primaryTransactionManager(@Qualifier("primaryDataSource") DataSource primaryDataSource) {
return new DataSourceTransactionManager(primaryDataSource);
}
@Bean(name = "primarySqlSessionTemplate")
@Primary
public SqlSessionTemplate primarySqlSessionTemplate(@Qualifier("primarySqlSessionFactory") SqlSessionFactory primarySqlSessionFactory) {
return new SqlSessionTemplate(primarySqlSessionFactory);
}
}
@Configuration
@MapperScan(basePackages = "com.example.demo.mapper.secondary", sqlSessionTemplateRef = "secondarySqlSessionTemplate")
public class SecondaryDataSourceConfig {
@Autowired
private DataSourceProperties dataSourceProperties;
@Bean(name = "secondaryDataSource")
public DataSource secondaryDataSource() {
return dataSourceProperties.initializeDataSourceBuilder().build();
}
@Bean(name = "secondarySqlSessionFactory")
public SqlSessionFactory secondarySqlSessionFactory(@Qualifier("secondaryDataSource") DataSource secondaryDataSource) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(secondaryDataSource);
return sessionFactory.getObject();
}
@Bean(name = "secondaryTransactionManager")
public DataSourceTransactionManager secondaryTransactionManager(@Qualifier("secondaryDataSource") DataSource secondaryDataSource) {
return new DataSourceTransactionManager(secondaryDataSource);
}
@Bean(name = "secondarySqlSessionTemplate")
public SqlSessionTemplate secondarySqlSessionTemplate(@Qualifier("secondarySqlSessionFactory") SqlSessionFactory secondarySqlSessionFactory) {
return new SqlSessionTemplate(secondarySqlSessionFactory);
}
}
- 创建controller并在其中注入主数据源的mapper和次数据源的mapper
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private PrimaryUserMapper primaryUserMapper;
@Autowired
private SecondaryUserMapper secondaryUserMapper;
@GetMapping("/primary/list")
public List<PrimaryUser> getPrimaryUsers() {
return primaryUserMapper.getAll();
}
@GetMapping("/secondary/list")
public List<SecondaryUser> getSecondaryUsers() {
return secondaryUserMapper.getAll();
}
}
- 启动应用并测试多数据源的配置是否正确。
二、Mybatis多数据源配置SpringBoot
当使用Mybatis作为数据持久层框架时,需要按以下步骤在SpringBoot中配置多数据源:
- 在pom.xml文件中添加以下依赖关系:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
- 在application.properties或application.yml文件中配置数据源信息:
# primary datasource
spring.datasource.primary.url=jdbc:mysql://localhost:3306/primary_db
spring.datasource.primary.username=dbuser
spring.datasource.primary.password=dbpassword
spring.datasource.primary.driver-class-name=com.mysql.jdbc.Driver
# secondary datasource
spring.datasource.secondary.url=jdbc:mysql://localhost:3306/secondary_db
spring.datasource.secondary.username=dbuser
spring.datasource.secondary.password=dbpassword
spring.datasource.secondary.driver-class-name=com.mysql.jdbc.Driver
- 创建数据源类、SqlSessionFactory、SqlSessionTemplate和TransactionManager:
@Configuration
@MapperScan(basePackages = "com.example.demo.mapper.primary", sqlSessionTemplateRef = "primarySqlSessionTemplate")
public class PrimaryDataSourceConfig {
@Autowired
private DataSource primaryDataSource;
@Bean(name = "primarySqlSessionFactory")
@Primary
public SqlSessionFactory primarySqlSessionFactory() throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(primaryDataSource);
return sessionFactory.getObject();
}
@Bean(name = "primaryTransactionManager")
@Primary
public DataSourceTransactionManager primaryTransactionManager() {
return new DataSourceTransactionManager(primaryDataSource);
}
@Bean(name = "primarySqlSessionTemplate")
@Primary
public SqlSessionTemplate primarySqlSessionTemplate(@Qualifier("primarySqlSessionFactory") SqlSessionFactory primarySqlSessionFactory) {
return new SqlSessionTemplate(primarySqlSessionFactory);
}
}
@Configuration
@MapperScan(basePackages = "com.example.demo.mapper.secondary", sqlSessionTemplateRef = "secondarySqlSessionTemplate")
public class SecondaryDataSourceConfig {
@Autowired
private DataSource secondaryDataSource;
@Bean(name = "secondarySqlSessionFactory")
public SqlSessionFactory secondarySqlSessionFactory() throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(secondaryDataSource);
return sessionFactory.getObject();
}
@Bean(name = "secondaryTransactionManager")
public DataSourceTransactionManager secondaryTransactionManager() {
return new DataSourceTransactionManager(secondaryDataSource);
}
@Bean(name = "secondarySqlSessionTemplate")
public SqlSessionTemplate secondarySqlSessionTemplate(@Qualifier("secondarySqlSessionFactory") SqlSessionFactory secondarySqlSessionFactory) {
return new SqlSessionTemplate(secondarySqlSessionFactory);
}
}
- 创建主数据源mapper和次数据源mapper,并在其中分别使用
@Qualifier
注解注入对应的数据源的SqlSessionTemplate。 - 对于需要使用次数据源的Service或Dao类中,使用
@Qualifier
注解指定对应的SqlSessionTemplate。 - 启动应用并测试多数据源的配置是否正确。
三、Springboot动态配置数据源
无论是配置双数据源还是多数据源,都需要在application.properties或application.yml文件中预先配置好数据源信息。然而,在生产环境中,可能需要根据用户的访问请求,动态选择不同的数据源。因此,需要在运行时动态配置数据源,具体步骤如下:
- 在pom.xml文件中添加以下依赖关系:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
- 配置与数据源相关的bean:
@Configuration
public class DynamicDataSourceConfig {
@Autowired
private DataSourceProperties dataSourceProperties;
private final Map<String, DataSource> dataSources = new ConcurrentHashMap<>();
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(dataSourceProperties.getDriverClassName());
dataSource.setUrl(dataSourceProperties.getUrl());
dataSource.setUsername(dataSourceProperties.getUsername());
dataSource.setPassword(dataSourceProperties.getPassword());
return new DynamicRoutingDataSource(dataSource);
}
@Bean
public DataSourceTransactionManager dataSourceTransactionManager() {
return new DataSourceTransactionManager(dataSource());
}
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
return sessionFactory.getObject();
}
@Bean
public SqlSessionTemplate sqlSessionTemplate() throws Exception {
return new SqlSessionTemplate(sqlSessionFactory());
}
}
- 创建动态数据源类
DynamicRoutingDataSource
和动态数据源上下文ThreadLocal
:
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
public DynamicRoutingDataSource(DataSource defaultTargetDataSource) {
super.setDefaultTargetDataSource(defaultTargetDataSource);
}
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceKey();
}
}
public class DynamicDataSourceContextHolder {
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
public static void setDataSourceKey(String key) {
CONTEXT_HOLDER.set(key);
}
public static String getDataSourceKey() {
return CONTEXT_HOLDER.get();
}
public static void clearDataSourceKey() {
CONTEXT_HOLDER.remove();
}
}
- 创建
DynamicDataSourceAspect
类,在函数执行前获取数据源的key并设置到DynamicDataSourceContextHolder
中,函数执行完毕后清除DynamicDataSourceContextHolder
。
@Aspect
@Component
public class DynamicDataSourceAspect {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Before("execution(* com.example.demo.service.*.*(..))")
public void before(JoinPoint point) {
String dataSourceKey = point.getSignature().getName().contains("primary") ? "primary" : "secondary";
DynamicDataSourceContextHolder.setDataSourceKey(dataSourceKey);
logger.info(String.format("设置数据源为 %s", dataSourceKey));
}
@After("execution(* com.example.demo.service.*.*(..))")
public void after(JoinPoint point) {
DynamicDataSourceContextHolder.clearDataSourceKey();
logger.info("清除数据源");
}
}
- 在需要多数据源的Service实现类中,添加以下注解,这将激活
DynamicDataSourceAspect
,从而动态设置数据源:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private PrimaryUserMapper primaryUserMapper;
@Autowired
private SecondaryUserMapper secondaryUserMapper;
@Primary
@DataSource(key = "primary")
public List<PrimaryUser> getAllPrimaryUsers() {
return primaryUserMapper.getAll();
}
@DataSource(key = "secondary")
public List<SecondaryUser> getAllSecondaryUsers() {
return secondaryUserMapper.getAll();
}
// ...
}
四、Springboot配置多数据源Druid
如果需要使用连接池,可以选择使用阿里巴巴的Druid。具体步骤如下:
- 在pom.xml文件中添加以下依赖关系:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.5</version>
</dependency>
- 在application.properties或application.yml文件中配置DruidDataSource的各项信息:
# primary datasource
spring.datasource.primary.url=jdbc:mysql://localhost:3306/primary_db?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
spring.datasource.primary.username=dbuser
spring.datasource.primary.password=dbpassword
spring.datasource.primary.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.primary.type=com.alibaba.druid.pool.DruidDataSource
# druid specific config
spring.datasource.primary.initial-size=0
spring.datasource.primary.min-idle=0
spring.datasource.primary.max-active=20
spring.datasource.primary.max-wait=-1
spring.datasource.primary.time-between-eviction-runs-millis=60000
spring.datasource.primary.min-evictable-idle-time-millis=300000
spring.datasource.primary.validation-query=SELECT 1 FROM DUAL
spring.datasource.primary.test-while-idle=true
spring.datasource.primary.test-on-borrow=false
spring.datasource.primary.test-on-return=false
spring.datasource.primary.pool-prepared-statements=true
spring.datasource.primary.max-pool-prepared-statement-per-connection-size=20
spring.datasource.primary.filters=stat,wall,log4j
spring.datasource.primary.connection-properties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# secondary datasource
spring.datasource.secondary.url=jdbc:mysql://localhost:3306/secondary_db?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
spring.datasource.secondary.username=dbuser
spring.datasource.secondary.password=dbpassword
spring.datasource.secondary.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.secondary.type=com.alibaba.druid.pool.DruidDataSource
# druid specific config
spring.datasource.secondary.initial-size=0
spring.datasource.secondary.min-idle=0
spring.datasource.secondary.max-active=20
spring.datasource.secondary.max-wait=-1
spring.datasource.secondary.time-between-eviction-runs-millis=600