Spring Boot与数据访问一–Spring Data介绍
Spring Boot与数据访问二–使用原生JDBC及源码解析
Spring Boot与数据访问三–整合Druid
Spring Boot与数据访问四–整合MyBatis(注解版)
Spring Boot与数据访问五–整合MyBatis(配置版)
Spring Boot与数据访问六–整合JPA
Spring Boot与数据访问七–多数据源
在用springboot开发项目时,随着业务量的扩大,我们通常会进行数据库拆分或是引入其他数据库,从而我们需要配置多个数据源,下面配置了三个数据源,database1和database2使用的是druid+MyBatis,database3使用的是Hikari+jpa。
项目目录结构:
1、application.yml文件:
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
filters: stat,wall,slf4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
database1:
url: jdbc:mysql://localhost:3306/database1?useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: 123456
driverClassName: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
# database2:
# url: jdbc:sqlserver://localhost:1433;DatabaseName=lf_jybtjcpg
# username: sa
# password: 123456
# driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
database2:
url: jdbc:mysql://localhost:3306/database2?useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: 123456
driverClassName: com.mysql.jdbc.Driver
database3:
url: jdbc:mysql://localhost:3306/database3?useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: 123456
driverClassName: com.mysql.jdbc.Driver
jpa:
database: mysql
show-sql: true
hibernate:
ddl-auto: update
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
database-platform: org.hibernate.dialect.MySQL5Dialect
2、配置数据源代码:
DataBase1Config.java:
package com.hui.demo.config;
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;
import java.sql.SQLException;
/**
1. 主数据源配置
*/
@Configuration
@MapperScan(basePackages = DataBase1Config.PACKAGE, sqlSessionFactoryRef = "masterSqlSessionFactory")
public class DataBase1Config {
/**
* 配置多数据源 关键就在这里 这里配置了不同的mapper对应不同的数据源
*/
static final String PACKAGE = "com.hui.demo.mapper.database1";
static final String MAPPER_LOCATION = "classpath:mapper/database1/*.xml";
/**
* 连接数据库信息 这个其实更好的是用配置中心完成
*/
@Value("${spring.datasource.database1.url}")
private String url;
@Value("${spring.datasource.database1.username}")
private String username;
@Value("${spring.datasource.database1.password}")
private String password;
@Value("${spring.datasource.database1.driverClassName}")
private String driverClassName;
/**
* 下面的配置信息可以读取配置文件,其实可以直接写死 如果是多数据源的话 还是考虑读取配置文件
*/
@Value("${spring.datasource.initialSize}")
private int initialSize;
@Value("${spring.datasource.minIdle}")
private int minIdle;
@Value("${spring.datasource.maxActive}")
private int maxActive;
@Value("${spring.datasource.maxWait}")
private int maxWait;
@Value("${spring.datasource.timeBetweenEvictionRunsMillis}")
private int timeBetweenEvictionRunsMillis;
@Value("${spring.datasource.minEvictableIdleTimeMillis}")
private int minEvictableIdleTimeMillis;
@Value("${spring.datasource.validationQuery}")
private String validationQuery;
@Value("${spring.datasource.testWhileIdle}")
private boolean testWhileIdle;
@Value("${spring.datasource.testOnBorrow}")
private boolean testOnBorrow;
@Value("${spring.datasource.testOnReturn}")
private boolean testOnReturn;
@Value("${spring.datasource.poolPreparedStatements}")
private boolean poolPreparedStatements;
@Value("${spring.datasource.maxPoolPreparedStatementPerConnectionSize}")
private int maxPoolPreparedStatementPerConnectionSize;
@Value("${spring.datasource.filters}")
private String filters;
@Value("{spring.datasource.connectionProperties}")
private String connectionProperties;
@Bean(name = "masterDataSource")
@Primary //标志这个 Bean 如果在多个同类 Bean 候选时,该 Bean 优先被考虑。
public DataSource masterDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setDriverClassName(driverClassName);
//具体配置
dataSource.setInitialSize(initialSize);
dataSource.setMinIdle(minIdle);
dataSource.setMaxActive(maxActive);
dataSource.setMaxWait(maxWait);
dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
dataSource.setValidationQuery(validationQuery);
dataSource.setTestWhileIdle(testWhileIdle);
dataSource.setTestOnBorrow(testOnBorrow);
dataSource.setTestOnReturn(testOnReturn);
dataSource.setPoolPreparedStatements(poolPreparedStatements);
dataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
/**
* 这个是用来配置 druid 监控sql语句的 非常有用 如果你有两个数据源 这个配置哪个数据源就坚实哪个数据源的sql 同时配置那就都监控
*/
try {
dataSource.setFilters(filters);
} catch (SQLException e) {
e.printStackTrace();
}
dataSource.setConnectionProperties(connectionProperties);
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(DataBase1Config.MAPPER_LOCATION));
return sessionFactory.getObject();
}
}
说明:
- @Primary: 多数据源配置的时候注意,必须要有一个主数据源, 用 @Primary 标志该 Bean。标志这个 Bean 如果在多个同类 Bean 候选时,该 Bean优先被考虑。
- dataSource.setFilters(filters): 这个是用来配置 druid 监控sql语句的, 如果你有两个数据源 这个配置哪个数据源就监控哪个 数据源的sql,同时配置那就都监控。
- 能够做到多个数据源的关键点 就是每个数据源所扫描的mapper包不一样,谁扫描到哪个mapper那么该mapper就用哪个数据源,同时都扫到了呢,
那当然就得用主数据源咯,也就是添加@Primary 的数据源。
DataBase2Config.java:
package com.hui.demo.config;
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;
import java.sql.SQLException;
/**
* 次数据源 另一个数据源配置
*/
@Configuration
@MapperScan(basePackages = DataBase2Config.PACKAGE, sqlSessionFactoryRef = "clusterSqlSessionFactory")
public class DataBase2Config {
/**
* 配置多数据源 关键就在这里 这里配置了不同的mapper对应不同的数据源
*/
static final String PACKAGE = "com.hui.demo.mapper.database2";
static final String MAPPER_LOCATION = "classpath:mapper/database2/*.xml";
@Value("${spring.datasource.database2.url}")
private String url;
@Value("${spring.datasource.database2.username}")
private String username;
@Value("${spring.datasource.database2.password}")
private String password;
@Value("${spring.datasource.database2.driverClassName}")
private String driverClass;
@Value("${spring.datasource.initialSize}")
private int initialSize;
@Value("${spring.datasource.minIdle}")
private int minIdle;
@Value("${spring.datasource.maxActive}")
private int maxActive;
@Value("${spring.datasource.maxWait}")
private int maxWait;
@Value("${spring.datasource.timeBetweenEvictionRunsMillis}")
private int timeBetweenEvictionRunsMillis;
@Value("${spring.datasource.minEvictableIdleTimeMillis}")
private int minEvictableIdleTimeMillis;
@Value("${spring.datasource.validationQuery}")
private String validationQuery;
@Value("${spring.datasource.testWhileIdle}")
private boolean testWhileIdle;
@Value("${spring.datasource.testOnBorrow}")
private boolean testOnBorrow;
@Value("${spring.datasource.testOnReturn}")
private boolean testOnReturn;
@Value("${spring.datasource.poolPreparedStatements}")
private boolean poolPreparedStatements;
@Value("${spring.datasource.maxPoolPreparedStatementPerConnectionSize}")
private int maxPoolPreparedStatementPerConnectionSize;
@Value("${spring.datasource.filters}")
private String filters;
@Value("{spring.datasource.connectionProperties}")
private String connectionProperties;
@Bean(name = "clusterDataSource")
public DataSource clusterDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setDriverClassName(driverClass);
//具体配置
dataSource.setInitialSize(initialSize);
dataSource.setMinIdle(minIdle);
dataSource.setMaxActive(maxActive);
dataSource.setMaxWait(maxWait);
dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
dataSource.setValidationQuery(validationQuery);
dataSource.setTestWhileIdle(testWhileIdle);
dataSource.setTestOnBorrow(testOnBorrow);
dataSource.setTestOnReturn(testOnReturn);
dataSource.setPoolPreparedStatements(poolPreparedStatements);
dataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
try {
dataSource.setFilters(filters);
} catch (SQLException e) {
e.printStackTrace();
}
dataSource.setConnectionProperties(connectionProperties);
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(DataBase2Config.MAPPER_LOCATION));
return sessionFactory.getObject();
}
}
说明:
- 发现次数据源所扫描的mapper和主是完全不一样的,说明每个数据源负责自己的mapper
- 次数据源是没有加@Primary。
- 这里也添加了dataSource.setFilters(filters):说明 次数据源也需要监听sql语句。
DataBase3Config.java:
package com.hui.demo.config;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactoryDataBase3", // 配置连接工厂
transactionManagerRef = "transactionManagerDatabase3", // 配置事物管理器
basePackages = {"com.hui.demo.dao.database3"} // 设置dao所在位置
)
public class DataBase3Config {
@Value("${spring.datasource.database3.url}")
private String url;
@Value("${spring.datasource.database3.username}")
private String username;
@Value("${spring.datasource.database3.password}")
private String password;
@Value("${spring.datasource.database3.driverClassName}")
private String driverClass;
// 配置数据源
@Autowired
@Qualifier("dataBase3DataSource")
private DataSource dataBase3DataSource;
@Bean(name = "entityManagerFactoryDataBase3")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryDataBase3(EntityManagerFactoryBuilder builder) {
return builder
// 设置数据源
.dataSource(dataBase3DataSource)
//设置实体类所在位置.扫描所有带有 @Entity 注解的类
.packages("com.hui.demo.entity.database3")
// Spring会将EntityManagerFactory注入到Repository之中.有了 EntityManagerFactory之后,
// Repository就能用它来创建 EntityManager 了,然后 EntityManager 就可以针对数据库执行操作
.persistenceUnit("database3PersistenceUnit")
.build();
}
/**
* 配置事物管理器
*/
@Bean(name = "transactionManagerDatabase3")
PlatformTransactionManager transactionManagerDatabase3(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(entityManagerFactoryDataBase3(builder).getObject());
}
@Bean(name = "dataBase3DataSource")
public DataSource dataBase3DataSource(){
System.out.println("dataBase3DataSource初始化----333333");
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setDriverClassName(driverClass);
return dataSource;
}
}
注:配置Druid的监控DruidConfig.java代码这里省略,可以参考Spring Boot与数据访问三–整合Druid
3、其他代码:
UsertDaoRepository.java:
package com.hui.demo.dao.database3;
import com.hui.demo.entity.database3.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
@Repository
public interface UsertDaoRepository extends JpaRepository<User, Integer>, JpaSpecificationExecutor<User> {
}
BaseServiceImpl.java:
package com.hui.demo.service.impl;
import com.hui.demo.entity.database1.User;
import com.hui.demo.entity.database2.Student;
import com.hui.demo.mapper.base.BaseMapper;
import com.hui.demo.service.BaseService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
/**
* @Description: 数据层公共实现类
*/
public abstract class BaseServiceImpl<T> implements BaseService<T> {
private static final Logger logger= LoggerFactory.getLogger(BaseServiceImpl.class);
protected abstract BaseMapper<T> getMapper();
@Override
public boolean insert(T entity) {
boolean falg=false;
try {
getMapper().insert(entity);
falg=true;
} catch (Exception e) {
logger.error("新增"+getClassName(entity)+"失败!原因是:",e);
}
return falg;
}
@Override
public List<T> findAll() {
List<T> list = null;
try {
list = getMapper().findAll();
} catch (Exception e) {
logger.error("查询失败!原因是:",e);
}
return list;
}
private String getClassName(T t){
String str="";
if(t instanceof User){
str="User";
}else if(t instanceof Student){
str="Studnet";
}
return str;
}
}
UserMapper.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.hui.demo.mapper.database1.UserMapper">
<resultMap id="BaseResultMap" type="com.hui.demo.entity.database1.User">
<result column="id" property="id" />
<result column="name" property="name" />
<result column="password" property="password" />
</resultMap>
<insert id="insert" parameterType="com.hui.demo.entity.database1.User">
insert into tbl_user (name,password)
values (#{name},#{password})
</insert>
<select id="findAll" resultType="com.hui.demo.entity.database1.User">
SELECT * FROM tbl_user;
</select>
</mapper>
StudentMapper.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.hui.demo.mapper.database2.StudentMapper">
<resultMap id="BaseResultMap" type="com.hui.demo.entity.database2.Student">
<result column="id" property="id" />
<result column="name" property="name" />
<result column="grade" property="grade" />
</resultMap>
<insert id="insert" parameterType="com.hui.demo.entity.database2.Student">
insert into tbl_student (name,grade)
values (#{name},#{grade})
</insert>
<select id="findAll" resultType="com.hui.demo.entity.database2.Student">
SELECT * FROM tbl_student;
</select>
</mapper>
4、maven文件配置:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.guoxin</groupId>
<artifactId>SpringbootTest</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.microsoft.sqlserver</groupId>-->
<!-- <artifactId>sqljdbc4</artifactId>-->
<!-- <version>4.0</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.alibaba</groupId>-->
<!-- <artifactId>druid-spring-boot-starter</artifactId>-->
<!-- <version>1.1.9</version>-->
<!-- </dependency>-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.9</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
</dependencies>
<build>
<finalName>SpringBootTest</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
5、数据库配置:
CREATE TABLE `tbl_user` (
`id` int(10),
`name` VARCHAR(255),
`password` VARCHAR(200)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT into tbl_user(id,name,password) VALUES("1","张三","123456");
INSERT into tbl_user(id,name,password) VALUES("3","王五","654321");
CREATE TABLE `tbl_student` (
`id` int(10),
`name` VARCHAR(255),
`grade` VARCHAR(200)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT into tbl_student(id,name,password) VALUES("2","李四","大二");
分别创建三个数据库database1、database2,database3,database1中创建tbl_user表,database2中创建tbl_student表,database3中创建tbl_user表,如下图:
6、接口实现:
在TestController.java中添加接口访问:
package com.hui.demo.web;
import com.hui.demo.dao.database3.UsertDaoRepository;
import com.hui.demo.entity.database2.Student;
import com.hui.demo.entity.database3.User;
import com.hui.demo.service.StudentService;
import com.hui.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
@RestController
public class TestController {
@Autowired
private UserService userService;
@Autowired
private UsertDaoRepository usertDaoRepository;
@Autowired
private StudentService studentService;
@Autowired
JdbcTemplate jdbcTemplate;
@GetMapping(value = "/getuser")
public List<com.hui.demo.entity.database1.User> getuser(){
List<com.hui.demo.entity.database1.User> all = userService.findAll();
return all;
}
@GetMapping(value = "/getusert")
public List<User> getusert(){
List<User> all = usertDaoRepository.findAll();
return all;
}
@GetMapping(value = "/getstudent")
public List<Student> getstudent(){
List<Student> all = studentService.findAll();
return all;
}
@ResponseBody
@GetMapping("/query")
public Map<String,Object> map(){
List<Map<String, Object>> list = jdbcTemplate.queryForList("select * FROM tbl_user");
return list.get(0);
}
}
至此,所有的类都已经实现,启动项目,再浏览器输入url访问数据:
7、访问druid监控页面:
http://localhost:8080/druid/login.html
参考:
整合Druid实现多数据源和可视化监控
Spingboot Jpa+Mybatis+ 多数据源持久层写法分享
拓展:
其他方法整合多数据源