Mybatis中如何生成全局主键ID

Mybatis中如何生成全局主键ID

上篇我讲了在mybatis中,新增数据时如何返回自增主键,依靠的是数据库可设置主键自动递增的机制,但是这种方法生成的主键扩展性比较差,如在一个分布式的系统中,会造成主键重复的问题。今天这篇文章讲下在分布式系统中如何生成全局唯一主键ID。

常见的解决方案大家可以参考下这篇文章,作者基于漫画的方式讲解的很清晰;

漫画:什么是SnowFlake算法?

本文主要讲下在spring boot中如何集成SnowFlake算法,生成全局主键

SnowFlake算法github的网址: https://github.com/twitter/snowflake 但貌似已经不维护了 ~~

好了在上一篇文章代码的基础上,开始撸代码~~

 

1.第一步导入IdWorker.java

public class IdWorker {
private final static long twepoch = 1483200000000L;
// 机器标识位数
private final static long workerIdBits = 5L;
// 数据中心标识位数
private final static long datacenterIdBits = 5L;
// 机器ID最大值
private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
// 数据中心ID最大值
private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
// 毫秒内自增位
private final static long sequenceBits = 12L;
// 机器ID偏左移12位
private final static long workerIdShift = sequenceBits;
// 数据中心ID左移17位
private final static long datacenterIdShift = sequenceBits + workerIdBits;
// 时间毫秒左移22位
private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

private final static long sequenceMask = -1L ^ (-1L << sequenceBits);
/* 上次生产id时间戳 */
private static long lastTimestamp = -1L;
// 0,并发控制
private long sequence = 0L;

private final long workerId;
// 数据标识id部分
private final long datacenterId;

/**
* @param workerId
* 工作机器ID
* @param datacenterId
* 序列号
*/
public IdWorker(long workerId, long datacenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
}
/**
* 获取下一个ID
* @return
*/
public synchronized long nextId() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}

if (lastTimestamp == timestamp) {
// 当前毫秒内,则+1
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
// 当前毫秒内计数满了,则等待下一秒
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
// ID偏移组合生成最终的ID,并返回ID
long nextId = ((timestamp - twepoch) << timestampLeftShift)
| (datacenterId << datacenterIdShift)
| (workerId << workerIdShift) | sequence;

return nextId;
}

private long tilNextMillis(final long lastTimestamp) {
long timestamp = this.timeGen();
while (timestamp <= lastTimestamp) {
timestamp = this.timeGen();
}
return timestamp;
}

private long timeGen() {
return System.currentTimeMillis();
}
}

2.对算法参数进行配置

导入idworker类后要想正常使用,肯定是要先配置一下:

首先在application.yml中新建如下2个配置参数,分别对应机器ID和序列号,多实例部署的话,不同实例该参数需配置不同值

util:

#工作机器ID   workerId: 5

#序列号          datacenterId: 10

然后创建配置类CustomerConfig,将IdWorker注入为bean

package com.wg.demo.config;

import com.wg.demo.util.IdWorker;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author: wangxiaobo
 * @create: 2020-10-20 22:41
 **/
@Configuration
@ConfigurationProperties(prefix = "util")
@Data
public class CustomerConfig {
    private Long workerId;
    private Long datacenterId;
    @Bean
    public IdWorker createIdWorker() {
        IdWorker worker = new IdWorker(workerId, datacenterId);
        return worker;
    }
}

3. 如何使用

配置好IdWorker后,使用非常简单,只要每次在调用insert操作时,我们显示的调用idworker的nextId()方法获取一个全局ID,然后将生成的ID赋值给即将插入的对象即可,具体代码如下:

DepartmentController.java

package com.wg.demo.controller;

import com.wg.demo.common.ResultMsg;
import com.wg.demo.po.Department;
import com.wg.demo.service.DepartmentService;
import com.wg.demo.util.IdWorker;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
* @Author: wanggang.io
* @Date: 2019/6/27 16:08
* @todo
*/
@Api(description = "department")
@RestController
@RequestMapping("dept")
public class DepartmentController {
@Autowired
public DepartmentService departmentService;

@Autowired
public IdWorker idWorker;

@ApiOperation(value = "新增部门")
@PostMapping("new")
// @RequestMapping(value = "/new" , method = {RequestMethod.POST} , produces = "application/json;charset=UTF-8")
public ResultMsg newDepartment(@RequestBody Department department)
{
//每次插入数据时,调用nextId()获取一个全局ID
department.setId(idWorker.nextId());
int result = departmentService.insertDept(department);
return ResultMsg.getMsg(department);
}
}

4.测试

打开浏览器输入:http://localhost:9292/mybatis/swagger-ui.html 进入我们Swagger接口测试界面找到dept->new方法,输入测试参数,然后点击按钮try it out

 

 

 

5.总结

相比较上篇文章中自增主键的方式,每条涉及插入的SQL语句都要设置useGeneratedKeys和keyProperty,全局获取ID的方式只要一次配置后,后边每次插入操作只需调用下idWorker.nextId()方法,简直不要太简单

建议大家在在实际开发过程中都采用这种方式来生成主键ID,可谓一劳永逸

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
mybatis-plus提供了多种主键生成策略,常用的有以下几种: 1. 默认的主键生成策略:使用数据库自增长方式生成主键。对于MySQL数据库,使用AUTO_INCREMENT关键字,对于Oracle数据库,使用SEQUENCE序列。 2. UUID主键生成策略:使用Java的UUID生成主键生成主键是32位的16进制字符串,不重复且无序。 3. 雪花算法主键生成策略:雪花算法是Twitter开源的一个分布式ID生成算法,可以保证在分布式环境下生成ID全局唯一且有序。 在使用mybatis-plus时,可以通过注解@TableId和@TableField来指定主键和字段的属性,包括主键生成策略。例如: ``` @TableId(value = "id", type = IdType.AUTO) private Long id; @TableId(value = "uuid", type = IdType.UUID) private String uuid; @TableId(value = "id", type = IdType.ID_WORKER) private Long idWorker; @TableId(value = "id", type = IdType.SNOWFLAKE) private Long snowflakeId; ``` 其,@TableId注解的value属性指定了数据库表对应的字段名,type属性指定了主键生成策略。MyBatis-Plus是一个基于MyBatis的增强工具,提供了许多方便开发的特性和功能,可以大大减少我们在开发的代码量和复杂度。 以下是使用MyBatis-Plus的主要策略: 1. 使用代码生成器:MyBatis-Plus提供了一个代码生成器,可以自动生成Mapper、Entity和Service层的代码,大大减少了手写代码的工作量。我们只需要配置好生成器的相关信息,就可以快速生成我们需要的代码。 2. 使用MyBatis-Plus的BaseMapper:MyBatis-Plus的BaseMapper提供了很多基础的CRUD操作,如selectById、selectBatchIds、insert、update、delete等,可以大大减少我们手写Mapper的工作量。同时,我们也可以扩展BaseMapper,添加自定义的方法。 3. 使用MyBatis-Plus的Wrapper:MyBatis-Plus的Wrapper提供了一种更加灵活的查询方式,可以通过构建Wrapper对象来构造复杂的查询条件。Wrapper可以用于查询、更新和删除操作。 4. 使用MyBatis-Plus的分页插件:MyBatis-Plus提供了一个分页插件,可以方便地进行分页查询操作。我们只需要在Mapper接口定义对应的方法,然后在Service层调用即可。 5. 使用MyBatis-Plus的自动填充功能:MyBatis-Plus的自动填充功能可以自动为我们的实体类填充一些公共字段,如创建时间、更新时间等。我们只需要在实体类定义对应的字段和注解,就可以自动填充这些字段。 总的来说,MyBatis-Plus是一个非常方便实用的工具,可以大大提高我们的开发效率和代码质量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

没有学不会的技术,只有不学习的人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值