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

时间:2017-09-07 10:51:18

标签: java spring 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 个答案:

答案 0 :(得分:1)

如果你将slug与Account表分开并将它自己放在(id, slug)表中,你可以先生成slug(重试直到你成功),然后保持{{1}带有刚刚生成的slug id的链接。

您无法通过Account方法实现此目的,因此,只要您创建新的@PrePersist,您的服务就需要创建slu ..但是它确实简化了应用程序方面的事情(例如,在持久化Account时,您不需要知道违反了哪个约束。)

根据您的其他代码,如果您采用乐观方法,您还可以绕过Account表甚至Account表。

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

new Slug()

答案 1 :(得分:0)

我可以想到JPA callbacks来生成slu ..在您的情况下,@PrePersist可能很有用。

那就是说,为什么在插入记录之前需要确保值是可用的,所以发生碰撞的窗口很小?你对列有唯一约束,对吧?

<强>更新

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

  1. 生成slug时使用JPA回调@PrePersist。用于随机UUID或时间戳以最小化冲突的可能性。没有检查碰撞,因为机会很小。
  2. 更新用户生成的slug的Account时,请务必首先使用查询进行冲突检查。此检查将在服务更新方法本身中发生。
  3. 这样我就可以与数据库无关,也不必在实体或监听器类中使用存储库/服务。

答案 2 :(得分:0)

我将做类似这样的单独的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();方法。