org.springframework.dao.DataIntegrityViolationException:具有相同标识符值的不同 object 已与 session 相关联

org.springframework.dao.DataIntegrityViolationException: A different object with the same identifier value was already associated with the session

此方法以事务开头

@Transactional(propagation = Propagation.REQUIRED)
public Account generateDirectCustomerAccountEntity(AccountReq source, Map<String, String> headers, String workflowId) {
    Account dbAccount = dCustomerModelToEntityMapper.map(source, headers);
    dbAccount = saveAccount(source, dbAccount, headers, workflowId);
    return dbAccount;
}   

这是一个映射器 class,我在其中创建数据库帐户实体并映射地址和联系人。

@Override
@LogExecutionTime(log=true)
public Account map(AccountReq accountReq, Map<String, String> headers) {
        logger.info("AccountModelToEntityMapper.mapDirectCustomer*****START");
        Account dbAccount = new Account();
        AccountCode accountCode = dbService.getAccountCodeForDirectCustomer(accountReq);
        String accountId = dbService.genarateAccountId(accountCode);
        logger.info("genarated AccountID for Direct Customer is={}", accountId);
        dbAccount.setAccountId(accountId);
        setAccountContact(dbAccount, accountReq, headers);
        setAddress(dbAccount, accountReq);
        dbAccount.setAccountType(accountCode.getCodeId());
        return dbAccount;
}

当我不在地图中调用以下方法时,一切正常。但是当我调用它时,我得到了标题中描述的错误。

private void setAccountContact(Account dbAccount, AccountReq accountReq, Map<String, String> headers) {

    try {
        if (null != accountReq.getContacts() && !accountReq.getContacts().isEmpty()) {
            logger.info("setAccountContact accountReq.getContacts().size()={}", accountReq.getContacts().size());
            List<String> emailList=new ArrayList<>();
            for (ContactReq contact : accountReq.getContacts()) {
                String email = contact.getEmail();
                logger.info("setAccountContact:"+email);
                if(emailList.contains(email)) {
                    logger.error("ERROR={}", "same emailId sent in multiple contacts");
                    throw new AccountException(AccountConstant.INVALID_FIELDS, AccountConstant.INVALID_FIELDS_CODE);
                
                }
                emailList.add(email);
                Contact dbcontact = contactRepository.findByEmail(email);
                if (null == dbcontact) {
                    dbcontact = new Contact();
                    dbcontact.setCreatedAt(dbAccount.getCreatedAt());
                    dbcontact.setCreatedBy(dbAccount.getCreatedBy());
                    dbcontact.setEmail(contact.getEmail());
                    dbcontact.setFirstName((contact.getFirstName()));
                    dbcontact.setLastName((contact.getLastName()));
                    dbcontact.setPhone(contact.getPhoneNumber());
                    dbcontact.setStatus(AccountConstant.STATUS_ACTIVE);
                    logger.info("contactRepository={} {}", contactRepository,contact.getEmail());

                    try {
                        dbcontact = contactRepository.save(dbcontact);
                    } catch (Exception e) {

                        logger.error("ERROR in updating contact table={}", e);
                        throw new InternalServerException(AccountConstant.INTERNAL_SERVER_ERROR,
                                AccountConstant.INTERNAL_SERVER_ERROR_CODE);
                    }
                }


                AccountContact dbAccountContact = new AccountContact();
                dbAccountContact.setCreatedAt(dbAccount.getCreatedAt());
                dbAccountContact.setCreatedBy(dbAccount.getCreatedBy());
                dbAccountContact.setStatus(AccountConstant.STATUS_ACTIVE);
                ContactRole contactRole = getContactRole(contact.getType());
                if (null == contactRole) {
                    logger.error("ERROR={}", "contact type is invalid");
                    throw new AccountException(AccountConstant.INVALID_FIELDS, AccountConstant.INVALID_FIELDS_CODE);
                }
                dbAccountContact.setContactRole(contactRole);
                dbAccountContact.setAccount(dbAccount);
                dbAccountContact.setContact(dbcontact);
                if (null != dbcontact.getAccountContact() && !dbcontact.getAccountContact().isEmpty()) {

                    logger.error("dbcontact.getAccountContact() is not null, dbcontact.getAccountContact().size()={}",
                            dbcontact.getAccountContact().size());  

                    List<AccountContact> accountContactList = dbcontact.getAccountContact();
                    accountContactList.add(dbAccountContact);
                } else {
                    logger.error("getAccountStatusHistory is null");
                    List<AccountContact> accountContactList = new ArrayList<>();
                    accountContactList.add(dbAccountContact);
                    dbcontact.setAccountContact(accountContactList);
                }
                
            
                    if (null != contact && AccountConstant.ADMIN_CONTACT_ROLE.equalsIgnoreCase(contact.getType())) {
                        if (null != contact.getAdminId()) {
                            dbService.saveExternalID(String.valueOf(dbcontact.getContactId()), contact.getAdminId(), headers, AccountConstant.STATUS_ACTIVE,
                                    CosConstants.ID_TYPE);
                        }
                    }
                

            }
        }
    } catch (Exception e) {
        logger.error("ERROR in setAccountContact to dbAccount contacts={},{}", accountReq.getContacts(), e);
        throw e;
    }
}

@LogExecutionTime(log = true)
public Account saveDirectCustomerAccount(AccountReq source, Account dbAccount, Map<String, String> headers, String workflowId) {
    
        try {
            String statusCode=AccountConstant.INITIAL_STATUS_CODE;
            dbAccount = updateAccountStatusHistory(dbAccount, statusCode);

        } catch (Exception e) {

            logger.error("ERROR in updateAccountStatusHistory={}", e);
            throw new InternalServerException(AccountConstant.INTERNAL_SERVER_ERROR,
                    AccountConstant.INTERNAL_SERVER_ERROR_CODE);
        }

        return dbAccount;
    }

@LogExecutionTime(log = true)
private Account updateAccountStatusHistory(Account dbAccount, String statusCode) {

    logger.info("updateAccountStatusHistory *****START");
    AccountStatusHistory accountStatusHistory = new AccountStatusHistory();
    accountStatusHistory.setAccount(dbAccount);
    accountStatusHistory.setCreatedAt(dbAccount.getCreatedAt());
    accountStatusHistory.setCreatedBy(dbAccount.getCreatedBy());
    try {
        updateAccountStatus(dbAccount, statusCode, accountStatusHistory);
    } catch (Exception e) {

        logger.error("ERROR updateAccountStatus e={}", e);
    }

    if (null != dbAccount.getAccountStatusHistory()) {

        logger.error("getAccountStatusHistory not null");
        dbAccount.getAccountStatusHistory().add(accountStatusHistory);

    } else {
        logger.error("getAccountStatusHistory is null");
        List<AccountStatusHistory> accountStatusHistoryList = new ArrayList<>();
        accountStatusHistoryList.add(accountStatusHistory);
        dbAccount.setAccountStatusHistory(accountStatusHistoryList);
    }

    Account createdAccount = saveAccount(dbAccount);
    logger.debug("createdAccount.getAccountId()={}", createdAccount.getAccountId());
    return createdAccount;

}

最后的方法是

public Account saveAccount(Account dbAccount) {
    try {

        Account createdAccount = accountRepository.save(dbAccount);
        logger.debug("createdAccount.getAccountId()={}", createdAccount.getAccountId());
        return createdAccount;
    } catch (Exception e) {

        logger.error("ERROR in account creation={}", e);
        throw new InternalServerException(AccountConstant.INTERNAL_SERVER_ERROR,
                AccountConstant.INTERNAL_SERVER_ERROR_CODE);
    }
}

异常堆栈跟踪

ERROR Throwable={}","stack_trace":"org.springframework.dao.DataIntegrityViolationException: A different object with the same identifier value was already associated with the session : [com.adobe.costheta.account.model.db.mysql.Account#1000000080]; nested exception is javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [com.adobe.costheta.account.model.db.mysql.Account#1000000080]\n\tat org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:400)\n\tat org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:256)\n\tat org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:537)\n\tat org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:746)\n\tat org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:714)\n\tat org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:534)\n\tat org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:305)\n\tat org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)\n\tat org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)\n\tat org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)\n\tat org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)\n\tat org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)\n\tat com.adobe.costheta.service.DbService$$EnhancerBySpringCGLIB$d6d52b1.generateDirectCustomerAccountEntity()\n\tat com.adobe.costheta.service.DirectCustomerAccountServiceImpl.createDirectCustomerAccount(DirectCustomerAccountServiceImpl.java:158)\n\tat com.adobe.costheta.service.DirectCustomerAccountServiceImpl$$FastClassBySpringCGLIB$f4a473.invoke()\n\tat org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)\n\tat org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:750)\n\tat org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)\n\tat org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)\n\tat org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)\n\tat org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)\n\tat com.adobe.costheta.service.DirectCustomerAccountServiceImpl$$EnhancerBySpringCGLIB$af2ff1.createDirectCustomerAccount()\n\tat com.adobe.costheta.resource.AccountResource.createAccount(AccountResource.java:93)\n\tat com.adobe.costheta.resource.AccountResource$$FastClassBySpringCGLIB$$dd5cfc46.invoke()\n\tat org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)\n\tat org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:750)\n\tat org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)\n\tat org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:88)\n\tat com.adobe.asr.logging.aspect.ExecutionTimeAspect.logExecutionTime(ExecutionTimeAspect.java:83)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n\tat java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n\tat java.base/java.lang.reflect.Method.invoke(Method.java:566)\n\tat org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644)\n\tat org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633)\n\tat org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)\n\tat org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:175)\n\tat org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)\n\tat org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)\n\tat org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)\n\tat com.adobe.costheta.resource.AccountResource$$EnhancerBySpringCGLIB$$d988334a.createAccount()\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n\tat java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n\tat java.base/java.lang.reflect.Method.invoke(Method.java:566)\n\tat org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:893)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:798)\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)\n\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:665)\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:750)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:97)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.boot.actuate.web.trace.servlet.HttpTraceFilter.doFilterInternal(HttpTraceFilter.java:88)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat com.adobe.asr.filter.AsrRequestResponseFilter.doFilterInternal(AsrRequestResponseFilter.java:88)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat com.adobe.asr.logging.http.servlet.AsrLoggingFilter.doFilter(AsrLoggingFilter.java:69)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat com.adobe.asr.filter.AsrRequestIdFilter.doFilterInternal(AsrRequestIdFilter.java:99)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat com.adobe.costheta.filters.ApiLoggingFilter.doFilter(ApiLoggingFilter.java:52)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat com.adobe.asr.exception.AsrExceptionFilter.doFilterInternal(AsrExceptionFilter.java:82)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat com.adobe.costheta.filters.MDCFilter.doFilter(MDCFilter.java:44)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:94)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:114)\n\tat org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:104)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:367)\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1639)\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\n\tat java.base/java.lang.Thread.run(Thread.java:834)\nCaused by: javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [com.adobe.costheta.account.model.db.mysql.Account#1000000080]\n\tat org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:123)\n\tat org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181)\n\tat org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:188)\n\tat org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1478)\n\tat org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:512)\n\tat org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3310)\n\tat org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2506)\n\tat org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:447)\n\tat org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:178)\n\tat org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access0(JdbcResourceLocalTransactionCoordinatorImpl.java:39)\n\tat org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:271)\n\tat org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:104)\n\tat org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:533)\n\t... 126 common frames omitted\n","requestId":"f5d17eccdfa90271","Content-Type":"application/json"}

好的,答案会有点复杂,但请耐心等待。

首先根本原因是手动生成的id.

setAccountContact(...) 内,您有: dbAccountContact.setAccount(dbAccount) 分配先前在外部 map(...) 方法中创建的 Account。我们将此帐户对象命名为 A.

稍后,在 saveAccount(...) 中,您调用 accountRepository.save(dbAccount)。这调用 returns 一个 Account 对象,我们称之为 B.

现在,Spring Data JPA 存储库 save 方法根据是否考虑实体来决定是在 save 内部调用 EntityManager.merge() 还是 EntityManager.persist()是不是新的。默认情况下,它假设任何已经有 id 的东西都不能是新实体(因此选择 merge())。

当您将 unmanaged/detached 实体传递给 EntityManager.merge() 时,始终会返回输入实体的 副本 。这意味着 A != B.

这正是 different object with the same identifier value was already associated with the session 错误的来源。在事务提交时,您有 Account 的不同副本(分配给 dbAccountContactA 和刚刚从 save 返回的 B)具有相同的 id.这种状态在JPA中是被禁止的,因为JPA不知道对象是哪个版本'wins'。请注意,如果 save 调用了 EntityManager.persist(),则不会创建任何副本,原始 Account 将简单地变为托管状态,因此问题不会存在。

解决这个问题的方法:

  1. 以Hibernate的序列生成器的形式封装底层数据库的id生成能力

您正在使用 dbService.genarateAccountId(accountCode),所以我假设其中涉及一些自定义逻辑,而简单的 @GeneratedValue(strategy = SEQUENCE|AUTO|TABLE) 是行不通的。但是,这些 不是 唯一的选择。请参阅 this article 了解如何推出自定义 ID 生成器。

(注意:文章建议从 SequenceStyleGenerator 继承,这允许您将 DB 序列与额外的内容连接起来。但是,如果您实现更通用的IdentifierGenerator,您将可以访问 SessionImplementor,您可以使用它在您的数据库上调用 arbitrary SQL,不一定访问序列) .

就个人而言,我强烈推荐此选项。但是,如果这对您不起作用,那么:

  1. 使用Persistable

你可以实现 Persistable 和实现 isNew() 来告诉 Spring 数据 '嘿,不要被 id 骗了,这个对象实际上是一个新对象! .正如您已经注意到的那样,这样做可以解决问题。当然,需要注意的是 Account 任何 个实例,新的或旧的,现在都将被 JpaRepository.save() 视为新的。如果您 在我们的应用程序的其他地方合并分离的 Account 实体,这将产生问题(否则,您应该完全没问题)。

  1. 将您的代码重新排列为 save Account

另一个解决方案是在 map:

中创建新帐户后, 立即调用 accountRepository.save()
Account dbAccount = new Account(); //Account A created
dbService.genarateAccountId(accountCode);
dbAccount = accountRepository.save(dbAccount); //Account A passed to `save`, `save` returns Account B, Account A is not referenced anywhere anymore, problem solved

(您也可以稍后调用 save,但必须在 之前 有问题的 dbAccountContact.setAccount(dbAccount) 行)。

这避免了在持久性上下文中存在两个 Account 副本的问题。 但是,您可能 运行 在执行查询时遇到问题,例如在这一行中:

Contact dbcontact = contactRepository.findByEmail(email);

在到达这一行之前,您需要确保 Account 的所有必需关联都已正确填充,因为此时持久性上下文将被刷新。如果您不能满足此要求,替代方法是使用 FlushMode.COMMIT 和查询方法本身:

@QueryHints(value = { @QueryHint(name = org.hibernate.annotations.QueryHints.FLUSH_MODE, value = "COMMIT") }) //  this prevents a flush before querying
Contact findByEmail(String email);

(不确定上述问题是否适用于您,但如果适用,只需重新安排您的代码,以便保存 Account 的当前状态不会触发约束违规 before 执行任何查询。或者,考虑在 之前 你甚至 save Account) 调用查询方法。此外,不必担心在这种情况下级联 persist 操作。级联仍应在事务提交时发生。

  1. 只需将 EntityManager 注入您的服务并自己调用 EntityManager.persist()

当然,这会引入泄漏抽象,因此您可能不应该认真考虑此解决方案。我只是说它会起作用。

这将解决 DataIntegrityViolationException

@Id
@Column(name="id")
@GeneratedValue(strategy=GenerationType.AUTO)
private long id;

其实我找到了另一个解决方案。

我在更新实体时遇到了这个问题。 首先,我通过 id 查询实体。 其次,我隐式更新了那个实体。

解决方案是使用显式调用来保存实体。

来自

Entity entityFromDb = repository.findById(id)
mapper.merge(newEntity, entityFromDb)

Entity entityFromDb = repository.findById(id)
mapper.merge(newEntity, entityFromDb)
repository.save(entityFromDb) // so here is the solution