0

複数のスレッドが同じエンティティをロードしJob、各スレッドがその子コレクションに追加される状況がありSet<JobError>ます。親自体が更新された場合、この例外を理解できますが、それでも親への唯一の「変更」はコレクションへの追加ですか?

親会社:

@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として注釈が付けられたサービス メソッドが返されると、次のようにスローされます。

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し、 のコレクションに追加してからJobErrorJobmergeを実行し、cascade.merge が子コレクションの保存を処理してくれることを願っています。

任意のポインタをいただければ幸いです。

4

1 に答える 1

0

これが問題の例外の原因であるかどうかはわかりませんが、そうでない場合は、後で問題が発生する可能性があります: HashSet はスレッドセーフなコレクションではありません。つまり、2 つのスレッドが同時に addError を呼び出すと、その場合、エラーの 1 つがセットに含まれない可能性があります。addError メソッドに「synchronized」キーワードを追加するか、HashSet を ConcurrentLinkedQueue や ConcurrentHashMap などのスレッドセーフな代替手段に置き換える必要があります。

于 2013-03-28T20:39:09.837 に答える