前言 在分布式环境中 一般数据id 都是全局唯一 拥有特定的生成规则 一般都是从专门的取号中心 取的 所以jpa中为了全局统一处理 id生成 也提供了扩展方案
此处取jpa hibernate实现处理
示例 hibernate 6.5版本之前的方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 package com.ming.core.orm;import org.hibernate.HibernateException;import org.hibernate.engine.spi.SharedSessionContractImplementor;import org.hibernate.id.IdentifierGenerator;import java.io.Serializable;import java.util.concurrent.ThreadLocalRandom;public class SnowflakeIdGenerator implements IdentifierGenerator { private static final long EPOCH = 1288834974657L ; private static final int WORKER_ID_BITS = 5 ; private static final int DATACENTER_ID_BITS = 5 ; private static final int SEQUENCE_BITS = 12 ; private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS); private static final long MAX_DATACENTER_ID = ~(-1L << DATACENTER_ID_BITS); private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS); private static final long WORKER_ID_SHIFT = SEQUENCE_BITS; private static final long DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS; private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS; private final long workerId; private final long datacenterId; private long sequence = 0L ; private long lastTimestamp = -1L ; public SnowflakeIdGenerator () { this (workerId(), datacenterId()); } public SnowflakeIdGenerator (long workerId, long datacenterId) { if (workerId > MAX_WORKER_ID || workerId < 0 ) { throw new IllegalArgumentException (String.format("worker Id can't be greater than %d or less than 0" , MAX_WORKER_ID)); } if (datacenterId > MAX_DATACENTER_ID || datacenterId < 0 ) { throw new IllegalArgumentException (String.format("datacenter Id can't be greater than %d or less than 0" , MAX_DATACENTER_ID)); } this .workerId = workerId; this .datacenterId = datacenterId; } private static long workerId () { return ThreadLocalRandom.current().nextLong(0 , MAX_WORKER_ID + 1 ); } private static long datacenterId () { return ThreadLocalRandom.current().nextLong(0 , MAX_DATACENTER_ID + 1 ); } private long tilNextMillis (long lastTimestamp) { long timestamp = timeGen(); while (timestamp <= lastTimestamp) { timestamp = timeGen(); } return timestamp; } private long timeGen () { return System.currentTimeMillis() - EPOCH; } @Override public Serializable generate (SharedSessionContractImplementor session, Object object) throws HibernateException { long timestamp = timeGen(); if (timestamp < lastTimestamp) { throw new HibernateException (String.format( "Clock moved backwards. Refusing to generate id for %d milliseconds" , lastTimestamp - timestamp)); } if (lastTimestamp == timestamp) { sequence = (sequence + 1 ) & SEQUENCE_MASK; if (sequence == 0 ) { timestamp = tilNextMillis(lastTimestamp); } } else { sequence = 0 ; } lastTimestamp = timestamp; return ((timestamp << TIMESTAMP_LEFT_SHIFT) | (datacenterId << DATACENTER_ID_SHIFT) | (workerId << WORKER_ID_SHIFT) | sequence); } }
1 2 3 4 @Id @GeneratedValue(generator = "snowflake") @GenericGenerator(name = "snowflake", type = SnowflakeIdGenerator.class) protected Long id;
hibernate 6.5版本之后的方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 package com.ming.core.orm;import org.hibernate.HibernateException;import org.hibernate.engine.spi.SharedSessionContractImplementor;import org.hibernate.generator.BeforeExecutionGenerator;import org.hibernate.generator.EventType;import java.io.Serializable;import java.util.EnumSet;import java.util.concurrent.ThreadLocalRandom;import static org.hibernate.generator.EventTypeSets.INSERT_ONLY;public class SnowflakeSequenceGenerator implements BeforeExecutionGenerator { private static final long EPOCH = 1288834974657L ; private static final int WORKER_ID_BITS = 5 ; private static final int DATACENTER_ID_BITS = 5 ; private static final int SEQUENCE_BITS = 12 ; private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS); private static final long MAX_DATACENTER_ID = ~(-1L << DATACENTER_ID_BITS); private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS); private static final long WORKER_ID_SHIFT = SEQUENCE_BITS; private static final long DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS; private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS; private final long workerId; private final long datacenterId; private long sequence = 0L ; private long lastTimestamp = -1L ; public SnowflakeSequenceGenerator () { this (workerId(), datacenterId()); } public SnowflakeSequenceGenerator (long workerId, long datacenterId) { if (workerId > MAX_WORKER_ID || workerId < 0 ) { throw new IllegalArgumentException (String.format("worker Id can't be greater than %d or less than 0" , MAX_WORKER_ID)); } if (datacenterId > MAX_DATACENTER_ID || datacenterId < 0 ) { throw new IllegalArgumentException (String.format("datacenter Id can't be greater than %d or less than 0" , MAX_DATACENTER_ID)); } this .workerId = workerId; this .datacenterId = datacenterId; } private static long workerId () { return ThreadLocalRandom.current().nextLong(0 , MAX_WORKER_ID + 1 ); } private static long datacenterId () { return ThreadLocalRandom.current().nextLong(0 , MAX_DATACENTER_ID + 1 ); } private long tilNextMillis (long lastTimestamp) { long timestamp = timeGen(); while (timestamp <= lastTimestamp) { timestamp = timeGen(); } return timestamp; } private long timeGen () { return System.currentTimeMillis() - EPOCH; } private Serializable nextId () { long timestamp = timeGen(); if (timestamp < lastTimestamp) { throw new HibernateException (String.format( "Clock moved backwards. Refusing to generate id for %d milliseconds" , lastTimestamp - timestamp)); } if (lastTimestamp == timestamp) { sequence = (sequence + 1 ) & SEQUENCE_MASK; if (sequence == 0 ) { timestamp = tilNextMillis(lastTimestamp); } } else { sequence = 0 ; } lastTimestamp = timestamp; return ((timestamp << TIMESTAMP_LEFT_SHIFT) | (datacenterId << DATACENTER_ID_SHIFT) | (workerId << WORKER_ID_SHIFT) | sequence); } @Override public Object generate (SharedSessionContractImplementor session, Object owner, Object currentValue, EventType eventType) { return nextId(); } @Override public EnumSet<EventType> getEventTypes () { return INSERT_ONLY; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.ming.core.orm;import org.hibernate.annotations.IdGeneratorType;import java.lang.annotation.Retention;import java.lang.annotation.Target;import static java.lang.annotation.ElementType.FIELD;import static java.lang.annotation.ElementType.METHOD;import static java.lang.annotation.RetentionPolicy.RUNTIME;@IdGeneratorType(SnowflakeSequenceGenerator.class) @Retention(RUNTIME) @Target({METHOD, FIELD}) public @interface SnowflakeSequence {}
1 2 3 @Id @SnowflakeSequence protected Long id;
hibernate 在高版本之后提供了更加简洁的方式 实现自定义id生成 自定义id生成器配合@MappedSuperclass 可以做到继承映射+自定义生成id 只要引用了InId id都是配置的生成方案
总结 jpa 我个人很喜欢用 虽然很多人说jpa会比直接写sql慢 不过我认为jpa如果配置的好 在大多数情况下 并不会慢多少 反而可以利用orm的特性 进行一些优化调整 充分利用硬件资源 大多数用mybatis的 都是觉得直接写sql简单 但是当项目越来越大的时候mybatis 写的sql调整起来反而更加考验开发的sql功底 而且不同的db 对sql的要求也有所不同 对开发要求就更高了