java Spring JPA 存储库:防止在保存时更新
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/35817584/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring JPA repository: prevent update on save
提问by Antonio
My userDB table looks like this:
我的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;
This is the field definitions of my entity:
这是我的实体的字段定义:
@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;
The field usernameis the key of my table/entity and it's up to me to set its value.
When I need to create another user, I do this in my service:
该字段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;
}
Anyway, if the username is already taken, the repository just do an update, because the specified identifier is not null. This is not the behaviour that I want, I need it to throw something like a duplicate entry exception. Is there any way to prevent it? Do I have to do it by myself? E.g.:
无论如何,如果用户名已经被占用,存储库只会做一个更新,因为指定的标识符不为空。这不是我想要的行为,我需要它抛出类似重复条目异常的东西。有什么办法可以防止吗?我必须自己做吗?例如:
User user = this.userRepository.findOne(username);
if(user != null) {
throw new RuntimeException("Username already taken"); // FIXME - eccezione applicativa
}
回答by saljuama
When using the default configuration, and using CrudRepository#save()or JpaRepository#save()it will delegate to the EntityManagerto use either persists()if it is a new entity, or merge()if it is not.
当使用默认的配置,并使用CrudRepository#save()或JpaRepository#save()将委托给EntityManager要么使用persists(),如果它是一个新的实体,或者merge()如果它不是。
The strategy followed to detect the entity state, new or not, to use the appropiate method, when using the default configuration is as follows:
检测实体状态的策略,无论是否新,使用合适的方法,使用默认配置时如下:
- By default, a Property-IDinspection is performed, if it is
null, then it is a new entity, otherwise is not. - If the entity implements
Persistablethe detection will be delegated to theisNew()method implemented by the entity. - There is a 3rd option, implementing
EntityInformation, but further customizations are needed.
- 默认情况下,执行Property-ID检查,如果是
null,则它是一个新实体,否则不是。 - 如果实体实现
Persistable了检测,则将委托给实体实现的isNew()方法。 - 有第三个选项,实现
EntityInformation,但需要进一步的自定义。
So in your case, as you are using the username as ID, and it isn't null, the Repository call ends up delegating to EntityManager.merge()instead of persist(). So there are two possible solutions:
因此,在您的情况下,当您使用用户名 asID并且它不为空时,存储库调用最终委托给EntityManager.merge()而不是persist()。所以有两种可能的解决方案:
- use a diferent
IDproperty, set it to null, and use any auto-generation method, or - make User implement
Persistableand use theisNew()method, to determine if it is a new entity or not.
- 使用不同的
ID属性,将其设置为 null,并使用任何自动生成方法,或 - 使用户实现
Persistable并使用该isNew()方法,以确定它是否是一个新实体。
If for some reason, you don't want to modify your entities, you can also change the behaviour modifying the flush mode configuration. By default, in spring data jpa, hibernate flush mode is set to AUTO. What you want to do is to change it to COMMIT, and the property to change it is org.hibernate.flushMode. You can modify this configuration by overriding a EntityManagerFactoryBeanin a @Configurationclass.
如果出于某种原因,您不想修改您的实体,您还可以更改修改刷新模式配置的行为。默认情况下,在 spring 数据 jpa 中,休眠刷新模式设置为 AUTO。你要做的就是把它改成COMMIT,改它的属性是org.hibernate.flushMode. 您可以通过覆盖类EntityManagerFactoryBean中的 a来修改此配置@Configuration。
And if you don't want to mess the configuration of the EntityManager, you can use the JpaRepository#flush()or JpaRepository#saveAndFlush()methods, to commit the pending changes to the database.
如果您不想弄乱 EntityManager 的配置,您可以使用JpaRepository#flush()或JpaRepository#saveAndFlush()方法,将挂起的更改提交到数据库。
回答by umesh
Instead of this.userRepository.save(user) , can you try this.userRepository.saveAndFlush(user)
而不是 this.userRepository.save(user) ,你可以试试 this.userRepository.saveAndFlush(user)
My best guess is, it will make your entity detached and as per the JPA documentation, it states an EntityExistsException is thrown by the persist method when the object passed in is a detached entity. Or any other PersistenceException when the persistence context is flushed or the transaction is committed.
我最好的猜测是,它会使您的实体分离,并且根据 JPA 文档,它指出当传入的对象是分离的实体时,persist 方法会抛出 EntityExistsException。或在刷新持久性上下文或提交事务时出现任何其他 PersistenceException。
回答by TGU
One can perhaps use existsById(ID primaryKey) to test it, if userRepository extends CrudRepository:
如果 userRepository 扩展了 CrudRepository,也许可以使用 existsById(ID primaryKey) 来测试它:
if(userRepository.existsById(username)){
//Throw your Exception
} else {
this.userRepository.save(user);
}
see https://docs.spring.io/spring-data/jpa/docs/current/reference/html/
见https://docs.spring.io/spring-data/jpa/docs/current/reference/html/

