测试事务机制时保存嵌套实体触发DuplicateKeyException异常的解决方法求助
测试事务机制时保存嵌套实体触发DuplicateKeyException异常的解决方法求助
我正在测试Spring事务的工作机制,写了一个三层关联实体的简单示例(BettingProgram → Event → Market),但在手动管理事务保存Market时,遇到了如下异常:
org.springframework.dao.DuplicateKeyException: A different object with the same identifier value was already associated with the session: [com.gestionebettingoffer.model.Event#EventId(idBettingProgram=784, idEvent=1726)]
想请教大家这个问题该怎么解决?
实体类代码
BettingProgram.java
public class BettingProgram { @Id private int id; @Column(name = "name", nullable = false) private String name; @Column(name = "date", nullable = false) private LocalDate date; @JsonManagedReference @OneToMany(mappedBy = "bettingProgram", fetch = FetchType.LAZY, cascade = CascadeType.ALL) private List<Event> events; }
Event.java
public class Event { @EmbeddedId private EventId eventId; @JsonBackReference @ManyToOne @MapsId("idBettingProgram") @JoinColumn(name = "idBettingProgram") private BettingProgram bettingProgram; @Column(name = "type", nullable = false) private String type; @Column(name = "name", nullable = false) private String name; @Column(name = "dateEvent", nullable = false) @JsonSerialize(using = LocalDateTimeSerializer.class) @JsonDeserialize(using = LocalDateTimeDeserializer.class) private LocalDateTime dateEvent; @JsonManagedReference @OneToMany(fetch = FetchType.LAZY, mappedBy = "event", cascade = CascadeType.ALL) private List<Market> marketList; }
Market.java
public class Market { @EmbeddedId private MarketId marketId; @JsonBackReference @ManyToOne @JoinColumns({ @JoinColumn(name = "idEvent", referencedColumnName = "idEvent"), @JoinColumn(name = "idBettingProgram", referencedColumnName = "idBettingProgram") }) @Valid @MapsId("eventId") private Event event; @Column(name = "name", nullable = false) private String name; @Enumerated(EnumType.STRING) @Column(name = "status", nullable = false) private Status status; }
事务相关业务方法
下面是负责实体插入的核心业务方法,我通过txManager手动管理事务:
public BettingProgramDTO createBettingProgramWithManualTransaction(BettingProgramDTO bettingProgramDTO) throws CustomException { log.info("Betting program da creare : {}", bettingProgramDTO); BettingProgram bettingProgramCreated = methodCreaBettingPAndEvent(bettingProgramDTO); BettingProgramDTO bettingProgramDTOCreated = bettingProgramMapper.toDTO(bettingProgramCreated); TransactionStatus txStatus = txManager.getTransaction(new DefaultTransactionDefinition()); try { List<MarketDTO> marketDTOList = methodCreaMArketSelection(bettingProgramCreated.getEvents().getFirst(), bettingProgramDTO.getEvents().getFirst().getMarketList()); bettingProgramDTOCreated.getEvents().getFirst().setMarketList(marketDTOList); } catch (Exception e) { log.error("{} Got an error:{}", LogUtil.errorLogString(), e.getMessage(), e); txManager.rollback(txStatus); } txManager.commit(); return bettingProgramDTOCreated; } public BettingProgram methodCreaBettingPAndEvent(BettingProgramDTO bettingProgramDTO) throws CustomException { Optional<BettingProgram> optBettingProgram = bettingProgramRepository.findById(bettingProgramDTO.getId()); if (optBettingProgram.isPresent()){ throw new NotValidFieldException("Betting program già esistente."); } BettingProgram bettingProgram = bettingProgramMapper.toEntityWithoutSon(bettingProgramDTO); BettingProgram bettingProgramInserted = bettingProgramRepository.save(bettingProgram); EventDTO eventDTO = bettingProgramDTO.getEvents().getFirst(); eventDTO.setIdBettingProgram(bettingProgramInserted.getId()); log.info("Event da creare : {}", eventDTO); Event eventCreated = eventMapper.toEntityWithoutSon(eventServiceTransactional.createEventWithTransaction(eventDTO)); bettingProgramInserted.getEvents().add(eventCreated); return bettingProgramInserted; } public List<MarketDTO> methodCreaMArketSelection(Event event, List<MarketDTO> marketDTO) throws CustomException { List<MarketDTO> marketDTOList = new ArrayList<>(); for (MarketDTO marketDTOToCreate : marketDTO){ marketDTOToCreate.setIdBettingProgram(event.getEventId().getIdBettingProgram()); marketDTOToCreate.setIdEvent(event.getEventId().getIdEvent()); log.info("Market dto da creare : {}", marketDTOToCreate); MarketDTO marketDTOCreated = marketServiceTransactional.createMarketWithTransaction(event, marketDTOToCreate); marketDTOList.add(marketDTOCreated); } return marketDTOList; }
我的疑问
我猜测问题可能和手动事务的边界、JPA持久化上下文的关联冲突有关?比如Event实体的引用在不同的事务/持久化上下文中重复关联了?有没有大佬能帮我分析下具体原因,并且给出可行的解决办法?
内容来源于stack exchange
相关产品推荐
相关产品推荐

