@Transactional方法的测试失败:表“ WORK”中的并发更新:另一个事务已更新或删除了同一行

时间:2018-10-23 13:45:28

标签: java spring hibernate spring-boot transactions

我有两个实体和一个服务。没有@Transactional,一切都可以正常工作(回滚除外)。现在,我在服务方法中添加了@Transactional,以使其成为事务并在发生错误时自动回滚。但是现在,所有使用此方法的测试都以javax.persistence.EntityNotFoundException: Unable to find org.kitodo.mediaserver.core.db.entities.Work with id xyz失败(xyz是我的工作项的ID)。

然后,我尝试将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());
}

1 个答案:

答案 0 :(得分:0)

我怀疑您的单元测试不在自动提交模式下工作。

问题可能是您没有在测试函数中进行插入事务。

因此,调用的方法actionService.performRequested()无法看到保存的数据,该方法启动了一个全新的事务。不允许该事务查看任何脏数据。 因此,可以通过设置自动提交模式或提交将actionData1保留在performRequestedAction中的事务来确保保存了数据。