fix(bootstrap): force EMF init + enable schema-gen on startup
CI / Build Native (push) Successful in 6m24s

El error 'Table USERS not found' en BootstrapAdmin.User.count() se daba
porque en Quarkus native-image, con database.generation=update, el
schema-gen NO corre en startup del container por defecto (corre solo en
dev mode). Resultado: en un H2 fresco, el primer SELECT COUNT(*) del
bootstrap pierde contra una DB sin tablas.

Dos cambios:
1. application.properties: agregar
   quarkus.hibernate-orm.schema-management.run-on-startup=true
   para forzar el schema-gen en startup en prod/native.
2. BootstrapAdmin:
   - @Transactional + EntityManager (em) inyectados
   - em.createNativeQuery('SELECT 1') al inicio de onStart() para
     asegurar que el EMF esté listo y la session esté abierta antes
     del count(). Belt-and-suspenders.
This commit is contained in:
2026-08-15 00:04:17 -04:00
parent 7ed41ac825
commit 07588979e4
2 changed files with 17 additions and 1 deletions
@@ -4,6 +4,8 @@ import io.quarkus.runtime.StartupEvent;
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes; import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.persistence.EntityManager;
import jakarta.transaction.Transactional;
import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
@@ -29,17 +31,28 @@ public class BootstrapAdmin {
@Inject @Inject
AuthService authService; AuthService authService;
@Inject
EntityManager em;
@ConfigProperty(name = "app.bootstrap.admin.enabled", defaultValue = "true") @ConfigProperty(name = "app.bootstrap.admin.enabled", defaultValue = "true")
boolean enabled; boolean enabled;
@ConfigProperty(name = "app.bootstrap.admin.username", defaultValue = "admin") @ConfigProperty(name = "app.bootstrap.admin.username", defaultValue = "admin")
String username; String username;
@Transactional
void onStart(@Observes StartupEvent ev) { void onStart(@Observes StartupEvent ev) {
if (!enabled) { if (!enabled) {
LOG.info("Bootstrap admin disabled (app.bootstrap.admin.enabled=false)"); LOG.info("Bootstrap admin disabled (app.bootstrap.admin.enabled=false)");
return; return;
} }
// El @Transactional + el toque al EntityManager fuerzan la inicialización
// del EntityManagerFactory (y por lo tanto la corrida del schema-gen)
// ANTES de preguntar `User.count()`. Sin esto, en un H2 fresco el
// SELECT count(*) FROM users se ejecuta antes de que Hibernate haya
// generado la tabla y falla con "Table USERS not found".
em.createNativeQuery("SELECT 1").getSingleResult();
long count = User.count(); long count = User.count();
if (count > 0) { if (count > 0) {
LOG.infof("Bootstrap admin skipped: %d users already exist", count); LOG.infof("Bootstrap admin skipped: %d users already exist", count);
@@ -54,7 +67,6 @@ public class BootstrapAdmin {
+ "BOOTSTRAP-ADMIN-CHANGE This password MUST be changed on first login via POST /api/auth/change-password\n"; + "BOOTSTRAP-ADMIN-CHANGE This password MUST be changed on first login via POST /api/auth/change-password\n";
LOG.warn(banner); LOG.warn(banner);
// Backup a stdout por si algún pipeline colecta stdout pero no el logger JBoss.
System.out.print(banner); System.out.print(banner);
System.out.flush(); System.out.flush();
} }
@@ -10,6 +10,10 @@ quarkus.datasource.username=sa
quarkus.datasource.password= quarkus.datasource.password=
quarkus.hibernate-orm.database.generation=update quarkus.hibernate-orm.database.generation=update
quarkus.hibernate-orm.log.sql=false quarkus.hibernate-orm.log.sql=false
# En Quarkus native, el schema-gen NO corre en startup por defecto en prod.
# Sin esto, `update` mode no crea las tablas en un H2 fresco y el primer
# `User.count()` en BootstrapAdmin falla con "Table USERS not found".
quarkus.hibernate-orm.schema-management.run-on-startup=true
# JWT # JWT
mp.jwt.verify.issuer=shot-crafter-calculator mp.jwt.verify.issuer=shot-crafter-calculator