2

我正在尝试将quarkus-hibernate-reactive扩展与扩展一起使用,quarkus-vertx但在持久化数据时遇到问题。我的项目大致是这样的:

水果资源:

@Inject
EventBus eventBus;

@POST
public Uni<Response> create(Fruit fruit) {
    if (fruit == null || fruit.getId() != null) {
        throw new WebApplicationException("Id was invalidly set on request.", 422);
    }

    return eventBus.<Void>request("create-fruit", fruit)
        .map(ignore -> Response.ok(fruit).status(201).build());
}

水果服务:

@Inject
FruitRepository fruitRepository;

@ConsumeEvent("create-fruit")
public Uni<Void> createFruit(final Fruit fruit) {
    return fruitRepository.create(fruit);
}

水果库:

@Inject
Mutiny.Session mutinySession;

public Uni<Void> create(final Fruit fruit) {
    return mutinySession
        .persist(fruit)
        .chain(mutinySession::flush);
}

我得到的例外是 a java.lang.IllegalStateException: Session/EntityManager is closed,我假设它发生在flush(). 我想我的会话会在某个地方关闭,但我不知道在哪里以及如何防止这种情况发生。

完整的例子可以在这里找到:https FruitsEndpointTest ://github.com/bamling/quarkus-hibernate-reactive-test模拟行为!

4

3 回答 3

1

我认为这是 Quarkus 中的一个错误,现在正在修复:https ://github.com/quarkusio/quarkus/issues/14595

我已经通过将版本更改为 quarkus 来测试您的项目1.12.1.Final 并且它有效。

于 2021-03-09T19:21:21.783 回答
0

只需这样做:

public Uni<Void> create(final Fruit fruit) {
    return sessionFactory.withTransaction((session, tx) -> session.persist(fruit));
}

为了通过 Hibernate 将任何内容保存到数据库中,您需要:

  • 一个会话
  • 一笔交易

Hibernate Reactive 有一个稍微不同的 API,因为它现在是非阻塞的,所以最好的方法是注入一个Mutiny.SessionFactory. 通过使用withTransaction() ,您将确保您有一个打开的会话和一个事务。

于 2021-02-22T13:03:21.607 回答
0

好的,我知道这个答案并不完全是您正在寻找的答案,但这是迄今为止我发现的唯一可行的解​​决方案。我将继续寻找一种具有所有 Hibernate 优点的方法来实现它,但这里有一个没有的解决方案:

@ApplicationScoped
public class FruitRepository {

    @Inject
    PgPool client;

    public Multi<Fruit> findAll() {
        return client.query("SELECT id, name FROM known_fruits ORDER BY name ASC").execute()
                .onItem().transformToMulti(set -> Multi.createFrom().iterable(set))
                .onItem().transform(FruitRepository::map);
    }

    public Uni<Void> create(final Fruit fruit) {
        return client.preparedQuery("INSERT INTO known_fruits (id, name) VALUES ($1, $2)")
                .execute(Tuple.tuple().addInteger(fruit.getId()).addString(fruit.getName()))
                .chain(e -> Uni.createFrom().voidItem());
    }

    public static Fruit map(Row row) {
        Fruit fruit = new Fruit();
        fruit.setId(row.getInteger("id"));
        fruit.setName(row.getString("name"));
        return fruit;
    }

}

它有效,它是反应式的,但它也意味着手动管理对象关系。不理想。

更新:

这是一个使用 Hibernate 的工作示例。但是(!)它远非完美。尽管它更接近于回答您的问题,但我总是更喜欢使用上面的示例而不是这个示例。会话应该关闭。我也想为 findAll 方法做一个“withSession()”,但这会返回一个 Uni,而我们需要一个 Multi。尝试使用资源不起作用,因为这将在读取 Multi 之前关闭会话。所以不要使用这个代码,但也许它会帮助其他人想出一个更好的答案:

@ApplicationScoped
public class FruitRepository {

    private final Mutiny.SessionFactory sessionFactory;

    public FruitRepository(EntityManagerFactory entityManagerFactory) {
        this.sessionFactory = entityManagerFactory.unwrap(Mutiny.SessionFactory.class);
    }

    public Multi<Fruit> findAll() {
        Mutiny.Session session = sessionFactory.openSession();
        return session.createNamedQuery("Fruits.findAll", Fruit.class).getResults();
    }

    public Uni<Void> create(final Fruit fruit) {
        return sessionFactory.withSession(
                session -> session.persist(fruit)
                .chain(e -> Uni.createFrom().voidItem())
        );
    }
}
于 2021-01-27T07:23:43.633 回答