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() を間接的に呼び出すため、メソッドが完了するまでに長い時間がかかります (約 7 秒)。
  • 最初のメソッドが終了する前に、間接的にメソッド salva() を呼び出します

あなたはそれを見ることができます: - メソッド salvaDaPrenotazione() に入ると、正しい MAX(numero) を取得します (これは、db に保存されたシーケンスの最後の値です)。 getNumeroBiglietto() へ db から正しい更新値を取得しました - メソッド salva() を入力すると、値 MAX(numero) を db から取得しましたが、値が間違っています! このトランザクションが他のトランザクションのコミットを見ていないかのように、メソッド salvaDaPrenotazione() の開始時に得たのと同じ古い値があります。したがって、トランザクションは失敗します

org.hibernate.exception.ConstraintViolationException

ご了承ください:

  • ユニークなエンターポイントがあります

    public synchronized S salva(S vendita, Long idPrenotazione) throws Exception {

このメソッドは同期化されています。

  • トランザクションの分離は、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 に答える