SpringBoot使用Sharding-JDBC读写分离

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
网络型负载均衡 NLB,每月750个小时 15LCU
简介: 本文介绍SpringBoot使用当当Sharding-JDBC进行读写分离。1.有关Sharding-JDBC本文还是基于当当网Sharding-Jdbc的依赖,与上一篇使用Sharding-Jdbc进行分库分表依赖一致,并且本文大致内容与上一篇文章相似,建议先查看我的另一篇在查看这篇会简单许多,传送门《SpringBoot使用Sharding-JDBC分库分表》。

本文介绍SpringBoot使用当当Sharding-JDBC进行读写分离。

1.有关Sharding-JDBC

本文还是基于当当网Sharding-Jdbc的依赖,与上一篇使用Sharding-Jdbc进行分库分表依赖一致,并且本文大致内容与上一篇文章相似,建议先查看我的另一篇在查看这篇会简单许多,传送门《SpringBoot使用Sharding-JDBC分库分表》

这里需要特殊介绍的是,使用Sharding-JDBC进行读写分离的时候,只允许设置一个主库,从库的话可以设置多个,访问策略的话从源码上看只有两种轮询(ROUND_ROBIN)和随机(RANDOM)。

源码代码如下:

package com.dangdang.ddframe.rdb.sharding.api.strategy.slave;

public enum MasterSlaveLoadBalanceStrategyType {
    ROUND_ROBIN(new RoundRobinMasterSlaveLoadBalanceStrategy()),
    RANDOM(new RandomMasterSlaveLoadBalanceStrategy());

    private final MasterSlaveLoadBalanceStrategy strategy;

    public static MasterSlaveLoadBalanceStrategyType getDefaultStrategyType() {
        return ROUND_ROBIN;
    }

    private MasterSlaveLoadBalanceStrategyType(MasterSlaveLoadBalanceStrategy strategy) {
        this.strategy = strategy;
    }

    public MasterSlaveLoadBalanceStrategy getStrategy() {
        return this.strategy;
    }
}

2.本文场景

由于本地环境并没有使用Mysql主从复制,只是创建了三个库,其中database0作为主库,database1和database2作为从库。主库进行增删改操作,从库进行查询操作,如下图为本文数据库的三个表。

如上图分别是三个数据库中的user表,其中master-user为database0数据库中的user表,salve-user1为database1中的user表,salve-user2为database2中的user表。

3.代码实现

本文使用SpringBoot2.0.3,SpringData-JPA,Druid连接池,和当当的sharding-jdbc。

3.1 建表SQL

创建表和数据库的SQL如下所示,这里默认在从库内分别插入了一条数据,name值分别存放dalaoyang1和dalaoyang2便于区分。

CREATE DATABASE database0;
USE database0;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`(
    id bigint(64) not null,
    city varchar(20) not null,
    name varchar(20) not null,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE DATABASE database1;
USE database1;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`(
    id bigint(64) not null,
    city varchar(20) not null,
    name varchar(20) not null,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `database1`.`user`(`id`, `city`, `name`) VALUES (101, 'beijing', 'dalaoyang1');

CREATE DATABASE database2;
USE database2;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`(
    id bigint(64) not null,
    city varchar(20) not null,
    name varchar(20) not null,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `database2`.`user`(`id`, `city`, `name`) VALUES (102, 'beijing', 'dalaoyang2');

3.2 依赖文件

新建项目,依赖文件还是当当的sharding-jdbc-core依赖和druid连接池,完整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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.dalaoyang</groupId>
    <artifactId>springboot2_shardingjdbc_dxfl</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot2_shardingjdbc_dxfl</name>
    <description>springboot2_shardingjdbc_dxfl</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.9</version>
        </dependency>
        <!-- sharding-jdbc -->
        <dependency>
            <groupId>com.dangdang</groupId>
            <artifactId>sharding-jdbc-core</artifactId>
            <version>1.5.4</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

3.3 配置信息

在配置信息中配置了三个数据库的信息和JPA的简单配置。

##Jpa配置
spring.jpa.database=mysql
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none

##数据库配置
##数据库database0地址
database0.url=jdbc:mysql://localhost:3306/database0?characterEncoding=utf8&useSSL=false
##数据库database0用户名
database0.username=root
##数据库database0密码
database0.password=root
##数据库database0驱动
database0.driverClassName=com.mysql.jdbc.Driver
##数据库database0名称
database0.databaseName=database0

##数据库database1地址
database1.url=jdbc:mysql://localhost:3306/database1?characterEncoding=utf8&useSSL=false
##数据库database1用户名
database1.username=root
##数据库database1密码
database1.password=root
##数据库database1驱动
database1.driverClassName=com.mysql.jdbc.Driver
##数据库database1名称
database1.databaseName=database1

##数据库database2地址
database2.url=jdbc:mysql://localhost:3306/database2?characterEncoding=utf8&useSSL=false
##数据库database1用户名
database2.username=root
##数据库database1密码
database2.password=root
##数据库database1驱动
database2.driverClassName=com.mysql.jdbc.Driver
##数据库database1名称
database2.databaseName=database2

3.4 启动类

上一篇文章说到在启动类加入了@EnableAutoConfiguration去除数据库自动配置,当时也没太注意,其实可以直接在@SpringBootApplication注解上去除数据库自动配置,剩下的和上一篇一样,使用@EnableTransactionManagement开启事务,使用@EnableConfigurationProperties注解加入配置实体,启动类完整代码如下所示。

package com.dalaoyang;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableTransactionManagement(proxyTargetClass = true)
@EnableConfigurationProperties
public class Springboot2ShardingjdbcDxflApplication {

    public static void main(String[] args) {
        SpringApplication.run(Springboot2ShardingjdbcDxflApplication.class, args);
    }

}

3.5 实体类和数据库操作层

User实体类。

package com.dalaoyang.entity;

import lombok.Data;

import javax.persistence.*;

@Entity
@Table(name="user")
@Data
public class User {

    @Id
    private Long id;

    private String city;

    private String name;
}

UserRepository类。

package com.dalaoyang.repository;

import com.dalaoyang.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User,Long> {
}

3.6 数据库参数类

数据库配置类,Database0Config。

package com.dalaoyang.database;

import com.alibaba.druid.pool.DruidDataSource;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;

/**
 * @author yangyang
 * @date 2019/1/30
 */
@Data
@ConfigurationProperties(prefix = "database0")
@Component
public class Database0Config {
    private String url;
    private String username;
    private String password;
    private String driverClassName;
    private String databaseName;

    public DataSource createDataSource() {
        DruidDataSource result = new DruidDataSource();
        result.setDriverClassName(getDriverClassName());
        result.setUrl(getUrl());
        result.setUsername(getUsername());
        result.setPassword(getPassword());
        return result;
    }
}

数据库配置类,Database1Config。

package com.dalaoyang.database;

import com.alibaba.druid.pool.DruidDataSource;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;

/**
 * @author yangyang
 * @date 2019/1/30
 */
@Data
@ConfigurationProperties(prefix = "database1")
@Component
public class Database1Config {
    private String url;
    private String username;
    private String password;
    private String driverClassName;
    private String databaseName;

    public DataSource createDataSource() {
        DruidDataSource result = new DruidDataSource();
        result.setDriverClassName(getDriverClassName());
        result.setUrl(getUrl());
        result.setUsername(getUsername());
        result.setPassword(getPassword());
        return result;
    }
}

数据库配置类,Database2Config。

package com.dalaoyang.database;

import com.alibaba.druid.pool.DruidDataSource;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;

/**
 * @author yangyang
 * @date 2019/1/30
 */
@Data
@ConfigurationProperties(prefix = "database2")
@Component
public class Database2Config {
    private String url;
    private String username;
    private String password;
    private String driverClassName;
    private String databaseName;

    public DataSource createDataSource() {
        DruidDataSource result = new DruidDataSource();
        result.setDriverClassName(getDriverClassName());
        result.setUrl(getUrl());
        result.setUsername(getUsername());
        result.setPassword(getPassword());
        return result;
    }
}

3.7 读写分离配置

创建一个DataSourceConfig类来设置读写分离,这里其实也与分库分表类似,也可以在分库分表的基础上进行读写分离,需要创建一个Map集合来接收从库。在创建数据源时需要传入五个参数,分别是:

  • name:数据源名称
  • masterDataSourceName:主库数据源名称
  • masterDataSource:主数据源
  • slaveDataSourceMap:从数据源集合
  • strategyType:访问策略

当然,也可以使用其他方法创建数据源,本文代码如下:

package com.dalaoyang.database;


import com.dangdang.ddframe.rdb.sharding.api.MasterSlaveDataSourceFactory;
import com.dangdang.ddframe.rdb.sharding.api.strategy.slave.MasterSlaveLoadBalanceStrategyType;
import com.dangdang.ddframe.rdb.sharding.keygen.DefaultKeyGenerator;
import com.dangdang.ddframe.rdb.sharding.keygen.KeyGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author yangyang
 * @date 2019/1/29
 */
@Configuration
public class DataSourceConfig {

    @Autowired
    private Database0Config database0Config;

    @Autowired
    private Database1Config database1Config;

    @Autowired
    private Database2Config database2Config;

    @Bean
    public DataSource getDataSource() throws SQLException {
        return buildDataSource();
    }

    private DataSource buildDataSource() throws SQLException {
        //设置从库数据源集合
        Map<String, DataSource> slaveDataSourceMap = new HashMap<>();
        slaveDataSourceMap.put(database1Config.getDatabaseName(), database1Config.createDataSource());
        slaveDataSourceMap.put(database2Config.getDatabaseName(), database2Config.createDataSource());

        //获取数据源对象
        DataSource dataSource = MasterSlaveDataSourceFactory.createDataSource("masterSlave",database0Config.getDatabaseName()
                ,database0Config.createDataSource(), slaveDataSourceMap, MasterSlaveLoadBalanceStrategyType.getDefaultStrategyType());
        return dataSource;
    }


    @Bean
    public KeyGenerator keyGenerator() {
        return new DefaultKeyGenerator();
    }

}

3.8 Controller

Controller做为测试类,创建两个方法,save方法和getAll方法,其中:

  • save方法用于测试主库的插入和修改
  • getAll方法用于测试读数据

UserController类如下所示。

package com.dalaoyang.controller;

import com.dalaoyang.entity.User;
import com.dalaoyang.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    @Autowired
    private UserRepository userRepository;

    @GetMapping("save")
    public void save(){
        User user = new User();
        user.setId(100L);
        user.setName("dalaoyang");
        user.setCity("beijing");
        userRepository.save(user);
    }

    @GetMapping("getAll")
    public Object getAll(){
        return userRepository.findAll();
    }
}

4.测试

4.1 测试主库

使用postman访问http://localhost:8080/save,控制台如图所示。

再次访问,如图。

主键冲突了,其实这是由于插入的时候使用的database0,但是查询使用的是database1和database2,但是我在从库内并没有ID是100的数据,所以JPA判定我为插入,但是数据库内缺有这样的数据。

我们接下来测试一下查询。访问http://localhost:8080/getAll

再次访问,如图。

证明从库的读取是正常的,接下来修改从库的ID为100。然后访问http://localhost:8080/save,查看控制台如图。

因为存在了ID为100的数据,所以SQL为修改语句。

5.源码

源码地址:https://gitee.com/dalaoyang/springboot_learn/tree/master/springboot2_shardingjdbc_dxfl

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
7月前
|
关系型数据库 MySQL Java
MySQL的主从复制 && SpringBoot整合Sharding-JDBC解决读写分离
MySQL的主从复制 && SpringBoot整合Sharding-JDBC解决读写分离
150 0
|
3月前
|
Java 数据库连接 数据库
SpringBoot 整合jdbc和mybatis
本文详细介绍了如何在SpringBoot项目中整合JDBC与MyBatis,并提供了具体的配置步骤和示例代码。首先,通过创建用户实体类和数据库表来准备基础环境;接着,配置Maven依赖、数据库连接及属性;最后,分别展示了JDBC与MyBatis的集成方法及其基本操作,包括增删查改等功能的实现。适合初学者快速入门。
100 3
SpringBoot 整合jdbc和mybatis
|
2月前
|
SQL Java 数据库
Springboot+spring-boot-starter-data-jdbc实现数据库的操作
本文介绍了如何使用Spring Boot的spring-boot-starter-data-jdbc依赖来操作数据库,包括添加依赖、配置数据库信息和编写基于JdbcTemplate的数据访问代码。
151 2
|
7月前
|
Java 数据库连接 网络安全
springboot使用Pivotal Greenplum JDBC如何进行配置
【5月更文挑战第23天】springboot使用Pivotal Greenplum JDBC如何进行配置
194 6
|
7月前
|
SQL Java 数据库连接
Springboot框架整合Spring JDBC操作数据
JDBC是Java数据库连接API,用于执行SQL并访问多种关系数据库。它包括一系列Java类和接口,用于建立数据库连接、创建数据库操作对象、定义SQL语句、执行操作并处理结果集。直接使用JDBC涉及七个步骤,包括加载驱动、建立连接、创建对象、定义SQL、执行操作、处理结果和关闭资源。Spring Boot的`spring-boot-starter-jdbc`简化了这些步骤,提供了一个在Spring生态中更便捷使用JDBC的封装。集成Spring JDBC需要添加相关依赖,配置数据库连接信息,并通过JdbcTemplate进行数据库操作,如插入、更新、删除和查询。
80 0
|
7月前
|
负载均衡 算法 Java
Sharding-JDBC如何实现读写分离
通过以上步骤,Sharding-JDBC能够实现数据库的读写分离,从而提高应用程序的读取性能。欢迎关注威哥爱编程,一起学习成长。
108 0
|
7月前
|
Java 数据库
SpringBoot出现com.mysql.cj.jdbc.exceptions.MysqlDataTruncation: Data truncation: Incorrect datetime va
SpringBoot出现com.mysql.cj.jdbc.exceptions.MysqlDataTruncation: Data truncation: Incorrect datetime va
411 0
|
2月前
|
Java 关系型数据库 MySQL
mysql5.7 jdbc驱动
遵循上述步骤,即可在Java项目中高效地集成MySQL 5.7 JDBC驱动,实现数据库的访问与管理。
311 1
|
2月前
|
SQL 分布式计算 关系型数据库
Hadoop-24 Sqoop迁移 MySQL到Hive 与 Hive到MySQL SQL生成数据 HDFS集群 Sqoop import jdbc ETL MapReduce
Hadoop-24 Sqoop迁移 MySQL到Hive 与 Hive到MySQL SQL生成数据 HDFS集群 Sqoop import jdbc ETL MapReduce
96 0
|
2月前
|
SQL 分布式计算 关系型数据库
Hadoop-23 Sqoop 数据MySQL到HDFS(部分) SQL生成数据 HDFS集群 Sqoop import jdbc ETL MapReduce
Hadoop-23 Sqoop 数据MySQL到HDFS(部分) SQL生成数据 HDFS集群 Sqoop import jdbc ETL MapReduce
43 0

热门文章

最新文章