有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java使用Spring Boot/JPA生成唯一字段的正确方法是什么?

首先,我没有考虑记录的主要id。我说的是一个字段,用户使用该字段来标识用户自动生成但可更改的记录,它不是顺序的,也不是UUID。例如,从帐户实体开始:

@Entity
@Data
class Account {
    @Id
    @GeneratedValue
    private int id;

    @Column(unique=true)
    @NotNull
    private String slug;

    @Column
    private String name;
}

然后我简单地创建一个记录:

@Autowired
private AccountRepository accountRepository;

Account account = new Account();
account.setName("ACME");
accountRepository.saveAndFlush(account);

此时,slug应该已经生成,要么完全随机生成,要么根据名称执行某些操作。应该怎样做呢

我知道,如果不锁定整个表,就不可能确保插入不会由于违反唯一性约束而导致异常。实际上,我可以阻塞整个表,甚至允许异常发生(在检查可用性和插入之间发生冲突时,每秒需要大量请求)


共 (3) 个答案

  1. # 1 楼答案

    我会像这样做一个单独的Bean、助手或服务类

    public class SlugService {
        public String generateSlug(String slug)
        {                                                   
          if (accountRepo.getBySlug(slug) != null){ //check if it is already
            return slug
          } else {
           slug.append("-"); //whatever the syntax
           generateSlug();
          }
        }
    
        public String makeSlug()
        {
          String slug = split by " ", replace by "_"(accountObject.getName);
          generateSlug(slug)
        }   
    }
    

    调用makeSlug();方法

  2. # 2 楼答案

    我可以想出JPA callbacks来生成slug。在你的情况下@PrePersist可能有用

    也就是说,为什么在插入记录之前需要确保select可以使用该值,所以发生冲突的窗口很小?柱上有唯一的约束,对吗

    更新

    就我个人而言,我更愿意这样说:

    1. 生成slug时使用JPA回调@PrePersist。使用随机UUID或时间戳来最小化冲突的可能性。没有检查碰撞的可能性很小
    2. 当为用户生成的slug更新Account时,请始终首先使用query for collision进行检查。这种检查当然会发生在服务更新方法本身中

    这样我就可以不依赖数据库,也不必在实体或侦听器类中使用存储库/服务

  3. # 3 楼答案

    如果将slug从Account表中分离出来,并将其单独放入(id, slug)表中,则可以先生成slug(重试直到成功),然后通过指向刚生成的slug id的链接持久化Account

    @PrePersist方法中无法实现这一点,因此每当您创建新的Account时,您的服务都需要创建slug。然而,它确实简化了应用程序方面的事情(例如,在持久化Account时,您不需要怀疑违反了哪个约束)

    根据其他代码的不同,如果采用乐观的方法,还可以绕过锁定Account表,甚至锁定Slug

    创建新帐户的服务方法的伪代码示例(提供new Slug()创建随机slug):

    @Autowired SlugRepository slugRepository;
    @Autowired AccountRepository accountRepository;
    
    public void createAccount(Account a) {
        Slug s = null;
        while(s == null) {
            try {
                s = slugRepository.save(new Slug());
            } catch(Exception e) {
            }
        }
        a.setSlug(s);
        accountRepository.save(a);
    }