Spring Jpa并发事务和陈旧数据

时间:2013-09-06 17:14:03

标签: java hibernate jpa transactions spring-data

我在我的项目中使用spring jpa。

我使用同步方法提供服务:

@Service("venditaCustom")
@Transactional
public class VenditaCustomRepositoryImpl implements VenditaCustomRepository {

@Override   
public synchronized <S extends Vendita> S salva(S vendita, Long idPrenotazione)      throws Exception {
    if (idPrenotazione == null || idPrenotazione < 0) {
        return salva(vendita);
    } else {
        return salvaDaPrenotazione(vendita, idPrenotazione);
    }
}

此方法向方法salva或salvaDaPrenotazione发送请求。

public <S extends Vendita> S salva(S vendita) throws Exception {
....do many operation on object Vendita.....
int numeroFiscale = getNumeroBiglietto();   
...
manager.persist(vendita);
manager.flush();

方法getNumeroBiglietto()执行查询以获取最后一个序列号:

private int getNumeroBiglietto() {      
    String sQuery = "SELECT MAX(numero) FROM Biglietto WHERE anno = :anno ";
    Query q = manager.createQuery(sQuery);
    q.setParameter("anno", new GregorianCalendar().get(Calendar.YEAR));
    Integer maxNum = 0;
    try {
        maxNum = (Integer) q.getSingleResult();         
        if (maxNum == null)
            maxNum = 0;
            } catch (NoResultException e) {

        maxNum = 0;
    } catch (Exception e) {
        log.error("", e);
        maxNum = 0;
    }
    if (maxNum == null || maxNum == 0) {
        maxNum = PRIMO_NUMERO_FISCALE_BIGLIETTI;
    } else {
        maxNum++;
    }
    log.trace("maxNum " + maxNum);
    return maxNum;
}

方法salvaDaPrenotazione非常类似于salva():

    public <S extends Vendita> S salvaDaPrenotazione(S vendita, Long idPrenotazione)     throws Exception {
 ...do many action on object Vendita
int numeroFiscale = getNumeroBiglietto();
.....
manager.persist(vendita);
manager.flush();

问题: 我按顺序执行此操作:

  • 使用大数据间接调用salvaDaPrenotazion()方法,因此该方法需要很长时间才能完成(约7s)
  • 在第一个方法完成之前我间接调用方法salva()

你可以看到: - 当你输入方法salvaDaPrenotazione()时,你得到正确的MAX(numero)(是db上保存的序列的最后一个值) - 在方法结束时salvaDaPrenotazion()在持久化和刷新后调用getNumeroBiglietto()我从db得到了正确的更新值 - 当你输入方法salva()并从db得到值MAX(numero)时我的错误值!我有一个陈旧的值,就像我在方法salvaDaPrenotazione()的开头一样,好像这个事务没有看到另一个事务的提交。 因此交易失败并带有

org.hibernate.exception.ConstraintViolationException

请注意:

  • 有一个唯一的输入点

    public synchronized S salva(S vendita,Long idPrenotazione)抛出异常{

并且此方法是同步的。

  • 事务隔离是弹簧jpa中的DEFAULT和my.cnf中的READ_COMMITTED(我使用的是Mysql)
  • 如果您在第一次失败后重试间接调用方法salva(),则可以正常工作!

我不明白问题出在哪里。我不认为与缓存问题有关,也许与隔离问题有关。

1 个答案:

答案 0 :(得分:1)

您是否尝试将其用于测序?它不起作用,因为调用该查询的每个进程都将获得相同的值,除非您保证它们仅通过锁定顺序执行。需要对查询执行悲观锁定,以便在第一个进程完成更新之前,其他任何进程都不能发出相同的查询。 您应该使用数据库或JPA排序策略,而不是重新发明轮子,请参阅http://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing