一个关于 JPA 下,表主键生成 UUID 的问题

341 天前
 Koril

项目使用 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>UserRepositorysave()方法,就可以插入一个记录,并且这条记录的 UUID 的值是框架自动生成的。

现在,碰到一个问题:我希望 User 的 id 如果没有被赋值,那么由框架去自动生成一个 UUID ,否则,使用我自己赋值的 UUID 插入到数据库中,换句话说,如果前端传来了一个特定的 UUID ,并且该 id 不存在于数据库中,就以该 id 为主键值插入数据。

但是经过我自己的测试,一旦注解了@GeneratedValue,框架似乎直接忽略了我给 entity 赋值的 UUID ,而是重新生成一个新的值,插入数据库。

我想到两种方案:

  1. 最简单粗暴的,去掉@GeneratedValue@GenericGenerator注解,每一次调用save()的时候手动赋值一个 UUID ( User user = new User(); user.setId(generateUUID())),也就是说,完全由自己控制主键的生成。

  2. 写一个自定义的主键生成器,替代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

请问,有没有同学碰到过这种情况,有什么更好的解决方案呢?感谢!

494 次点击
所在节点    问与答
0 条回复

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/948368

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX