2013-03-28 2 views
0

여러 스레드가 동일한 엔터티 Job을로드 한 다음 각 스레드가 하위 컬렉션 Set<JobError>에 추가되는 상황이 있습니다. 부모 자체가 업데이트 된 경우이 예외를 이해할 수 있지만 상위 노드에 대한 유일한 변경은 컬렉션에도 추가 된 것입니까?자식 컬렉션에 추가하는 여러 스레드에서 StaleObjectStateException이 발생합니다.

부모 엔티티 :

@Entity 
@Table(name = "JOB") 
public class Job extends BaseEntity { 

private Set<JobError> jobErrors = new HashSet<JobError>(); 


/** 
* @return the jobErrors 
*/ 
@OneToMany(mappedBy = "job", cascade = { CascadeType.PERSIST, 
     CascadeType.MERGE, CascadeType.REMOVE }) 
public Set<JobError> getJobErrors() { 
    return jobErrors; 
} 

/** 
* @param jobErrors 
*   the jobErrors to set 
*/ 
public void setJobErrors(Set<JobError> jobErrors) { 
    this.jobErrors = jobErrors; 
} 

/** 
* Helper to take care of both sides of the association 
* @param message 
* @param currentProfileId 
*/ 
public void addError(String message, Long currentProfileId, 
     String firstName, String lastName) { 
    JobError er = new JobError(message, currentProfileId, firstName, 
      lastName, this); 
    jobErrors.add(er); 
} 
} 

아이 엔티티 :

@Entity 
@Table(name = "JOB_ERROR") 
public class JobError extends BaseEntity { 
private Job job; 

    public JobError(String description, Long profileId, String firstName, 
     String lastName, Job job) { 
    this.description = description; 
    this.profileId = profileId; 
    this.firstName = firstName; 
    this.lastName = lastName; 
    this.job = job; 
} 
/** 
* 
*/ 
@ManyToOne(fetch = FetchType.LAZY) 
@JoinColumn(name = "JOB_ID", nullable = false) 
public Job getJob() { 
    return job; 
} 

/** 
* @param jobErrors 
*   the jobErrors to set 
*/ 
public void setJob(Job job) { 
    this.job = job; 
} 
} 

서비스 코드, 이것은 여러 개의 동시 스레드에서 실행 : 같이 주석이 서비스 메서드가 반환되면

job = jobDao.findById(er.getJobId(), false); 

for (Long profileId : er.getProfileIds()) { 
// do stuff 
try { 
    sendEmail(emailTemplateDto, user); 
} catch (RuntimeException re) { 
    job.addError(re.getLocalizedMessage(), currentProfileId, profile.getPersonalData().getFirstName(), profile.getPersonalData().getLastName()); 
} 

@Transactional(propagation = Propagation.REQUIRED) StaleObjectStateException이 throw됩니다.

2013-03-28 13:22:52,578 ERROR  org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(324): - Could not synchronize database state with session 
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.test.project.domain.Job#2] 
at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:1950) 
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2594) 
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2494) 
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2821) 
at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:113) 
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:273) 
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:265) 
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:185) 
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321) 
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51) 
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1216) 
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:383) 
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:133) 
at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:76) 
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:467) 
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754) 
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723) 
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393) 
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120) 
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202) 
at $Proxy162.processSendEmail(Unknown Source) 
at com.test.project.service.messaging.EmailRequestMessageListener.onMessage(EmailRequestMessageListener.java:57) 
at org.springframework.jms.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:560) 
at org.springframework.jms.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:498) 
at org.springframework.jms.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:467) 
at org.springframework.jms.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:439) 
at org.springframework.jms.listener.SimpleMessageListenerContainer.processMessage(SimpleMessageListenerContainer.java:311) 
at org.springframework.jms.listener.SimpleMessageListenerContainer$2.onMessage(SimpleMessageListenerContainer.java:287) 
at org.apache.activemq.ActiveMQMessageConsumer.dispatch(ActiveMQMessageConsumer.java:1321) 
at org.apache.activemq.ActiveMQSessionExecutor.dispatch(ActiveMQSessionExecutor.java:131) 
at org.apache.activemq.ActiveMQSessionExecutor.iterate(ActiveMQSessionExecutor.java:202) 
at org.apache.activemq.thread.PooledTaskRunner.runTask(PooledTaskRunner.java:129) 
at org.apache.activemq.thread.PooledTaskRunner$1.run(PooledTaskRunner.java:47) 
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) 
at java.lang.Thread.run(Thread.java:662) 

나는 JobError을 직접 저장하려고 시도하는 것을 제외하고는 아무 것도 생각할 수 없습니다. 현재 Job을로드하고 JobError 컬렉션에 추가 한 다음 merge 작업을 추가하고 cascade.merge가 하위 컬렉션 저장을 담당하게되기를 바랍니다.

모든 포인터가 제공됩니다.

답변

0

이것이 문제의 예외 원인인지는 모르지만 그렇지 않은 경우 문제가 발생할 수 있습니다. HashSet은 스레드로부터 안전한 컬렉션이 아닙니다. 즉, 두 스레드가 addError 동시에 오류 중 하나가 집합에 포함되지 않을 수 있습니다. addError 메서드에 "synchronized"키워드를 추가해야합니다. 그렇지 않으면 HashSet을 스레드로부터 안전한 대안으로 대체해야합니다. ConcurrentLinkedQueue 또는 ConcurrentHashMap

관련 문제