雪花算法是越来越流行了,他比传统的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条数据的测试,效果如下:
~~~~~~~~~~~~~~非常好用~~~~~~~~~