My Spring Boot应用程序包含以下类:
董事会(JPA实体)
@Entity
@Table(name = "board")
public class Board {
public static final int IN_PROGRESS = 1;
public static final int AFK = 2;
public static final int COMPLETED = 3;
@Column(name = "id")
@Generated(GenerationTime.INSERT)
@GeneratedValue(strategy = GenerationType.AUTO)
@Id
private Long id;
@Column(name = "status", nullable = false)
private int status = IN_PROGRESS;
}
BoardRepository (JPA存储库)
public interface BoardRepository extends JpaRepository<Board, Long> {}
CommonBoardService (基本服务)
public interface CommonBoardService {
Board save(Board board);
Board update(Board board, int status);
}
CommonBoardServiceImpl (基本服务实施)
@Service
@Transactional
public class CommonBoardServiceImpl implements CommonBoardService {
@Autowired
private BoardRepository boardRepository;
public Board save(final Board board) {
return boardRepository.save(board);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Board update(final Board board, final int status) {
board.setStatus(status);
return save(board);
}
}
BoardService (特定服务界面)
public interface BoardService {
Board startBoard();
void synchronizeBoardState(Board board);
}
BoardServiceImpl (特定服务实施)
@Service
@Transactional
public class BoardServiceImpl implements BoardService {
@Autowired
private CommonBoardService commonBoardService;
public Board startBoard() { return new Board(); }
public void synchronizeBoardState(final Board board) {
if (board != null && inProgress(board)) {
if (!canPlayWithCurrentBoard(board)) {
commonBoardService.update(board, Board.AFK);
}
else {
commonBoardService.update(board, Board.COMPLETED);
}
}
}
private boolean canPlayWithCurrentBoard(final Board board) {
return !inProgress(board);
}
private boolean inProgress(final Board board) {
return board != null && board.getStatus() == Board.IN_PROGRESS;
}
}
BoardServiceTest (单元测试)
1. @RunWith(SpringJUnit4ClassRunner.class)
2. @Transactional
3. public class BoardServiceTest {
4. @Autowired
5. private BoardRepository boardRepository;
6.
7. @Autowired
8. private BoardService boardService;
9. @Autowired
10. private CommonBoardService commonBoardService;
11.
12. @Test
13. public void testSynchronizeBoardStatus() {
14. Board board = boardService.startBoard();
15.
16. board = commonBoardService.save(board);
17.
18. assertEquals(1, boardRepository.count());
19.
20. boardService.synchronizeBoardState(board);
21.
22. assertEquals(1, boardRepository.count());
23. }
24. }
此测试在第22行失败,错误为java.lang.AssertionError: Expected :1 Actual:2
。 Hibernate SQL日志显示在第20行而不是INSERT
上触发了UPDATE
。由于我在整个测试中使用了相同的Board
对象,因此我希望第20行能够触发UPDATE
而不是INSERT
。
任何人都可以解释为什么会发生这种情况以及如何获得预期的行为(第20行UPDATE
)?
答案 0 :(得分:1)
罪魁祸首是这一行:@Transactional(propagation = Propagation.REQUIRES_NEW)
。让我们看一下测试用例执行时会发生什么。
BoardServiceTest
注明@Transactional
,因此BoardServiceTest.testSynchronizeBoardStatus
开始执行时会启动新事务。Board
实例。Board
实例并触发数据库INSERT
。CommonBoardServiceImpl.update
,注释@Transactional(propagation = Propagation.REQUIRES_NEW)
。这将暂停正在进行的事务(请参阅JavaDocs for Propagation),该事务迄今尚未提交或已回滚。CommonBoardServiceImpl.update
反过来尝试保存传递给它的Board
个实例。INSERT
。CommonBoardServiceImpl.update
提交内部事务。外部交易恢复。SELECT
查询之前将其刷新。这意味着数据库中现在有两个实例,因此测试失败。删除@Transactional(propagation = Propagation.REQUIRES_NEW)
可确保整个测试在同一事务中执行,从而通过。