保存实体

时间:2017-03-13 13:03:59

标签: java spring hibernate spring-data-jpa

基于Spring Boot的大型REST Web应用程序,spring-data-jpa。我PersonEntityParentEntity具有ManyToOne关系。关系的所有者是PersonEntity,其表中有parentId

在执行了许多SoapUI并发测试之后,我们发现在一个地方ConcurrentModificationException随机抛出(随机,实际上我们看到一次,应用程序运行超过一年,但我想我们有一些并发访问来自另一个主题)

出现问题的代码:

@Override
@Transactional
public void disconnectPersonFromParent(final String parentId, final String personId) {
    //throw NotFound when null from repo
    final PersonEntity personEntity = personService.getPerson(personId); 
    //throw NotFound when null from repo        
    final ParentEntity parentEntity = parentService.getParent(parentId); 
    if(parentEntity.equals(personEntity.getParent())) {
        personEntity.setParent(null);
        //Addresses comes from parent, when we disconnecting we have disconnect addresses also
        personEntity.setAddresses(new HashSet<>()); 
        personRepository.save(personEntity);
    }
}

堆栈跟踪:

||ERROR|http-nio-auto-1-exec-487|error|[TransactionSystemExceptionMapper:353]|Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction
Caused by: javax.persistence.RollbackException: Error while committing the transaction
    at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:94)
    at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:517)
    ... 130 more
Caused by: java.util.ConcurrentModificationException
    at java.util.LinkedHashMap$LinkedHashIterator.nextNode(LinkedHashMap.java:719)
    at java.util.LinkedHashMap$LinkedEntryIterator.next(LinkedHashMap.java:752)
    at java.util.LinkedHashMap$LinkedEntryIterator.next(LinkedHashMap.java:750)
    at org.hibernate.internal.util.collections.IdentityMap.entryArray(IdentityMap.java:163)
    at org.hibernate.internal.util.collections.IdentityMap.concurrentEntries(IdentityMap.java:75)
    at org.hibernate.event.internal.AbstractFlushingEventListener.postFlush(AbstractFlushingEventListener.java:379)
    at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:57)
    at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1258)
    at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:425)
    at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
    at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:177)
    at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:77)
    ... 131 more

代码有问题吗?我怎么能避免它?

1 个答案:

答案 0 :(得分:1)

向数据库中的所有表添加一个VERSION列,并在实体中使用@Version注释,如下所示。

@Version
private Long version

一旦版本列到位,JPA / Hibernate会在保存实体之前自动检查版本。如果实体加载和保存之间存在任何版本不匹配,您将获得javax.persistence.OptimisticLockException。您可以捕获异常并采取任何必要的操作,如记录,通知用户等。我们在网上有关于@Version实现的各种文章。

希望它有所帮助。

您可以在Repo调用或添加的Service方法中进行异常处理 @COntrollerAdvice捕获异常并共同处理它们。下面的示例是所有REST调用发送500错误&#34;自定义消息&#34;作为回应。

@ControllerAdvice
public class ExceptionHandlerAdvice extends ResponseEntityExceptionHandler {

@ExceptionHandler(value = { Exception.class })
protected ResponseEntity<Object> handleAllExcecption(Exception ex, WebRequest request) {
    // Do what ever you want. Log the error message, send notification etc
    return handleExceptionInternal(ex, logId.longValue(), new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR,
            request);
}

}