Using Dropwizard’s HibernateBundle for Request‑Scoped JPA Transactions
Learn how Dropwizard’s HibernateBundle simplifies JPA integration by managing the EntityManagerFactory, injecting request‑scoped EntityManagers, and enabling automatic transactions with @UnitOfWork.
17 Jan 2026, 19:22 UTC

Problem: Managing JPA sessions in Dropwizard without boilerplate
When building a Dropwizard service that needs relational persistence, developers often face repetitive code: creating an EntityManagerFactory, opening and closing sessions per request, and manually committing transactions. Forgetting any step can lead to leaked connections or uncommitted changes.
Why the HibernateBundle helps
The HibernateBundle (part of dropwizard-hibernate) bundles three concerns:
- It reads the
database:andhibernate:sections ofconfig.yamlto build anEntityManagerFactoryduring application start. - It registers a lifecycle listener that cleanly shuts down the factory when the JVM stops.
- It provides a request‑scoped
EntityManagerthat can be injected into any Jersey resource, service, or managed class.
With the bundle in place, you only need to annotate resource methods that perform writes with @UnitOfWork to start and commit a transaction automatically.
Configuration steps
Assume a configuration class that extends Configuration and implements HibernateBundleConfiguration:
public class MyConfiguration extends Configuration implements HibernateBundleConfiguration {
@Valid
@NotNull
private HibernateBundleConfiguration hibernateBundle = new HibernateBundleConfiguration();
public HibernateBundleConfiguration getHibernateBundleConfiguration() {
return hibernateBundle;
}
}
In config.yaml supply the database connection and Hibernate properties:
database:
# the *driverClass*, *user*, *password*, *url* are passed straight to Hibernate
driverClass: org.postgresql.Driver
user: dbuser
password: dbpass
url: jdbc:postgresql://localhost:5432/mydb
hibernate:
# any Hibernate property can go here; the bundle will prefix them with "hibernate."
dialect: org.hibernate.dialect.PostgreSQLDialect
hbm2ddl.auto: validate
show_sql: false
Finally, add the bundle to the application’s run method:
public class MyApplication extends Application<MyConfiguration> {
private final HibernateBundle<MyConfiguration> hibernateBundle = new HibernateBundle<MyConfiguration>(
Item.class // list your annotated entities here
) {
@Override
public HibernateBundleConfiguration getHibernateBundleConfiguration(MyConfiguration configuration) {
return configuration.getHibernateBundleConfiguration();
}
};
@Override
public void initialize(Bootstrap<MyConfiguration> bootstrap) {
bootstrap.addBundle(hibernateBundle);
}
@Override
public void run(MyConfiguration config, Environment env) {
final EntityManagerFactory emf = hibernateBundle.getEntityManagerFactory();
env.jersey().register(new ItemResource(emf));
}
public static void main(String[] args) throws Exception {
new MyApplication().run(args);
}
}
Worked example: injecting EntityManager and using @UnitOfWork
The bundle registers a provider that makes EntityManager injectable via @Inject. Below is a simple resource that lists items with pagination and creates a new item.
@Path("/items")
@Produces(MediaType.APPLICATION_JSON)
public class ItemResource {
private final EntityManager em;
@Inject
public ItemResource(EntityManager em) {
this.em = em;
}
@GET
public List list(@QueryParam("offset") @DefaultValue("0") int offset,
@QueryParam("limit") @DefaultValue("20") int limit) {
TypedQuery q = em.createQuery("SELECT i FROM Item i", Item.class);
q.setFirstResult(offset);
q.setMaxResults(limit);
return q.getResultList();
}
@POST
@UnitOfWork
public Response create(Item item) {
em.persist(item);
return Response.status(Response.Status.CREATED).entity(item).build();
}
}
The @UnitOfWork annotation on the POST method tells Dropwizard to begin a transaction before the method runs and commit it afterward (or roll back on an exception). The GET method runs outside a transaction because it only reads; the request‑scoped EntityManager still participates in the current Hibernate session, which is flushed automatically at the end of the request if any changes were made.
Trade‑off: session‑per‑request memory usage
The bundle’s default strategy binds one Hibernate Session (via the injected EntityManager) to each HTTP request. For short‑lived requests this is efficient, but a long‑running request that loads many entities can cause the session’s first‑level cache to grow unbounded, increasing heap consumption. If you anticipate such workloads, consider:
- Enabling eager fetching only for the associations you truly need.
- Using a second, dedicated
EntityManagerFactoryfor batch jobs or streaming consumers, bypassing the request‑scoped bundle. - Manually clearing the session with
em.clear()after processing chunks of data.
These approaches add a bit of extra code but keep memory usage predictable.
Closing: when to adopt the bundle
If your Dropwizard service needs straightforward JPA persistence—entity mapping, basic CRUD, and occasional pagination—the HibernateBundle removes most of the boilerplate while keeping the programming model explicit. Verify the setup by checking the startup log for a line like "Hibernate initialized" and confirming that a GET request returns expected JSON. For write operations, ensure the method is annotated with @UnitOfWork or manually manage the transaction; otherwise, changes will not be flushed to the database.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.