项目使用 Spring Data JPA + MySQL ,碰到了主键 id 使用 UUID 的一个问题。 如果只是单纯的自动生成 UUID ,那好像挺简单的:
@Entity
@Table(name = "t_user")
public class User {
@Id
@GeneratedValue(generator = "UUID")
@GenericGenerator(
name = "UUID",
strategy = "org.hibernate.id.UUIDHexGenerator",
)
@Column(name = "id")
private String id;
@Column(name = "name")
private String name;
}
这样子使用@GenericGenerator
的 Entity ,调用继承了JpaRepository<User, String>
的UserRepository
的 save()
方法,就可以插入一个记录,并且这条记录的 UUID 的值是框架自动生成的。
现在,碰到一个问题:我希望 User 的 id 如果没有被赋值,那么由框架去自动生成一个 UUID ,否则,使用我自己赋值的 UUID 插入到数据库中,换句话说,如果前端传来了一个特定的 UUID ,并且该 id 不存在于数据库中,就以该 id 为主键值插入数据。
但是经过我自己的测试,一旦注解了@GeneratedValue
,框架似乎直接忽略了我给 entity 赋值的 UUID ,而是重新生成一个新的值,插入数据库。
我想到两种方案:
最简单粗暴的,去掉@GeneratedValue
和@GenericGenerator
注解,每一次调用save()
的时候手动赋值一个 UUID ( User user = new User(); user.setId(generateUUID())),也就是说,完全由自己控制主键的生成。
写一个自定义的主键生成器,替代org.hibernate.id.UUIDHexGenerator
,我写的代码如下:
package cn.korilweb.demojpa.generator;
// 省略 import
public class CustomUUIDGenerator extends UUIDHexGenerator {
@Override
public Serializable generate(SharedSessionContractImplementor session, Object obj) {
try {
// 获取 entity id 字段的值
Field field = obj.getClass().getDeclaredField("id");
field.setAccessible(true);
String id = (String) field.get(obj);
// 如果已经设置,直接返回
if (Objects.nonNull(id)) {
return id;
}
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}
// 没有 id ,自动生成随机的 UUID
String sep = "";
return format( getIP() ) + sep
+ format( getJVM() ) + sep
+ format( getHiTime() ) + sep
+ format( getLoTime() ) + sep
+ format( getCount() );
}
}
然后 User 类的主键注解中的 strategy 参数改成:
@Id
@GeneratedValue(generator = "UUID")
@GenericGenerator(
name = "UUID",
strategy = "cn.korilweb.demojpa.generator.CustomUUIDGenerator"
)
@Column(name = "id")
private String id;
第二种方式奏效了,但我想问问有没有其他的方式呢?
另外,我的问题和 Stack Overflow 上的这个问题很相似: https://stackoverflow.com/questions/45102456/how-to-set-autogenerated-id-manually
请问,有没有同学碰到过这种情况,有什么更好的解决方案呢?感谢!