1

我在我的项目中使用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

注意:

  • 有一个独特的进入点是

    公共同步 S salva(S vendita, Long idPrenotazione) 抛出异常 {

并且这个方法是同步的。

  • 事务隔离在 spring jpa 中为 DEFAULT,在 my.cnf 中为 READ_COMMITTED(我使用的是 Mysql)
  • 如果您在第一次失败后重试间接调用方法 salva() ,它会起作用!

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

4

1 回答 1

1

你想用它来测序吗?它不会起作用,因为调用该查询的每个进程都将获得相同的值,除非您保证它们仅通过锁定顺序执行。需要对查询进行悲观锁定,这样在第一个进程完成更新之前,没有其他进程可以发出相同的查询。
与其重新发明轮子,不如只使用数据库或 JPA 排序策略,请参阅http://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing

于 2013-09-06T17:51:51.890 回答