唯一id

自定义注解+mybatis的自定义拦截器+雪花算法解决自定义唯一id

自定义注解

1
2
3
4
5
6
7
8
9
10
11
12
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})//作用于属性
public @interface AutoId {
AutoId.IdType value() default AutoId.IdType.SNOWFLAKE;//默认雪花
public static enum IdType {
UUID,
SNOWFLAKE;
private IdType() {
}
}
}

自定义拦截器

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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
@Intercepts({
@Signature(type = Executor.class, method = "update", args = {
MappedStatement.class, Object.class
}),
})
public class AutoIdInterceptor implements Interceptor {
private int workerId;
private int dataCenterId;
public AutoIdInterceptor(int workerId, int dataCenterId) {
this.workerId = workerId;
this.dataCenterId = dataCenterId;
}
/**
* key值为class对象 value可以理解成是该类带有AutoId注解的属性,只不过对属性封装了一层。
* 它是非常能够提高性能的处理器 它的作用就是不用每一次一个对象经来都要看下它的哪些属性带有AutoId注解
* 毕竟类的反射在性能上并不友好。只要key包含该对象那就不需要检查它哪些属性带AutoId注解。
*/
private Map<Class<?>, List<Handler>> handlerMap = new ConcurrentHashMap<>();
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object[] args = invocation.getArgs();
// args数组对应对象就是上面@Signature注解中args对应的对应类型
MappedStatement mappedStatement = (MappedStatement) args[0];
//实体对象
Object entity = args[1];
if (SqlCommandType.INSERT.equals(mappedStatement.getSqlCommandType())) {
// 获取实体集合
Set<Object> entitySet = getEntitySet(entity);
// 批量设置id
for (Object object : entitySet) {
process(object);
}
}
return invocation.proceed();
}
/**
* object是需要插入的实体数据,它可能是对象,也可能是批量插入的对象。
* 如果是单个对象,那么object就是当前对象
* 如果是批量插入对象,那么object就是一个map集合,key值为"list",value为ArrayList集合对象
*/
private Set<Object> getEntitySet(Object object) {
//
Set<Object> set = new HashSet<>();
if (object instanceof Map) {
//批量插入对象
/*
* modify by liuzixi 2020-04-17
* 修改处理对象的方式,原代码会导致自定义insert方法带多个入参时报错
*/
Map<String, Object> map = (Map<String, Object>) object;
map.forEach((key, value) -> {
if (value instanceof Collection) {
set.addAll((Collection) value);
} else {
set.add(value);
}
});
// Collection values = (Collection) ((Map) object).get("list");
// for (Object value : values) {
// if (value instanceof Collection) {
// set.addAll((Collection) value);
// } else {
// set.add(value);
// }
// }
} else {
//单个插入对象
set.add(object);
}
return set;
}

private void process(Object object) throws Exception {
Class<?> handlerKey = object.getClass();
List<Handler> handlerList = handlerMap.get(handlerKey);
//TODO 性能优化点,如果有两个都是user对象同时,那么只需有个进行反射处理属性就好了,另一个只需执行下面的for循环
sync:
if (handlerList == null || handlerList.isEmpty()) {
synchronized (this) {
handlerList = handlerMap.get(handlerKey);
//如果到这里map集合已经存在,则跳出到指定SYNC标签
if (handlerList != null && !handlerList.isEmpty()) {
break sync;
}
handlerMap.put(handlerKey, handlerList = new ArrayList<>());
// 反射工具类 获取带有AutoId注解的所有属性字段
Class<?> cls = object.getClass();
List<Field> idFields = getIdFields(cls);
// 增加父类,最多两层
Class<?> superClass = cls.getSuperclass();
if (superClass != null && !superClass.equals(Object.class)) {
idFields.addAll(getIdFields(superClass));
Class<?> doubleSuperClass = superClass.getSuperclass();
if (!doubleSuperClass.equals(Object.class)) {
idFields.addAll(getIdFields(doubleSuperClass));
}
}
// 如果无AutoId注解,不执行
if (idFields != null && !idFields.isEmpty()) {
for (Field idField : idFields) {
AutoId annotation = idField.getAnnotation(AutoId.class);
// 1、添加UUID字符串作为主键
if (idField.getType().isAssignableFrom(String.class)) {
if (annotation.value().equals(AutoId.IdType.UUID)) {
handlerList.add(new UUIDHandler(idField));
// 2、添加String类型雪花ID
// 注意:此处每个含AutoId注解的字段对应一个SnowflakeIdWorker对象,实现每张表各自使用独立的Sequence
} else if (annotation.value().equals(AutoId.IdType.SNOWFLAKE)) {
handlerList.add(new UniqueLongHexHandler(idField, new SnowflakeIdWorker(workerId, dataCenterId)));
}
} else if (idField.getType().isAssignableFrom(Long.class)) {
// 3、添加Long类型的雪花ID
// 注意:此处每个含AutoId注解的字段对应一个SnowflakeIdWorker对象,实现每张表各自使用独立的Sequence
if (annotation.value().equals(AutoId.IdType.SNOWFLAKE)) {
handlerList.add(new UniqueLongHandler(idField, new SnowflakeIdWorker(workerId, dataCenterId)));
}
}
}
}
}
}
for (Handler handler : handlerList) {
handler.accept(object);
}
}
/**
* 获取
* @param clazz
* @return
*/
private List<Field> getIdFields(Class<?> clazz) {
Field[] allFields = clazz.getDeclaredFields();
return Arrays.stream(allFields).filter(field -> field.getAnnotation(AutoId.class) != null).collect(Collectors.toList());
}
/**
* ID处理器基类
*/
private static abstract class Handler {
Field field;

Handler(Field field) {
this.field = field;
}
abstract void handle(Field field, Object object) throws Exception;

private boolean checkField(Object object, Field field) throws IllegalAccessException {
if (!field.isAccessible()) {
field.setAccessible(true);
}
//如果该注解对应的属性已经被赋值,那么就不用通过雪花生成的ID
return field.get(object) == null;
}

public void accept(Object o) throws Exception {
if (checkField(o, field)) {
handle(field, o);
}
}
}
/**
* UUID处理器
*/
private static class UUIDHandler extends Handler {
UUIDHandler(Field field) {
super(field);
}
/**
* 1、插入UUID主键
*/
@Override
void handle(Field field, Object object) throws Exception {
field.set(object, UUID.randomUUID().toString().replace("-", ""));
}
}
/**
* Long型雪花ID处理器
*/
private static class UniqueLongHandler extends Handler {
private SnowflakeIdWorker idWorker;
UniqueLongHandler(Field field, SnowflakeIdWorker idWorker) {
super(field);
this.idWorker = idWorker;
}
/**
* 2、插入Long类型雪花ID
*/
@Override
void handle(Field field, Object object) throws Exception {
field.set(object, idWorker.nextId());
}
}
/**
* String型雪花ID处理器
*/
private static class UniqueLongHexHandler extends Handler {
private SnowflakeIdWorker idWorker;
UniqueLongHexHandler(Field field, SnowflakeIdWorker idWorker) {
super(field);
this.idWorker = idWorker;
}
/**
* 3、插入String类型雪花ID
*/
@Override
void handle(Field field, Object object) throws Exception {
field.set(object, String.valueOf(idWorker.nextId()));
}
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
}