Mutiny.SessionFactory:使用现有会话保存实体



我有一个简单的任务:通过artemis连接获取数据->将数据保存到数据库。如果我们有打开的会话-使用它。保存功能:

fun <T> merge(entity: T): Uni<T> {
return sessionFactory.withTransaction { session -> session.merge(entity) }
}

如果我们有一个单一或罕见的事件,那么它是有效的。但是,如果我们在短时间内有多个事件(示例二(,那么并不是所有的数据都会被保存。第一个事件中的数据已保存,第二个事件中没有。通过日志记录和测试,我再现了这种行为:

sessionFactory.withTransaction { session1 ->
session1.merge(entity1).invoke { _ ->
sessionFactory.withTransaction { session2 ->
session2.merge(entity2)
}.subscribe().with(
{ println("success save entity2: $entity2") },
{ error -> println("error save entity2: $error") }
)
}
}.subscribe().with(
{ println("success save entity1: $entity1") },
{ error -> println("error save entity1: $error") }
)

也就是说,如果在调用merge函数之后,但在会话关闭之前开始第二个事件的数据记录,则不会保存数据。

下一步1.如果我使用persistent而不是merge(session2.merge -> session2.persist(,数据将被保存。

下一步2.如果我在persistent之前添加find,则行为与>合并相同(不保存(:

sessionFactory.withTransaction { session2 ->
session2.find(Entry::class.java, entity2.id)
.flatMap { session2.persist(entity2) }
//    session2.merge(entity2)
}

下一步3.如果我在合并之前添加刷新-我得到错误:

sessionFactory.withTransaction { session2 ->
session2.flush()
.flatMap { session2.merge(entity2) }
//    session2.merge(entity2)
}
E 13:24:23 23 [vert.x-eventloop-thread-0] errors.logSqlException - HR000057: Failed to execute statement [$1/* load Entity */ select entity0_.id as id1_3_0_, ... from Entity entity0_ where entity0_.id=$1]: $2could not load an entity: [Entity#b3c699b4-d490-4e56-bb76-904a1cb508a1]
error save entity2: javax.persistence.PersistenceException: org.hibernate.HibernateException: java.util.concurrent.CompletionException: io.vertx.pgclient.PgException: ERROR: current transaction is aborted, commands ignored until end of transaction block (25P02)
error save entity1: javax.persistence.PersistenceException: org.hibernate.HibernateException: io.vertx.pgclient.PgException: ERROR: duplicate key value violates unique constraint "entity_pkey" (23505)

其中b3c699b4-d490-4e56-bb76-904a1cb508a1实体2.id.

在这种情况下,实体1保存两次:第一次保存在会话2.flush((之后,第二次保存在会话1关闭之前。

问题:如何使用现有会话保存两个事件的数据如果我每次都打开一个新会话,就没有问题。

在最初的示例中,您使用的是.invoke而不是.call,不需要订阅两次。但您也需要确保同一会话不会在管道之间并行共享。

我的意思是你不应该依赖这样的代码:

List<Uni<Void>> list = ...
for (Entity entity : entities) {
list.add( session.persist(entity) );
}
Uni.join().all( list ).chain(session::flush);

在某些情况下,它可能看起来是有效的,但它可能会导致难以调试的错误。这是因为会话不是线程安全的。

在您的情况下,如果要使用同一会话合并两个实体,则需要确保操作以正确的顺序进行。

这应该起作用,例如:

sessionFactory.withTransaction { session ->
session.merge(entity1)
.invoke{ entity -> println("success save entity1: $entity") }
.call { _ ->
session.merge(entity2)
.invoke{ entity -> println("success save entity2: $entity") }
}
}.subscribe().with(
{ println("Finished!") },
{ error -> println("Error: $error") }
)

最新更新