1

I am going to write an Api with Vert.x web and GraphQL
I have connected with the database with Hibernate reactive

    public void start(final Promise<Void> startPromise)
    {
        try
        {
            vertx.executeBlocking(e ->
            {
                try
                {
                    hibernateConfig();
                    e.complete();
                }
                catch (Exception exception)
                {
                    e.fail(exception.getCause());
                }

            }).onComplete(event ->
            {
                try
                {
                    runServer(startPromise);
                }
                catch (Exception e)
                {
                    throw new RuntimeException(e);
                }
            }).onFailure(Throwable::printStackTrace);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    private void runServer(final Promise<Void> startPromise) throws Exception
    {
        final HttpServer httpServer = vertx.createHttpServer();

        final Router router = Router.router(vertx);

        router.route().handler(BodyHandler.create());

        router.post("/graphql").handler(super::graphqlHandler);

        // register `/graphiql` endpoint for the GraphiQL UI
        final GraphiQLHandlerOptions graphiqlOptions = new GraphiQLHandlerOptions().setEnabled(true);
        router.route("/graphiql/*").handler(GraphiQLHandler.create(graphiqlOptions));

        final URL resource = getClass().getResource("/static");

        if (resource != null) router.route("/static/*").handler(StaticHandler.create(resource.getFile()));
        else throw new Exception("Cannot set static");

        httpServer.requestHandler(router).listen(PORT , "localhost" , event ->
        {
            if (event.succeeded())
            {
                System.out.printf("Server run on port %d!\n" , PORT);
                startPromise.complete();
            }
            else
            {
                System.out.println("Error run server!");
                startPromise.fail(event.cause());
            }
        });
    }

    private void hibernateConfig()
    {
        Uni.createFrom().deferred(Unchecked.supplier(() ->
        {
            final Configuration configuration = new Configuration().setProperties(getHibernateProperties());

            final Set<Class<?>> entitiesClasses = getEntitiesClasses();

            if (entitiesClasses != null)
            {
                for (final Class<?> entity : entitiesClasses) configuration.addAnnotatedClass(entity);
            }
            else logger.error("Cannot found entities");

            final StandardServiceRegistryBuilder builder = new ReactiveServiceRegistryBuilder()
                    .addService(Server.class , this)
                    .applySettings(configuration.getProperties());

            final StandardServiceRegistry registry = builder.build();

            sessionFactory = configuration.buildSessionFactory(registry).unwrap(Mutiny.SessionFactory.class);

            if (!sessionFactory.isOpen()) throw new RuntimeException("Session is close!");

            logger.info("✅ Hibernate Reactive is ready");

            return Uni.createFrom().voidItem();
        })).convert().toCompletableFuture().join();
    }

    private Properties getHibernateProperties()
    {
        final Properties properties = new Properties();
        properties.setProperty(Environment.DRIVER , "org.mysql.jdbc.DRIVER");
        properties.setProperty(Environment.URL , "jdbc:mysql://localhost:3306/DBNAME");
        properties.setProperty(Environment.USER , "USENAME");
        properties.setProperty(Environment.PASS , "PASSWORD");
        properties.setProperty(Environment.DIALECT , "org.hibernate.dialect.MySQL55Dialect");
        properties.setProperty(Environment.HBM2DDL_DATABASE_ACTION , "create");
        properties.setProperty(Environment.SHOW_SQL , "false");
        properties.setProperty(Environment.POOL_SIZE , "10");
        return properties;
    }

So far it doesn't give any error and creates the entities

The error is given when I want to do an Insert

    public Future<UsersDto> addUserTest(final DataFetchingEnvironment environment)
    {
        return Future.future(event ->
        {
            final AddUsersDto addUsersDto = Dto.mapped(environment.getArguments() , "user" , AddUsersDto.class);

            final Users user = UsersMapper.toUsers(addUsersDto);

            try
            {
                vertx.executeBlocking(e ->
                        {
                            try
                            {
                                sessionFactory.withTransaction(
                                                (session , transaction) ->
                                                        session.persist(user)
                                                                .chain(session::flush)
                                        )
                                        .invoke(() -> e.complete(user))

                                        .onFailure()
                                        .invoke(e::fail)

                                        .await()
                                        .indefinitely();
                            }
                            catch (Exception exception)
                            {
                                exception.printStackTrace();
                                e.fail(exception.getCause());
                            }
                        })
                        .onComplete(e -> event.complete(UsersMapper.toUsersDto((Users) e.result())))
                        .onFailure(e -> event.fail(e.getCause()));
            }
            catch (Exception e)
            {
                e.printStackTrace();
                event.complete(UsersDto.builder().build());
            }
        });
    }

Error:

sessionFactory.withTransaction(
                (session , transaction) ->
                        session.persist(user)
                                .chain(session::flush)
        )
        .invoke(() -> e.complete(user))

        .onFailure()
        .invoke(e::fail)

        .await()
        .indefinitely(); // This line gives an error

Error text:

java.lang.IllegalStateException: HR000068: This method should exclusively be invoked from a Vert.x EventLoop thread; currently running on thread 'vert.x-worker-thread-1'

Please help me if anyone knows the problem

Bardia Namjoo
  • 11
  • 1
  • 4

1 Answers1

0

The problem is that you are running everything in the worker thread (using executeBlocking).

If you are writing reactive code, you don't need to wait for the result to be available.

The code should look something like this:

    public Future<UsersDto> addUserTest(final DataFetchingEnvironment environment) {
        return Future.future(event -> {
            final AddUsersDto addUsersDto = Dto.mapped(environment.getArguments() , "user" , AddUsersDto.class);
            final Users user = UsersMapper.toUsers(addUsersDto);
            sessionFactory
                .withTransaction( (session , tx) -> session.persist(user) )
                .subscribe().with( event::complete, event::fail )
        });
    }

You also don't need to flush because there's a transaction.

There is a working example on the Hibernate Reactive repository

Davide D'Alto
  • 7,421
  • 2
  • 16
  • 30