@TransactionalEventListener 注释方法未在 @Transactional 测试中调用 [英] @TransactionalEventListener annotated method not invoked in @Transactional test

查看:30
本文介绍了@TransactionalEventListener 注释方法未在 @Transactional 测试中调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试按照以下帖子中提到的示例从实体实现域事件发布:

I'm trying to implement domain event publishing from an entity by following the examples mentioned on the post below:

@DomainEvents 和 @AfterDomainEventsPublication 的示例

但是我还没有设法让 Spring 调用我的方法,并用 @TransactionalEventListener 注释.

However I haven't managed to have Spring calling my method annotated with @TransactionalEventListener.

见下面的实体、服务、事件监听器和测试代码:

See below the entity, service, event listener and test code:

@Entity
public class Book extends AbstractAggregateRoot<Book>
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(unique = true)
    private String isbn;

    @Column
    private String name;

    public Book(String isbn, String name)
    {
        this.isbn = isbn;
        this.name = name;
    }

    public void purchase()
    {
        registerEvent(new BookPurchasedEvent(id));
    }

    // getters omitted for brevity
}

服务:

@Service
@Transactional
public class BookService
{
    private final BookRepository bookRepository;

    public BookService(BookRepository bookRepository)
    {
        this.bookRepository = bookRepository;
    }

    public void purchaseBook(Integer bookId)
    {
        Book book = bookRepository.findById(bookId)
                                .orElseThrow(NoSuchElementException::new);

        book.purchase();

        bookRepository.save(book);
    }
}

听众:

@Service
public class EventListener
{
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @TransactionalEventListener
    public void handleEvent(BookPurchasedEvent event)
    {
        logger.info("Received event {}", event);
    }
}

测试:

@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class BookEventsTest
{
    @Autowired
    private BookService bookService;

    @Autowired
    private EntityManager entityManager;

    @Test
    public void test()
    {
        Book book = new Book("abcd-efgh", "El Quijote");
        book = entityManager.merge(book);

        bookService.purchaseBook(book.getId());
    }
}

未记录来自侦听器的日志消息.当部署为 REST 服务并调用时,它可以工作,例如通过邮递员

The log message from the listener is not logged. It works though when deployed as a REST service and invoked e.g. via Postman

推荐答案

明白了.由于我的测试是用@Transactional 注释的,所以包装测试方法的事务将被回滚.因此,不会调用用@TransactionalEventListener 注释的方法,因为默认情况下它在阶段 TransactionPhase.AFTER_COMMIT 触发(并且我对调用它不感兴趣,除非事务成功).所以测试的工作版本如下所示:

Got it. Since my test is annotated with @Transactional, the transaction wrapping the test method will be rolled back. Therefore the method annotated with @TransactionalEventListener won't be called, since by default it triggers at the phase TransactionPhase.AFTER_COMMIT (and I'm not interested in having it called unless the transaction is successful). So the working version of the test looks as follows:

@RunWith(SpringRunner.class)
@SpringBootTest
public class BookEventsTest
{
    @Autowired
    private BookService bookService;

    @Autowired
    private BookRepository bookRepository;

    @MockBean
    private EventListener eventListener;

    private Book book;

    @Before
    public void init() {
        book = bookRepository.save(new Book("abcd-efgh", "El Quijote"));
    }

    @After
    public void clean() {
        bookRepository.deleteAll();
    }

    @Test
    public void testService()
    {
        bookService.purchaseBook(book.getId());

        then(eventListener)
                .should()
                .handleEvent(any(BookPurchasedEvent.class));
    }
}

这篇关于@TransactionalEventListener 注释方法未在 @Transactional 测试中调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆