最近在学习SpringBoot的一些知识,主要是参考了纯洁的微笑的一些博客作为练手,想着他已经把入门的教程写的很详细了,如果再简单的把他的教程拷贝到自己的简书,这是是赤裸裸的剽窃,很不地道,作为一个技术人员,应该有一定的技术底线。所以,就把纯洁微笑的博客链接放在参考链接中的第一条,( ̄▽ ̄)"
目的
多数据源是指在同一个项目中写入/读取相同类型数据源的不同库的情况,比如在项目中需要访问mysql两个不同的数据库,常见于主从模式、或者数据库的分库的场景。
多数据源不包含不同类型数据源的情况,例如:一个项目中可能引入mysql数据库,同时也引入redis缓存数据库,这的确是两个数据源,但是类型不同,可以直接使用SpringBoot的相关配置即可完成。
开始动手干活吧
话不多说,直接开整
开发环境
- IDE:idea
- 项目构建:maven
- 测试工具:postman
创建SpringBoot的项目
在这里说一下纯洁的微笑的项目结构,首先,创建了一个spring-boot-example的maven工程,该工程的打包类型为pom,每一个功能作为一个子module 项目,使用IDEA有一个比较高尴尬的事情,使用Spring 脚手架(spring initializr) 创建的module 在父项目的pom.xml文件中没有增加<module>标签。也有可能是因为我用的姿势不正确~
使用spring initalizr,在创建的时候,可以勾选所需要的依赖,避免手工填写相关的依赖,所选依赖如下图所示:
在spring-boot-example项目里创建一个新的module,名称为mybatis-mutidbsource。在父项目的pom.xml文件,手工增加 <module>mybatis-mutidbsource</module> ,如果不加的话,应该也没问题,这个没尝试过。迫于强迫症的习惯,还是手工加上了。
引入相关的依赖
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<mybatis.version>2.1.2</mybatis.version>
</properties>
<dependencies>
<!-- web -->
<!-- web 模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 测试相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
配置application.properties
在配置数据库信息时,需要注意一点,单数据源的数据库连接使用的参数为:spring.datasource.url ,具体示例如下:
spring.datasource.url = jdbc:mysql://localhost:3306/spring_boot_test?useSSL=true&serverTimezone=UTC
在使用多数据源的场景下,要将url修改为jdbc-url,对于多数据源参数的前缀信息也没有强制要求,但为了更方便的配置参数,建议相同数据源使用相同的前缀信息,修改后的配置参数如下:
spring.datasource.test1.jdbc-url = jdbc:mysql://localhost:3306/spring_boot_test1?useSSL=true&serverTimezone=UTC
其他的配置参数没有特别说明,完整的配置信息如下:
# 数据库相关
## 配置多数据源 test1
spring.datasource.test1.username=root
spring.datasource.test1.password=1qaz@WSX
spring.datasource.test1.driver-class-name=com.mysql.cj.jdbc.Driver
#此处url 修改为jdbc-url
spring.datasource.test1.jdbc-url = jdbc:mysql://localhost:3306/spring_boot_test1?useSSL=true&serverTimezone=UTC
## 配置多数据源 test2
spring.datasource.test2.username=root
spring.datasource.test2.password=1qaz@WSX
spring.datasource.test2.driver-class-name=com.mysql.cj.jdbc.Driver
#此处url 修改为jdbc-url
spring.datasource.test2.jdbc-url = jdbc:mysql://localhost:3306/spring_boot_test2?useSSL=true&serverTimezone=UTC
# mybatis
mybatis.type-aliases-package=com.fulin.example.bootexample.mybatismutidbsource.model
最最最最重要的数据源配置
第一个数据源
@Configuration
@MapperScan(basePackages = "com.fulin.example.bootexample.mybatismutidbsource.mapper.test1",sqlSessionTemplateRef = "test1SqlSessionTemplate")
public class DataSource1Config {
@Bean(name = "test1DataSource")
@ConfigurationProperties(prefix = "spring.datasource.test1")
@Primary
public DataSource testDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "test1SqlSessionFactory")
@Primary
public SqlSessionFactory testSqlSessionFactory(@Qualifier("test1DataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath:mybatis/mapper/test1/*.xml"));
return bean.getObject();
}
@Bean(name = "test1SqlSessionTemplate")
@Primary
public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("test1SqlSessionFactory") SqlSessionFactory sqlSessionFactory){
return new SqlSessionTemplate(sqlSessionFactory);
}
@Bean(name = "test1TransactionManager")
@Primary
public DataSourceTransactionManager testTransactionManager(@Qualifier("test1DataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
第二个数据源
@Configuration
@MapperScan(basePackages = "com.fulin.example.bootexample.mybatismutidbsource.mapper.test2",sqlSessionTemplateRef = "test2SqlSessionTemplate")
public class DataSource2Config {
@Bean(name = "test2DataSource")
@ConfigurationProperties(prefix = "spring.datasource.test2")
public DataSource testDataSource(){
return DataSourceBuilder.create().build();
}
@Bean(name = "test2SqlSessionFactory")
public SqlSessionFactory testSqlSessionFactory(@Qualifier("test2DataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath:mybatis/mapper/test2/*.xml"));
return bean.getObject();
}
@Bean(name = "test2TransactionManager")
public DataSourceTransactionManager testTransactionManager(@Qualifier("test1DataSource") DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "test2SqlSessionTemplate")
public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("test2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
比对两个数据源配置文件
使用文本比较软件,比对两个数据源配置文件,比对结果如下:
数据源配置文件差异比对
两个配置文件的主要差异有两点:
- 第一个数据源需要增加@Primary标签,用于区分是否为主库
- 两个数据源的bean 类型完全相同,应该使用名称进行区分
验证功能
为了能够验证配置的数据源,使用用户信息查询的简单demo验证配置是否正确。可以使用SpringBoot的Test类进行测试,也可以使用Restful接口的方式进行验证,在这里使用了后面的Restful接口的方式进行验证
创建model类
为了能够更加清晰的演示,针对不同的数据源,创建不同的model类,只是类名不同,具体字段等内容完全一致。使用了lombok的@Data标签可以省略getter和setter方法的编码。为了减少篇幅大小,在这里只显示了第一个数据源的model类。
@Data
public class User1Entity implements Serializable {
private static final long serialVersionUID = 1L;
public long id;
public String userName;
public String passWord;
public UserSexEnum userSex;
public String nickName;
public User1Entity() {
super();
}
public User1Entity(String userName, String passWord, UserSexEnum userSex) {
super();
this.passWord = passWord;
this.userName = userName;
this.userSex = userSex;
}
@Override
public String toString() {
return "userName: " + this.userName + ", pasword :" + this.passWord + ", sex :" + userSex.name();
}
}
创建Mapper类
创建Mapper类,主要写了一些增删改查的方法,便于后面的测试。
@Mapper
@Repository
public interface User1Mapper {
List<User1Entity> getAll();
User1Entity getOne(Long id);
void inserUser(User1Entity userEntity);
void updateUser(User1Entity userEntity);
void deleteById(Long id);
User1Entity getOneByUserName(String userName);
void deleteAll();
}
创建mapper.xml
创建mybatis的mapper.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="com.fulin.example.bootexample.mybatismutidbsource.mapper.test1.User1Mapper" >
<resultMap id="BaseResultMap" type="com.fulin.example.bootexample.mybatismutidbsource.model.test1.User1Entity">
<id column="id" property="id" jdbcType="BIGINT" />
<result column="userName" property="userName" jdbcType="VARCHAR" />
<result column="passWord" property="passWord" jdbcType="VARCHAR" />
<result column="user_sex" property="userSex" javaType="com.fulin.example.bootexample.mybatismutidbsource.enums.UserSexEnum" />
<result column="nick_name" property="nickName" jdbcType="VARCHAR" />
</resultMap>
<sql id="Base_Column_List">
id , userName,passWord,user_sex,nick_name
</sql>
<select id="getAll" resultMap="BaseResultMap">
select
<include refid="Base_Column_List"/>
from users
</select>
<select id="getOne" parameterType="java.lang.Long" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from users
where id = #{id}
</select>
<select id="getOneByUserName" parameterType="java.lang.String" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from users
where userName = #{userName}
</select>
<insert id="inserUser" parameterType="com.fulin.example.bootexample.mybatismutidbsource.model.test1.User1Entity">
insert into users (userName,passWord,nick_name,user_sex)
values(#{userName},#{passWord},#{nickName},#{userSex})
</insert>
<select id="updateUser" parameterType="com.fulin.example.bootexample.mybatismutidbsource.model.test1.User1Entity">
update users
set
<if test="userName != null"> userName = #{userName} , </if>
<if test="passWord != null"> passWord = #{passWord} , </if>
nick_name=#{nickName}
</select>
<delete id="deleteById" parameterType="java.lang.Long">
delete from users
where id = #{id}
</delete>
<delete id="deleteAll" >
delete from users
</delete>
</mapper>
controller
为了简单的测试相关的接口,没有编写service 层,直接在controller层调用dao层相关的接口。
@RestController
public class UserController {
@Autowired
private User1Mapper user1Mapper;
@Autowired
private User2Mapper user2Mapper;
@GetMapping("/user1/getAllUser")
public List<User1Entity> getAllUser1(){
return user1Mapper.getAll();
}
@GetMapping("/user2/getAllUser")
public List<User2Entity> getAllUser2(){
return user2Mapper.getAll();
}
@GetMapping("/user1/{id}")
public User1Entity getOneUser1(@PathVariable(value = "id") Long id){
return user1Mapper.getOne(id);
}
@GetMapping("/user2/{id}")
public User2Entity getOneUser2(@PathVariable(value = "id") Long id){
return user2Mapper.getOne(id);
}
@PostMapping("/user1/inserUser1")
public String insertUser1(@RequestParam(value = "userName") String userName,
@RequestParam(value = "passWord") String passWord,
@RequestParam(value = "userSex") String userSex){
UserSexEnum userSexEnum = UserSexEnum.valueOf(userSex);
User1Entity user1Entity = new User1Entity( userName, passWord, userSexEnum);
user1Mapper.inserUser(user1Entity);
return "insert user ["+user1Entity+"] OK !";
}
@PostMapping("/user2/inserUser2")
public String insertUser2(@RequestParam(value = "userName") String userName,
@RequestParam(value = "passWord") String passWord,
@RequestParam(value = "userSex") String userSex){
UserSexEnum userSexEnum = UserSexEnum.valueOf(userSex);
User2Entity user2Entity = new User2Entity( userName, passWord, userSexEnum);
user2Mapper.inserUser(user2Entity);
return "insert user ["+user2Entity+"] OK !";
}
}
项目结构
最后的项目结构:
项目结构
最后的测试
使用postman进行接口测试,在这里只展示insertUser的测试界面
postman调用一个数据源的insertUser接口 第一个数据库查询结果 postman调用一个数据源的insertUser接口 第二个数据库查询结果问题列表
-
程序正常启动,调用系统接口时,提示“java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName”,具体错误信息如下:
jdbcUrl is required with driverClassName 错误截图
解决方法:在配置多数据源时,数据库连接串的关键字应该改为jdbc-url
-
在controller层引入mapper时,会提示"Could not autowire. No beans of 'User1Mapper' type found. "错误信息
Could not autowire. No beans of 'User1Mapper' type found. 错误截图
解决方法:在User1Mapper增加@Repository注解,这是一个相对比较简单的方法,其他修改方法可以参考文献3
网友评论