quarkus-hibernate-reactive + Vert.x EventBus вызывает java.lang.IllegalStateException: сеанс /EntityManager закрыт
Я пытаюсь использовать
quarkus-hibernate-reactive
расширение с
quarkus-vertx
extension, и у меня проблемы с сохранением данных. Мой проект выглядит примерно так:
FruitResource:
@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());
}
FruitService:
@Inject
FruitRepository fruitRepository;
@ConsumeEvent("create-fruit")
public Uni<Void> createFruit(final Fruit fruit) {
return fruitRepository.create(fruit);
}
FruitRepository:
@Inject
Mutiny.Session mutinySession;
public Uni<Void> create(final Fruit fruit) {
return mutinySession
.persist(fruit)
.chain(mutinySession::flush);
}
Исключение, которое я получаю, это
java.lang.IllegalStateException: Session/EntityManager is closed
, что, как я предполагаю, происходит во время
flush()
. Я предполагаю, что моя сессия где-то закрывается, но я не знаю, где и как это предотвратить.
Полный пример можно найти здесь: https://github.com/bamling/quarkus-hibernate-reactive-test
FruitsEndpointTest
имитирует поведение!
3 ответа
Я думаю, что это была ошибка в Quarkus, которая сейчас исправлена: https://github.com/quarkusio/quarkus/issues/14595 .
Я проверил ваш проект, изменив версию на quarkus
1.12.1.Final
и это сработало.
Хорошо, я знаю, что этот ответ будет не совсем тем, что вы ищете, но это единственное рабочее решение, которое я нашел до этого момента. Я буду продолжать искать способ реализовать его со всеми достоинствами 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. НО (!) Он далек от совершенства. Хотя это ближе к ответу на ваш вопрос, я всегда предпочитаю использовать приведенный выше пример, а не этот. Сессию следует закрыть. Я также хотел бы сделать withSession () для метода findAll, но он возвращает 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())
);
}
}
Просто сделай это:
public Uni<Void> create(final Fruit fruit) {
return sessionFactory.withTransaction((session, tx) -> session.persist(fruit));
}
Чтобы что-либо сохранить в БД через Hibernate, вам необходимо иметь:
- сессия
- сделка
Hibernate Reactive имеет немного другой API, поскольку теперь он неблокирующий, поэтому лучший способ сделать это - внедрить
Mutiny.SessionFactory
. Используя
withTransaction()
вы убедитесь, что у вас есть открытый сеанс и транзакция.