IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Spring-动态数据源 -> 正文阅读

[Java知识库]Spring-动态数据源

Spring-动态数据源

动态数据源的原理得先说清。

原理

平常在使用Mysql的时候是通过JDBC的,得给一个url,userName,和password,如下:

 jdbc:mysql://localhost:3306/t_db1?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC

一个url对应一个Connection对象,需要在url中指定需要连接的库。

之后的Mysql的CRUD的操作都是通过Connection对象来做的。所以,动态,就是在这个时候操作的,在获取Connection的时候来通过指定的key来判断要用哪个数据源。这得有一个前提,得先建立Connection,之后在获取Connection的时候在判断,

  1. 通过什么判断呢?存放key和Connection的数据结构是什么?

    只要指定一个回调方法,key随便,存放key和Connection的数据结构是Map。在获取Connection的时候,先确定Key,在获取Connection就好了。

再看Spring

Java连接Mysql指定了接口,需要实现DataSource接口,Spring中提供了抽象类(AbstractDataSource

在这里插入图片描述

解释说明

DriverManagerDataSource

这个没有什么可说,只是封装了一下连接mysql需要用的一些属性,比如userName,password,url,driver,就是就老一套的获取Connection封装了一下

AbstractRoutingDataSource

Spring提供的动态数据源的抽象类

首先,它里面有一个Map,Map的key是Object(这是自定义的),V是Object(虽然是Object,代码里面规定了,只能为String,和DataSource),其实它本质就应该是DataSource,这里是String的目的是为了可以通过DataSourceLookup来获取数据源。(比如,可以在配置数据源的时候,直接配置数据源的bean的名字,然后自己写一个DataSourceLookup的实现类,从BeanFactory中获取DataSource)。

还提供了一个determineCurrentLookupKey()方法来判断当前数据源的key,其实就是map中的key。在最终获取Connection的时候通过当前的key获取DataSource,在通过DataSource获取真正的Connection。

代码分析
  1. 属性

在这里插入图片描述

  1. 实现接口

在这里插入图片描述

实现了InitializingBean那它肯定在afterPropertiesSet会做操作。等会看看

  1. 重要方法

    • afterPropertiesSet

    在这里插入图片描述

    • 获取Connection之前确定Key

在这里插入图片描述

 `determineCurrentLookupKey`方法是留给子类拓展的。determineCurrentLookupKey怎么来确定key呢?它是一个无参的方法,一般来说,都是放在ThreadLocal中,在执行sql操作之前,在对应的ThreadLocal放这次需要的key,就可以在determineCurrentLookupKey中获取ThreadLocal中的值,确定key。

 <font color='red'>Spring提供了一个动态数据源的实现类,`IsolationLevelDataSourceRouter`,key是事务的隔离级别。意思就是可以根据不同的隔离级别来选择DataSource</font>

 还有,一般都是用切面来操作ThreadLocal的

例子

我这里为了简单,就不写切面了。直接代码cv。

  1. 配置类

    package datasource.dynamic;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.jdbc.datasource.DriverManagerDataSource;
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    
    import javax.sql.DataSource;
    import java.util.HashMap;
    import java.util.Map;
    
    @Configuration
    public class Config {
    	@Bean
    	public DriverManagerDataSource dataSource1(){
    		String url = "jdbc:mysql://localhost:3306/t_db1?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC";
    		String userName = "root";
    		String password = "123456";
    		return new DriverManagerDataSource(url,userName,password);
    	}
    	@Bean
    	public DriverManagerDataSource dataSource2(){
    		String url = "jdbc:mysql://localhost:3306/t_db2?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC";
    		String userName = "root";
    		String password = "123456";
    		return new DriverManagerDataSource(url,userName,password);
    	}
    	@Bean
    	public DriverManagerDataSource dataSource3(){
    		String url = "jdbc:mysql://localhost:3306/t_db3?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC";
    		String userName = "root";
    		String password = "123456";
    		return new DriverManagerDataSource(url,userName,password);
    	}
    	@Bean
    	public MyDynamicDataSource dynamicDataSource(Map<String,DriverManagerDataSource> map){
    		HashMap<Object, Object> original = new HashMap<>(map);
    		MyDynamicDataSource myDynamicDataSource = new MyDynamicDataSource();
    		myDynamicDataSource.setTargetDataSources(original);
    		return myDynamicDataSource;
    	}
    
    	@Bean
    	public JdbcTemplate jdbcTemplate(AbstractRoutingDataSource dataSource){
    		return new JdbcTemplate(dataSource);
    	}
    }
    

    配置了三个数据库,也就是三个数据源,利用JdbcTemplate来快捷的执行sql,JdbcTemplate需要指定用自己定义的动态数据源(MyDynamicDataSource)。

    此外,之前说了,动态数据源里面有个map,它得需要数据源,此外还需要key,这里用bean的名字做为key,创建MyDynamicDataSource之后,设置TargetDataSources。当MyDynamicDataSource被Spring创建的时候,InitializingBean#afterPropertiesSet()会通过DataSourceLookup转换。

  2. ThreadLocal

    package datasource.dynamic;
    
    import org.springframework.util.Assert;
    
    public class DataSourceKeyHolder {
    	private static final ThreadLocal<String> keyHolder = new ThreadLocal<>();
    
    	public static void setKey(String key) {
    		keyHolder.remove();
    		keyHolder.set(key);
    	}
    
    	public static String getKey() {
    		String s = keyHolder.get();
    		Assert.notNull(s, "key 不能为空");
    		return s;
    	}
    	public static void clear(){
    		keyHolder.remove();
    	}
    }
    
  3. 动态数据源实现类

    package datasource.dynamic;
    
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    
    public class MyDynamicDataSource extends AbstractRoutingDataSource {
    	@Override
    	protected Object determineCurrentLookupKey() {
    		return DataSourceKeyHolder.getKey();
    	}
    }
    

    只是从ThreadLocal中获取当前的key就好了。

  4. 实体对象

    public class TestBean {
    	private Integer id;
    	private String name;
    	private Double age;
    
    	public Integer getId() {
    		return id;
    	}
    
    	public void setId(Integer id) {
    		this.id = id;
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    
    	public Double getAge() {
    		return age;
    	}
    
    	public void setAge(Double age) {
    		this.age = age;
    	}
    
    	@Override
    	public String toString() {
    		return "TestBean{" +
    				"id=" + id +
    				", name='" + name + '\'' +
    				", age=" + age +
    				'}';
    	}
    }
    
  5. sql

    -- auto-generated definition
    create table t_test
    (
        id   int auto_increment
            primary key,
        name text   null,
        age  double null
    );
    

    创建三个数据库,在三个数据库里面都创建这个表,填写点数据。

  6. 主要测试类

    package datasource.dynamic;
    
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.jdbc.core.BeanPropertyRowMapper;
    import org.springframework.jdbc.core.JdbcTemplate;
    
    
    public class MainTest {
    	public static void main(String[] args) {
    		try {
    			AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
    			JdbcTemplate jdbcTemplate = context.getBean(JdbcTemplate.class); // 这里从头到尾用的都是一个JdbcTemplate
    			dataSource1(jdbcTemplate); // 动态数据源1
    			dataSource2(jdbcTemplate);// 动态数据源2
    			dataSource3(jdbcTemplate);// 动态数据源3
    		}catch (Exception e){
    			e.printStackTrace();
    		}
    
    	}
    	public static void dataSource1(	JdbcTemplate jdbcTemplate ){
    		DataSourceKeyHolder.setKey("dataSource1");
    		try {
    			TestBean testBean = jdbcTemplate.queryForObject("select * from t_test where id=?", new BeanPropertyRowMapper<>(TestBean.class), 1);
    			System.out.println(testBean);
    		}finally {
    			DataSourceKeyHolder.clear();
    		}
    	}
    	public static void dataSource2(	JdbcTemplate jdbcTemplate ){
    		DataSourceKeyHolder.setKey("dataSource2");
    		try {
    			TestBean testBean = jdbcTemplate.queryForObject("select * from t_test where id=?", new BeanPropertyRowMapper<>(TestBean.class), 1);
    			System.out.println(testBean);
    		}finally {
    			DataSourceKeyHolder.clear();
    		}
    	}
    	public static void dataSource3(	JdbcTemplate jdbcTemplate ){
    		DataSourceKeyHolder.setKey("dataSource3");
    		try {
    			TestBean testBean = jdbcTemplate.queryForObject("select * from t_test where id=?", new BeanPropertyRowMapper<>(TestBean.class), 1);
    			System.out.println(testBean);
    		}finally {
    			DataSourceKeyHolder.clear();
    		}
    	}
    }
    
  7. 结果

在这里插入图片描述


关于博客这件事,我是把它当做我的笔记,里面有很多的内容反映了我思考的过程,因为思维有限,不免有些内容有出入,如果有问题,欢迎指出。一同探讨。谢谢。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-03 15:57:54  更:2022-03-03 16:00:43 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:44:51-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码