英文:
Springboot + Hibernate: Duplicate entry Exception while save entity
问题
我有两个简单的实体:
@Entity
@Table(name = "user")
public class User extends Base {
private String firstname;
private String lastname;
@OneToMany
@JoinTable(
name="user_emails",
joinColumns = @JoinColumn(name="user_id"),
inverseJoinColumns = @JoinColumn(name="email_id")
)
private List<Email> emails = new ArrayList<>();
// getter setter
}
而 Email 实体(不包含任何对 User 的引用):
@Entity
@Table(name = "email")
public class Email extends Base {
private String sender;
private String receiver;
}
使用第一个邮件一切正常,
但现在我想通过以下代码将第二个邮件保存到用户中(抛出错误)。问题是,在 "saveUser-line" 处有两封邮件,我认为 Hibernate 尝试再次保存第一个邮件并引发重复条目异常。我应该如何处理?重要的是要知道,还有其他像用户这样的对象,它们都尝试保存邮件(也不起作用)。
// 创建 Email 对象
eMailRepository.save(email);
if (user != null) {
user.getEmails().add(email);
userDatabaseRepository.save(user); // 抛出错误
}
以下是创建的实体表格:
以下是问题的 SQL 语句:
Hibernate: insert into user_emails (user_id, email_id) values (?, ?)
这是异常:
org.springframework.beans.factory.BeanCreationException: 在文件 [E:\Arbeit---\target\classes\de---\config\DatabaseSeedConfig.class] 中定义的 bean 名称为 'databaseSeedConfig' 的创建出错;初始化方法的调用失败;嵌套异常是 org.springframework.dao.DataIntegrityViolationException: 无法执行语句;SQL [n/a];约束 [UK_1j6q2cqg40ooyo8m7kq2miws2];嵌套异常是 org.hibernate.exception.ConstraintViolationException: 无法执行语句
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1778)
// 更多堆栈跟踪...
原因是 org.hibernate.exception.ConstraintViolationException: 无法执行语句
// 更多堆栈跟踪...
以上是您提供的内容的翻译。如果您需要更多帮助或有任何问题,请随时问我。
<details>
<summary>英文:</summary>
i got 2 simple entities:
@Entity
@Table(name = "user")
public class User extends Base {
private String firstname;
private String lastname;
@OneToMany
@JoinTable(
name="user_emails",
joinColumns = @JoinColumn( name="user_id"),
inverseJoinColumns = @JoinColumn( name="email_id")
)
private List<Email> emails = new ArrayList<>();
// getter setter
}
And Email (dont have any reference to User)
@Entity
@Table(name = "email")
public class Email extends Base {
private String sender;
private String receiver;
}
With the first mail everything is working
but now I want to save a second email to the user with the following code (error is thrown). The problem is, that they are 2 mails at the "saveUser-line" and i think hibernate try to save the first one again and get the duplicate entry exceptions. How i have to do it? Important to know: there are other objects like user and all of them try to save emails (also not working).
// Create Email object
eMailRepository.save(email);
if (user != null) {
user.getEmails().add(email);
userDatabaseRepository.save(user); // ERROR THROWN
}
Here are the created entity table
[![][1]][1]
[1]: https://i.stack.imgur.com/iJGV5.jpg
Here are the problem sql:
Hibernate: insert into user_emails (user_id, email_id) values (?, ?)
And here the exception:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'databaseSeedConfig' defined in file [E:\Arbeit\---\target\classes\de\---\config\DatabaseSeedConfig.class]: Invocation of init method failed; nested exception is org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [UK_1j6q2cqg40ooyo8m7kq2miws2]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1778)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:845)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:742)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:389)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:311)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1213)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1202)
at de.dev.Pfffff.main(Pfffff.java:10)
Caused by: org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [UK_1j6q2cqg40ooyo8m7kq2miws2]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:296)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:253)
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:536)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:746)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:714)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:534)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:305)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:138)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy134.save(Unknown Source)
at de.dev.service.EMailQueueService.addEMailToQueue(EMailQueueService.java:132)
at de.dev.service.EMailQueueService.addEMailToQueue(EMailQueueService.java:91)
at de.dev.service.UserService.addUser(UserService.java:173)
at de.dev.config.DatabaseSeedConfig.setUpAdminUser(DatabaseSeedConfig.java:133)
at de.dev.config.DatabaseSeedConfig.afterPropertiesSet(DatabaseSeedConfig.java:99)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1837)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1774)
... 16 common frames omitted
Caused by: org.hibernate.exception.ConstraintViolationException: could not execute statement
at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:59)
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:113)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:99)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:178)
at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:45)
at org.hibernate.persister.collection.AbstractCollectionPersister.recreate(AbstractCollectionPersister.java:1340)
at org.hibernate.action.internal.CollectionUpdateAction.execute(CollectionUpdateAction.java:83)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:604)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:478)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:356)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39)
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1454)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:511)
at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3290)
at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2486)
at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:473)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:178)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$300(JdbcResourceLocalTransactionCoordinatorImpl.java:39)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:271)
at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:104)
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:532)
... 39 common frames omitted
Caused by: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '2' for key 'UK_1j6q2cqg40ooyo8m7kq2miws2'
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:117)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:955)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1094)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1042)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeLargeUpdate(ClientPreparedStatement.java:1345)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdate(ClientPreparedStatement.java:1027)
at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeUpdate(HikariProxyPreparedStatement.java)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:175)
... 56 common frames omitted
</details>
# 答案1
**得分**: 0
不要在第二次调用时使用 ```userDatabaseRepository.save(user);```,而是使用 ```userDatabaseRepository.saveOrUpdate(user);``` 会更有帮助吗?
<details>
<summary>英文:</summary>
won't help you ```userDatabaseRepository.saveOrUpdate(user);``` instead of ```userDatabaseRepository.save(user);``` when you calling it for second time?
</details>
# 答案2
**得分**: 0
```java
尝试类似以下方式
### Email.java
```java
@Entity
@Table(name = "user")
@Getter
@Setter
public class User extends Base {
private String firstname;
private String lastname;
@OneToMany
@JoinTable(
name="user_emails",
joinColumns = @JoinColumn( name="user_id"),
inverseJoinColumns = @JoinColumn( name="email_id")
)
private List<Email> emails = new ArrayList<>();
private void addEmail(Email email){
emails.add(email);
}
private void removeEmail(Email email){
emails.remove(email);
}
}
UserRepository.java
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
User findByNameAndSurname(String name, String surname);
}
EmailRepository.java
@Repository
public interface EmailRepository extends JpaRepository<Email, Long> {
Email findBySenderAndReceiver(String sender, String receiver);
}
SomeClass.java
@Autowired
private EmailRepository emailRepository;
@Autowired
private UserRepository userRepository;
/*
Some code
*/
emailRepository.saveAndFlush(email);
Email email = emailRepository.findBySenderAndReceiver("Sender", "Receiver");
emailRepository.flush();
User user = userRepository.findByNameAndSurname("Carlos", "Matos");
if(user != null){
user.addEmail(email);
userRepository.saveAndFlush(user);
}
英文:
Try something like this
Email.java
@Entity
@Table(name = "user")
@Getter
@Setter
public class User extends Base {
private String firstname;
private String lastname;
@OneToMany
@JoinTable(
name="user_emails",
joinColumns = @JoinColumn( name="user_id"),
inverseJoinColumns = @JoinColumn( name="email_id")
)
private List<Email> emails = new ArrayList<>();
private void addEmail(Email email){
emails.add(email);
}
private void removeEmail(Email email){
emails.remove(email);
}
}
UserRepository.java
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
User findByNameAndSurname(String name, String surname);
}
EmailRepository.java
@Repository
public interface EmailRepository extends JpaRepository<Email, Long> {
Email findBySenderAndReceiver(String sender, String receiver);
}
SomeClass.java
@Autowired
private EmailRepository emailRepository;
@Autowired
private UserRepository userRepository;
/*
Some code
*/
emailRepository.saveAndFlush(email);
Email email = emailRepository.findBySenderAndReceiver("Sender", "Receiver");
emailRepository.flush();
User user = userRepository.findByNameAndSurname("Carlos", "Matos");
if(user != null){
user.addEmail(email);
userRepository.saveAndFlush(user);
}
专注分享java语言的经验与见解,让所有开发者获益!
评论