SpringBoot+MybatisPlus实现sharding-jdbc分表

介绍

学习sharding-jdbc的分库分表功能,以分库,分表,分库分表三种方式来实现,此文章只展示分表,为简洁其余两种分篇写

1.环境搭建

代码结构

SpringBoot+MybatisPlus实现sharding-jdbc分表_第1张图片

数据库搭建(一个mysql数据库下多个表,表结构一致)

SpringBoot+MybatisPlus实现sharding-jdbc分表_第2张图片

SpringBoot+MybatisPlus实现sharding-jdbc分表_第3张图片

pom.xml依赖

 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.20</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
            <version>4.0.0-RC1</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.0.5</version>
        </dependency>
    </dependencies>

application.yml (配置文件分离,结构更清晰)

spring:
  main:
    allow-bean-definition-overriding: true # 需要配置否则加载数据源报错 是否允许定义重名的bean对象覆盖原有的bean
  profiles:
    active: table

mybatis-plus:
  mapperLocations: classpath*:mapper/*Mapper.xml

application-table.yml

# 数据源命名
spring.shardingsphere.datasource.names=book2021

# datasource1
spring.shardingsphere.datasource.book2021.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.book2021.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.book2021.url=jdbc:mysql://192.168.31.212:3306/book2021?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowMultiQueries=true
spring.shardingsphere.datasource.book2021.username=root
spring.shardingsphere.datasource.book2021.password=123


#库名.表名
spring.shardingsphere.sharding.tables.book.actual-data-nodes=book2021.book_info_20210${1..9},book2021.book_info_20211${0..2}

# 数据表分片字段
spring.shardingsphere.sharding.tables.book.table-strategy.standard.sharding-column=create_time
# 数据表分表策略
spring.shardingsphere.sharding.tables.book.table-strategy.standard.preciseAlgorithmClassName=cn.xwl.xshardingjdbc.config.table.PreciseTableShardingAlgorithm
spring.shardingsphere.sharding.tables.book.table-strategy.standard.rangeAlgorithmClassName=cn.xwl.xshardingjdbc.config.table.RangeTableShardingAlgorithm

# 打印sql
spring.shardingsphere.props.sql.show=true



2.代码编写

1.精准分片策略

package cn.xwl.xshardingjdbc.config.table;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;

import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collection;


/**
 * 表精确分片算法
 */

public class PreciseTableShardingAlgorithm implements PreciseShardingAlgorithm<Timestamp> {


	/**
	 * 表精确分片算法
	 *
	 * @param availableTargetNames 所有配置的表列表,这里代表所匹配到库的所有表
	 * @param shardingValue        分片值
	 * @return 所匹配表的结果
	 */
	@Override
	public String doSharding(Collection<String> availableTargetNames,
							 PreciseShardingValue<Timestamp> shardingValue) {
		// 分片键值
		Timestamp value = shardingValue.getValue();

		if (value == null) {
			throw new UnsupportedOperationException("分表出现异常:{分片键值为空}");
		}
		LocalDateTime localDateTime = value.toLocalDateTime();
		//根据键值匹配对应的表
		String yearMonth = localDateTime.format(DateTimeFormatter.ofPattern("yyyyMM"));
		for (String availableTargetName : availableTargetNames) {
			if (availableTargetName.endsWith(yearMonth)) {
				return availableTargetName;
			}
		}
		throw new UnsupportedOperationException();
	}
}

2.区间分片策略


package cn.xwl.xshardingjdbc.config.table;

import com.google.common.collect.Range;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingValue;

import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Stream;


/**
 * 表范围分片算法
 *
 */

public class RangeTableShardingAlgorithm implements RangeShardingAlgorithm<Timestamp> {
 

	/**
	 * 表范围分片算法
	 */

	@Override
	public Collection<String> doSharding(Collection<String> availableTargetNames,
										 RangeShardingValue<Timestamp> rangeShardingValue) {
		Range<Timestamp> range = rangeShardingValue.getValueRange();
		LocalDateTime minTime = range.lowerEndpoint().toLocalDateTime();
		LocalDateTime maxTime = range.upperEndpoint().toLocalDateTime();
		return getMonthBetween(minTime, maxTime, availableTargetNames);
	}
 

	/**
	 * 计算有效的库表名
	 */

	public static Collection<String> getMonthBetween(LocalDateTime minTime, LocalDateTime maxTime,
			Collection<String> availableTargetNames) {
		Collection<String> result = new ArrayList<>();
		LocalDate startDate = minTime.toLocalDate();
		LocalDate endDate = maxTime.toLocalDate();
		long distance = ChronoUnit.MONTHS.between(startDate, endDate);

		Stream.iterate(startDate, d -> d.plusMonths(1)).limit(distance + 1).forEach(f -> {
			availableTargetNames.forEach(availableTargetName -> {
				if (availableTargetName.endsWith(f.format(DateTimeFormatter.ofPattern("yyyyMM")))) {
					result.add(availableTargetName);
				}
			});
		});
		return result;
	}
}

3.mybatisplus配置实体类

package cn.xwl.xshardingjdbc.model;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.ToString;

import java.math.BigDecimal;
import java.time.LocalDateTime;

@Data
@ToString
public class Book {

    @TableId(type = IdType.ID_WORKER) //id生成策略 唯一id
    private Long id;
 
    private String name;
 
    private BigDecimal money;

    private LocalDateTime createTime;

}
package cn.xwl.xshardingjdbc.model;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface BookMapper extends BaseMapper<Book> {
 
}

3.测试

package cn.xwl.xshardingjdbc;


import cn.xwl.xshardingjdbc.model.Book;
import cn.xwl.xshardingjdbc.model.BookMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;

@SpringBootTest
class TestDatabase {

    @Resource
    public BookMapper bookMapper;

    //插入 这里采用的是= 所以采用的是精准分片策略
    @Test
    void insert() {
        Book book = new Book();
        book.setName("王大毛");
        LocalDateTime dateTime=LocalDateTime.of(2021,12,1,12,59,50);
        book.setCreateTime(dateTime);
        book.setMoney(new BigDecimal("888.88"));
        bookMapper.insert(book);
    }

    //查 这里采用了between所以会触发区间分片策略
    @Test
    void select() {
        LocalDateTime start=LocalDateTime.of(2021,12,5,12,59,50);
        LocalDateTime end=LocalDateTime.of(2022,12,5,12,59,50);
        List<Book> books = bookMapper.selectList(new LambdaQueryWrapper<Book>().between(Book::getCreateTime,start,end));
        books.forEach(x -> {
            System.out.println(x.toString());
        });
    }

    //改
    @Test
    void update() {
        Book book=new Book();
        book.setId(1693868063067258882L);
        book.setName("李大帅");
        bookMapper.updateById(book);
    }

    //删
    @Test
    void delete() {
        bookMapper.delete(new LambdaQueryWrapper<Book>()
                         .eq(Book::getId,1693868063067258882L));
    }




}

你可能感兴趣的:(spring,boot,后端,java)