0

私はしばらくこの問題に悩まされ、悩まされてきましたが、最終的に何が起こっているのかを示す例を作成しました. うまくいけば、他の誰かが何が起こっているのか少しでも手がかりを持っています.

を使用した Spring Transactional JUnit テストがあり@Rollback(true)ます。テストは、単体テストの最後にデータベースの変更を自動的にロールバックする HibernateTransaction にラップされます。これは機能しているように見えますが、この 1 つのクエリを使用したこの奇妙なシナリオでは、単体テストでのみ、この 1 つの@Transactionalビジネス ロジック メソッドが null を返します。

@Test
@Rollback(true)
public void testObscureIssue() throws Exception {
    // Not important...
    l = createLeague();
    t1 = createTeam(l);
    User u = userBo.getUser(1L, false);

    Player player = TestUtils.getInstance().createTestData(Player.class, 1).get(0);
    player.setUser(u);
    player.setGender("M");
    player.setStartingActivityLevel(ActivityLevelEnum.Sedentary);
    playerBo.addOrUpdate(player);
    TeamPlayer tp = new TeamPlayer(t1, player);
    leagueStructureBo.addOrUpdate(tp);

    // This test will pass 10% of the time, seemingly random. Randomness only inside of unit test
    Team t = playerBo.getCurrentTeam(player.getPlayerID());
    if (t == null) throw new OutOfMemoryError("What is this... I don't even...");

    Team expected = playerBo.getCurrentTeam(player.getPlayerID());
    assertNotNull(expected);
    assertEquals(t1, expected);
}

したがって、メソッドplayerBo.getCurrentTeamはアプリケーションで常に正しく戻り、単体テストの任意の場所にブレークポイントを配置して一度に 1 行ずつコードを実行すると、常に正しく戻ります。ただし、デバッグせずに単体テストを実行すると、ほとんどの場合失敗します。

ここで何らかの競合状態が発生しているのではないかと考えましたが、Thread.sleep(400000L);この Transactional メソッドを呼び出す前にステートメントを置いても、それでも失敗します。

トランザクション メソッドのコード:

@Override
@Transactional
public Team getCurrentTeam(long playerId) {
    String qry = "select t from Team as t inner join t.teamPlayers as tp " +
            "inner join tp.player as tpp where tpp.playerID = :playerId and (((current_timestamp() between tp.startDate and tp.endDate " +
            "and tp.endDate is not null) or (tp.endDate is null and current_timestamp() > tp.startDate)))";
    Object wtf = sessionFactory.getCurrentSession().createQuery(qry)
            .setParameter("playerId", new Long(playerId)).uniqueResult();
    return (Team)wtf;
}

Transaction 属性はすべて、Spring Hibernate4 TransactionManager のデフォルトです。

コード例を見ると、このチーム エンティティが明確に作成されており、ログに新しいレコード用に生成された ID が表示されていることがわかります。HQL を使用してその ID でレコードを直接クエリできますが、返されますが、この Transactional メソッドの上記の HQL クエリは、デバッグ モードでステップスルーしない限り、null を返し、動作します。

最も外側のトランザクションがロールバックされるまで何もロールバックされないという印象を受けていたので、これはネストされたトランザクションの問題ですか? なぜこの1つの特定の方法だけで?Hibernate 4 または Spring 3.1.1 のバグですか? MySQL InnoDB を使用していますが、これは MySQL InnoDB がデータベース トランザクションを処理する方法に問題がある可能性がありますか?

私はここで完全にアイデアがないので、試してみるべき追加の提案は大歓迎です。

4

1 に答える 1

1

私の推測では、問題はcurrent_timestamp(). おそらく今を開始日として TeamPlayer を作成しました。ブレークポイントを設定すると、現在のタイムスタンプは開始日よりも体系的に遅れますが、ブレークポイントを設定しない場合、コードは十分に高速で、現在のタイムスタンプがチーム プレーヤーの開始日と同じになるようにします。

于 2012-11-20T22:35:50.900 に答える