日常开发中,我们可能需要连接多个数据源,例如数据库进行了主从配置,写操作走主库,读操作走从库。本来结合Spring Boot + MyBatis + Druid 来演示如何配置多个数据源。
1. pom.xml
org.springframework.boot
spring-boot-starter-parent
1.5.6.RELEASE
4.0.0
spring-boot-mybatis3-multi-datasource
jar
spring-boot-mybatis3-multi-datasource
http://maven.apache.org
1.3.0
1.0.25
5.1.39
UTF-8
org.springframework.boot
spring-boot-starter-web
org.mybatis.spring.boot
mybatis-spring-boot-starter
${mybatis.spring.boot.version}
mysql
mysql-connector-java
${mysql.connector.version}
com.alibaba
druid
${druid.version}
spring-boot-mybatis3-multi-datasource
org.springframework.boot
spring-boot-maven-plugin
exec
2. application.properties
mybatis.config-locations=classpath:mybatis-config.xml
spring.datasource.master.driverClassName=com.mysql.jdbc.Driver
spring.datasource.master.url=jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8
spring.datasource.master.username=root
spring.datasource.master.password=root
spring.datasource.slave.driverClassName=com.mysql.jdbc.Driver
spring.datasource.slave.url=jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8
spring.datasource.slave.username=root
spring.datasource.slave.password=root
3. 数据源配置
多数据源配置的时候注意,必须要有一个主数据源(使用 @Primary 标识),本例中即 MasterDataSourceConfig 配置:
package com.mindflow.springboot.mybatis.config.datasource;
import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
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;
/**
* @author Ricky Fung
*/
@Configuration
@MapperScan(basePackages = MasterDataSourceConfig.PACKAGE, sqlSessionFactoryRef = "masterSqlSessionFactory")
public class MasterDataSourceConfig {
static final String PACKAGE = "com.mindflow.springboot.mybatis.mapper.master";
static final String MAPPER_LOCATION = "classpath:mapper/master/*.xml";
@Value("${spring.datasource.master.url}")
private String url;
@Value("${spring.datasource.master.username}")
private String user;
@Value("${spring.datasource.master.password}")
private String password;
@Value("${spring.datasource.master.driverClassName}")
private String driverClass;
@Bean(name = "masterDataSource")
@Primary
public DataSource masterDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driverClass);
dataSource.setUrl(url);
dataSource.setUsername(user);
dataSource.setPassword(password);
dataSource.setInitialSize(5);
dataSource.setMaxActive(50);
dataSource.setMinIdle(0);
return dataSource;
}
@Bean(name = "masterTransactionManager")
@Primary
public DataSourceTransactionManager masterTransactionManager() {
return new DataSourceTransactionManager(masterDataSource());
}
@Bean(name = "masterSqlSessionFactory")
@Primary
public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource masterDataSource)
throws Exception {
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(masterDataSource);
sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources(MasterDataSourceConfig.MAPPER_LOCATION));
return sessionFactory.getObject();
}
}
@Primary 标志这个 Bean 如果在多个同类 Bean 候选时,该 Bean 优先被考虑。「多数据源配置的时候注意,必须要有一个主数据源,用 @Primary 标志该 Bean」;
@MapperScan 扫描 Mapper 接口并容器管理,包路径精确到 master,为了和下面 slave数据源做到精确区分;
sqlSessionFactoryRef 表示定义了 key ,表示一个唯一 SqlSessionFactory 实例。
同理,SlaveDataSourceConfig 如下:
package com.mindflow.springboot.mybatis.config.datasource;
import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
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.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
/**
* @author Ricky Fung
*/
@Configuration
@MapperScan(basePackages = SlaveDataSourceConfig.PACKAGE, sqlSessionFactoryRef = "slaveSqlSessionFactory")
public class SlaveDataSourceConfig {
static final String PACKAGE = "com.mindflow.springboot.mybatis.mapper.slave";
static final String MAPPER_LOCATION = "classpath:mapper/slave/*.xml";
@Value("${spring.datasource.slave.url}")
private String url;
@Value("${spring.datasource.slave.username}")
private String user;
@Value("${spring.datasource.slave.password}")
private String password;
@Value("${spring.datasource.slave.driverClassName}")
private String driverClass;
@Bean(name = "slaveDataSource")
public DataSource clusterDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driverClass);
dataSource.setUrl(url);
dataSource.setUsername(user);
dataSource.setPassword(password);
dataSource.setInitialSize(5);
dataSource.setMaxActive(50);
return dataSource;
}
@Bean(name = "slaveTransactionManager")
public DataSourceTransactionManager clusterTransactionManager() {
return new DataSourceTransactionManager(clusterDataSource());
}
@Bean(name = "slaveSqlSessionFactory")
public SqlSessionFactory clusterSqlSessionFactory(@Qualifier("slaveDataSource") DataSource clusterDataSource)
throws Exception {
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(clusterDataSource);
sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources(SlaveDataSourceConfig.MAPPER_LOCATION));
return sessionFactory.getObject();
}
}
4. service层
service照常注入了两个 DAO,如同以前一样正常工作。不用关心和指定到具体说明数据源。
UserService :
package com.mindflow.springboot.mybatis.service.impl;
import com.mindflow.springboot.mybatis.domain.UserDO;
import com.mindflow.springboot.mybatis.domain.UserDOExample;
import com.mindflow.springboot.mybatis.mapper.master.UserDOMapper;
import com.mindflow.springboot.mybatis.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @author Ricky Fung
*/
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDOMapper userDOMapper;
@Override
public UserDO getUserByName(String username) {
UserDOExample example = new UserDOExample();
List list = userDOMapper.selectByExample(example);
if(list==null || list.isEmpty()) {
return null;
}
return list.get(0);
}
@Override
public int insert(UserDO userDO) {
return userDOMapper.insertSelective(userDO);
}
}
OrderService :
package com.mindflow.springboot.mybatis.service.impl;
import com.mindflow.springboot.mybatis.domain.OrderDO;
import com.mindflow.springboot.mybatis.domain.OrderDOExample;
import com.mindflow.springboot.mybatis.mapper.slave.OrderDOMapper;
import com.mindflow.springboot.mybatis.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @author Ricky Fung
*/
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderDOMapper orderDOMapper;
@Override
public List getOrders(Long userId) {
OrderDOExample example = new OrderDOExample();
example.createCriteria().andUserIdEqualTo(userId);
return orderDOMapper.selectByExample(example);
}
@Override
public int insert(OrderDO orderDO) {
return orderDOMapper.insert(orderDO);
}
}
源码
spring-boot-tutorials