Spring JPA 存储库:防止在保存时更新

Spring JPA repository: prevent update on save

我的 user 数据库 table 看起来像这样:

CREATE TABLE user (
    username VARCHAR(32) PRIMARY KEY,
    first_name VARCHAR(256) NOT NULL,
    last_name VARCHAR(256) NOT NULL,
    password VARCHAR(32) NOT NULL,
    enabled BOOL
) ENGINE = InnoDB;

这是我的实体的字段定义:

@Entity
public class User implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @Column(nullable = false)
    private String username;

    @Column(nullable = false)
    private String firstName;

    @Column(nullable = false)
    private String lastName;

    @Column(nullable = false)
    private String password;

字段 username 是我的 table/entity 的键值,由我来设置它的值。 当我需要创建另一个用户时,我会在我的服务中这样做:

public User insertUserImpl(String username, String firstName, String lastName) {
    Assert.hasText(username);
    Assert.hasText(firstName);
    Assert.hasText(lastName);

    String password = UUID.randomUUID().toString().substring(0, 4); // temp
    User user = new User(username, password);
    user.setFirstName(firstName);
    user.setLastName(lastName);
    user.setEnabled(false);
    this.userRepository.save(user);
    // FIXME - assegnare un ruolo
    return user;
}

无论如何,如果用户名已被占用,存储库只会进行更新,因为指定的标识符不为空。这不是我想要的行为,我需要它抛出类似重复条目异常的东西。 有什么办法可以预防吗?我必须自己做吗? 例如:

User user = this.userRepository.findOne(username);
if(user != null) {
    throw new RuntimeException("Username already taken"); // FIXME - eccezione applicativa
}

而不是 this.userRepository.save(用户) , 你能试一下吗 this.userRepository.saveA​​ndFlush(用户)

我最好的猜测是,它会使您的实体分离,并且根据 JPA 文档,它指出当传入的对象是分离的实体时,persist 方法会抛出 EntityExistsException。或刷新持久性上下文或提交事务时的任何其他 PersistenceException。

使用默认配置并使用 CrudRepository#save()JpaRepository#save() 时,它将委托给 EntityManager 使用 persists()(如果它是一个新实体),或者merge()如果不是。

检测实体状态的策略,无论新的还是新的,使用适当的方法,当使用默认配置时如下:

  • 默认会进行属性-ID检查,如果是null则为新实体,否则不是。
  • 如果实体实现 Persistable,检测将委托给实体实现的 isNew() 方法。
  • 有第三个选项,实施 EntityInformation,但需要进一步定制。

source

因此,在您的情况下,由于您将用户名用作 ID,并且它不为空,因此存储库调用最终委托给 EntityManager.merge() 而不是 persist()。所以有两种可能的解决方案:

  • 使用不同的 ID 属性,将其设置为空,并使用任何自动生成方法,或者
  • 让用户实现 Persistable 并使用 isNew() 方法来确定它是否是一个新实体。

如果出于某种原因,您不想修改您的实体,您也可以更改修改刷新模式配置的行为。默认情况下,在 spring data jpa 中,休眠刷新模式设置为 AUTO。你要做的是把它改成COMMIT,改成属性就是org.hibernate.flushMode。您可以通过覆盖 @Configuration class.

中的 EntityManagerFactoryBean 来修改此配置

如果你不想弄乱 EntityManager 的配置,你可以使用 JpaRepository#flush()JpaRepository#saveAndFlush() 方法,将待处理的更改提交到数据库。

也许可以使用 existsById(ID primaryKey) 来测试它,如果 userRepository extends CrudRepository:

if(userRepository.existsById(username)){
    //Throw your Exception
} else {
    this.userRepository.save(user);
}

https://docs.spring.io/spring-data/jpa/docs/current/reference/html/