代码之家  ›  专栏  ›  技术社区  ›  Sergey Bulavkin

将事件发布到saga时没有实际事务的EntityManager

  •  0
  • Sergey Bulavkin  · 技术社区  · 6 年前

    我使用的是“axon 4.0.3+spring boot 2+spring data(postgresql)”默认配置。

    将事件发布到eventstore并等待@sagaeventhandler捕获它之后,我收到以下异常:

    javax.persistence.transactionRequiredException:没有具有 当前线程可用的实际事务-不可靠 处理“persist”调用 Org.Spring Frask.Orm .jP.SythTythMaungErrorCortRealSoalTythMaungEnguroCutoServ.Lojk(SyrasyTyberMauleCorcor.java:292) ~[spring-orm-5.1.5.release.jar:5.1.5.release]在 com.sun.proxy.$proxy104.persist(未知源)~[na:na]位于 Java.UTI.Studio.FutoCops$OutRoop$OFRIF.Access(前缀.java:184) ~[Na:1.8.0_191]在 Java.UTI.Studio .RealEngEppILL $ 3 $ 1 .Access(RealEngPixLay.java:193) ~[Na:1.8.0_191]在 Java.UTILayLayList$ARayListSpultActua.FurrAccess(AlayList.java:1382) ~[Na:1.8.0_191]在 Java.UTI.Studio.Abtudio.Pultual.CopyOnin(抽象管道.java:481) ~[Na:1.8.0_191]在 Java.UTI.Studio.Abtudio.Buttual.WrAPANDopION(抽象管道.java:471) ~[Na:1.8.0_191]在 Java.U.L.Studio.FutoCops$PopaCop.ExaltSealEdsiple(Poopop.java:151) ~[Na:1.8.0_191]在 Java.UTI.Studio.FutoCops$OutRoop.OfReF.ExaltSealStand(前缀.java:174) ~[Na:1.8.0_191]在 Java.UTI.Studio.ActudioPultual.Exalk(AbtudioPultua.java:234) ~[Na:1.8.0_191]在 Java.UTI.Studio.RealEnjPixLe.FrEACH(RealEngPixLay.java:418) ~[Na:1.8.0_191]在 Orac.Axon Frask.EngEngest. EngestStury.jp.jPAVestReStAgEngEng.AppEnvices(JPAVER StaseGeorgEng.java:276) ~[axon-eventsourcing-4.0.3.jar:4.0.3]在 EXG.ActhFrask.EngEvestor. EngestStury.AcExcExtEngEngEngEngE.AppEngEnter(ActudioSturtEngEngEng. Java:98) ~[axon-eventsourcing-4.0.3.jar:4.0.3]

    事件存储处理此情况需要哪些附加配置?

    在方法上添加@transactional可以解决这个问题,但我不明白为什么这是必要的。

    最小代码示例(以下端点127.0.0.1:8080/1可用,但另一个端点127.0.0.1:8080/1不可用):

    @SpringBootApplication
    class TestAxonApplication
    
    class UserId(val userId: String = IdentifierFactory.getInstance().generateIdentifier()) : Serializable
    
    class TestCommand(@TargetAggregateIdentifier val userId: UserId)
    
    class TestedEvent(val userId: UserId)
    
    fun main(args: Array<String>) {
        runApplication<TestAxonApplication>(*args)
    }
    
    @RestController
    @RequestMapping
    class Controller(var commandGateway: CommandGateway, var eventStore: EventStore) {
    
        @GetMapping("/1")
        fun done(): UserId? {
            return commandGateway.sendAndWait<UserId>(TestCommand(UserId()))
        }
    
        @GetMapping("/2")
        fun failure() {
            eventStore.publish(
                    GenericEventMessage.asEventMessage<Void>(
                            TestedEvent(UserId())
                    )
            )
        }
    
    }
    
    @Aggregate
    class User() {
    
        @AggregateIdentifier
        private lateinit var userId: UserId
    
        @CommandHandler
        constructor(cmd: TestCommand) : this() {
            AggregateLifecycle.apply(TestedEvent(cmd.userId))
        }
    
        @EventHandler
        fun on(event: TestedEvent) {
            this.userId = event.userId
        }
    
    }
    
    @Saga
    @ProcessingGroup("mySaga")
    class MySaga {
    
        @StartSaga
        @SagaEventHandler(associationProperty = "userId")
        fun start(event: TestedEvent) {
            println("DONE ${event.userId.userId}")
        }
    
    }
    
    0 回复  |  直到 6 年前
        1
  •  2
  •   Allard    6 年前

    调用之间的区别在于,一个通过命令总线,而另一个跳过命令总线并直接发布到事件总线。默认情况下,a TransactionManager 在命令总线上配置。然而,事件总线上的情况并非如此。

    这意味着您发布的事件没有事务处于活动状态。Hibernate不喜欢这样。

    解决办法是 @Transactional 在端点上,以确保存储事件时事务处于活动状态。

        2
  •  0
  •   Makensi    6 年前

    似乎您忘记设置命令总线以允许事务管理。像下面的例子一样添加它,它将起作用:

    @Bean
    public CommandBus commandBus(TransactionManager transactionManager) {
        return new SimpleCommandBus(transactionManager, NoOpMessageMonitor.INSTANCE);
    }
    

    更新

    我的错,首先我使用的是Axon3.3(不是最新的),我认为您正在使用事件存储的自定义配置。

    AxonSpringBoot默认使用InMemory事件存储,这就是为什么如果不定义自定义事件存储,事务将无法工作。

    这是配置中缺少的:

    @Bean
    public EventStorageEngine eventStorageEngine(EntityManagerProvider entityManagerProvider, TransactionManager transactionManager) {
        return new JpaEventStorageEngine(entityManagerProvider, transactionManager);
    }
    
    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
        return new JpaTransactionManager(emf);
    }
    

    第二代

    这是我为其他提案做的一个简单示例,它工作得很好。

    记住

    另一个有趣的地方是,您必须定义聚合存储库,因为axon试图寻找与定义的聚合匹配的存储库。

    @Bean
    public Repository<User> documentAggregateRepository(EventStore eventStore) {
        return new EventSourcingRepository<>(User.class, eventStore);
    }
    

    同样对于传奇,你必须注册它们否则传奇永远不会被触发

    // remember to call the method sagaName + Configuration
    // or you must set up the @Saga configurationBean name pointing this method
    @Bean
    public SagaConfiguration<MySaga> mySagaConfiguration() {
        return SagaConfiguration.subscribingSagaManager(MySaga.class);
    }
    

    在本例中是事件源,但是如果您对遗留迁移感兴趣,那么您可能会在迁移过程中使用genericjparepository(如果您需要示例,请告诉我)。

    Hth.