Spring data jpa 无法使用Spring Boot更新现有实体

Spring data jpa 无法使用Spring Boot更新现有实体,spring-data-jpa,jpa-2.1,Spring Data Jpa,Jpa 2.1,我的Spring Boot应用程序具有以下类: 董事会(JPA实体) BoardRepository(JPA存储库) CommonBoardServiceImpl(基本服务实现) BoardService(特定服务接口) BoardServiceImpl(具体服务实施) BoardServiceTest(单元测试) 该测试在第22行失败,错误为java.lang.AssertionError:Expected:1 Actual:2。Hibernate SQL日志显示在第20行触发的是插入,而不是

我的Spring Boot应用程序具有以下类:

董事会(JPA实体)

BoardRepository(JPA存储库)

CommonBoardServiceImpl(基本服务实现)

BoardService(特定服务接口)

BoardServiceImpl(具体服务实施)

BoardServiceTest(单元测试)

该测试在第22行失败,错误为
java.lang.AssertionError:Expected:1 Actual:2
。Hibernate SQL日志显示在第20行触发的是
插入
,而不是
更新
。由于我在整个测试过程中使用相同的
对象,我希望第20行触发
更新
,而不是
插入


有人能解释为什么会发生这种情况,以及如何获得预期的行为(
UPDATE
第20行)?

罪魁祸首是这一行:
@Transactional(propagation=propagation.REQUIRES\u NEW)
。让我们看看执行测试用例时会发生什么

  • 由于
    BoardServiceTest
    @Transactional
    注释,因此当
    BoardServiceTest.testSynchronizeBoardStatus
    开始执行时,将启动一个新事务
  • 第14行创建一个新的
    实例
  • 第16行尝试保存在第14行创建的
    实例,并触发数据库
    插入
  • 第20行间接调用
    CommonBoardServiceImpl.update
    ,该更新用
    @Transactional(传播=传播.需要\u NEW)
    注释。这将暂停正在进行的事务(请参阅),该事务到目前为止既没有提交也没有回滚
  • CommonBoardServiceImpl.update
    依次尝试保存传递给它的
    Board
    实例
  • 给定实例无法识别为现有实例,因为将其保存到数据库的事务当前处于挂起状态。因此,假定它是一个新实例,并导致第二次
    INSERT
  • 第20行现在完成,它提交了为
    CommonBoardServiceImpl.update
    启动的内部事务。外部事务恢复
  • 第22行找到脏会话并在触发
    SELECT
    查询之前刷新它。这意味着数据库中现在有两个实例,因此测试失败

删除
@Transactional(propagation=propagation.REQUIRES_NEW)
可确保整个测试在同一事务中执行,从而通过。

TestDataSourceConfiguration.class指向哪个数据库?它是否有您要更新的董事会实体?在
BoardServiceTest
中,什么是
boardService
commonBoardService
boardService.startBoard(admin)
commonBoardService.save(board)
看起来像什么?大概第一个是创建一个新的
,第二个是保存它?另外,
用户
类的代码是什么样的?如果您能创建一个小示例来演示这个问题,可能会更好,因为有很多代码要介绍。@koder23,谢谢您的回复。我正在使用postgres。是的,它应该有as
Board Board=boardService.startBoard(admin)-创建新板,实体ID等于1。您可以发布完整的代码来测试它吗?仍然有许多部分缺失,这使得重现问题变得困难。可能会在Github上发布一个示例应用程序?@manish,你说得对,
boardService.startBoard(admin)
-为用户创建新的board。如您所见,
boardService.synchronizeBoardState(board,admin)
具有不同的检查。因此,我想在进入
if(!canPlayWithCurrentBoard(board))
代码块时触发该情况。为了实现这一点,我需要更新
实体以满足这些条件。这就是为什么我在测试中使用
commonBoardService.save(board)
@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;
}
public interface BoardRepository extends JpaRepository<Board, Long> {}
public interface CommonBoardService {
  Board save(Board board);
  Board update(Board board, int status);
}
@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);
  }
}
public interface BoardService {
  Board startBoard();
  void synchronizeBoardState(Board board);
}
@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;
  }
}
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. }