将RepositoryRestResource与另一个存储库合并时使用事务

时间:2018-06-29 10:53:56

标签: java spring spring-boot spring-data-rest spring-transactions

我有一个简单的JpaRepository并带有@RepositoryRestResource注释:

@RepositoryRestResource
public interface ItemRepository extends JpaRepository<Item, UUID> { }

每当数据库中发生更改时,我都想更新一个文件。我使用RepositoryEventHandler

@Component
@RepositoryEventHandler
public class ItemRepositoryEventHandler {

    @HandleAfterCreate
    @HandleAfterSave
    @HandleAfterDelete
    public void itemChanged(Item item) {
        writeToFile();
    }
}

我想做的是,如果将内容写入文件时出错,则应回滚数据库。

我尝试通过将@Transactional注释添加到ItemRepository来进行尝试,但是它没有用。调试显示,RepositoryRestResource执行三个步骤:发出BeforeXXX事件,持久化到数据库,然后发出AfterXXX事件。它仅在持久化步骤中使用事务,而不在所有三个步骤中使用一个事务。

因此,我看不到在整个操作中使用事务的方法,我看到的唯一选择是不使用@RepositoryRestResource,而是手动实现Web层,然后使用在两个存储库中都采用事务的服务。有没有更简单的方法?

1 个答案:

答案 0 :(得分:0)

一种方法是使用自定义控制器和服务来实现您的业务逻辑。但是这种方式抵消了Spring Data REST的“优势”。

另一种选择(我认为对于SDR更自然)是使用published events from aggregate roots。在这种情况下,您应该从AbstractAggregateRoot扩展您的实体,并实现将发布一些“事件”的方法。然后,您将能够在保存实体的过程中在同一事务中处理该事件(借助@EventListener)。例如:

@Entity
public class Order extends AbstractAggregateRoot {
    //...

    public void registerItems(List<Item> items) {
        this.registerEvent(new RegisterItemsEvent(this, items));
    }
}
@Getter
@RequiredArgsConstructor
public class RegisterItemsEvent {
    private final Order order;
    private final List<Item> items;
}
@RequiredArgsConstructor
@Component
public class EventHandler {

    private final ItemRepo itemRepo;

    @EventListener
    @Transactional(propagation = MANDATORY)
    public void handleRegisterItemsEvent(RegisterItemsEvent e) {
        Order order = e.getOrder();
        List<Item> items = e.getItems();

        // update items with order - skipped...

        itemRepo.saveAll(items);
    }
}

用法示例:

@Component
@RepositoryEventHandler
public class OrderEventHandler {

    @BeforeCreate
    public void handleOrderCreate(Order order) {

        // prepare a List of items - skipped...

        order.registerItems(items);        
    }
}

当SDR保存Order时,它将发出RegisterItemsEvent,这由您handleRegisterItemsEvent的{​​{1}}方法处理,该方法将准备好的项目保存在同一笔交易中(我们使用{ EventHandler注释的{1}}参数,以确保存在交易)。

其他信息:Domain event publication from aggregate roots

已更新

关于您的特定任务,您可以创建类propagation = MANDATORY

@Transaction

ItemChangedEvent实体中的实施方法@Getter @RequiredArgsConstructor public class ItemChangedEvent { private final Item item; }

markAsChanged

Item发生更改时,您会将其标记为“已更改”:

@Entity
public class Item extends AbstractAggregateRoot {
    //...

    public void markAsChanged() {
        this.registerEvent(new ItemChangedEvent(this));
    }
}

并在同一事务中将其写入item处理程序中的文件中:

@Component
@RepositoryEventHandler
public class ItemRepositoryEventHandler {

    @BeforeCreate
    @BeforeSave
    @BeforeDelete
    public void itemChanged(Item item) {
        item.markAsChanged();
    }
}