Saltar a contenido

Persistencia (Hibernate / JPA)

Cómo persiste el backend: provider, dialecto propio, modo de flush, naming strategy y administración del DDL. Todo está configurado en application.yml y en com.tipre.tifacturaonlinemanager.action.db.SQLServerDialect + config.SeamCompatConfig. Para las entidades concretas ver Modelo de dominio; para por qué el flush es MANUAL, Invariantes.

De un vistazo

Aspecto Valor Dónde
Provider Hibernate 7.2.x (vía BOM Spring Boot 4.0) pom.xml (BOM)
Starter spring-boot-starter-data-jpa pom.xml
Dialecto com.tipre.tifacturaonlinemanager.action.db.SQLServerDialect (propio) spring.jpa.database-platform
ddl-auto none application.yml
Naming físico PhysicalNamingStrategyStandardImpl application.yml
open-in-view false application.yml
Flush por defecto MANUAL (FlushModeType.COMMIT en el entityManager del shim) SeamCompatConfig
Driver com.microsoft.sqlserver.jdbc.SQLServerDriver (mssql-jdbc 12.8.1.jre11) pom.xml + application-{dev,prod}.yml
Cache 2do nivel No (sin @Cache; dependencia comentada en pom.xml)
Tests H2 (src/test/resources/application.yml pisa al base) pom.xml (scope test)

Dialecto propio: SQLServerDialect

action/db/SQLServerDialect.java extiende org.hibernate.dialect.SQLServerDialect (el de Hibernate, ya version-aware) y solo sobreescribe el mapeo de BOOLEAN:

public class SQLServerDialect extends org.hibernate.dialect.SQLServerDialect {
    @Override
    protected String columnType(int sqlTypeCode) {
        if (sqlTypeCode == SqlTypes.BOOLEAN) {
            return "bit";
        }
        return super.columnType(sqlTypeCode);
    }
}

Es un port del legacy (SPEC-002): el legacy registraba BOOLEAN -> "bit" para que los boolean del modelo (p. ej. EnteFacturador.habilitado, los flags de TipoComprobante) mapeen a columnas bit de SQL Server.

Hibernate 6 cambió la API

Hibernate 6 eliminó los dialectos versionados (SQLServer2012Dialect) y la API registerColumnType(int, String). El reemplazo idiomático es extender SQLServerDialect y sobreescribir columnType(int). El comentario del propio archivo lo deja claro.

El dialecto es prácticamente inerte

Con hbm2ddl=none la generación de DDL está deshabilitada, así que el override de BOOLEAN->bit solo afectaría a la generación de DDL, que no ocurre. Se conserva por fidelidad al legacy y trazabilidad (SPEC-002), no porque cambie el runtime.

DDL administrado por el cliente

spring.jpa.hibernate.ddl-auto: none. El esquema lo administra el cliente, la app NO lo genera ni lo altera. Esta es una invariante explícita en application.yml.

Las tablas nuevas que introdujo la migración (no del legacy) se versionan como scripts SQL en src/main/resources/db/migration/ para que el cliente los aplique a mano (no es Flyway/Liquibase automático — son scripts de referencia):

  • add-JobEjecucion.sql — tabla JobEjecucion.
  • add-TrxErrorLog.sql — tabla TrxErrorLog.
  • add-cert-EntesFacturadores.sql — columnas certP12* en EntesFacturadores.
  • add-validadoArca-SucPosPV.sql, add-unique-indexes-SucPosPV.sql, add-PV-validacion-obtencion-SucPosPV.sql — columnas/índices de SucPosPV.
  • add-CockpitStat.sql, add-Trx-*.sql, schedule-nc-job.sql, cleanup-parametros-legacy.sql, drop-tablas-sin-uso.sql, verify-cae-caea-nc.sql.

Validar mapeos contra la DB real

El perfil de test application-realdb.yml (LOCAL, gitignored) apunta a una copia de prod restaurada y usa ddl-auto: none igualmente — la validación real son las queries del test, no el validate de Hibernate (pedante con tipos equivalentes como numeric vs bigint que en runtime andan).

Naming strategy

spring.jpa.hibernate.naming.physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl.

La estrategia estándar respeta @Table/@Column tal cual: nombres literales en PascalCase, sin convertir a snake_case. Es el comportamiento del Hibernate 4 del legacy. Por eso las entidades pueden mapear tablas como EntesFacturadores, TiposComprobante, Caeas o columnas como idEnteFacturador y fFechaUltObtencionPOS sin transformación.

Flush MANUAL

El bean entityManager que usa el seam-compat se crea en SeamCompatConfig como shared entity manager y se le fija FlushModeType.COMMIT:

@Bean(name = "entityManager")
public EntityManager seamEntityManager(EntityManagerFactory emf) {
    EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
    try { em.setFlushMode(FlushModeType.COMMIT); } catch (Exception ignored) {} // INVARIANTE 5.1 (MANUAL ~ COMMIT)
    return em;
}

Esto replica el default-flush-mode=MANUAL del components.xml legacy. En JPA no existe un MANUAL puro; COMMIT es el equivalente más cercano: Hibernate no auto-flushea antes de cada query, solo al commit de la transacción (o cuando se llama a flush() explícitamente).

Por eso el código de negocio hace flush() a mano donde necesita que algo pegue en la DB. El EntityHome del shim lo hace en persist()/update()/remove():

// org/jboss/seam/framework/EntityHome.java
getEntityManager().flush();   // INVARIANTE: flush explícito (modo MANUAL)
...
getEntityManager().flush();   // INVARIANTE 5.2: SOLO flush, sin merge

update() = solo flush, sin merge (invariante §5.2)

EntityHome.update() hace únicamente flush() sobre la entidad ya managed; no llama a merge(). El patrón de negocio (ParentEntityManager.updateOrPersistTrx) carga/setea la instancia en el Home y la deja en estado managed para que el flush la escriba. Romper esto (meter un merge) cambia la semántica de persistencia.

Otros puntos con flush() explícito: SucPosPVAdminService (backstop para que una violación de UQ salte en el flush y no en el commit), CaeaConsultaService, CockpitStatSnapshot, TrxNextGenPersistHelper.

Driver y datasource

Driver mssql-jdbc 12.8.1.jre11 (scope runtime; inerte en tests, que usan H2). La URL JDBC incluye encrypt=true;trustServerCertificate=true en dev y prod:

jdbc:sqlserver://<host>:<port>;databaseName=<db>;encrypt=true;trustServerCertificate=true

Detalle de hosts/credenciales por perfil y variables de entorno en Config y perfiles.

Cache de segundo nivel

No hay 2nd-level cache hoy

El legacy tenía 2nd-level + query cache con ehcache (persistence-prod.xml), pero ninguna entidad migrada lleva @Cache, así que el cache no se usa. La dependencia (hibernate-jcache + ehcache) está comentada en el pom.xml (rotulada SPEC-002): hibernate-ehcache no existe en Hibernate 7 (el 2nd-level pasó a JCache), y habría que reescribir el bloque al abordar SPEC-002. open-in-view está en false, así que tampoco hay sesión abierta en la capa de vista.

Por dónde seguir