Spring JPA存储库:阻止保存更新
我的user
数据库表如下所示:
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; }
无论如何,如果已经使用了用户名,则存储库只会进行更新,因为指定的标识符不为null。 这不是我想要的行为,我需要它来抛出类似重复的条目exception。 有什么办法可以预防吗? 我必须自己做吗? 例如:
User user = this.userRepository.findOne(username); if(user != null) { throw new RuntimeException("Username already taken"); // FIXME - eccezione applicativa }
当使用默认配置,并使用CrudRepository#save()
或JpaRepository#save()
,它将委托给EntityManager
,如果它是新实体则使用CrudRepository#save()
persists()
如果不是则使用merge()
。
在使用默认配置时,遵循以下策略来检测实体状态(新的或不是新的)以使用适当的方法如下:
- 默认情况下,执行Property-ID检查,如果为
null
,则它是新实体,否则不是。 - 如果实体实现
Persistable
则检测将委托给实体实现的isNew()
方法。 - 有第三个选项,实现
EntityInformation
,但需要进一步的自定义。
资源
因此,在您的情况下,当您使用用户名作为ID
,并且它不为空时,Repository调用最终委托给EntityManager.merge()
而不是persist()
。 所以有两种可能的解决方案:
- 使用不同的
ID
属性,将其设置为null,并使用任何自动生成方法,或 - make User实现
Persistable
并使用isNew()
方法确定它是否是新实体。
如果由于某种原因,您不想修改实体,还可以更改修改刷新模式配置的行为。 默认情况下,在spring数据jpa中,hibernate刷新模式设置为AUTO。 你想要做的是将它更改为COMMIT,并且要更改它的属性是org.hibernate.flushMode
。 您可以通过覆盖@Configuration
类中的EntityManagerFactoryBean
来修改此配置。
如果您不想弄乱EntityManager的配置,可以使用JpaRepository#flush()
或JpaRepository#saveAndFlush()
方法将挂起的更改提交到数据库。
而不是this.userRepository.save(用户),你可以尝试this.userRepository.saveAndFlush(用户)
我最好的猜测是,它将使您的实体分离,并且根据JPA文档,它声明当传入的对象是分离的实体时,persist方法抛出EntityExistsException。 或者在刷新持久性上下文或提交事务时的任何其他PersistenceException。