当前位置: 首页 > news >正文

Sharding-Jdbc+Mybatis+SpringBoot的分库分表实现

Sharding-Jdbc+Mybatis+SpringBoot的分库分表实现

1、sharding jdbc简介

Sharding-JDBC定位为轻量级java框架,使用客户端直连数据库,以jar包形式提供服务,未使用中间层,无需额外

部署,无其他依赖,DBA也无需改变原有的运维方式,可理解为增强版的JDBC驱动,旧代码迁移成本几乎为零。

它主要的功能:分库分表;读写分离; 柔性事务;分布式主键;兼容性;灵活多样的配置;分布式治理能力 (2.0

新功能)。

前两个功能无需多说,柔性事务主要表现在:最大努力送达型事务,TCC型事务(TBD);分布式主键也不需要多

说,兼容性主要体现在:可适用于任何基于java的ORM框架,如:JPA,Hibernate,Mybatis,Spring JDBC

Template或直接使用JDBC,可基于任何第三方的数据库连接池,如:DBCP,C3P0,BoneCP, Druid等,理论上

可支持任意实现JDBC规范的数据库。目前支持MySQL,Oracle,SQLServer和PostgreSQL;灵活多样的配置主要

支持这些配置Java,YAML,Inline表达式,Spring命名空间,Spring boot starter。

分布式治理能力: 配置集中化与动态化,可支持数据源、表与分片策略的动态切换(2.0.0.M1), 客户端的数据库

治理,数据源失效自动切换(2.0.0.M2),。

当当网的sharding jdbc抽离出来了,由Apache负责,所以我的依赖从dangdang的:

<dependency><groupId>com.dangdang</groupId><artifactId>sharding-jdbc-core</artifactId><version>1.3.3</version>
</dependency>

变为了:

<dependency><groupId>io.shardingjdbc</groupId><artifactId>sharding-jdbc-core</artifactId><version>2.0.3</version>
</dependency>

2、开始动手

下面进行sharding jdbc的分库分表。

2.1 建库建表

建立数据库和表(分别建了两个库两张表):

CREATE DATABASE `user_0`;
use `user_0`;
CREATE TABLE `user_info_1` (`user_id` bigint(19) NOT NULL,`user_name` varchar(45) DEFAULT NULL,`account` varchar(45) NOT NULL,`password` varchar(45) DEFAULT NULL,PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE `user_info_0` (`user_id` bigint(19) NOT NULL,`user_name` varchar(45) DEFAULT NULL,`account` varchar(45) NOT NULL,`password` varchar(45) DEFAULT NULL,PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE DATABASE `user_1`;
use `user_1`;
CREATE TABLE `user_info_1` (`user_id` bigint(19) NOT NULL,`user_name` varchar(45) DEFAULT NULL,`account` varchar(45) NOT NULL,`password` varchar(45) DEFAULT NULL,PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE `user_info_0` (`user_id` bigint(19) NOT NULL,`user_name` varchar(45) DEFAULT NULL,`account` varchar(45) NOT NULL,`password` varchar(45) DEFAULT NULL,PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2.2 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 https://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.5.4</version><relativePath/></parent><groupId>com.example</groupId><artifactId>springboot-shardingjdbc</artifactId><version>0.0.1-SNAPSHOT</version><name>springboot-shardingjdbc</name><description>springboot-shardingjdbc</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>1.3.1</version></dependency><dependency><groupId>io.shardingjdbc</groupId><artifactId>sharding-jdbc-core</artifactId><version>2.0.3</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.44</version></dependency><dependency><groupId>commons-dbcp</groupId><artifactId>commons-dbcp</artifactId><version>1.4</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

2.3 mybatis的配置

我创建一个entity包(实体包),里面有一个UserInfo的类:

package com.example.entity;public class UserInfo {private Long userId;private String userName;private String account;private String password;public Long getUserId() {return userId;}public void setUserId(Long userId) {this.userId = userId;}public void setUserName(String userName) {this.userName = userName == null ? null : userName.trim();}public String getAccount() {return account;}public void setAccount(String account) {this.account = account == null ? null : account.trim();}public String getPassword() {return password;}public void setPassword(String password) {this.password = password == null ? null : password.trim();}
}

然后我有一个mapper包(映射包),里面有一个映射类UserInfoMapper

package com.example.mapper;import com.example.entity.UserInfo;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface UserInfoMapper {int insert(UserInfo record);int insertSelective(UserInfo record);UserInfo selectByPrimaryKey(Long userId);int updateByPrimaryKeySelective(UserInfo record);int updateByPrimaryKey(UserInfo record);
}

还有再resources目录下的mapper目录中的UserInfoMapper.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.example.mapper.UserInfoMapper"><resultMap id="BaseResultMap" type="com.example.entity.UserInfo"><id column="user_id" jdbcType="BIGINT" property="userId" /><result column="user_name" jdbcType="VARCHAR" property="userName" /><result column="account" jdbcType="VARCHAR" property="account" /><result column="password" jdbcType="VARCHAR" property="password" /></resultMap><sql id="Base_Column_List">user_id, user_name, account, password</sql><select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">select <include refid="Base_Column_List" />from user_infowhere user_id = #{userId,jdbcType=BIGINT}</select><insert id="insert" parameterType="com.example.entity.UserInfo">insert into user_info (user_id, user_name, account, password)values (#{userId,jdbcType=BIGINT}, #{userName,jdbcType=VARCHAR}, #{account,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR})</insert><insert id="insertSelective" parameterType="com.example.entity.UserInfo">insert into user_info<trim prefix="(" suffix=")" suffixOverrides=","><if test="userId != null">user_id,</if><if test="userName != null">user_name,</if><if test="account != null">account,</if><if test="password != null">password,</if></trim><trim prefix="values (" suffix=")" suffixOverrides=","><if test="userId != null">#{userId,jdbcType=BIGINT},</if><if test="userName != null">#{userName,jdbcType=VARCHAR},</if><if test="account != null">#{account,jdbcType=VARCHAR},</if><if test="password != null">#{password,jdbcType=VARCHAR},</if></trim></insert><update id="updateByPrimaryKeySelective" parameterType="com.example.entity.UserInfo">update user_info<set><if test="userName != null">user_name = #{userName,jdbcType=VARCHAR},</if><if test="account != null">account = #{account,jdbcType=VARCHAR},</if><if test="password != null">password = #{password,jdbcType=VARCHAR},</if></set>where user_id = #{userId,jdbcType=BIGINT}</update><update id="updateByPrimaryKey" parameterType="com.example.entity.UserInfo">update user_infoset user_name = #{userName,jdbcType=VARCHAR},account = #{account,jdbcType=VARCHAR},password = #{password,jdbcType=VARCHAR}where user_id = #{userId,jdbcType=BIGINT}</update>
</mapper>

mybatis-config.xml配置文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><typeAliases><package name="com.example.entity"/></typeAliases><mappers><mapper resource="mapper/UserInfoMapper.xml"/></mappers>
</configuration>

2.4 sharding jdbc的配置

首先,我们先实现我们的分库分表的策略分库策略的类,DemoDatabaseShardingAlgorithm

package com.example.config;import io.shardingjdbc.core.api.algorithm.sharding.PreciseShardingValue;
import io.shardingjdbc.core.api.algorithm.sharding.standard.PreciseShardingAlgorithm;import java.util.Collection;public class DemoDatabaseShardingAlgorithm implements PreciseShardingAlgorithm<Long> {@Overridepublic String doSharding(Collection<String> collection, PreciseShardingValue<Long> preciseShardingValue) {for (String each : collection) {System.out.println("database:" + each);if (each.endsWith(Long.parseLong(preciseShardingValue.getValue().toString()) % 2 + "")) {return each;}}throw new IllegalArgumentException();}
}

使用io.shardingjdbc,就应该实现PreciseShardingAlgorithm接口,然后实现doSharding方法,对应SQL中

的=、IN,还有RangeShardingAlgorithm接口中,对应SQL中的BETWEEN AND,因为我只需要=、in操作,所

以只实现了PreciseShardingAlgorithm接口,你如果都需要,你可以都实现。

如果你使用的当当网的sharding jdbc,那么你需要实现SingleKeyDatabaseShardingAlgorithm这个接口,实

现其中的三个方法。

分表策略的类,DemoTableShardingAlgorithm

package com.example.config;import io.shardingjdbc.core.api.algorithm.sharding.PreciseShardingValue;
import io.shardingjdbc.core.api.algorithm.sharding.standard.PreciseShardingAlgorithm;import java.util.Collection;public class DemoTableShardingAlgorithm implements PreciseShardingAlgorithm<Long> {@Overridepublic String doSharding(Collection<String> collection, PreciseShardingValue<Long> preciseShardingValue) {for (String each : collection) {System.out.println("table:" + each);if (each.endsWith(Long.parseLong(preciseShardingValue.getValue().toString()) % 2 + "")) {return each;}}throw new IllegalArgumentException();}
}

与分库的步骤一致,也是需要实现PreciseShardingAlgorithmRangeShardingAlgorithm两个接口的类。

剩下的就是最重要的部分,sharding jdbc的配置:

DataSourceConfig

package com.example.config;import io.shardingjdbc.core.api.config.ShardingRuleConfiguration;
import io.shardingjdbc.core.api.config.TableRuleConfiguration;
import io.shardingjdbc.core.api.config.strategy.StandardShardingStrategyConfiguration;
import io.shardingjdbc.core.jdbc.core.datasource.ShardingDataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
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;
import java.util.HashMap;
import java.util.Map;@Configuration
@MapperScan(basePackages = "com.example.demo.mapper", sqlSessionTemplateRef = "testSqlSessionTemplate")
public class DataSourceConfig {@Bean(name = "shardingDataSource")DataSource getShardingDataSource() throws SQLException {ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();shardingRuleConfig.getTableRuleConfigs().add(getUserTableRuleConfiguration());shardingRuleConfig.getBindingTableGroups().add("user_info");shardingRuleConfig.setDefaultDatabaseShardingStrategyConfig(new StandardShardingStrategyConfiguration("user_id", DemoDatabaseShardingAlgorithm.class.getName()));shardingRuleConfig.setDefaultTableShardingStrategyConfig(new StandardShardingStrategyConfiguration("user_id", DemoTableShardingAlgorithm.class.getName()));return new ShardingDataSource(shardingRuleConfig.build(createDataSourceMap()));}@BeanTableRuleConfiguration getUserTableRuleConfiguration() {TableRuleConfiguration orderTableRuleConfig = new TableRuleConfiguration();orderTableRuleConfig.setLogicTable("user_info");orderTableRuleConfig.setActualDataNodes("user_${0..1}.user_info_${0..1}");orderTableRuleConfig.setKeyGeneratorColumnName("user_id");return orderTableRuleConfig;}/*** 需要手动配置事务管理器** @param shardingDataSource* @return*/@Beanpublic DataSourceTransactionManager transactitonManager(DataSource shardingDataSource) {return new DataSourceTransactionManager(shardingDataSource);}@Bean@Primarypublic SqlSessionFactory sqlSessionFactory(DataSource shardingDataSource) throws Exception {SqlSessionFactoryBean bean = new SqlSessionFactoryBean();bean.setDataSource(shardingDataSource);bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));return bean.getObject();}@Bean@Primarypublic SqlSessionTemplate testSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) throws Exception {return new SqlSessionTemplate(sqlSessionFactory);}private Map<String, DataSource> createDataSourceMap() {Map<String, DataSource> result = new HashMap<>();result.put("user_0", createDataSource("user_0"));result.put("user_1", createDataSource("user_1"));return result;}private DataSource createDataSource(final String dataSourceName) {BasicDataSource result = new BasicDataSource();result.setDriverClassName(com.mysql.jdbc.Driver.class.getName());result.setUrl(String.format("jdbc:mysql://localhost:3306/%s", dataSourceName));result.setUsername("root");result.setPassword("root");return result;}}

2.5 启动类

package com.example;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.transaction.annotation.EnableTransactionManagement;@SpringBootApplication
@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
@EnableTransactionManagement(proxyTargetClass = true)
public class SpringbootShardingjdbcApplication {public static void main(String[] args) {SpringApplication.run(SpringbootShardingjdbcApplication.class, args);}}

2.6 配置文件

mybatis.config-location=classpath:mybatis-config.xml
mybatis.mapper-locations=classpath:mapper/*.xml
#datasource
spring.devtools.remote.restart.enabled=false
#data source1
spring.datasource.user0.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.user0.url=jdbc:mysql://localhost:3306/user_0
spring.datasource.user0.username=root
spring.datasource.user0.password=root
#data source2
spring.datasource.user1.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.user1.url=jdbc:mysql://localhost:3306/user_1
spring.datasource.user1.username=root
spring.datasource.user1.password=root

2.7 测试

我写了一个测试服务和一个测试类

测试服务:DemoService

package com.example.service;import com.example.entity.UserInfo;
import com.example.mapper.UserInfoMapper;
import groovy.util.logging.Slf4j;
import io.shardingjdbc.core.api.HintManager;
import io.shardingjdbc.core.hint.HintManagerHolder;
import org.springframework.stereotype.Service;import javax.annotation.Resource;@Slf4j
@Service
public class DemoService {@ResourceUserInfoMapper userInfoMapper;public static Long userId = 150L;public void demo() {for (int i = 1; i <= 40; i++) {UserInfo userInfo = new UserInfo();userInfo.setUserId(userId);userInfo.setAccount("Account" + i);userInfo.setPassword("pass" + i);userInfo.setUserName("name" + i);userId++;if (i == 3) {HintManagerHolder.clear();HintManager hintManager = HintManager.getInstance();hintManager.addDatabaseShardingValue("user_info", "user_id", 3L);hintManager.addTableShardingValue("user_info", "user_id", 3L);}userInfoMapper.insert(userInfo);}}
}

测试类DemoApplicationTests

package com.example;import com.example.mapper.UserInfoMapper;
import com.example.service.DemoService;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;import javax.annotation.Resource;@RunWith(SpringRunner.class)
@SpringBootTest
class SpringbootShardingjdbcApplicationTests {@ResourceUserInfoMapper userInfoMaper;@ResourceDemoService demoService;@Testpublic void contextLoads() {demoService.demo();}}

查询数据库查看结果:

在这里插入图片描述

在这里插入图片描述

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Vue学习记录之六(组件实战及BEM框架了解)
  • STM32巡回研讨会总结(2024)
  • [000-01-008].第05节:OpenFeign特性-重试机制
  • LeetCode题练习与总结: 数字 1 的个数--233
  • Resnet50网络——口腔癌病变识别
  • 在HTML中添加视频
  • Dell PowerEdge 网络恢复笔记
  • 胤娲科技:谷歌DeepMind祭出蛋白质设计新AI——癌症治疗迎来曙光
  • Thymeleaf
  • 网安面试题1
  • 计算机二级 MS office总结
  • 移植Linux:如何制作rootfs?
  • Alluxio Enterprise AI on K8s FIO 测试教程
  • 2024年中国研究生数学建模竞赛ABCDEF题【附带解题思路代码+结果】
  • 【RPA私教课:UIPath】RPA 赋能科技企业,登录时验证码自动截取
  • 2017 前端面试准备 - 收藏集 - 掘金
  • 2019.2.20 c++ 知识梳理
  • 5、React组件事件详解
  • avalon2.2的VM生成过程
  • ES6系统学习----从Apollo Client看解构赋值
  • Java 实战开发之spring、logback配置及chrome开发神器(六)
  • js对象的深浅拷贝
  • PHP CLI应用的调试原理
  • SpiderData 2019年2月25日 DApp数据排行榜
  • Webpack 4x 之路 ( 四 )
  • Zsh 开发指南(第十四篇 文件读写)
  • 大快搜索数据爬虫技术实例安装教学篇
  • 欢迎参加第二届中国游戏开发者大会
  • 坑!为什么View.startAnimation不起作用?
  • 微信开源mars源码分析1—上层samples分析
  • 项目管理碎碎念系列之一:干系人管理
  • 一个普通的 5 年iOS开发者的自我总结,以及5年开发经历和感想!
  • ​​​​​​​STM32通过SPI硬件读写W25Q64
  • ​猴子吃桃问题:每天都吃了前一天剩下的一半多一个。
  • ​批处理文件中的errorlevel用法
  • # AI产品经理的自我修养:既懂用户,更懂技术!
  • # 利刃出鞘_Tomcat 核心原理解析(二)
  • ###项目技术发展史
  • (02)Hive SQL编译成MapReduce任务的过程
  • (1)Nginx简介和安装教程
  • (c语言+数据结构链表)项目:贪吃蛇
  • (NO.00004)iOS实现打砖块游戏(十二):伸缩自如,我是如意金箍棒(上)!
  • (力扣)1314.矩阵区域和
  • (原創) 如何讓IE7按第二次Ctrl + Tab時,回到原來的索引標籤? (Web) (IE) (OS) (Windows)...
  • (原創) 系統分析和系統設計有什麼差別? (OO)
  • (转)VC++中ondraw在什么时候调用的
  • (转载)(官方)UE4--图像编程----着色器开发
  • *++p:p先自+,然后*p,最终为3 ++*p:先*p,即arr[0]=1,然后再++,最终为2 *p++:值为arr[0],即1,该语句执行完毕后,p指向arr[1]
  • .bat批处理(六):替换字符串中匹配的子串
  • .bat批处理(三):变量声明、设置、拼接、截取
  • .NET Core/Framework 创建委托以大幅度提高反射调用的性能
  • .NET 某和OA办公系统全局绕过漏洞分析
  • .NET 事件模型教程(二)
  • .NET3.5下用Lambda简化跨线程访问窗体控件,避免繁复的delegate,Invoke(转)
  • .NET8使用VS2022打包Docker镜像