### 多数据源的应用场景 ###
目前,业界流行的数据操作框架是 Mybatis,那 Druid 是什么呢?
Druid 是 Java 的数据库连接池组件。Druid 能够提供强大的监控和扩展功能。比如可以监控 SQL ,在监控业务可以查询慢查询 SQL 列表等。Druid 核心主要包括三部分:
(1) DruidDriver 代理 Driver,能够提供基于 Filter-Chain 模式的插件体系。
(2) DruidDataSource 高效可管理的数据库连接池
(3) SQLParser
当业务数据量达到了一定程度,DBA 需要合理配置数据库资源。即配置主库的机器高配置,把核心高频的数据放在主库上;把次要的数据放在从库,低配置。开源节流,就这个意思。把数据放在不同的数据库里,就需要通过不同的数据源进行操作数据。这里我们举个 springboot-mybatis-mutil-datasource 工程案例:
User 用户表在主库 master 上,地址表 city和work表分别在在从库 cluster 和cluster2上。下面实现获取根据用户名获取用户信息,地址信息,以及工作信息,使用RESTful API进行测试,从主库和从库中分别获取数据
### 数据库准备 ###
#### 主数据库(Oracle) ####
主数据库使用Oracle,创建CUser表,脚本如下:
1 2 3 4 5 6
| CREATE TABLE "C##ZHOUXU"."CUser" ( "id" NUMBER NOT NULL , "user_name" VARCHAR2(25 BYTE) NULL , "description" VARCHAR2(200 BYTE) NULL , PRIMARY KEY ("id") )
|
插入数据,脚本如下:
1
| insert into CUSER values(1,'zhouxu','个人博客: https://zhouxu2016.github.io/')
|
#### 从数据库(Mysql) ####
(1) 从数据库一
创建数据库,springbootdb_cluster,脚本如下:
1
| create database springbootdb_cluster;
|
然后在数据库springbootdb_cluster下创建表city,脚本如下:
1 2 3 4 5 6 7
| CREATE TABLE `city` ( `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '城市编号' , `province_id` int(10) UNSIGNED NOT NULL COMMENT '省份编号' , `city_name` varchar(25) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '城市名称' , `description` varchar(25) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '描述' , PRIMARY KEY (`id`) )
|
向city表插入数据,脚本如下:
1
| insert into city values(1,1,'上海市','上海徐汇区')
|
(2) 从数据库二
创建数据库,springbootdb_cluster2,脚本如下:
1
| create database springbootdb_cluster2;
|
然后在数据库springbootdb_cluster下创建表work,脚本如下:
1 2 3 4 5 6 7
| CREATE TABLE `work` ( `id` int(11) NOT NULL , `work_address` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL , `company` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL , `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL , PRIMARY KEY (`id`) )
|
向work表插入数据,脚本如下:
1
| insert into work values(1,'上海徐汇区','上海互联网科技有限公司','周旭')
|
### 项目结构介绍 ###
org.spring.springboot.config.ds - 配置层,这里是数据源的配置,包括 master 和 cluster 的数据源配置
org.spring.springboot.controller - Controller 层
org.spring.springboot.dao - 数据操作层 DAO,细分了 master 和 cluster 包下的 DAO 操作类
org.spring.springboot.domain - 实体类
org.spring.springboot.service - 业务逻辑层
Application - 应用启动类
application.properties - 应用配置文件,应用启动会自动读取配置
### Maven依赖pom.xml ###
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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| <?xml version="1.0" encoding="UTF-8"?> <project> <modelVersion>4.0.0</modelVersion> <groupId>springboot</groupId> <artifactId>springboot-mybatis-mutil-datasource</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>springboot-mybatis-mutil-datasource :: Spring Boot 实现 Mybatis 多数据源配置</name> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.1.RELEASE</version> </parent> <properties> <mybatis-spring-boot>1.2.0</mybatis-spring-boot> <mysql-connector>5.1.39</mysql-connector> <druid>1.0.18</druid> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis-spring-boot}</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql-connector}</version> </dependency> <dependency> <groupId>com.github.noraui</groupId> <artifactId>ojdbc7</artifactId> <version>12.1.0.2</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>${druid}</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> </project>
|
### application.properties 配置 ###
application.properties 配置三个数据源配置,主数据源master,使用Oracle,从数据源cluster,clustertwo使用Mysql
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| ## master 主数据源配置(oracle) master.datasource.url=jdbc:oracle:thin:@127.0.0.1:1521:ORCL master.datasource.username=C##zhouxu master.datasource.password=123456 master.datasource.driverClassName=oracle.jdbc.driver.OracleDriver ## cluster 从数据源配置(Mysql) cluster.datasource.url=jdbc:mysql: cluster.datasource.username=root cluster.datasource.password=root cluster.datasource.driverClassName=com.mysql.jdbc.Driver ## cluster 从数据源配置2(Mysql) clustertwo.datasource.url=jdbc:mysql: clustertwo.datasource.username=root clustertwo.datasource.password=root clustertwo.datasource.driverClassName=com.mysql.jdbc.Driver
|
### 数据源配置 ###
(1) MasterDataSourceConfig 配置
多数据源配置的时候注意,必须要有一个主数据源,即 MasterDataSourceConfig 配置
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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| package org.spring.springboot.config.ds; 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; @Configuration @MapperScan(basePackages = MasterDataSourceConfig.PACKAGE, sqlSessionFactoryRef = "masterSqlSessionFactory") public class MasterDataSourceConfig { static final String PACKAGE = "org.spring.springboot.dao.master"; static final String MAPPER_LOCATION = "classpath:mapper/master/*.xml"; @Value("${master.datasource.url}") private String url; @Value("${master.datasource.username}") private String user; @Value("${master.datasource.password}") private String password; @Value("${master.datasource.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); 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,为了和下面 cluster 数据源做到精确区分
@Value 获取全局配置文件 application.properties 的 kv 配置,并自动装配sqlSessionFactoryRef 表示定义了 key ,表示一个唯一 SqlSessionFactory 实例
(2) ClusterDataSourceConfig 配置
从数据源 ClusterDataSourceConfig 配置如下:
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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| package org.spring.springboot.config.ds; 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; @Configuration @MapperScan(basePackages = ClusterDataSourceConfig.PACKAGE, sqlSessionFactoryRef = "clusterSqlSessionFactory") public class ClusterDataSourceConfig { static final String PACKAGE = "org.spring.springboot.dao.cluster"; static final String MAPPER_LOCATION = "classpath:mapper/cluster/*.xml"; @Value("${cluscter.datasoure.url}") private String url; @Value("${cluster.datasource.username}") private String user; @Value("${cluster.datasource.password}") private String password; @Value("${cluster.datasource.driverClassName}") private String driverClass; @Bean(name = "clusterDataSource") public DataSource clusterDataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(driverClass); dataSource.setUrl(url); dataSource.setUsername(user); dataSource.setPassword(password); return dataSource; } @Bean(name = "clusterTransactionManager") public DataSourceTransactionManager clusterTransactionManager() { return new DataSourceTransactionManager(clusterDataSource()); } @Bean(name = "clusterSqlSessionFactory") public SqlSessionFactory clusterSqlSessionFactory(@Qualifier("clusterDataSource") DataSource clusterDataSource) throws Exception { final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(clusterDataSource); sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver() .getResources(ClusterDataSourceConfig.MAPPER_LOCATION)); return sessionFactory.getObject(); } }
|
(3) WorkDataSourceConfig配置
从数据源 WorkDataSourceConfig配置如下:
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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| package org.spring.springboot.config.ds; 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; @Configuration @MapperScan(basePackages = WorkDataSourceConfig.PACKAGE,sqlSessionFactoryRef = "clusterTwoSqlSessionFactory") public class WorkDataSourceConfig { static final String PACKAGE = "org.spring.springboot.dao.work"; static final String MAPPER_LOCATION = "classpath:mapper/work/*.xml"; @Value("${clustertwo.datasource.url}") private String url; @Value("${clustertwo.datasource.username}") private String user; @Value("${clustertwo.datasource.password}") private String password; @Value("${clustertwo.datasource.driverClassName}") private String driverClass; @Bean(name = "clustertwoDataSource") public DataSource clusterDataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(driverClass); dataSource.setUrl(url); dataSource.setUsername(user); dataSource.setPassword(password); return dataSource; } @Bean(name = "clusterTwoTransactionManager") public DataSourceTransactionManager clusterTransactionManager() { return new DataSourceTransactionManager(clusterDataSource()); } @Bean(name = "clusterTwoSqlSessionFactory") public SqlSessionFactory clusterSqlSessionFactory(@Qualifier("clustertwoDataSource") DataSource clusterDataSource) throws Exception { final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(clusterDataSource); sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver() .getResources(WorkDataSourceConfig.MAPPER_LOCATION)); return sessionFactory.getObject(); } }
|
上面数据源配置分别扫描 Mapper 接口,org.spring.springboot.dao.master.UserDao(对应 xml classpath:mapper/master/UserMapper.xml)
### 持久层 ###
(1) 用户Dao接口类UserDao
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package org.spring.springboot.dao.master; import org.apache.ibatis.annotations.Param; import org.spring.springboot.domain.User; public interface UserDao { User findByName(@Param("userName") String userName); }
|
UserDao对应Mapper xml文件UserMapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" > <mapper namespace="org.spring.springboot.dao.master.UserDao"> <resultMap id="BaseResultMap" type="org.spring.springboot.domain.User"> <result column="id" property="id" /> <result column="user_name" property="userName" /> <result column="description" property="description" /> </resultMap> <parameterMap id="User" type="org.spring.springboot.domain.User"/> <sql id="Base_Column_List"> "id", "user_name", "description" </sql> <select id="findByName" resultMap="BaseResultMap" parameterType="java.lang.String"> select <include refid="Base_Column_List" /> from cuser where "user_name" = #{userName} </select> </mapper>
|
(2) 城市Dao接口类CityDao
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package org.spring.springboot.dao.cluster; import org.apache.ibatis.annotations.Param; import org.spring.springboot.domain.City; public interface CityDao { City findByName(@Param("cityName") String cityName); }
|
CityDao对应Mapper xml文件CityMapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" > <mapper namespace="org.spring.springboot.dao.cluster.CityDao"> <resultMap id="BaseResultMap" type="org.spring.springboot.domain.City"> <result column="id" property="id" /> <result column="province_id" property="provinceId" /> <result column="city_name" property="cityName" /> <result column="description" property="description" /> </resultMap> <parameterMap id="City" type="org.spring.springboot.domain.City"/> <sql id="Base_Column_List"> id, province_id, city_name, description </sql> <select id="findByName" resultMap="BaseResultMap" parameterType="java.lang.String"> select <include refid="Base_Column_List" /> from city where city_name = #{cityName} </select> </mapper>
|
(3) 城市 WorkDao 接口类WorkDao
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package org.spring.springboot.dao.work; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.type.Alias; import org.spring.springboot.domain.Work; public interface WorkDao { Work findByName(@Param("name") String name); }
|
WorkDao对应Mapper xml文件WorkMapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" > <mapper namespace="org.spring.springboot.dao.work.WorkDao"> <resultMap id="BaseResultMap" type="org.spring.springboot.domain.Work"> <result column="id" property="id"/> <result column="work_address" property="workAddress"/> <result column="company" property="company"/> <result column="name" property="name"/> </resultMap> <parameterMap id="Work" type="org.spring.springboot.domain.Work"/> <sql id="Base_Column_List"> id, work_address, company,name </sql> <select id="findByName" resultMap="BaseResultMap" parameterType="java.lang.String"> select <include refid="Base_Column_List"/> from work where name = #{name} </select> </mapper>
|
### 业务层 ###
依赖注入了三个 Dao
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
| package org.spring.springboot.service.impl; import org.spring.springboot.dao.cluster.CityDao; import org.spring.springboot.dao.master.UserDao; import org.spring.springboot.dao.work.WorkDao; import org.spring.springboot.domain.City; import org.spring.springboot.domain.User; import org.spring.springboot.domain.Work; import org.spring.springboot.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Autowired private CityDao cityDao; @Autowired private WorkDao workDao; @Override public User findByName(String userName) { User user = userDao.findByName(userName); City city = cityDao.findByName("上海市"); Work work = workDao.findByName("周旭"); user.setCity(city); user.setWork(work); return user; } }
|
### Web层 ###
根据用户名获取用户信息,包括从库的地址信息
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
| package org.spring.springboot.controller; import org.spring.springboot.domain.User; import org.spring.springboot.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController public class UserRestController { @Autowired private UserService userService; @GetMapping(value = "/api/user/{userName}") public User findByName(@PathVariable(value = "userName") String name) { return userService.findByName(name); } }
|
运行项目,在浏览器中输入以下地址:
会返回下面json串:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| { "id": 1, "userName": "zhouxu", "description": "个人博客: https://zhouxu2016.github.io/", "city": { "id": 1, "provinceId": 1, "cityName": "上海市", "description": "上海徐汇区" }, "work": { "id": 1, "workAddress": "上海徐汇区", "company": "上海互联网科技有限公司", "name": "周旭" } }
|
源码地址:
Spring Boot整合Mybatis实现Druid多数据源### 总结 ###
多数据源适合的场景很多。不同的 DataSource ,不同的 SqlSessionFactory 和不同的 DAO 层,在业务逻辑层做整合
分享
新浪微博
QQ空间
QQ好友
豆瓣
Facebook
取消