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.saveAndFlush(用户)
我最好的猜测是,它会使您的实体分离,并且根据 JPA 文档,它指出当传入的对象是分离的实体时,persist 方法会抛出 EntityExistsException。或刷新持久性上下文或提交事务时的任何其他 PersistenceException。
使用默认配置并使用 CrudRepository#save()
或 JpaRepository#save()
时,它将委托给 EntityManager
使用 persists()
(如果它是一个新实体),或者merge()
如果不是。
检测实体状态的策略,无论新的还是新的,使用适当的方法,当使用默认配置时如下:
- 默认会进行属性-ID检查,如果是
null
则为新实体,否则不是。
- 如果实体实现
Persistable
,检测将委托给实体实现的 isNew()
方法。
- 有第三个选项,实施
EntityInformation
,但需要进一步定制。
因此,在您的情况下,由于您将用户名用作 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/
我的 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.saveAndFlush(用户)
我最好的猜测是,它会使您的实体分离,并且根据 JPA 文档,它指出当传入的对象是分离的实体时,persist 方法会抛出 EntityExistsException。或刷新持久性上下文或提交事务时的任何其他 PersistenceException。
使用默认配置并使用 CrudRepository#save()
或 JpaRepository#save()
时,它将委托给 EntityManager
使用 persists()
(如果它是一个新实体),或者merge()
如果不是。
检测实体状态的策略,无论新的还是新的,使用适当的方法,当使用默认配置时如下:
- 默认会进行属性-ID检查,如果是
null
则为新实体,否则不是。 - 如果实体实现
Persistable
,检测将委托给实体实现的isNew()
方法。 - 有第三个选项,实施
EntityInformation
,但需要进一步定制。
因此,在您的情况下,由于您将用户名用作 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/