Postgresql JPA:生成非pk唯一和随机字母数字值

Postgresql JPA:生成非pk唯一和随机字母数字值,postgresql,jpa,eclipselink,jpa-2.0,Postgresql,Jpa,Eclipselink,Jpa 2.0,我想在不使用主键的情况下唯一标识一个实体。所以我考虑生成一个唯一的随机值。此外,该值必须易于读取/手动复制,预计长度为6或7个字符 设计 我的实体A: public class A{ // ... @Column(name="value", unique=true, nullable=false, insertable=false, updatable=false) private String value; // ... public String g

我想在不使用主键的情况下唯一标识一个实体。所以我考虑生成一个唯一的随机值。此外,该值必须易于读取/手动复制,预计长度为6或7个字符

设计 我的实体A:

public class A{
    // ...
    @Column(name="value", unique=true, nullable=false, insertable=false, updatable=false)
    private String value;
    // ...

    public String getValue(){
        return value;
    }
    protected void setValue(String value){
        this.value = value;
    }
}
在数据库中由表表示

CREATE TABLE IF NOT EXISTS schema.mytable{
    -- ...
    value TEXT NOT NULL DEFAULT generate_unique_value_for_mytable(),
    -- ...
    CONSTRAINT "un_value" UNIQUE (value),
    -- ...
}
我想让数据库处理这个,然后获取值

问题 在当前设计中,值在数据库中正确生成,但当JPA获取实体时,值字段为空

  • 我不能删除insertable=false,否则,它将碰到notnull约束
  • 如果我删除insertable=false并放入一些伪数据,则该数据将覆盖由generate_unique_value_为_mytable()生成的值
  • 如果删除列注释中的所有内容,则可以保存A实体,但值仍然为空
  • 丑陋的解决方案 我找不到证据,但让数据库生成值似乎是个坏主意。对于由序列生成的非主键字段,我确实存在同样的问题:我无法从数据库中获取值

    因此,我丑陋的解决方案是修饰负责实体的EJB的create()方法:

    public class Aejb{
    
        public void create(A entity){
    
            // method kind of ensures randomness
            String value = MyUtil.generateRandomValue();
            A isThereAnyoneHere = findByValue(value);
    
            while(isThereAnyoneHere != null){
                String value = MyUtil.generateRandomValue();
                isThereAnyoneHere = findByValue(value);
            }
    
            // unicity is ensured
            entity.setValue(value);
    
            em.persist(entity);
        }
    
    }
    
    问题
  • 我可以从JPA实体获取数据库生成的非主键值吗?值可以由函数或序列生成
  • 有没有比我丑陋的变通方法更优雅的解决方案来提供唯一和随机的值
  • 是的。您没有提到您的数据库,但有可能 Oracle返回通过触发器插入的值,并 Eclipselink在模型中获取此值-请参阅
  • 使用将要执行的@PrePersist方法设置该值 在插入实体之前,但如果您依赖一个或多个数据库查询,则会遇到性能问题,因为插入新的查询将非常昂贵。您可以只插入随机值并处理偶尔的冲突,然后选择一些重叠机会较小的随机值,如UUID
  • 是的。您没有提到您的数据库,但有可能 Oracle返回通过触发器插入的值,并 Eclipselink在模型中获取此值-请参阅
  • 使用将要执行的@PrePersist方法设置该值 在插入实体之前,但如果您依赖一个或多个数据库查询,则会遇到性能问题,因为插入新的查询将非常昂贵。您可以只插入随机值并处理偶尔的冲突,然后选择一些重叠机会较小的随机值,如UUID

  • 如果我理解正确的话,@Generated注解应该可以做到这一点。此注释根据数据库默认字段值设置值

    例如:

    @Generated(GenerationTime.INSERT)
    @Column(name="value", unique=true, nullable=false, insertable=false, updatable=false)
    private String value;
    

    但是有一个缺点:如果您决定用Java设置字段的值,它将被Hibernate使用数据库中默认值的结果覆盖。

    如果我理解正确,@Generated annotation应该可以。此注释根据数据库默认字段值设置值

    例如:

    @Generated(GenerationTime.INSERT)
    @Column(name="value", unique=true, nullable=false, insertable=false, updatable=false)
    private String value;
    
    但也有一个缺点:如果您决定用Java设置字段的值,它将被Hibernate使用数据库中默认值的结果覆盖。

    自动回答以将问题标记为关闭


    最终解决方案 我们最终选择了两种方法的结合

    • 存储过程:数据库将生成值。该过程还确保该值在整个表中是唯一的
    • 命名查询:通过过程获取生成的值。我没有使用NamedStoredProcess,因为我们使用的是PostgreSQL,PostgreSQL JDBC驱动程序不支持名称参数,这会引发一些问题
    通过这种配置,EJB确保最多有一个数据库调用来获取请求的值

    对其他答复的答复 以下是其他答案反馈的摘要,供自我参考和后续读者参考:

    • Oracle触发器:我们正在使用PostgreSQL:(
    • UUID:我们的唯一和随机代码具有可读性的限制。假定最终用户能够手动重写它。因此,我们不能拥有像UUID这样的长字符串
    • PrePersist:其他业务操作在同一事务中的代码生成之后发生,这意味着这些操作需要在发生冲突时重新执行。我对管理JPA异常(事务范围等)不是很有信心,所以我不想玩它
    • @Generated:这是一个特定于Hibernate的功能。我们正在使用EclipseLink
    • 数据库触发器:如果代码纯粹是在数据库级别生成的,我会遇到与不获取值相同的问题:值正确地生成为数据库级别,但实体的值将为
      null
    自我回答,将问题标记为已结束


    最终解决方案 我们最终选择了两种方法的结合

    • 存储过程:数据库将生成该值。该过程还确保该值在整个表中是唯一的
    • 命名查询:通过过程获取生成的值。我没有使用NamedStoredProcess,因为我们使用的是PostgreSQL,PostgreSQL JDBC驱动程序不支持名称参数,这会引发一些问题
    通过这种配置,EJB确保最多有一个数据库调用来获取请求的值

    对其他答复的答复 以下是其他答案反馈的摘要,供自我参考和后续读者参考:

    • Oracle触发器:我们正在使用PostgreSQL:(
    • UUID:我们有一个限制条件,那就是必须让我们唯一的、随机的人类代码可读