简体   繁体   English

Spring Jpa并发事务和陈旧数据

[英]Spring Jpa concurrent transaction and stale data

I'm using spring jpa in my project. 我在项目中使用spring jpa。

I've a service with a synchonized method: 我有一个采用同步方法的服务:

@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);
    }
}

This method send request to the method salva or salvaDaPrenotazione. 此方法将请求发送到方法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();

the method getNumeroBiglietto() do a query to obtain the last sequence number: 方法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;
}

The method salvaDaPrenotazione is quite similar to salva(): 方法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();

The problem: I do this action in sequence: 问题:我按顺序执行此操作:

  • call indirectly method salvaDaPrenotazion() with big data so the method take long time to complete (about 7s) 用大数据间接调用方法salvaDaPrenotazion(),因此该方法需要很长时间才能完成(大约7s)
  • before first method finish I call indirectly method salva() 在第一个方法完成之前,我间接调用方法salva()

You can see that: - when you enter in method salvaDaPrenotazione() you get the correct MAX(numero) (is the last value of sequence saved on db) - at the end of the method salvaDaPrenotazion() after persist and flush doing a call to getNumeroBiglietto() I've the correct updated value from db - when you enter in the method salva() and i get the value MAX(numero) from db I've a wrong value! 您会看到:-在方法salvaDaPrenotazione()中输入时,您将获得正确的MAX(numero)(是在db上保存的序列的最后一个值)-在方法salvaDaPrenotazion()结束后,持久化并刷新后进行调用到getNumeroBiglietto()我从db获得了正确的更新值-当您输入方法salva()时,我从db获得了MAX(numero)值,我有一个错误的值! I've a stale value, the same I got at the start of method salvaDaPrenotazione() as if this transaction don't see the commit of the other transaction. 我有一个过时的值,与方法salvaDaPrenotazione()开始时得到的值相同,就好像该事务没有看到其他事务的提交一样。 So the transaction fails with a 因此交易失败并显示

org.hibernate.exception.ConstraintViolationException

Note that: 注意:

  • there is a unique enter point that is 有一个独特的进入点是

    public synchronized S salva(S vendita, Long idPrenotazione) throws Exception { 公共同步S salva(S vendita,长idPrenotazione)引发异常{

and this method is syncronized. 并且此方法已同步。

  • the transaction isolation is DEFAULT in spring jpa and READ_COMMITTED in my.cnf (I'm using Mysql) 事务隔离在spring jpa中是DEFAULT,在my.cnf中是READ_COMMITTED(我正在使用Mysql)
  • If you retry to call indirectly the method salva() after first fail it works! 如果您尝试在第一次失败后间接调用方法salva(),它将起作用!

I don't understand where can be the problem. 我不知道哪里可能出问题了。 I don't think is related to cache problem, maybe to isolation problem. 我认为与缓存问题无关,也许与隔离问题无关。

Are you trying to use this for sequencing? 您是否要使用它进行测序? It won't work as every process calling that query will all get the same value, unless you guarantee they are only executed sequentially via locking. 它将无法正常工作,因为调用该查询的每个进程都将获得相同的值,除非您保证它们仅通过锁定顺序执行。 Pessimistic locking would need to be done on the query so that no other process can issue the same query until the first process is done its update. 悲观锁定将需要在查询上进行,以便在第一个进程完成其更新之前,没有其他进程可以发出相同的查询。
Rather than reinventing the wheel though, you should just use a database or JPA sequencing strategy, see http://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing 但是,您不必使用新的方法,而应该使用数据库或JPA排序策略,请参阅http://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM