私はしばらくこの問題に悩まされ、悩まされてきましたが、最終的に何が起こっているのかを示す例を作成しました. うまくいけば、他の誰かが何が起こっているのか少しでも手がかりを持っています.
を使用した 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 がデータベース トランザクションを処理する方法に問題がある可能性がありますか?
私はここで完全にアイデアがないので、試してみるべき追加の提案は大歓迎です。