use*_*624 0 session jpa transactions
此方法以事务开头
@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;
}
Run Code Online (Sandbox Code Playgroud)
这是一个映射器类,我在其中创建 DB Account 实体并映射 address 和 contacts 。
@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;
}
Run Code Online (Sandbox Code Playgroud)
当我不在地图内调用下面的方法时,一切正常。但是当我调用它时,我得到了标题中描述的错误。
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;
}
Run Code Online (Sandbox Code Playgroud)
最后的方法是
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);
}
}
Run Code Online (Sandbox Code Playgroud)
异常堆栈跟踪
错误 Throwable={}","stack_trace":"org.springframework.dao.DataIntegrityViolationException:具有相同标识符值的不同对象已与会话关联:[com.adobe.costtheta.account.model.db.mysql。帐号#1000000080]; 嵌套异常是 javax.persistence.EntityExistsException:具有相同标识符值的不同对象已经与会话关联:[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.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:256)\n\tat .JpaTransactionManager.doCommit(JpaTransactionManager.java:537)\n\tat org.springframework。
好吧,答案会有点复杂,但请耐心等待。
首先,根本原因是手动生成的id.
在里面setAccountContact(...),你有:dbAccountContact.setAccount(dbAccount)分配Account先前在外部map(...)方法中创建的地方。我们称这个帐户对象为A。
稍后,在内部saveAccount(...),您调用accountRepository.save(dbAccount)。此调用返回一个Account对象,我们将调用该对象B。
现在,Spring Data JPA 存储库save方法决定是调用EntityManager.merge()还是EntityManager.persist()inside save,这取决于它是否认为实体是新的。默认情况下,它假定任何已经有 id 的东西都不能是新实体(因此merge()被选中)。
当您将非托管/分离的实体传递给 时EntityManager.merge(),将始终返回输入实体的副本。这意味着A != B.
这正是different object with the same identifier value was already associated with the session错误的来源。在事务提交时,你有不同的副本(在A分配到dbAccountContact和B刚刚从返回save的)Account具有相同id。这种状态在 JPA 中是被禁止的,因为 JPA 不知道哪个版本的对象“获胜”。请注意,如果save调用EntityManager.persist(),则不会创建副本,原始文件Account将简单地被管理,因此问题将不存在。
你可以解决这个问题的方法:
您正在使用dbService.genarateAccountId(accountCode),所以我假设涉及到一些自定义逻辑,而简单的方法是@GeneratedValue(strategy = SEQUENCE|AUTO|TABLE) 行不通的。然而,这些并不是唯一的选择。请参见本文就如何推出一个定制的ID生成。
(注意:文章建议从 继承SequenceStyleGenerator,它允许您将数据库序列与额外内容连接起来。但是,如果您实现更通用的IdentifierGenerator,您将可以访问SessionImplementor,您可以使用它在您的数据库上调用任意SQL,不一定访问序列)。
就个人而言,我强烈推荐此选项。但是,如果这对您不起作用,那么:
Persistable您可以实现Persistable并实现isNew()告诉 Spring Data '嘿,不要被 id 愚弄,这个对象实际上是一个新对象!'。正如您已经注意到的,这样做可以解决问题。当然,需要注意的是 的任何实例,无论是Account新的还是旧的,现在都将被 视为新的JpaRepository.save()。如果您在我们的应用程序中的其他地方合并分离的Account实体,这将产生问题(如果没有,您应该完全没问题)。
save的Account早期另一种解决方案是在您创建新帐户后accountRepository.save() 立即调用map:
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
Run Code Online (Sandbox Code Playgroud)
(你也可以save稍后打电话,但必须在有问题的dbAccountContact.setAccount(dbAccount)线路之前)。
这避免了Account持久性上下文中存在两个副本的问题。但是,您在执行查询时可能会遇到问题,例如在这一行中:
Contact dbcontact = contactRepository.findByEmail(email);
Run Code Online (Sandbox Code Playgroud)
在到达这一行之前,您需要确保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);
Run Code Online (Sandbox Code Playgroud)
(不知道上面的问题适用于你,但如果是的话,只需重新安排你的代码,以便节约的当前状态Account不会触发约束违反之前执行任何查询。另外,考虑调用查询方法之前,你甚至save在Account) . 另外,不要担心persist在这种情况下级联操作。级联仍应在事务提交时发生。
EntityManager您的服务并调用您EntityManager.persist()自己当然,这引入了一个泄漏的抽象,所以你可能不应该认真考虑这个解决方案。我只是说它会起作用。
| 归档时间: |
|
| 查看次数: |
2403 次 |
| 最近记录: |