【问题标题】:Spring JPA repository: prevent update on saveSpring JPA 存储库:防止更新保存
【发布时间】:2016-06-19 11:01:26
【问题描述】:

我的user DB 表如下所示:

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 字段是我的表/实体的键,由我来设置它的值。 当我需要创建另一个用户时,我会在我的服务中执行此操作:

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
}

【问题讨论】:

  • 如果它正在更新值而不是进行新的插入,这意味着它认为它是同一个对象,但是具有更新的值,您是否重复使用该对象进行多次插入?你用的是什么冲洗模式?
  • 我没有重复使用同一个对象,我编辑了文本,以便您可以看到完整的方法。如何检查我的冲洗模式?我认为这是 Spring Boot 默认设置,我没有设置任何内容。

标签: java spring spring-boot spring-data-jpa


【解决方案1】:

而不是 this.userRepository.save(用户) , 你能试一下吗 this.userRepository.saveAndFlush(user)

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

【讨论】:

    【解决方案2】:

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

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

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

    source

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

    • 使用不同的ID 属性,将其设置为null,并使用任何自动生成方法,或者
    • 让User实现Persistable并使用isNew()方法,判断是否为新实体。

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


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

    【讨论】:

    • 有人可以总结一下吗,我可以在JpaRepository 类上添加某种insert-only 注释以使实体不可更新? org.hibernate.flushMode 不是适用于整个项目吗?
    【解决方案3】:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-10-04
      • 2021-11-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多