GuicedEE Persistence
Reactive JPA persistence using Hibernate Reactive 4.x, Hibernate ORM 7.x, and Vert.x 5 SQL clients, managed by the GuicedEE lifecycle. Resolve exact versions from Versioner and HibernateBOM; ORM and Reactive use different release numbers.
Core Concept
Extend DatabaseModule, point it at a persistence.xml unit, and the module wires a Mutiny.SessionFactory into Guice — fully reactive, annotation-driven, with built-in support for PostgreSQL, MySQL, SQL Server, Oracle, and DB2.
Required Flow
- Add
com.guicedee:persistencedependency. - Create a
persistence.xmlwithReactivePersistenceProvider:<persistence xmlns="https://jakarta.ee/xml/ns/persistence" version="3.0"> <persistence-unit name="mydb"> <provider>org.hibernate.reactive.provider.ReactivePersistenceProvider</provider> <class>com.example.entities.User</class> <properties> <property name="jakarta.persistence.jdbc.url" value="${DB_URL:jdbc:postgresql://localhost:5432/mydb}"/> <property name="jakarta.persistence.jdbc.user" value="${DB_USER:postgres}"/> <property name="jakarta.persistence.jdbc.password" value="${DB_PASSWORD:secret}"/> <property name="hibernate.hbm2ddl.auto" value="update"/> </properties> </persistence-unit> </persistence> - Create a
DatabaseModulesubclass:public class MyDatabaseModule extends DatabaseModule<MyDatabaseModule> { @Override protected String getPersistenceUnitName() { return "mydb"; } @Override protected ConnectionBaseInfo getConnectionBaseInfo( PersistenceUnitDescriptor unit, Properties properties) { return ConnectionBaseInfoFactory.createConnectionBaseInfo("postgresql"); } } - Register via JPMS:
Add the same provider to each correspondingmodule my.app { requires com.guicedee.persistence; provides com.guicedee.client.services.lifecycle.IGuiceModule with my.app.MyDatabaseModule; provides com.guicedee.client.services.lifecycle.IGuicePostStartup with my.app.MyDatabaseModule; provides com.guicedee.client.services.lifecycle.IGuicePreDestroy with my.app.MyDatabaseModule; }META-INF/services/<SPI>file for classpath launches. Do not self-register by calling service-loader accessors from the constructor; that can recursively instantiate the provider. - Inject
Mutiny.SessionFactoryand use reactive sessions:@Inject private Mutiny.SessionFactory sessionFactory; public Uni<User> createUser(String name) { User user = new User(); user.setName(name); return sessionFactory.withTransaction(session -> session.persist(user).replaceWith(user)); }
Startup Flow
IGuiceContext.instance().inject()
└─ IGuiceModule hooks
└─ MyDatabaseModule (extends DatabaseModule)
├─ Parse persistence.xml
├─ IPropertiesEntityManagerReader SPIs (env var resolution, DB-specific props)
├─ IPropertiesConnectionInfoReader SPIs
├─ ConnectionBaseInfo.toPooledDatasource() (Vert.x SQL pool init)
└─ JtaPersistModule
├─ bind PersistService @Named("mydb")
└─ bind Mutiny.SessionFactory @Named("mydb") + default
└─ IGuicePostStartup hooks
└─ DatabaseModule.postLoad()
└─ PersistService.start() (creates EntityManagerFactory on Vert.x context)
└─ IGuicePreDestroy hooks
└─ DatabaseModule.onDestroy()
└─ PersistService.stop()
@EntityManager Annotation
| Attribute | Default | Purpose |
|---|---|---|
| value | "" | Persistence unit name (maps to persistence.xml) |
| allClasses | true | Include all entity classes or only the annotated package |
| defaultEm | true | Mark as the default SessionFactory binding |
Apply at class level (on DatabaseModule subclasses) or package level (package-info.java) to scope entities to specific persistence units.
Multiple Persistence Units
Bind multiple DatabaseModule subclasses with distinct @Named qualifiers. One should be marked as the default with defaultEm = true:
@Inject @Named("orders")
private Mutiny.SessionFactory ordersFactory;
@Inject @Named("users")
private Mutiny.SessionFactory usersFactory;
Supported Databases
Built-in ConnectionBaseInfo implementations:
postgresql— PostgreSQL via Vert.x PG Clientmysql— MySQL via Vert.x MySQL Clientsqlserver— SQL Server via Vert.x MSSQL Clientoracle— Oracle via Vert.x Oracle Clientdb2— DB2 via Vert.x DB2 Client
Environment Variable Resolution
${VAR_NAME} placeholders in persistence.xml properties are resolved from system properties or environment variables. Default values supported with ${VAR_NAME:default} syntax.
Session, pool, and shutdown ownership
VertxPersistenceModule.getSqlClientByEntityManager(name) resolves the named persistence unit's pool; an unknown unit does not authorize falling back to a different database. Session and transaction ownership remains with the caller's composed reactive operation. ActivityMaster consumers additionally use its stateless/FSDM contracts rather than assuming this generic stateful example applies to them.
DatabaseModule.shutdownSortOrder() closes persistence resources before Vert.x (MAX_VALUE - 1000). The module implements startup and destroy hooks as well as IGuiceModule: register the hooks used by the application, in both service mechanisms. Validate partial startup and repeated cleanup against PersistenceLifecycleTest; do not equate compilation with a database transaction or schema-migration test.
Module requirements
- Always use
ReactivePersistenceProvider— standard JPA providers are not supported. persistence.xmlmust exist inMETA-INF/.- DatabaseModule subclass must be registered as an
IGuiceModuleSPI. - Entity packages must
openstotools.jackson.databindandorg.hibernate.orm.core. - Module must
requires com.guicedee.persistence;. - SPI implementations must be dual-registered for tests to find services (
module-info.java+META-INF/services/).