У меня есть две сущности и служба.Без @Transactional все работало нормально (кроме отката).Теперь я добавил @Transactional в метод service, чтобы сделать его транзакцией и автоматически откатывать ошибки.Но теперь все тесты, использующие этот метод, терпят неудачу с javax.persistence.EntityNotFoundException: Unable to find org.kitodo.mediaserver.core.db.entities.Work with id xyz
(xyz - это идентификатор моего рабочего элемента).
Затем я попытался добавить cascade = {CascadeType.PERSIST, CascadeType.MERGE}
в рабочее поле сущности ActionData.После того, как я получил еще одно исключение в той же позиции, что и раньше: org.h2.jdbc.JdbcSQLException: Concurrent update in table "WORK": another transaction has updated or deleted the same row [90131-196]
Я предполагаю, что по какой-то причине он пытается использовать два перехода одновременно.
В чем причина и как я могусделать эту работу?
Сущности
@Entity
public class Work {
private String id;
private String title;
private String path;
private String hostId;
private Instant indexTime;
private Set<Collection> collections;
private String allowedNetwork = "global";
protected Work() {}
public Work(String id, String title) {
this.id = id;
this.title = title;
}
@Id
public String getId() {
return id;
}
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "work_collection",
joinColumns = @JoinColumn(name = "work_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "collection_name", referencedColumnName = "name"))
public Set<Collection> getCollections() {
return collections;
}
// getters/setters
}
@Entity
public class ActionData {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@ElementCollection
@CollectionTable(name = "action_parameter")
private Map<String, String> parameter = new HashMap<>();
@ManyToOne
@JoinColumn(name = "work_id", referencedColumnName = "id", nullable = false)
private Work work;
private String actionName;
private Instant requestTime;
private Instant startTime;
private Instant endTime;
private ActionData() {}
public ActionData(Work work, String actionName, Map<String, String> parameter) {
this.work = work;
this.parameter = parameter;
this.actionName = actionName;
}
// getters/setters
}
Метод обслуживания
@Service
public class ActionService {
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public Object performRequested(ActionData actionData) throws Exception {
// some checks
actionData.setStartTime(Instant.now());
// !!! javax.persistence.EntityNotFoundException: Unable to find org.kitodo.mediaserver.core.db.entities.Work with id xyz
actionRepository.save(actionData);
IAction actionInstance = getActionInstance(actionData.getActionName());
Object result;
result = actionInstance.perform(actionData.getWork(), actionData.getParameter());
actionData.setEndTime(Instant.now());
actionRepository.save(actionData);
return result;
}
}
Тест
@Test
public void performRequestedAction() throws Exception {
// given
init();
work1 = entityManager.persist(work1);
actionData1 = new ActionData(work1, "mockAction", parameter1);
actionData1.setRequestTime(Instant.now());
actionData1 = entityManager.persist(actionData1);
entityManager.flush();
// when
Object action = actionService.performRequested(actionData1);
// then
assertThat(action).isNotNull();
assertThat(action).isInstanceOf(String.class);
assertThat(action).isEqualTo("performed");
assertThat(actionData1.getStartTime()).isBetween(Instant.now().minusSeconds(2), Instant.now());
assertThat(actionData1.getEndTime()).isBetween(Instant.now().minusSeconds(2), Instant.now());
}