V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
Koril
V2EX  ›  问与答

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

  •  
  •   Koril · 330 天前 · 474 次点击
    这是一个创建于 330 天前的主题,其中的信息可能已经有所发展或是发生改变。

    项目使用 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

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

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3189 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 14:55 · PVG 22:55 · LAX 07:55 · JFK 10:55
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.