3.4.5.5. Transaction Listeners

Transaction listeners предназначены для реакции на события жизненного цикла транзакций. В отличие от entity listeners, они не привязаны к типу сущности и вызываются для каждой транзакции.

Слушатель должен быть Spring-бином, реализующим один или оба интерфейса BeforeCommitTransactionListener и AfterCompleteTransactionListener.

BeforeCommitTransactionListener

Метод beforeCommit() вызывается перед коммитом транзакции после всех entity listeners если транзакция не является read-only. Метод принимает текущий EntityManager и коллекцию сущностей текущего персистентного контекста.

Данный слушатель можно использовать для обеспечения сложных бизнес-правил, вовлекающих различные сущности. В примере ниже атрибут amount сущности Order должен рассчитываться на основе значения discount, находящегося в заказе, и атрибутов price и quantity экземпляров сущности OrderLine, составляющих заказ.

@Component("demo_OrdersTransactionListener")
public class OrdersTransactionListener implements BeforeCommitTransactionListener {

    @Inject
    private PersistenceTools persistenceTools;

    @Override
    public void beforeCommit(EntityManager entityManager, Collection<Entity> managedEntities) {
        // gather all orders affected by changes in the current transaction
        Set<Order> affectedOrders = new HashSet<>();

        for (Entity entity : managedEntities) {
            // skip not modified entities
            if (!persistenceTools.isDirty(entity))
                continue;

            if (entity instanceof Order)
                affectedOrders.add((Order) entity);
            else if (entity instanceof OrderLine) {
                Order order = ((OrderLine) entity).getOrder();
                // a reference can be detached, so merge it into current persistence context
                affectedOrders.add(entityManager.merge(order));
            }
        }
        // calculate amount for each affected order by its lines and discount
        for (Order order : affectedOrders) {
            BigDecimal amount = BigDecimal.ZERO;
            for (OrderLine orderLine : order.getOrderLines()) {
                if (!orderLine.isDeleted()) {
                    amount = amount.add(orderLine.getPrice().multiply(orderLine.getQuantity()));
                }
            }
            BigDecimal discount = order.getDiscount().divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_DOWN);
            order.setAmount(amount.subtract(amount.multiply(discount)));
        }
    }
}
AfterCompleteTransactionListener

Метод afterComplete() вызывается после завершения транзакции. Метод принимает параметр, указывающий, была ли транзакция успешно закоммичена, и коллекцию detached сущностей, содержавшихся в персистентном контексте завершенной транзакции.

Пример использования:

@Component("demo_OrdersTransactionListener")
public class OrdersTransactionListener implements AfterCompleteTransactionListener {

    private Logger log = LoggerFactory.getLogger(OrdersTransactionListener.class);

    @Override
    public void afterComplete(boolean committed, Collection<Entity> detachedEntities) {
        if (!committed)
            return;

        for (Entity entity : detachedEntities) {
            if (entity instanceof Order) {
                log.info("Order: " + entity);
            }
        }
    }
}