
- 外文名
- Data Source
- 中文名
- 数据源
-
Spring Boot 默认数据源 HikariDataSource 与 JdbcTemplate
2018-08-19 08:37:07默认数据源 CRUD 数据库 PhoneController 测试结果 自动配置原理 DataSourceConfiguration 1、《Spring Boot 数据库访问 简介》中已经介绍,Spring Boot 可以通过多种方式访问各种数据库,本文将介绍 Spr...目录
环境准备与依赖
1、本文介绍 Spring Boot 内部集成的 JDBC 模板访问 Mysql 数据库,环境:java jdk 1.8 + Spring boot 2.0.4 + Mysql 。
2、pom. xml 依赖如下:
<?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>www.wmx.com.horse</groupId> <artifactId>horse</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>horse</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <!-- 引入Spring封装的jdbc--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!-- 引入html模板引擎Thymeleaf--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!-- 因为web项目启动模块--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 引入mysql数据库连接驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- 引入Spring Boot 测试模块--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <!-- Spring Boot 打包插件--> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
mysql 数据库
1、此 Mysql 数据库是在 CentOS 7.2 中的 Docker 容器中,所以用了 CentOS 的 3307 端口映射了 Docker 容器的 3306 端口,CentOS 主机 ip 为 192.168.58.129
2、账号 root、密码 root。
数据库 CRUD
1、全局配置文件内容如下:
spring: datasource: username: root password: root url: jdbc:mysql://192.168.58.129:3307/horse?characterEncoding=UTF-8 driver-class-name: com.mysql.jdbc.Driver
高版本的 spring boot 搭配 mysql 驱动版本较高时,如 mysql-connector-java:8.0.16,此时 driver-class-name 的值要带 cj;url 的值要带时区 serverTimezone,如:
url: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=UTF-8&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver1、配置的内容以及意义就如同以前在 Sping 核心配置文件 beans.xml 中配置时是一样的
2、到这里就已经可以操作数据库了,因为 Spring Boot 都已经自动配置好了,如 Spring Boot 默认已经提供了数据源
3、关于上面的数据源配置内容,都可以从 Spring Boot 官方文档 查看
# DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties) spring.datasource.continue-on-error=false # Whether to stop if an error occurs while initializing the database. spring.datasource.data= # Data (DML) script resource references. spring.datasource.data-username= # Username of the database to execute DML scripts (if different). spring.datasource.data-password= # Password of the database to execute DML scripts (if different). spring.datasource.dbcp2.*= # Commons DBCP2 specific settings spring.datasource.driver-class-name= # Fully qualified name of the JDBC driver. Auto-detected based on the URL by default. spring.datasource.generate-unique-name=false # Whether to generate a random datasource name. spring.datasource.hikari.*= # Hikari specific settings spring.datasource.initialization-mode=embedded # Initialize the datasource with available DDL and DML scripts. spring.datasource.jmx-enabled=false # Whether to enable JMX support (if provided by the underlying pool). spring.datasource.jndi-name= # JNDI location of the datasource. Class, url, username & password are ignored when set. spring.datasource.name= # Name of the datasource. Default to "testdb" when using an embedded database. spring.datasource.password= # Login password of the database. spring.datasource.platform=all # Platform to use in the DDL or DML scripts (such as schema-${platform}.sql or data-${platform}.sql). spring.datasource.schema= # Schema (DDL) script resource references. spring.datasource.schema-username= # Username of the database to execute DDL scripts (if different). spring.datasource.schema-password= # Password of the database to execute DDL scripts (if different). spring.datasource.separator=; # Statement separator in SQL initialization scripts. spring.datasource.sql-script-encoding= # SQL scripts encoding. spring.datasource.tomcat.*= # Tomcat datasource specific settings spring.datasource.type= # Fully qualified name of the connection pool implementation to use. By default, it is auto-detected from the classpath. spring.datasource.url= # JDBC URL of the database. spring.datasource.username= # Login username of the database. spring.datasource.xa.data-source-class-name= # XA datasource fully qualified name. spring.datasource.xa.properties= # Properties to pass to the XA data source.
4、也可以从 org.springframework.boot.autoconfigure.jdbc.DataSourceProperties 数据源配置文件类中进行查看
默认数据源 HikariDataSource
1、全局配置文件 application.yml 中 spring.datasource 下只配置了账号、密码、数据库地址、连接驱动,因为默认使用的是 class com.zaxxer.hikari.HikariDataSource 数据源
2、如果过是自定义数据源,比如 DruidDataSource,则可以使用 type 指定,如下所示:type: com.alibaba.druid.pool.DruidDataSource,可以参考《切换 Druid 数据源》
3、测试数据源如下:
import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import javax.annotation.Resource; import javax.sql.DataSource; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.SQLException; @RunWith(SpringRunner.class) @SpringBootTest public class HorseApplicationTests { /** * Spring Boot 默认已经配置好了数据源,程序员可以直接 DI 注入然后使用即可 */ @Resource DataSource dataSource; @Test public void contextLoads() throws SQLException { Connection connection = dataSource.getConnection(); DatabaseMetaData metaData = connection.getMetaData(); //数据源>>>>>>class com.zaxxer.hikari.HikariDataSource System.out.println("数据源>>>>>>" + dataSource.getClass()); System.out.println("连接>>>>>>>>" + connection); System.out.println("连接地址>>>>" + connection.getMetaData().getURL()); System.out.println("驱动名称>>>>" + metaData.getDriverName()); System.out.println("驱动版本>>>>" + metaData.getDriverVersion()); System.out.println("数据库名称>>" + metaData.getDatabaseProductName()); System.out.println("数据库版本>>" + metaData.getDatabaseProductVersion()); System.out.println("连接用户名称>" + metaData.getUserName()); connection.close(); } }
1、可以看出 Spring Boot 2.0.4 默认使用 com.zaxxer.hikari.HikariDataSource 数据源,而以前版本,如 Spring Boot 1.5 默认使用 org.apache.tomcat.jdbc.pool.DataSource 作为数据源;
2、HikariDataSource 号称 Java WEB 当前速度最快的数据源,相比于传统的 C3P0 、DBCP、Tomcat jdbc 等连接池更加优秀;
3、HikariDataSource 的内容本文暂时不做延伸,有了数据库连接,显然就可以 CRUD 操作数据库了。
JdbcTemplate CRUD 数据库
1、有了数据源(com.zaxxer.hikari.HikariDataSource),然后拿到l了数据库连接(java.sql.Connection),自然就可以使用连接和原生的 JDCB 语句来操作数据库
2、即使不使用第三方第数据库操作框架,如 MyBatis、Hibernate 、JDBC Utils 等,Spring 本身也对 原生的 JDBC 做了轻量级的封装,即 org.springframework.jdbc.core.JdbcTemplate。这原本是 Spring 的知识点!
3、数据库操作的所有 CRUD 方法都在 JdbcTemplate 中,有了 JdbcTemplate 就能更加轻松的操作数据库。
4、Spring Boot 不仅提供了默认的数据源,同时默认已经配置好了 JdbcTemplate 放在了容器中,程序员只需自己注入即可使用
5、JdbcTemplate 的自动配置原理是依赖 org.springframework.boot.autoconfigure.jdbc 包下的 org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration 类
PhoneController 控制层
1、为了尽可能符合实际开发,新建一个控制层,通过浏览器访问来进行 CRUD,但是不再进行细致的分层,如 dao、service、domain 等都省略
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.annotation.Resource; import java.util.List; import java.util.Map; /** * Created by Administrator on 2018/8/19 0019. */ @Controller public class PhoneController { /** * JdbcTemplate 是 core 包的核心类,用于简化 JDBC 操作,还能避免一些常见的错误,如忘记关闭数据库连接 * Spring Boot 默认提供了数据源,默认提供了 org.springframework.jdbc.core.JdbcTemplate * JdbcTemplate 中会自己注入数据源,使用起来也不用再自己来关闭数据库连接 */ @Resource JdbcTemplate jdbcTemplate; /** * 查询 phone 表所有数据 * * @return */ @ResponseBody @GetMapping("phoneList") public List<Map<String, Object>> userList() { /** * 查询 phone 表所有数据 * List 中的1个 Map 对应数据库的 1行数据 * Map 中的 key 对应数据库的字段名,value 对应数据库的字段值 */ List<Map<String, Object>> mapList = jdbcTemplate.queryForList("SELECT * FROM phone"); return mapList; } /** * 新增 phone 数据 * * @return */ @GetMapping("savePhone") public String savePhone() { String sql = "INSERT INTO phone(number,region) VALUES (?,?)"; Object[] objects = new Object[2]; objects[0] = "18673886425"; objects[1] = "湖南"; jdbcTemplate.update(sql, objects); return "forward:/phoneList"; } /** * 修改 phone 数据 * * @return */ @GetMapping("updatePhone") public String updatePhone() { String sql = "UPDATE phone SET number=? WHERE pid=?"; Object[] objects = new Object[2]; objects[0] = "18666668888"; objects[1] = "1"; jdbcTemplate.update(sql, objects); return "forward:/phoneList"; } /** * 删除 phone 数据 * update 方法可以做查询以外的 增加、修改、删除操作 * * @return */ @GetMapping("deletePhone") public String deletePhone() { String sql = "DELETE FROM phone WHERE number=?"; Object[] objects = new Object[1]; objects[0] = "18673886425"; jdbcTemplate.update(sql, objects); return "forward:/phoneList"; } }
1、浏览器访问测试
查询:http://localhost:8080/phoneList
添加:http://localhost:8080/savePhone
修改:http://localhost:8080/updatePhone
删除:http://localhost:8080/deletePhone数据源自动配置原理
1、自动配置都在 org.springframework.boot.autoconfigure.jdbc 包下。
2、org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration 数据源配置类作用是根据逻辑判断之后,添加数据源
3、SpringBoot 默认支持如下数据源;
1、com.zaxxer.hikari.HikariDataSource (Spring Boot 2.0 以上,默认使用此数据源)
2、org.apache.tomcat.jdbc.pool.DataSource
3、org.apache.commons.dbcp2.BasicDataSource4、可以使用 spring.datasource.type 指定自定义的数据源类型,值为 要使用的连接池实现的完全限定名。默认情况下,它是从类路径自动检测的。
@ConditionalOnMissingBean({DataSource.class}) @ConditionalOnProperty( name = {"spring.datasource.type"} ) static class Generic { Generic() { } @Bean public DataSource dataSource(DataSourceProperties properties) { return properties.initializeDataSourceBuilder().build(); } }
-
MybatisPlus--切换多个数据源
2020-08-18 02:20:19多数据源配置 MP官网的文档写的很详细,这里不做累赘直接截图 从其特性可以看出MP对于常见的框架都有继承案例 特性12条是关于sharding-jdbc的集成, 我的sharding-jdbc专栏 集成案例 第一步、引入依赖 <!-- ...官方文档
MP官网的文档写的很详细,这里不做累赘直接截图
从其特性可以看出MP对于常见的框架都有继承案例特性12条是关于
sharding-jdbc
的集成, 我的sharding-jdbc专栏
集成案例
第一步、引入依赖
<!-- dynamic-datasource-spring-boot-starter --> <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.2.0</version> </dependency>
第二步、添加多数据源配置
spring: datasource: dynamic: primary: master #设置默认的数据源或者数据源组,默认值即为master strict: false #设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候会抛出异常,不启动则使用默认数据源. datasource: master: url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置 slave_1: url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver slave_2: url: ENC(xxxxx) # 内置加密,使用请查看详细文档 username: ENC(xxxxx) password: ENC(xxxxx) driver-class-name: com.mysql.jdbc.Driver schema: db/schema.sql # 配置则生效,自动初始化表结构 data: db/data.sql # 配置则生效,自动初始化数据 continue-on-error: true # 默认true,初始化失败是否继续 separator: ";" # sql默认分号分隔符 #......省略 #以上会配置一个默认库master,一个组slave下有两个子库slave_1,slave_2
第三步、通过注解指定操作的数据源
使用
@DS
切换数据源。@DS
可以注解在方法上和类上,同时存在则 方法注解优先于类上注解
。强烈建议只注解在service实现上。
注解 结果 没有@DS 默认数据源 @DS(“dsName”) dsName可以为组名也可以为具体某个库的名称 如下注解的使用案例
@Service @DS("slave") //注解在类上 public class UserServiceImpl implements UserService { @Autowired private JdbcTemplate jdbcTemplate; public List selectAll() { return jdbcTemplate.queryForList("select * from user"); } @Override @DS("slave_1") //注解在方法上,指明使用数据源使用名字叫 slave_1 的数据源 public List selectByCondition() { return jdbcTemplate.queryForList("select * from user where age >10"); } }
-
DataSource数据源简单理解
2018-06-22 11:40:17DataSource数据源简单理解 因为热爱,所以拼搏。 –RuiDer 什么是数据源 JDBC2.0 提供了javax.sql.DataSource接口,它负责建立与数据库的连接,当在应用程序中访问数据库时 不必编写连接数据库的代码,...DataSource数据源简单理解
因为热爱,所以拼搏。 –RuiDer
什么是数据源
JDBC2.0 提供了javax.sql.DataSource接口,它负责建立与数据库的连接,当在应用程序中访问数据库时
不必编写连接数据库的代码,直接引用DataSource获取数据库的连接对象即可。用于获取操作数据Connection对象。数据源与数据库连接池
数据源建立多个数据库连接,这些数据库连接会保存在数据库连接池中,当需要访问数据库时,只需要从数据库连接池中 获取空闲的数据库连接,当程序访问数据库结束时,数据库连接会放回数据库连接池中。
数据源DataSource与JNDI
数据源DataSource是JNDI资源的一种,很简单,就是将“DataSource”字符串名称与真正的DataSource对象绑定起来,方便获取。
数据库连接池的优势
传统的JDBC访问数据库技术,每次访问数据库都需要通过数据库驱动器Driver和数据库名称以及密码等等资源建立数据库连接。 这样的连接存在两大问题: 1. 频繁的建立数据库连接与断开数据库,这样会消耗大量的资源和时间,降低性能。 2. 数据库的连接需要用户名和密码等等,这些需要一定的内存和CPU一定开销。
About Me
欢迎各位读者访问,大家一起相互学习。
带给你惊喜的不是我,也不是他,而是技术。
我的博客 -
Spring Boot MyBatis 动态数据源切换、多数据源,读写分离
2017-12-21 11:11:31项目地址:https://github.com/helloworlde/SpringBoot-DynamicDataSource/tree/dev 在 Spring Boot... 在这个项目中使用注解方式声明要使用的数据源,通过 AOP 查找注解,从而实现数据源的动态切换;该项目为 Product项目地址 https://github.com/helloworlde/SpringBoot-DynamicDataSource
本项目使用 Spring Boot 和 MyBatis 实现多数据源,动态数据源的切换;有多种不同的实现方式,在学习的过程中发现没有文章将这些方式和常见的问题集中处理,所以将常用的方式和常见的问题都写在了在本项目的不同分支上:
- master: 使用了多数据源的 RESTful API 接口,使用 Druid 实现了 DAO 层数据源动态切换和只读数据源负载均衡
- dev: 最简单的切面和注解方式实现的动态数据源切换
- druid: 通过切面和注解方式实现的使用 Druid 连接池的动态数据源切换
- aspect_dao: 通过切面实现的 DAO 层的动态数据源切换
- roundrobin: 通过切面使用轮询方式实现的只读数据源负载均衡
- hikari: 升级到SpringBoot 2.0, 数据源使用 Hikari
以上分支都是基于 dev 分支修改或扩充而来,基本涵盖了常用的多数据源动态切换的方式,基本的原理都一样,都是通过切面根据不同的条件在执行数据库操作前切换数据源
在使用的过程中基本踩遍了所有动态数据源切换的坑,将常见的一些坑和解决方法写在了 Issues 里面
该项目使用了一个可写数据源和多个只读数据源,为了减少数据库压力,使用轮循的方式选择只读数据源;考虑到在一个 Service 中同时会有读和写的操作,所以本应用使用 AOP 切面通过 DAO 层的方法名切换只读数据源;但这种方式要求数据源主从一致,并且应当避免在同一个 Service 方法中写入后立即查询,如果必须在执行写入操作后立即读取,应当在 Service 方法上添加
@Transactional
注解以保证使用主数据源需要注意的是,使用 DAO 层切面后不应该在 Service 类层面上加
@Transactional
注解,而应该添加在方法上,这也是 Spring 推荐的做法动态切换数据源依赖
configuration
包下的4个类来实现,分别是:- DataSourceRoutingDataSource.java
- DataSourceConfigurer.java
- DynamicDataSourceContextHolder.java
- DynamicDataSourceAspect.java
添加依赖
dependencies { compile('org.mybatis.spring.boot:mybatis-spring-boot-starter:1.3.1') compile('org.springframework.boot:spring-boot-starter-web') compile('org.springframework.boot:spring-boot-starter-aop') compile('com.alibaba:druid-spring-boot-starter:1.1.6') runtime('mysql:mysql-connector-java') testCompile('org.springframework.boot:spring-boot-starter-test') }
创建数据库及表
- 分别创建数据库
product_master
,product_slave_alpha
,product_slave_beta
,product_slave_gamma
- 在以上数据库中分别创建表
product
,并插入不同数据
DROP DATABASE IF EXISTS product_master; CREATE DATABASE product_master; CREATE TABLE product_master.product( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, price DOUBLE(10,2) NOT NULL DEFAULT 0); INSERT INTO product_master.product (name, price) VALUES('master', '1'); DROP DATABASE IF EXISTS product_slave_alpha; CREATE DATABASE product_slave_alpha; CREATE TABLE product_slave_alpha.product( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, price DOUBLE(10,2) NOT NULL DEFAULT 0); INSERT INTO product_slave_alpha.product (name, price) VALUES('slaveAlpha', '1'); DROP DATABASE IF EXISTS product_slave_beta; CREATE DATABASE product_slave_beta; CREATE TABLE product_slave_beta.product( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, price DOUBLE(10,2) NOT NULL DEFAULT 0); INSERT INTO product_slave_beta.product (name, price) VALUES('slaveBeta', '1'); DROP DATABASE IF EXISTS product_slave_gamma; CREATE DATABASE product_slave_gamma; CREATE TABLE product_slave_gamma.product( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, price DOUBLE(10,2) NOT NULL DEFAULT 0); INSERT INTO product_slave_gamma.product (name, price) VALUES('slaveGamma', '1');
配置数据源
- application.properties
# Master datasource config spring.datasource.druid.master.name=master spring.datasource.druid.master.driver-class-name=com.mysql.jdbc.Driver spring.datasource.druid.master.url=jdbc:mysql://localhost/product_master?useSSL=false spring.datasource.druid.master.port=3306 spring.datasource.druid.master.username=root spring.datasource.druid.master.password=123456 # SlaveAlpha datasource config spring.datasource.druid.slave-alpha.name=SlaveAlpha spring.datasource.druid.slave-alpha.driver-class-name=com.mysql.jdbc.Driver spring.datasource.druid.slave-alpha.url=jdbc:mysql://localhost/product_slave_alpha?useSSL=false spring.datasource.druid.slave-alpha.port=3306 spring.datasource.druid.slave-alpha.username=root spring.datasource.druid.slave-alpha.password=123456 # SlaveBeta datasource config spring.datasource.druid.slave-beta.name=SlaveBeta spring.datasource.druid.slave-beta.driver-class-name=com.mysql.jdbc.Driver spring.datasource.druid.slave-beta.url=jdbc:mysql://localhost/product_slave_beta?useSSL=false spring.datasource.druid.slave-beta.port=3306 spring.datasource.druid.slave-beta.username=root spring.datasource.druid.slave-beta.password=123456 # SlaveGamma datasource config spring.datasource.druid.slave-gamma.name=SlaveGamma spring.datasource.druid.slave-gamma.driver-class-name=com.mysql.jdbc.Driver spring.datasource.druid.slave-gamma.url=jdbc:mysql://localhost/product_slave_gamma?useSSL=false spring.datasource.druid.slave-gamma.port=3306 spring.datasource.druid.slave-gamma.username=root spring.datasource.druid.slave-gamma.password=123456 # Druid dataSource config spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.druid.initial-size=5 spring.datasource.druid.max-active=20 spring.datasource.druid.min-idle=5 spring.datasource.druid.max-wait=60000 spring.datasource.druid.pool-prepared-statements=false spring.datasource.druid.validation-query=SELECT 1 spring.datasource.druid.validation-query-timeout=30000 spring.datasource.druid.test-on-borrow=false spring.datasource.druid.test-on-return=false spring.datasource.druid.test-while-idle=true #spring.datasource.druid.time-between-eviction-runs-millis= #spring.datasource.druid.min-evictable-idle-time-millis= #spring.datasource.druid.max-evictable-idle-time-millis=10000 # Druid stat filter config spring.datasource.druid.filters=stat,wall,log4j spring.datasource.druid.web-stat-filter.enabled=true spring.datasource.druid.web-stat-filter.url-pattern=/* spring.datasource.druid.web-stat-filter.exclusions=*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/* spring.datasource.druid.web-stat-filter.session-stat-enable=true spring.datasource.druid.web-stat-filter.session-stat-max-count=10 spring.datasource.druid.web-stat-filter.principal-session-name=user #spring.datasource.druid.web-stat-filter.principal-cookie-name= spring.datasource.druid.web-stat-filter.profile-enable=true spring.datasource.druid.filter.stat.db-type=mysql spring.datasource.druid.filter.stat.log-slow-sql=true spring.datasource.druid.filter.stat.slow-sql-millis=1000 spring.datasource.druid.filter.stat.merge-sql=true spring.datasource.druid.filter.wall.enabled=true spring.datasource.druid.filter.wall.config.delete-allow=true spring.datasource.druid.filter.wall.config.drop-table-allow=false spring.datasource.druid.filter.slf4j.enabled=true # Druid manage page config spring.datasource.druid.stat-view-servlet.enabled=true spring.datasource.druid.stat-view-servlet.url-pattern=/druid/* spring.datasource.druid.stat-view-servlet.reset-enable=true spring.datasource.druid.stat-view-servlet.login-username=admin spring.datasource.druid.stat-view-servlet.login-password=admin #spring.datasource.druid.stat-view-servlet.allow= #spring.datasource.druid.stat-view-servlet.deny= spring.datasource.druid.use-global-data-source-stat=true # Druid AOP config spring.datasource.druid.aop-patterns=cn.com.hellowood.dynamicdatasource.service.* spring.aop.proxy-target-class=true # MyBatis config mybatis.type-aliases-package=cn.com.hellowood.dynamicdatasource.mapper mybatis.mapper-locations=mappers/**Mapper.xml server.port=9999
配置数据源
- DataSourceKey.java
package cn.com.hellowood.dynamicdatasource.common; public enum DataSourceKey { master, slaveAlpha, slaveBeta, slaveGamma }
- DataSourceRoutingDataSource.java
该类继承自
AbstractRoutingDataSource
类,在访问数据库时会调用该类的determineCurrentLookupKey()
方法获取数据库实例的 keypackage cn.com.hellowood.dynamicdatasource.configuration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicRoutingDataSource extends AbstractRoutingDataSource { private final Logger logger = LoggerFactory.getLogger(getClass()); @Override protected Object determineCurrentLookupKey() { logger.info("Current DataSource is [{}]", DynamicDataSourceContextHolder.getDataSourceKey()); return DynamicDataSourceContextHolder.getDataSourceKey(); } }
- DataSourceConfigurer.java
数据源配置类,在该类中生成多个数据源实例并将其注入到
ApplicationContext
中package cn.com.hellowood.dynamicdatasource.configuration; import org.mybatis.spring.SqlSessionFactoryBean; import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; @Configuration public class DataSourceConfigurer { /** * master DataSource * @Primary 注解用于标识默认使用的 DataSource Bean,因为有5个 DataSource Bean,该注解可用于 master * 或 slave DataSource Bean, 但不能用于 dynamicDataSource Bean, 否则会产生循环调用 * * @ConfigurationProperties 注解用于从 application.properties 文件中读取配置,为 Bean 设置属性 * @return data source */ @Bean("master") @Primary @ConfigurationProperties(prefix = "spring.datasource.druid.master") public DataSource master() { return DruidDataSourceBuilder.create().build(); } /** * Slave alpha data source. * * @return the data source */ @Bean("slaveAlpha") @ConfigurationProperties(prefix = "spring.datasource.druid.slave-alpha") public DataSource slaveAlpha() { return DruidDataSourceBuilder.create().build(); } /** * Slave beta data source. * * @return the data source */ @Bean("slaveBeta") @ConfigurationProperties(prefix = "spring.datasource.druid.slave-beta") public DataSource slaveBeta() { return DruidDataSourceBuilder.create().build(); } /** * Slave gamma data source. * * @return the data source */ @Bean("slaveGamma") @ConfigurationProperties(prefix = "spring.datasource.druid.slave-gamma") public DataSource slaveGamma() { return DruidDataSourceBuilder.create().build(); } /** * Dynamic data source. * * @return the data source */ @Bean("dynamicDataSource") public DataSource dynamicDataSource() { DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource(); Map<Object, Object> dataSourceMap = new HashMap<>(4); dataSourceMap.put(DataSourceKey.master.name(), master()); dataSourceMap.put(DataSourceKey.slaveAlpha.name(), slaveAlpha()); dataSourceMap.put(DataSourceKey.slaveBeta.name(), slaveBeta()); dataSourceMap.put(DataSourceKey.slaveGamma.name(), slaveGamma()); // 将 master 数据源作为默认指定的数据源 dynamicRoutingDataSource.setDefaultTargetDataSource(master()); // 将 master 和 slave 数据源作为指定的数据源 dynamicRoutingDataSource.setTargetDataSources(dataSourceMap); // 将数据源的 key 放到数据源上下文的 key 集合中,用于切换时判断数据源是否有效 DynamicDataSourceContextHolder.dataSourceKeys.addAll(dataSourceMap.keySet()); // 将 Slave 数据源的 key 放在集合中,用于轮循 DynamicDataSourceContextHolder.slaveDataSourceKeys.addAll(dataSourceMap.keySet()); DynamicDataSourceContextHolder.slaveDataSourceKeys.remove(DataSourceKey.master.name()); return dynamicRoutingDataSource; } /** * 配置 SqlSessionFactoryBean * @ConfigurationProperties 在这里是为了将 MyBatis 的 mapper 位置和持久层接口的别名设置到 * Bean 的属性中,如果没有使用 *.xml 则可以不用该配置,否则将会产生 invalid bond statement 异常 * * @return the sql session factory bean */ @Bean @ConfigurationProperties(prefix = "mybatis") public SqlSessionFactoryBean sqlSessionFactoryBean() { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); // 配置数据源,此处配置为关键配置,如果没有将 dynamicDataSource 作为数据源则不能实现切换 sqlSessionFactoryBean.setDataSource(dynamicDataSource()); return sqlSessionFactoryBean; } /** * 注入 DataSourceTransactionManager 用于事务管理 */ @Bean public PlatformTransactionManager transactionManager() { return new DataSourceTransactionManager(dynamicDataSource()); } }
- DynamicDataSourceContextHolder.java
该类为数据源上下文配置,用于切换数据源
package cn.com.hellowood.dynamicdatasource.configuration; import cn.com.hellowood.dynamicdatasource.common.DataSourceKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class DynamicDataSourceContextHolder { private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class); /** * 用于在切换数据源时保证不会被其他线程修改 */ private static Lock lock = new ReentrantLock(); /** * 用于轮循的计数器 */ private static int counter = 0; /** * Maintain variable for every thread, to avoid effect other thread */ private static final ThreadLocal<Object> CONTEXT_HOLDER = ThreadLocal.withInitial(DataSourceKey.master); /** * All DataSource List */ public static List<Object> dataSourceKeys = new ArrayList<>(); /** * The constant slaveDataSourceKeys. */ public static List<Object> slaveDataSourceKeys = new ArrayList<>(); /** * To switch DataSource * * @param key the key */ public static void setDataSourceKey(String key) { CONTEXT_HOLDER.set(key); } /** * Use master data source. */ public static void useMasterDataSource() { CONTEXT_HOLDER.set(DataSourceKey.master); } /** * 当使用只读数据源时通过轮循方式选择要使用的数据源 */ public static void useSlaveDataSource() { lock.lock(); try { int datasourceKeyIndex = counter % slaveDataSourceKeys.size(); CONTEXT_HOLDER.set(String.valueOf(slaveDataSourceKeys.get(datasourceKeyIndex))); counter++; } catch (Exception e) { logger.error("Switch slave datasource failed, error message is {}", e.getMessage()); useMasterDataSource(); e.printStackTrace(); } finally { lock.unlock(); } } /** * Get current DataSource * * @return data source key */ public static String getDataSourceKey() { return CONTEXT_HOLDER.get(); } /** * To set DataSource as default */ public static void clearDataSourceKey() { CONTEXT_HOLDER.remove(); } /** * Check if give DataSource is in current DataSource list * * @param key the key * @return boolean boolean */ public static boolean containDataSourceKey(String key) { return dataSourceKeys.contains(key); } }
- DynamicDataSourceAspect.java
动态数据源切换的切面,切 DAO 层,通过 DAO 层方法名判断使用哪个数据源,实现数据源切换
package cn.com.hellowood.dynamicdatasource.configuration; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Aspect @Component public class DynamicDataSourceAspect { private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class); private final String[] QUERY_PREFIX = {"select"}; @Pointcut("execution( * cn.com.hellowood.dynamicdatasource.mapper.*.*(..))") public void daoAspect() { } @Before("daoAspect()") public void switchDataSource(JoinPoint point) { Boolean isQueryMethod = isQueryMethod(point.getSignature().getName()); if (isQueryMethod) { DynamicDataSourceContextHolder.useSlaveDataSource(); logger.info("Switch DataSource to [{}] in Method [{}]", DynamicDataSourceContextHolder.getDataSourceKey(), point.getSignature()); } } @After("daoAspect()") public void restoreDataSource(JoinPoint point) { DynamicDataSourceContextHolder.clearDataSourceKey(); logger.info("Restore DataSource to [{}] in Method [{}]", DynamicDataSourceContextHolder.getDataSourceKey(), point.getSignature()); } private Boolean isQueryMethod(String methodName) { for (String prefix : QUERY_PREFIX) { if (methodName.startsWith(prefix)) { return true; } } return false; } }
配置 Product REST API 接口
- ProductController.java
package cn.com.hellowood.dynamicdatasource.controller; import cn.com.hellowood.dynamicdatasource.common.CommonResponse; import cn.com.hellowood.dynamicdatasource.common.ResponseUtil; import cn.com.hellowood.dynamicdatasource.modal.Product; import cn.com.hellowood.dynamicdatasource.service.ProductService; import cn.com.hellowood.dynamicdatasource.utils.ServiceException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/product") public class ProductController { @Autowired private ProductService productService; @GetMapping("/{id}") public CommonResponse getProduct(@PathVariable("id") Long productId) throws ServiceException { return ResponseUtil.generateResponse(productService.select(productId)); } @GetMapping public CommonResponse getAllProduct() { return ResponseUtil.generateResponse(productService.getAllProduct()); } @PutMapping("/{id}") public CommonResponse updateProduct(@PathVariable("id") Long productId, @RequestBody Product newProduct) throws ServiceException { return ResponseUtil.generateResponse(productService.update(productId, newProduct)); } @DeleteMapping("/{id}") public CommonResponse deleteProduct(@PathVariable("id") long productId) throws ServiceException { return ResponseUtil.generateResponse(productService.delete(productId)); } @PostMapping public CommonResponse addProduct(@RequestBody Product newProduct) throws ServiceException { return ResponseUtil.generateResponse(productService.add(newProduct)); } }
- ProductService.java
package cn.com.hellowood.dynamicdatasource.service; import cn.com.hellowood.dynamicdatasource.mapper.ProductDao; import cn.com.hellowood.dynamicdatasource.modal.Product; import cn.com.hellowood.dynamicdatasource.utils.ServiceException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; @Service public class ProductService { @Autowired private ProductDao productDao; public Product select(long productId) throws ServiceException { Product product = productDao.select(productId); if (product == null) { throw new ServiceException("Product:" + productId + " not found"); } return product; } @Transactional(rollbackFor = DataAccessException.class) public Product update(long productId, Product newProduct) throws ServiceException { if (productDao.update(newProduct) <= 0) { throw new ServiceException("Update product:" + productId + "failed"); } return newProduct; } @Transactional(rollbackFor = DataAccessException.class) public boolean add(Product newProduct) throws ServiceException { Integer num = productDao.insert(newProduct); if (num <= 0) { throw new ServiceException("Add product failed"); } return true; } @Transactional(rollbackFor = DataAccessException.class) public boolean delete(long productId) throws ServiceException { Integer num = productDao.delete(productId); if (num <= 0) { throw new ServiceException("Delete product:" + productId + "failed"); } return true; } public List<Product> getAllProduct() { return productDao.getAllProduct(); } }
- ProductDao.java
package cn.com.hellowood.dynamicdatasource.mapper; import cn.com.hellowood.dynamicdatasource.modal.Product; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.List; @Mapper public interface ProductDao { Product select(@Param("id") long id); Integer update(Product product); Integer insert(Product product); Integer delete(long productId); List<Product> getAllProduct(); }
- ProductMapper.xml
启动项目,此时访问
/product/1
会返回product_master
数据库中product
表中的所有数据,多次访问/product
会分别返回product_slave_alpha
、product_slave_beta
、product_slave_gamma
数据库中product
表中的数据,同时也可以在看到切换数据源的 log,说明动态切换数据源是有效的
注意
在该应用中因为使用了 DAO 层的切面切换数据源,所以
@Transactional
注解不能加在类上,只能用于方法;有@Trasactional
注解的方法无法切换数据源 -
Spring项目中使用两种方法动态切换数据源,多数据源切换
2018-08-13 22:03:55本文介绍两种切换数据库的方法。 方法1:数据库信息都配置在spring xml中,适用于一般数据库切换。...方法2:将数据库信息配置在默认数据源中,适用于切换数据库操作同一方法,相当于批量执行方法。... -
Win10如何配置数据源ODBC数据源
2019-05-06 20:03:25如何配置数据源ODBC数据源 1.打开控制面板,按下图,依次进行操作: 2.双击打开 “ODBC数据源” ,按下图进行操作: 3.选择 “Microsoft Access Driver”,如下图: 4.对 “ODBC Microsoft Access” 进行安装,... -
springBoot 动态数据源以及Mybatis多数据源
2017-11-02 14:32:49前言在开发过程中可能需要用到多个数据源,比如一个项目(MySQL)就是和(SQL Server)混合使用,就需要使用多数据源;如果业务场景比较复炸,可以使用动态数据源,灵活切换,典型的应用就是读写分离。下面分两个... -
spring boot多数据源配置,多数据源事务
2018-03-29 17:24:09spring boot多数据源配置,多数据源事务 springboot 中数据源配置,连接池配置,源码剖析,如何选择连接池 阅读本文前 ,请先阅读笔者另一片文章Spring Boot 源码深入分析 需求描述 在业务开发中,需要读写... -
Spring Boot 2.x 自定义数据源 DruidDataSource(操作 mysql 数据库)
2018-08-20 08:16:31自定义数据源 Druid 引入数据源 切换 Druid 数据源 配置 Druid 数据源参数 配置 Druid 数据源监控 配置 Druid 后台管理 Servlet 配置 Druid web 监控 filter DRUID 简介 1、Druid 是阿里巴巴开源平台上一个... -
Spring Boot 动态数据源(多数据源自动切换)
2016-01-24 20:04:43为了在开发中以最简单的方法使用,本文基于注解和AOP的方法实现,在spring boot框架的项目中,添加本文实现的代码类后,只需要配置好数据源就可以直接通过注解使用,简单方便。一配置二使用 1. 配置文件中配置多个... -
Spring Boot 集成Mybatis实现多数据源
2017-06-30 19:12:38项目提交测试,趁着中当间的这个空档期,把springboot的多数据源配置学习一下,总体来说多数据源配置有两种方式,一种是静态的,一种是动态的。 静态的方式 我们以两套配置方式为例,在项目中有两套配置文件,两套... -
Spring动态切换多数据源Demo
2015-02-03 15:24:55请自行修改com/resources/datasource.properties中数据库配置,Demo中配置的两个数据源,一个是Mysql,一个是Oracle。 运行之前请自行建立数据库的表。 -
springboot中多数据源的配置
2018-07-27 15:11:08在实际项目中,一个工程配置多个数据源很常见,工程可能会根据业务或者模块访问不同的数据库或表;今天笔者就springboot中配置多数据源作个详细的讲解 2.实现方案 注意:一个应用工程中有且只有一个启动类,其... -
spring动态加载数据源+动态添加数据源
2016-11-24 09:07:17相信很多开发者也遇到过这样的问题,随着项目的增大,使用的数据源会越来越多,现在比较流行配置数据源的方法是在xml配置文件配置数据源, ![图片说明]... -
AbstractRoutingDataSource动态数据源切换,AOP实现动态数据源切换
2017-08-21 16:08:12AbstractRoutingDataSource动态数据源切换 上周末,室友通宵达旦的敲代码处理他的多数据源的问题,搞的非常的紧张,也和我聊了聊天,大概的了解了他的业务的需求。一般的情况下我们都是使用SSH或者SSM框架进行处理... -
SpringBoot配置数据源与手动配置数据源
2019-05-06 21:39:54SpringBoot配置数据源 1.创建项目 创建好项目以后,pom文件应该是这样的: <?xml version="1.0" encoding="UTF-8"?> <project xmlns=... -
Spark多数据源处理
2016-05-10 15:55:34Spark多数据源处理教程,该课程主要介绍如何通过Spark的DataSource API来读写外部数据源中的数据,并结合一些具体场景来分析和解释使用DataSource API的好处以及需要注意的问题。 -
springboot+mybatis多数据源配置,AOP注解动态切换数据源
2018-05-06 21:32:56springboot默认加载application.properties或application.yml配置,配置规则已经定好且为单数据源,想要配置多数据源必须禁用默认加载,然后手动去配置多数据源,完整代码如下:数据源配置:application.properties#... -
数据源、元数据、数据元
2020-01-10 15:14:57##数据源(data source) 顾名思义,数据的来源,是提供某种所需要数据的器件或原始媒体。在数据源中存储了所有建立数据库连接的信息。就像通过指定文件名称可以在文件系统中找到文件一样,通过提供正确的数据源名称,... -
Springboot 动态数据源 (项目启动后添加新数据源)
2020-06-03 14:31:34之前写过一篇文章是静态切换双数据源,yml里面初始化2个数据源一个mapper管理一个数据源,调用mapper1的就是从A库取数据,调用mapper2从B库取数据 地址:springboot双数据源 这次有一个需求是,项目启动以后根据一个新的... -
SSM 多数据源配置(配置两个数据源)
2019-01-08 11:25:17SSM多数据源配置(配置两个数据源) 前言:SSM项目需要配置多数据源,以达到操作不同数据库的数据,网上看到大多是通过动态切换数据源的方式,但在项目中总会出现问题,这里通过配置两个spring-mybatis.xml... -
创建Access数据的ODBC数据源时没有Access数据源驱动器
2017-11-07 21:44:41在作数据库实例的时候,需要创建Access数据库的ODBC数据源,管理工具 ODBC数据源管理器里面只有SQL Server ,没有Access等其他,因为Access数据源驱动器是32位的,64位不支持,只要打开32位版本的ODBC管理工具就可以... -
Spring动态数据源与运行时动态添加数据源
2019-08-05 11:34:361、多数据源与动态数据源 当项目不只是要用到一个数据库的时候就需要使用到多个数据源了,这种场景很多,比如要查找的数据不在同一个数据库库中,或者是要做数据库读写分离。应对上面的问题主要有两种解决方法。 ... -
spring、mybatis实现数据库多数据源和多数据源事务
2018-06-22 17:07:00动态数据源在多数据源的基础上,要实现数据源的动态切换。这两种复杂系统的数据库联接有相关性,也有不同应用场景的区别。如要连接两个不同的业务库,一般会使用多数据源,如要动态切换不同数据源的相同业务库,会... -
Weblogic Xa数据源和非Xa数据源的选择
2018-02-05 05:42:32背景:在项目上,不管是ADF应用,BPM应用,或者是OSB、SOA接口,数据源和出站连接池的建立必然少不了,而数据源的类型就有Xa数据源和非Xa数据源,那我们如何选择呢,选择不当会造成什么后果呢,下面给出一点小小的... -
数据源(DataSource)是什么以及SpringBoot中数据源配置
2018-08-09 16:46:10数据源 数据源,简单理解为数据源头,提供了应用程序所需要数据的位置。数据源保证了应用程序与目标数据之间交互的规范和协议,它可以是数据库,文件系统等等。其中数据源定义了位置信息,用户验证信息和交互时所需... -
spring + druid 配置动态配置数据源以及多数据源切换功能实现
2018-08-09 17:48:01spring + druid 配置动态配置数据源以及多数据源切换功能实现 数据源连接池使用druid 其他的数据源基本原理相同 spring中配置默认数据源连接池如下: <!-- 数据源配置, 使用 BoneCP 数据库连接池 --> &... -
SpringBoot多数据源切换,AOP实现动态数据源切换
2018-10-30 16:32:25SpringBoot多数据源切换,AOP实现动态数据源切换 操作数据一般都是在DAO层进行处理,可以选择直接使用JDBC进行编程 或者是使用多个DataSource 然后创建多个SessionFactory,在使用Dao层的时候通过不同的... -
谈谈Spring Boot 数据源加载及其多数据源简单实现
2019-04-12 13:17:10提供所有微服务数据源的图形化维护功能 代码生成可以根据选择的数据源加载表等源信息 数据源管理要支持动态配置,实时生效 附录效果图 实现思路 本文提供方法仅供类似简单业务场景,在生产环境和复杂的业务场景 ... -
SpringBoot多数据源切换详解,以及开启事务后数据源切换失败处理
2019-11-21 15:37:47最近项目需要指出多数据源,同时支持事务回滚,这里记录一下 1、多数据源方式介绍 主要方式有以下两种: 通过配置多个SqlSessionFactory 来实现多数据源,这么做的话,未免过于笨重,而且无法实现动态添加数据源...
-
VIVADO_lic.rar
-
Java Web开发之Java语言基础
-
Duplicate Bug Report Detection Using Dual-Channel Convolution Neural Networks
-
Vim编辑器与Shell命令脚本_2021-01-23
-
C语言:调整数组是奇数位与偶数之前。
-
linux网络命令
-
天然气管理系统.zip
-
三维地图GIS大数据可视化
-
商业的本质——杰克·韦尔奇著
-
性能测试面面观
-
matplotlib坐标变换详解_python草堂.ipynb
-
echartsMap.zip
-
第3章 入门程序、常量、变量
-
武汉理工大学计算机基础与编程综合实验——网吧计费管理系统第二个版本
-
C#文件传输、Socket通信、大文件断点续传
-
基于单片机RFID-RC522门禁防盗报警系统(原理图、源程序、论文)
-
阿里云云计算ACP考试必备教程
-
UG模具设计的八大分模方法,建议收藏
-
易语言4行代码实现ASCII转UniCode.zip
-
Unity游戏开发之数字华容道