Springboot + Hibernate:保存实体时出现重复条目异常

huangapple 未分类评论41阅读模式
英文:

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); // 抛出错误
}

以下是创建的实体表格:

Springboot + Hibernate:保存实体时出现重复条目异常

以下是问题的 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 = &quot;user&quot;)
    public class User extends Base {

       private String firstname;
       private String lastname;

       @OneToMany
       @JoinTable(
            name=&quot;user_emails&quot;,
            joinColumns = @JoinColumn( name=&quot;user_id&quot;),
            inverseJoinColumns = @JoinColumn( name=&quot;email_id&quot;)
       )
       private List&lt;Email&gt; emails = new ArrayList&lt;&gt;();

       // getter setter
    }

And Email (dont have any reference to User)

    @Entity
    @Table(name = &quot;email&quot;)
    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 &quot;saveUser-line&quot; 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 &#39;databaseSeedConfig&#39; 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 &#39;2&#39; for key &#39;UK_1j6q2cqg40ooyo8m7kq2miws2&#39;
    	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&#39;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 = &quot;user&quot;)
@Getter
@Setter
public class User extends Base {

   private String firstname;
   private String lastname;

   @OneToMany
   @JoinTable(
        name=&quot;user_emails&quot;,
        joinColumns = @JoinColumn( name=&quot;user_id&quot;),
        inverseJoinColumns = @JoinColumn( name=&quot;email_id&quot;)
   )
   private List&lt;Email&gt; emails = new ArrayList&lt;&gt;();

   private void addEmail(Email email){
      emails.add(email);
   }

   private void removeEmail(Email email){
      emails.remove(email);
   }
}

UserRepository.java

@Repository
public interface UserRepository extends JpaRepository&lt;User, Long&gt; {

  User findByNameAndSurname(String name, String surname);

}

EmailRepository.java

@Repository
public interface EmailRepository extends JpaRepository&lt;Email, Long&gt; {

   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(&quot;Sender&quot;, &quot;Receiver&quot;);
emailRepository.flush();

User user = userRepository.findByNameAndSurname(&quot;Carlos&quot;, &quot;Matos&quot;);

if(user != null){
 user.addEmail(email);
 userRepository.saveAndFlush(user);
}

huangapple
  • 本文由 发表于 2020年4月10日 04:08:42
  • 转载请务必保留本文链接:https://java.coder-hub.com/61129411.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定