文章目录
- 背景介绍
- 实现思路
- 环境介绍
- 实现步骤
- 1、配置数据库信息
- 2、数据源配置文件
- 3、补全其他组件代码
- bean包,里面随意放了两个简单的类
- mapper包
- dao包
- service包
- controller包
- 启动类
- 资源包
- pom文件
- 总结
背景介绍
最近的项目需求,需要将两个现有项目进行整合,原因是跨系统访问数据库,使用接口稍显复杂,而且还得不停的维护接口,所以想采取双数据源方案解决。由此,在网上查资料,自己写了个demo,仅供参考。
实现思路
分包思想,将涉及到数据库操作的代码分别放到两个包,在数据源的配置文件中,指定扫描的路径即可,下面来操作一下
环境介绍
- springboot2.1.6(直接从springboot官网下载的,可以参考官网部署)
- MySQL8.0(使用select version(); 查询当前版本)
- 代码结构预览
实现步骤
1、配置数据库信息
## test1 database
spring.datasource.test1.jdbc-url=jdbc:mysql://localhost:3306/aipc-sit?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
spring.datasource.test1.username=root
spring.datasource.test1.password=123456
spring.datasource.test1.driver-class-name=com.mysql.jdbc.Driver
## test2 database
spring.datasource.test2.jdbc-url=jdbc:mysql://localhost:3306/aipc-uat?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
spring.datasource.test2.username=root
spring.datasource.test2.password=123456
spring.datasource.test2.driver-class-name=com.mysql.jdbc.Driver
- 注意一下:url和驱动名称的命名写法,如果启动时报jdbcUrl is required with driverClassName错误,参考我之前的博客springboot启动报错——jdbcUrl is required with driverClassName
2、数据源配置文件
这个步骤是比较核心的,主要是用来扫描不同路径的的mapper来实现双库查询,注意其中包名的的指定。
- 配置第一个数据源:DataSourceConfig1 .java
package com.shenzhouyh.multiDataSource.dataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
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 javax.sql.DataSource;
/**
* @author: yinhao
* @date:2019/9/9
* @time:9:54
* @desciption
*/
//表示这个类为一个配置类
@Configuration
// 配置mybatis的接口类放的地方
@MapperScan(basePackages = "com.shenzhouyh.multiDataSource.mapper.test01", sqlSessionFactoryRef = "test1SqlSessionFactory") public class DataSourceConfig1 {
// 将这个对象放入Spring容器中
@Bean(name = "test1DataSource")
// 表示这个数据源是默认数据源
@Primary
// 读取application.properties中的配置参数映射成为一个对象
// prefix表示参数的前缀
@ConfigurationProperties(prefix = "spring.datasource.test1") public DataSource getDateSource1() {
return DataSourceBuilder.create().build();
}
@Bean(name = "test1SqlSessionFactory")
// 表示这个数据源是默认数据源
@Primary
// @Qualifier表示查找Spring容器中名字为test1DataSource的对象
public SqlSessionFactory test1SqlSessionFactory(@Qualifier("test1DataSource") DataSource datasource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(datasource);
bean.setMapperLocations(
// 设置mybatis的xml所在位置
new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/test01/*.xml"));
return bean.getObject();
}
@Bean("test1SqlSessionTemplate")
// 表示这个数据源是默认数据源
@Primary public SqlSessionTemplate test1sqlsessiontemplate(
@Qualifier("test1SqlSessionFactory") SqlSessionFactory sessionfactory) {
return new SqlSessionTemplate(sessionfactory);
}
}
如果是自己搭建的话,这块建议直接复制,里面已经有了较为详细的说明,只要注意扫描的文件路径即可。@Primary标签不可缺少,指明了默认数据源。
- 配置第二个数据源文件:DataSourceConfig2.java
package com.shenzhouyh.multiDataSource.dataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;
/**
* @author: yinhao
* @date:2019/9/9
* @time:11:41
* @desciption
*/
@Configuration @MapperScan(basePackages = "com.shenzhouyh.multiDataSource.mapper.test02", sqlSessionFactoryRef = "test2SqlSessionFactory") public class DataSourceConfig2 {
@Bean(name = "test2DataSource") @ConfigurationProperties(prefix = "spring.datasource.test2") public DataSource getDateSource2() {
return DataSourceBuilder.create().build();
}
@Bean(name = "test2SqlSessionFactory") public SqlSessionFactory test2SqlSessionFactory(@Qualifier("test2DataSource") DataSource datasource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(datasource);
bean.setMapperLocations(
new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/test02/*.xml"));
return bean.getObject();
}
@Bean("test2SqlSessionTemplate") public SqlSessionTemplate test2sqlsessiontemplate(
@Qualifier("test2SqlSessionFactory") SqlSessionFactory sessionfactory) {
return new SqlSessionTemplate(sessionfactory);
}
}
3、补全其他组件代码
bean包,里面随意放了两个简单的类
- Student.java
package com.shenzhouyh.multiDataSource.bean;
import lombok.Data;
/**
* @author: yinhao
* @date:2019/9/9
* @time:11:53
* @desciption
*/
@Data public class Student {
private String name;
private String studentId;
private String classId;
@Override public String toString() {
return "student{" + "name='" + name + '\'' + ", studentId='" + studentId + '\'' + ", classId='" + classId + '\''
+ '}';
}
}
- Teacher.java
package com.shenzhouyh.multiDataSource.bean;
import lombok.Data;
/**
* @author: yinhao
* @date:2019/9/9
* @time:11:53
* @desciption
*/
@Data public class Teacher {
private String name;
private String teacherId;
private String classId;
@Override public String toString() {
return "teacher{" + "name='" + name + '\'' + ", teacherId='" + teacherId + '\'' + ", classId='" + classId + '\''
+ '}';
}
}
注:里面使用了lombok组件,没有使用过的童鞋建议可以了解一下,简化代码好用的一bi,还有更高阶的用法。
mapper包
数据库的配置文件,主要就是扫描这个包下面的类
-TransactionMapping1.java
package com.shenzhouyh.multiDataSource.mapper.test01;
import com.shenzhouyh.multiDataSource.bean.Student;
import org.springframework.stereotype.Repository;
/**
* @author: yinhao
* @date:2019/9/9
* @time:12:02
* @desciption
*/
@Repository public interface TransactionMapping1 {
/**
* 保存学生信息
*
* @param student 学生实体
*/
void save(Student student);
}
-TransactionMapping2.java
package com.shenzhouyh.multiDataSource.mapper.test02;
import com.shenzhouyh.multiDataSource.bean.Teacher;
import org.springframework.stereotype.Repository;
/**
* @author: yinhao
* @date:2019/9/9
* @time:12:02
* @desciption
*/
@Repository public interface TransactionMapping2 {
/**
* 保存老师信息
*
* @param teacher 老师实体
*/
void save(Teacher teacher);
}
注:@Repository、@Service、@Controller 和 @Component 将类标识为Bean,作用一样,但是语义不一样,分别对应存储层Bean,业务层Bean,和展示层Bean以及通用Bean
dao包
- TransactionDao1.java
package com.shenzhouyh.multiDataSource.dao.test01;
import com.shenzhouyh.multiDataSource.bean.Student;
import com.shenzhouyh.multiDataSource.mapper.test01.TransactionMapping1;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author: yinhao
* @date:2019/9/9
* @time:12:09
* @desciption
*/
@Component public class TransactionDao1 {
@Autowired private TransactionMapping1 mapping1;
public void save(Student student) {
mapping1.save(student);
}
}
- TransactionDao1.java
package com.shenzhouyh.multiDataSource.dao.test02;
import com.shenzhouyh.multiDataSource.bean.Teacher;
import com.shenzhouyh.multiDataSource.mapper.test02.TransactionMapping2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author: yinhao
* @date:2019/9/9
* @time:12:09
* @desciption
*/
@Component public class TransactionDao2 {
@Autowired private TransactionMapping2 mapping2;
public void save(Teacher teacher) {
mapping2.save(teacher);
}
}
service包
- TransactionService1.java
package com.shenzhouyh.multiDataSource.service;
import com.shenzhouyh.multiDataSource.bean.Student;
import com.shenzhouyh.multiDataSource.dao.test01.TransactionDao1;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author: yinhao
* @date:2019/9/9
* @time:12:25
* @desciption
*/
@Service public class TransactionService1 {
@Autowired private TransactionDao1 transactionDao1;
public void save(Student student) {
transactionDao1.save(student);
}
}
- TransactionService2.java
package com.shenzhouyh.multiDataSource.service;
import com.shenzhouyh.multiDataSource.bean.Teacher;
import com.shenzhouyh.multiDataSource.dao.test02.TransactionDao2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author: yinhao
* @date:2019/9/9
* @time:12:25
* @desciption
*/
@Service public class TransactionService2 {
@Autowired private TransactionDao2 transactionDao2;
public void save(Teacher teacher) {
transactionDao2.save(teacher);
}
}
controller包
package com.shenzhouyh.multiDataSource.controller;
import com.shenzhouyh.multiDataSource.bean.Student;
import com.shenzhouyh.multiDataSource.bean.Teacher;
import com.shenzhouyh.multiDataSource.service.TransactionService1;
import com.shenzhouyh.multiDataSource.service.TransactionService2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
/**
* @author: yinhao
* @date:2019/9/9
* @time:12:29
* @desciption
*/
@RestController public class TransactionColler {
@Autowired private TransactionService1 studentService;
@Autowired private TransactionService2 teacherService;
@RequestMapping(value = "saveStudent") public String saveStudent() {
Student student = new Student();
student.setStudentId(UUID.randomUUID().toString().replaceAll("-", ""));
student.setClassId("一班");
student.setName("张三同学");
studentService.save(student);
return "success1";
}
@RequestMapping(value = "/saveTeacher") public String saveTeacher() {
Teacher teacher = new Teacher();
teacher.setTeacherId(UUID.randomUUID().toString().replaceAll("-", ""));
teacher.setClassId("一班");
teacher.setName("李四老师");
teacherService.save(teacher);
return "success2";
}
}
启动类
package com.shenzhouyh.multiDataSource;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import java.util.Arrays;
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class }) @MapperScan("com.shenzhouyh.multiDataSource.mapper")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
return args -> {
System.out.println("Let's inspect the beans provided by Spring Boot:");
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}
};
}
}
注意:
- 启动类的SpringBootApplication注解,相当于@Configuration,@EnableAutoConfiguration,@ComponentScan,因为他们基本一块使用,所以springboot2.0直接就进行了整合。详细请参考SpringBootApplication的使用
- SpringBootApplication后使用exclude = {
DataSourceAutoConfiguration.class },否则会报:Failed to determine a suitable driver class异常,原因是应用没有使用到DataSource,但是在pom.xml里引入了mybatis-spring-boot-starter,排除一个就好了。 - @MapperScan注解用于,要扫描mapper类包的路径,可配置多路径且可使用通配符
资源包
-TransactionMapping1.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.shenzhouyh.multiDataSource.mapper.test01.TransactionMapping1">
<insert id="save" parameterType="com.shenzhouyh.multiDataSource.bean.Student">
insert into student (name,student_id,class_id) values (#{name},#{studentId},#{classId});
</insert>
</mapper>
TransactionMapping2.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.shenzhouyh.multiDataSource.mapper.test02.TransactionMapping2">
<insert id="save" parameterType="com.shenzhouyh.multiDataSource.bean.Teacher">
insert into teacher (name,teacher_id,class_id) values (#{name},#{teacherId},#{classId});
</insert>
</mapper>
pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>gs-spring-boot</artifactId>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- tag::actuator[] -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- end::actuator[] -->
<!-- tag::tests[] -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- end::tests[] -->
<!--日志组件-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
</dependency>
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
</dependencies>
<properties>
<java.version>1.8</java.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
总结
这个demo是网上现有的例子,我只是在本地手敲了一遍,但是不可否认,遇坑无数,老是卡壳,本文中也就挑了一个说明了一下,我也会在后面的博客中进行总结和介绍。这个demo只是一个双库查询的简单demo,距离生产操作还差的很远,最明显的一点是没有进行事务控制,我参考的大神文章中都有介绍,除了这种方式之外,还有好多种方案,比如AOP请求拦截等分割数据源。还需努力啊!!!
本文主要参考
,推荐一下,里面还提供了github地址,多向大佬学习。