0

一段时间以来,我一直对这个问题感到恼火和困扰,最后想出了一个例子来说明正在发生的事情。希望其他人对发生的事情有丝毫线索。

我有一个 Spring Transactional JUnit 测试@Rollback(true)。测试包含在 HibernateTransaction 中,它在单元测试结束时自动回滚数据库更改。这似乎是有效的,但是在这个使用这个查询的奇怪场景中,只有在我的单元测试中,这个@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始终在应用程序中正确返回,并且如果我在单元测试中的任何位置放置断点然后一次单步执行代码,则始终正确返回。但是,在没有调试的情况下简单地运行单元测试时,大多数情况下它会失败。

我想也许这里发生了一些赛车情况,但即使我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;
}

事务属性都是 Spring Hibernate4 TransactionManager 的默认值。

您可以在代码示例中看到我已经清楚地创建了这个 Team 实体,并且日志显示了为新记录生成的 ID。我可以使用 HQL 通过该 ID 直接查询记录,它会返回,但是在这个 Transactional 方法中上面的这个 HQL 查询仍然会返回 null,除非我在调试模式下单步执行它,然后它就可以工作了。

这是嵌套事务的问题吗,因为我的印象是在最外面的事务回滚之前,什么都不会回滚。为什么只使用这一种特定方法?这是 Hibernate 4 或 Spring 3.1.1 的错误吗?我正在使用 MySQL InnoDB,这可能是 MySQL InnoDB 处理数据库事务的方式的问题吗?

欢迎任何关于其他尝试的建议,因为我在这里完全没有想法。

4

1 回答 1

1

我的猜测是问题出在使用current_timestamp(). 您可能使用 now 作为开始日期创建了 TeamPlayer,如果您设置断点,则当前时间戳系统会比开始日期更早,而如果您不设置断点,则代码足够快,10%时间,使当前时间戳等于团队玩家的开始日期。

于 2012-11-20T22:35:50.900 回答