一,前言
大家在开发过程中,如果项目大一点就会遇到一种情况,同一个项目中可能会用到很多个数据源,那么这篇文章,博主为大家分享在spring应用中如何采用数据库连接池的方式配置配置多数据源。
本篇文章采用大家用的最多也是最受欢迎的HikariCP进行示范。
二,数据库连接池
数据库连接池是一个管理数据库连接的工具,它允许应用程序通过连接池来获取数据库连接,而不是每次需要时都重新创建连接。连接池通过维护一组预先创建好的数据库连接,以及管理连接的分配和释放,可以提高应用程序对数据库的访问性能和效率。
我们可以利用数据库连接池取实现连接的细节化配置。如最大连接数,最大空闲时间,最大连接等待时间等,这些方便了我们操作数据库,更可以有效避免过长等待导致程序卡死的情况。
常见的数据库连接池包括 HikariCP、Tomcat JDBC Pool、Apache Commons DBCP、C3P0、Druid等等。
三,实际应用
1,准备数据库以及表和基本数据
本文测试多库的情况,所以我们需要创建最少两个库进行测试。数据库sql我就不提供了,各位根据自己方便手动去创建吧。我这里简单创建两个库和三张表。如下图所示
2,基本框架准备(启动类就不展示了,有需要请看博主其它文档)
2.1 pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.5.4</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version> <!-- 这里是最新版本 -->
</dependency>
<!-- 日志框架-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.5</version> <!-- 你可以替换为其他版本 -->
</dependency>
<!-- mysql 连接 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>2.5.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
2.2 application.properties
这里密码采用了加密的方式,运行时会自动解密,如果不知道怎么做的请查看SpringBoot启动自动解密加密配置项_springboot environment 配置项解密-CSDN博客
spring.profiles.active=dev,database
#mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
datasource1.url=jdbc:mysql://localhost:3306/rojertest?serverTimezone=Asia/Tokyo
datasource1.username=root
datasource1.password={decrypt}bGoxMDA0MDE4Mjc3
datasource2.url=jdbc:mysql://localhost:3306/roejrtest?serverTimezone=Asia/Tokyo
datasource2.username=root
datasource2.password={decrypt}bGoxMDA0MDE4Mjc3
2.3 HikariCP配置
#HikariCP 配置信息
#连接超时时间,指连接数据库时最大等待时间,单位是毫秒
spring.datasource.hikari.connection-timeout=3000
#连接空闲超时时间,指连接在连接池中保持空闲状态的最大时间,超过此时间连接将被释放,单位是毫秒
spring.datasource.hikari.idle-timeout=3000
#连接池的最大连接数,指连接池中允许的最大连接数量
spring.datasource.hikari.maximum-pool-size=10
#连接池的最小空闲连接数,指连接池中保持的最小空闲连接数量。
spring.datasource.hikari.minimum-idle=5
#设置连接的事务隔离级别
#1,DEFAULT:使用数据库系统的默认隔离级别。
#2,READ_UNCOMMITTED:允许事务读取未提交的数据更改。这是最低的隔离级别,它允许事务读取未提交的更改,可能会导致脏读、不可重复读和幻读等问题。
#3,READ_COMMITTED:确保一个事务只能读取到已经提交的数据更改。在这个级别下,事务不会读取到其他事务未提交的更改,可以避免脏读,但仍可能存在不可重复读和幻读的问题。
#4,REPEATABLE_READ:确保一个事务可以多次读取相同的数据而不受其他事务的影响。在这个级别下,事务不会读取到其他事务已提交的更改,可以避免脏读和不可重复读,但仍可能存在幻读的问题。
#5,SERIALIZABLE:最高的隔离级别,确保事务之间完全隔离,每个事务都像是在独立运行。在这个级别下,事务不会读取到其他事务已提交或未提交的更改,可以避免脏读、不可重复读和幻读,但是会降低并发性能。
spring.datasource.hikari.transaction-isolation=DEFAULT
#连接验证超时时间,指连接在被取出后最大等待数据库验证的时间,单位是毫秒
spring.datasource.hikari.validation-timeout=3000
#用于测试连接的 SQL 查询语句
spring.datasource.hikari.connection-test-query=SHOW TABLES
2.3 日志输出配置
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 定义输出到控制台的日志记录器 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- 设置日志输出格式 -->
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!--打印数据库连接池日志信息-->
<logger name="com.zaxxer.hikari" level="DEBUG"/>
<!--打印sql信息-->
<logger name="com.luojie.dao" level="DEBUG"/>
<!-- 设置根日志级别为 INFO -->
<root level="INFO">
<appender-ref ref="CONSOLE"/> <!-- 将日志输出到控制台 -->
</root>
</configuration>
2.4 datasourceConfig 编写(重要)
package com.luojie.config;
import com.zaxxer.hikari.HikariDataSource;
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.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;
@Configuration
@MapperScan(basePackages = "com.luojie.dao.mapper1", sqlSessionFactoryRef = "sqlSessionFactory1")
public class DataSource1Config {
@Value("${datasource1.url}")
private String url;
@Value("${datasource1.username}")
private String username;
@Value("${datasource1.password}")
private String password;
@Bean(name = "dataSource1")
public DataSource dataSource1() {
return DataSourceBuilder.create()
.url(url)
.username(username)
.password(password)
// 使用HikariCP数据连接池管理
.type(HikariDataSource.class)
.build();
}
@Bean(name = "sqlSessionFactory1")
public SqlSessionFactory sqlSessionFactory1(@Qualifier("dataSource1") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource);
sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:rojerTestMapper/mapper1/*.xml"));
return sessionFactoryBean.getObject();
}
}
package com.luojie.config;
import com.zaxxer.hikari.HikariDataSource;
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.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;
@Configuration
@MapperScan(basePackages = "com.luojie.dao.mapper2", sqlSessionFactoryRef = "sqlSessionFactory2")
public class DataSource2Config {
@Value("${datasource2.url}")
private String url;
@Value("${datasource2.username}")
private String username;
@Value("${datasource2.password}")
private String password;
@Bean(name = "dataSource2")
public DataSource dataSource1() {
return DataSourceBuilder.create()
.url(url)
.username(username)
.password(password)
// 使用HikariCP数据连接池管理
.type(HikariDataSource.class)
.build();
}
@Bean(name = "sqlSessionFactory2")
public SqlSessionFactory sqlSessionFactory1(@Qualifier("dataSource2") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource);
sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:rojerTestMapper/mapper2/*.xml"));
return sessionFactoryBean.getObject();
}
}
3. 业务代码编写
3.1 controller
package com.luojie.controller;
import com.luojie.controImpl.DatabaseTestImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DatabaseTestController {
@Autowired
DatabaseTestImpl test;
@GetMapping("/database/test")
public void test1() {
test.add();
}
}
3.2 impl
package com.luojie.controImpl;
import com.luojie.dao.mapper1.Mapper1;
import com.luojie.dao.mapper2.Mapper2;
import com.luojie.moudle.LibraryModel;
import com.luojie.moudle.UserModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class DatabaseTestImpl {
@Autowired
private Mapper1 mapper1;
@Autowired
private Mapper2 mapper2;
public void add() {
UserModel userModel = new UserModel();
userModel.setUsername("Rojer");
userModel.setSex("男");
userModel.setUserid("007");
userModel.setRoles("admin,member");
userModel.setMoney("10");
LibraryModel libraryModel = new LibraryModel();
libraryModel.setId(1);
libraryModel.setAmount(1);
mapper2.addLibrary(libraryModel);
mapper2.addUserBalance(userModel);
mapper1.addUser(userModel);
}
}
3.3 model文件
package com.luojie.moudle;
import lombok.Data;
@Data
public class UserModel {
private String username;
private String money;
private String sex;
private String roles;
private String userid;
}
package com.luojie.moudle;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class LibraryModel {
private String name;
private BigDecimal price;
private int amount;
private int id;
}
3.4 mapper接口
注意这里放的位置一定要与前面config中配置的一致
package com.luojie.dao.mapper1;
import com.luojie.moudle.UserModel;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface Mapper1 {
void addUser(UserModel userModel);
}
package com.luojie.dao.mapper2;
import com.luojie.moudle.LibraryModel;
import com.luojie.moudle.UserModel;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface Mapper2 {
void addUserBalance(UserModel model);
void addLibrary(LibraryModel model);
}
3.5 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> 标签用于定义 Mapper XML 文件。-->
<!--namespace 属性指定了该 Mapper XML 文件对应的 Mapper 接口的类路径。-->
<mapper namespace="com.luojie.dao.mapper1.Mapper1">
<select id="addUser">
insert into userpro (`userid`, `roles`, `username`) values (#{userid}, #{roles}, #{username})
</select>
</mapper>
<?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> 标签用于定义 Mapper XML 文件。-->
<!--namespace 属性指定了该 Mapper XML 文件对应的 Mapper 接口的类路径。-->
<mapper namespace="com.luojie.dao.mapper2.Mapper2">
<update id="addUserBalance" parameterType="com.luojie.moudle.UserModel">
update user set money = money + #{money} where userid = #{userid}
</update>
<update id="addLibrary" parameterType="com.luojie.moudle.LibraryModel">
update library set amount = amount + #{amount} where id = #{id}
</update>
</mapper>
四,代码测试
sql和连接池都是正常打印日志的
整体执行ok
希望对各位大佬有帮助。麻烦加个关注点个赞谢谢!