(SpringBoot)Hibernate主键生成策略-雪花算法

雪花算法是越来越流行了,他比传统的id自增或者uuid更加的安全,号称在全局分布式系统中唯一.关于他的详细,我这里就不多介绍,想了解更多的找度娘就好.

这里借用大神已经写好的雪花算法Java代码实现:

package com.zwd.hibernate.config;

import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;

import java.net.Inet4Address;
import java.net.UnknownHostException;

/**
 * twitter的snowflake算法 -- java实现
 *
 * @author beyond
 * @date 2016/11/26
 * @see <a href="https://github.com/beyondfengyu/SnowFlake/blob/master/SnowFlake.java">github<a/>
 */
public class SnowFlake {

    /**
     * 起始的时间戳
     */
    private final static long START_STAMP = 1480166465631L;

    /**
     * 序列号占用的位数
     */
    private final static long SEQUENCE_BIT = 12;
    /**
     * 机器标识占用的位数
     */
    private final static long MACHINE_BIT = 5;
    /**
     * 数据中心占用的位数
     */
    private final static long DATACENTER_BIT = 5;


    /**
     * 最大支持的数据中心数量:31
     */
    private final static long MAX_DATACENTER_NUM = ~(-1L << DATACENTER_BIT);
    /**
     * 最大支持的机器数量:31
     */
    private final static long MAX_MACHINE_NUM = ~(-1L << MACHINE_BIT);
    private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);

    /**
     * 每一部分向左的位移
     */
    private final static long MACHINE_LEFT = SEQUENCE_BIT;
    private final static long DATACENTER_LEFT = MACHINE_LEFT + MACHINE_BIT;
    private final static long TIMESTAMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;

    /**
     * 数据中心
     */
    private final long dataCenterId;

    /**
     * 机器标识
     */
    private final long machineId;

    /**
     * 序列号
     */
    private long sequence = 0L;

    /**
     * 上一次时间戳
     */
    private long lastTimestamp = -1L;

    public SnowFlake(long dataCenterId) {
        long machineId = generateMachineId();

        if (dataCenterId > MAX_DATACENTER_NUM || dataCenterId < 0) {
            throw new IllegalArgumentException("dataCenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
        }
        if (machineId > MAX_MACHINE_NUM || machineId < 0) {
            throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
        }
        this.dataCenterId = dataCenterId;
        this.machineId = machineId;
    }

    /**
     * 产生下一个ID
     *
     * @return id
     */
    public synchronized long nextId() {
        long currentStamp = getNewTimestamp();
        if (currentStamp < lastTimestamp) {
            throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
        }

        if (currentStamp == lastTimestamp) {
            //相同毫秒内,序列号自增
            sequence = (sequence + 1) & MAX_SEQUENCE;
            //同一毫秒的序列数已经达到最大
            if (sequence == 0L) {
                currentStamp = getNextMill();
            }
        } else {
            //不同毫秒内,序列号置为0
            sequence = 0L;
        }

        lastTimestamp = currentStamp;

        return (currentStamp - START_STAMP) << TIMESTAMP_LEFT
                | dataCenterId << DATACENTER_LEFT
                | machineId << MACHINE_LEFT
                | sequence;
    }

    private long getNextMill() {
        long mill = getNewTimestamp();
        while (mill <= lastTimestamp) {
            mill = getNewTimestamp();
        }
        return mill;
    }

    /**
     * 获取当前时间戳
     *
     * @return 时间戳
     */
    private long getNewTimestamp() {
        return System.currentTimeMillis();
    }

    private static Long generateMachineId() {
        try {
            String hostAddress = Inet4Address.getLocalHost().getHostAddress();
            int[] ints = StringUtils.toCodePoints(hostAddress);
            int sums = 0;
            for (int b : ints) {
                sums += b;
            }
            return (long) (sums % 32);
        } catch (UnknownHostException e) {
            // 如果获取失败,则使用随机数备用
            return RandomUtils.nextLong(0, 31);
        }
    }
}

第二步:写一个自己的id生成器

package com.zwd.hibernate.config;

import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGenerator;
import org.springframework.stereotype.Component;

import java.io.Serializable;

@Component
public class SnowFlakeIdGenerator implements IdentifierGenerator {


    private static final SnowFlake snowFlake;

    static {
        //这里为了测试方便,参数我直接HardCode
        snowFlake = new SnowFlake(1);
    }


    /**
     * Generate a new identifier.
     *
     * @param session The session from which the request originates
     * @param object  the entity or collection (idbag) for which the id is being generated
     * @return a new identifier
     * @throws HibernateException Indicates trouble generating the identifier
     */
    @Override
    public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
        return snowFlake.nextId()+"";
    }
}

PS:这里本来想用注入的方式漂亮的引入SnowFlake类,但是遇到一个问题他需要带参,而一般的@Component只能作用无参构造.一时半会也没有想到注入的好办法,只能写成静态代码块实例化,这个参数1是雪花算法的唯一id(如果有小伙伴能注入,请留言告诉我哦,谢谢!)

第三步:实践

package com.zwd.hibernate.model;

import lombok.Data;
import org.hibernate.annotations.GenericGenerator;

import javax.persistence.*;
import java.io.Serializable;

@Entity
@Table(name = "t_user")
@Data
public class User implements Serializable {
    @Id
    @GeneratedValue(generator = "snowFlakeId")
    @GenericGenerator(name = "snowFlakeId", strategy = "com.zwd.hibernate.config.SnowFlakeIdGenerator")
    private String id;

    private String userName;
    private String password;


}
public void saveUserTest() {
        for (int i = 0; i < 100; i++) {
            User user = new User();
            user.setUserName("name" + i);
            user.setPassword(i + "");
            UserDao.save(user);
        }
    }

随便写了个实体类和service进行新增100条数据的测试,效果如下:

 ~~~~~~~~~~~~~~非常好用~~~~~~~~~

猜你喜欢

转载自blog.csdn.net/qq183293/article/details/120246086
今日推荐