
项目使用 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
请问,有没有同学碰到过这种情况,有什么更好的解决方案呢?感谢!