Grails 自定义Id生成策略(雪花算法)

参考

参考官网自定义id生成器

class CustomGenerator implements IdentifierGenerator {

    SnowflakeIdGenerator snowflakeIdGenerator = new SnowflakeIdGenerator()

    @Override
    Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
        def id = snowflakeIdGenerator.nextId()
        println id
        return id
    }

    @Override
    boolean supportsJdbcBatchInserts() {
        return super.supportsJdbcBatchInserts()
    }
}

代码里面的SnowflakeIdGenerator 为参考雪花算法实现的java类

package utils;

import org.springframework.beans.factory.annotation.Value;

import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.List;
import java.util.Random;

/**
* @Description:    id生成策略 雪花算法(改进版本)
* @Author:         [email protected]
* @CreateDate:     2020/2/28 21:21
* @UpdateUser:     [email protected]
* @UpdateDate:     2020/2/28 21:21
* @UpdateRemark:   修改内容
* @Version:        1.0
*/
public class SnowflakeIdGenerator {
    /**
     * 业务线标识id所占的位数
     **/
    private final long serviceIdBits = 8L;
    /**
     * 业务线标识支持的最大数据标识id(这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
     */
    private final long maxServiceId = -1L ^ (-1L << serviceIdBits);

    @Value("${snowflake.serviceId}")
    private long serviceId;


    /**
     * 机器id所占的位数
     **/
    private final long workerIdBits = 10L;
    /**
     * 支持的最大机器id
     */
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    private final long workerId;


    /**
     * 序列在id中占的位数
     **/
    private final long sequenceBits = 7L;
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);


    /**
     * 开始时间戳(2018年1月1日)
     **/
    private final long twepoch = 1514736000000L;
    /**
     * 最后一次的时间戳
     **/
    private volatile long lastTimestamp = -1L;
    /**
     * 毫秒内序列
     **/
    private volatile long sequence = 0L;
    /**
     * 随机生成器
     **/
    private static volatile Random random = new Random();


    /**
     * 机器id左移位数
     **/
    private final long workerIdShift = sequenceBits;
    /**
     * 业务线id左移位数
     **/
    private final long serviceIdShift = workerIdBits + sequenceBits;
    /**
     * 时间戳左移位数
     **/
    private final long timestampLeftShift = serviceIdBits + workerIdBits + sequenceBits;

    public SnowflakeIdGenerator() {
        if ((serviceId > maxServiceId) || (serviceId < 0)) {
            throw new IllegalArgumentException(String.format("service Id can't be greater than %d or less than 0", maxServiceId));
        }
        workerId = getWorkerId();
        if ((workerId > maxWorkerId) || (workerId < 0)) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
    }

    public SnowflakeIdGenerator(long serviceId) {
        if ((serviceId > maxServiceId) || (serviceId < 0)) {
            throw new IllegalArgumentException(String.format("service Id can't be greater than %d or less than 0", maxServiceId));
        }
        workerId = getWorkerId();
        if ((workerId > maxWorkerId) || (workerId < 0)) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        this.serviceId = serviceId;
    }

    public synchronized long nextId() throws IllegalArgumentException {
        long timestamp = System.currentTimeMillis();
        if (timestamp < lastTimestamp) {
            throw new IllegalArgumentException("Clock moved backwards.  Refusing to generate id for " + (
                    lastTimestamp - timestamp) + " milliseconds.");
        }
        //如果是同一时间生成的,则进行毫秒内序列
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            //跨毫秒时,序列号总是归0,会导致序列号为0的ID比较多,导致生成的ID取模后不均匀,所以采用10以内的随机数
            sequence = random.nextInt(10) & sequenceMask;
        }
        //上次生成ID的时间截(设置最后时间戳)
        lastTimestamp = timestamp;

        //移位并通过或运算拼到一起组成64位的ID
        return ((timestamp - twepoch) << timestampLeftShift) //时间戳
                | (serviceId << serviceIdShift) //业务线
                | (workerId << workerIdShift) //机器
                | sequence; //序号
    }

    /**
     * 等待下一个毫秒的到来, 保证返回的毫秒数在参数lastTimestamp之后
     * 不停获得时间,直到大于最后时间
     */
    private long tilNextMillis(final long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }

    /**
     * 根据机器的MAC地址获取工作进程Id,也可以使用机器IP获取工作进程Id,取最后两个段,一共10个bit
     * 极端情况下,MAC地址后两个段一样,产品的工作进程Id会一样;再极端情况下,并发不大时,刚好跨毫秒,又刚好随机出来的sequence一样的话,产品的Id会重复
     *
     * @return
     */
    protected long getWorkerId() throws IllegalArgumentException {
        try {
            java.util.Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces();
            while (en.hasMoreElements()) {
                NetworkInterface iface = en.nextElement();
                List<InterfaceAddress> addrs = iface.getInterfaceAddresses();
                for (InterfaceAddress addr : addrs) {
                    InetAddress ip = addr.getAddress();
                    NetworkInterface network = NetworkInterface.getByInetAddress(ip);
                    if (network == null) {
                        continue;
                    }
                    byte[] mac = network.getHardwareAddress();
                    if (mac == null) {
                        continue;
                    }
                    long id = ((0x000000FF & (long) mac[mac.length - 1]) | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 11;
                    if (id > maxWorkerId) {
                        return new Random(maxWorkerId).nextInt();
                    }
                    return id;
                }
            }
            return new Random(maxWorkerId).nextInt();
        } catch (SocketException e) {
            throw new IllegalArgumentException(e);
        }
    }

    /**
     * 获取序号
     *
     * @param id
     * @return
     */
    public static Long getSequence(Long id) {
        String str = Long.toBinaryString(id);
        int size = str.length();
        String sequenceBinary = str.substring(size - 7, size);
        return Long.parseLong(sequenceBinary, 2);
    }

    /**
     * 获取机器
     *
     * @param id
     * @return
     */
    public static Long getWorker(Long id) {
        String str = Long.toBinaryString(id);
        int size = str.length();
        String sequenceBinary = str.substring(size - 7 - 10, size - 7);
        return Long.parseLong(sequenceBinary, 2);
    }

    /**
     * 获取业务线
     *
     * @param id
     * @return
     */
    public static Long getService(Long id) {
        String str = Long.toBinaryString(id);
        int size = str.length();
        String sequenceBinary = str.substring(size - 7 - 10 - 8, size - 7 - 10);
        return Long.parseLong(sequenceBinary, 2);
    }


    public static void main(String[] args) {
        SnowflakeIdGenerator snowflakeIdGenerator = new SnowflakeIdGenerator();
        System.out.println(snowflakeIdGenerator.nextId());
    }
}

在application.groovy里全局设置id生成方式

grails.gorm.default.mapping = {
	id column: 'id', generator:'utils.CustomGenerator'
}

注意:如果使用了Spring Security 安全插件,并且使用Requestmap数据库映射权限的,需要注意Requestmap的id策略不可以改变,否则创建权限的时候,权限刷新代码不起作用

//不起作用
springSecurityService.clearCachedRequestmaps()

具体没有去深入查询原因,Requestmap表的映射如下

static mapping = {
    id   generator: "identity"
	cache true
}
发布了102 篇原创文章 · 获赞 40 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/qq_16165281/article/details/104566043