Search code examples
hibernatekotlintransactionsquarkuspostconstruct

Using Transaction in PostConstruct


I'm using Hibernate with Panache and I need to add a user when the application starts. For that, I'm annotating my bean with @Startup and then I have a method with the annotation @PostConstruct.

Currently, I'm using the following code:

@Startup
@ApplicationScoped
class AuthService {
    @Inject
    lateinit var userRepository: UserRepository

    @PostConstruct
    fun init() {
        logger.info("Creating admin user")

        val user = User(
            "Admin", ADMIN_NAME, BcryptUtil.bcryptHash(ADMIN_PASS), mutableSetOf(Role.ADMIN)
        )

        Panache.withTransaction {
            userRepository.persist(user)
        }.subscribe().with({
            logger.info("Done")
        }, { fail ->
            logger.error("Failed admin creation: $fail")
        })
    }
}

From what I found, when this method is called there are no guarantees that everything is already set, and I guess that is why it sometimes fails with the error Session/EntityManager is closed. I have already checked this question but since it is for Spring, the method doesn't work and I didn't find anything similar for Quarkus.

Am I missing any solution or is there a better approach to this?


Solution

  • This should work (I wrote it in Java because I'm not familiar with Kotlin):

        @PostConstruct
        public void init() {
            logger.info( "Creating admin user" );
    
            User user = new User(
                "Admin", ADMIN_NAME, BcryptUtil.bcryptHash(ADMIN_PASS), mutableSetOf(Role.ADMIN)
            );
    
            Panache
                    .withTransaction( () -> userRepository.persist( user ) )
                    .onItemOrFailure().invoke( (v, e ) -> {
                            if (e != null) {
                                logger.info( "Done!" );
                            }
                            else {
                                logger.errorf( "Failed admin creation: %s", e );
                            }
                    } )
                    .await().indefinitely();
        }
    

    Note that this code is not reactive though. The .await().indefinitely() will block the thread until the operation is completed.

    The correct approach should be having a method like:

        @PostConstruct
        public Uni<Void> init() {
           ...
           return Panache.withTransaction(...);
        }
    

    But this doesn't work at the moment in Quarkus.