Apache Camel - Row已被另一个事务更新或删除

时间:2015-06-03 11:18:07

标签: java hibernate orm transactions apache-camel

我有一个使用camel的批处理来消耗数据库中的一些记录。所以端点是一个带有namedQuery的jpa。

当我启动批处理时,我会在运行结束时获得异常。 在运行期间,我的代码更新了端点正在使用的实体。我的问题是如何避免这种例外?

以下是例外:

[03.06.2015 10:47:32,277] WARN  org.apache.camel.util.CamelLogger.log:224 :Consumer Consumer[jpa://ch.gma.nova.vaudoise.entity.ImpactEntity?consumer.namedQuery=mutationsContrat&consumer.parameters=%23params&maximumResults=500] failed polling endpoint: Endpoint[jpa://ch.gma.nova.vaudoise.entity.ImpactEntity?consumer.namedQuery=mutationsContrat&consumer.parameters=%23params&maximumResults=500]. Will try again at next poll. Caused by: [javax.persistence.OptimisticLockException - Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [ch.gma.nova.vaudoise.entity.ImpactEntity#101]]
javax.persistence.OptimisticLockException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [ch.gma.nova.vaudoise.entity.ImpactEntity#101]
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.wrapStaleStateException(AbstractEntityManagerImpl.java:1788) ~[hibernate-entitymanager-4.3.6.Final.jar:4.3.6.Final]
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1705) ~[hibernate-entitymanager-4.3.6.Final.jar:4.3.6.Final]
    at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1677) ~[hibernate-entitymanager-4.3.6.Final.jar:4.3.6.Final]
    at org.hibernate.jpa.internal.QueryImpl.getResultList(QueryImpl.java:458) ~[hibernate-entitymanager-4.3.6.Final.jar:4.3.6.Final]
    at org.apache.camel.component.jpa.JpaConsumer$1.doInTransaction(JpaConsumer.java:90) ~[camel-jpa-2.12.3.jar:2.12.3]
    at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133) ~[spring-tx-4.1.2.RELEASE.jar:4.1.2.RELEASE]
    at org.apache.camel.component.jpa.JpaConsumer.poll(JpaConsumer.java:80) ~[camel-jpa-2.12.3.jar:2.12.3]
    at org.apache.camel.impl.ScheduledPollConsumer.doRun(ScheduledPollConsumer.java:187) [camel-core-2.12.3.jar:2.12.3]
    at org.apache.camel.impl.ScheduledPollConsumer.run(ScheduledPollConsumer.java:114) [camel-core-2.12.3.jar:2.12.3]
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:482) [na:1.7.0]
    at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:315) [na:1.7.0]
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:193) [na:1.7.0]
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:308) [na:1.7.0]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1176) [na:1.7.0]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) [na:1.7.0]
    at java.lang.Thread.run(Thread.java:795) [na:1.7.0]

实体:

@Entity
@Table(name = "IMPACT_FLUX")
@NamedQueries({
    @NamedQuery(name = ImpactEntity.NAMED_QUERY_MUTATION, query = "Select i from ImpactEntity i where i.process = 'MUTATION_CONTRAT' and i.status = :status and i.application = :application"),
    @NamedQuery(name = ImpactEntity.NAMED_QUERY_ANNONCE_SINISTRE, query = "Select i from ImpactEntity i where i.process = 'ANNONCE_SINISTRE' and i.status = :status and i.application = :application"), })
public class ImpactEntity {

  public static final String NAMED_QUERY_MUTATION = "mutationsContrat";

  public static final String NAMED_QUERY_ANNONCE_SINISTRE = "annonceSinistre";

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO, generator = "seqImpactFlux")
  @SequenceGenerator(name = "seqImpactFlux", sequenceName = "SEQ_IMPACT_FLUX")
  @Column(name = "ID_IMPACT")
  private Long id;

  @Column(name = "ID_CONTRAT")
  private Long numContrat;

  @Column(name = "ID_ANCIEN_CONTRAT")
  private Long numAncienContrat;

  @Column(name = "ID_PARTENAIRE")
  private Long numPartenaire;

  @Column(name = "ID_PRESTATION")
  private Long numPrestation;

  @Column(name = "STATUS")
  private String status;

  @Column(name = "PROCESSUS")
  private String process;

  @Column(name = "APPLICATION")
  private String application;

  @Column(name = "CODE")
  private String code;

  @Version
  @Column(name = "OPTLOCK")
  private Integer version;

  @ManyToOne
  @JoinColumn(name = "ID_SUIVI")
  private SuiviMessageEntity suiviMessage;

  @Column(name = "DT_CREATION")
  private Date dateCreation;
...

2 个答案:

答案 0 :(得分:0)

您遇到此问题是因为两个并发线程更新了同一个实体而optimistic locking mechanism阻止了losing updates

有几种方法可以解决此问题:

  • 如果两个交易更新了ImpactEntity的不同部分,您有两个选择:

    1. 您可以使用version-less optimistic locking

    2. 您使用DML UPDATE来绕过版本检查

  • 如果两个事务更新ImpactEntity impactEntity = ... session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_WRITE)) .lock(impactEntity); 的相同字段,则需要在两个事务中使用pessimistic locking,因此一旦一个事务获取行级锁定,另一个事务将等待锁定被释放

    $('[id*="start_date"]').length
    

答案 1 :(得分:0)

可能是您的批处理作业实例彼此交错,因此它们会读取和更新相同的数据。

也许您正在为JPA组件使用固定费率(默认),因此另一个作业实例在上一个作业实例完成之前就开始了。

要解决此问题,您应该使用fixed delay

consumer.useFixedDelay=true