Skip to content
Exposed docs1.11

Hibernate and Exposed

Latest stable Based on Exposed release 1.11.0

Hibernate and Exposed are not interchangeable syntax layers. They expose different persistence models. Choose the model whose ownership and query style fit the application; avoid turning the decision into a framework ranking.

ConcernHibernateExposed
Primary modelEntity graph managed by a persistence contextExplicit tables, SQL DSL, DAO, and transaction blocks
Change trackingDirty checking inside the persistence contextExplicit statement/DAO changes inside an Exposed transaction
RelationshipsObject associations, proxies, fetch plansJoins and referenced entities selected explicitly
Query styleJPQL/HQL, Criteria, native SQLType-safe SQL DSL, DAO queries, custom SQL where needed
Unit of workSession/entity manager commonly owns itThe transaction block or surrounding framework transaction owns it
SchemaJPA mapping plus migrations or schema toolingExposed table definitions plus migrations or schema tooling
Reactive pathHibernate Reactive has its own session modelExposed R2DBC uses coroutine transactions and R2DBC drivers

Hibernate is attractive when a rich domain model, object navigation, standardized JPA integration, and persistence-context change tracking are central requirements. It can reduce explicit persistence code for aggregate-oriented CRUD. The trade-off is that proxy loading, flush timing, cascades, and fetch plans become part of correctness and performance reasoning.

Exposed is attractive when SQL shape, table ownership, joins, batching, and transaction boundaries should remain visible in Kotlin. The DSL works well for query-heavy services and database-specific features. DAO is available when an entity-like interface is useful, but it does not require the application to adopt JPA’s persistence-context model.

Do not rewrite a stable persistence layer only to standardize syntax. Migrate one bounded context or one repository at a time when a concrete reason exists: unsupported SQL, transaction opacity, reactive requirements, operational complexity, or maintenance cost. During coexistence, keep one owner for each table and transaction. Sharing the same DataSource does not automatically make two persistence contexts one safe unit of work.

Contract tests should pin observable repository behavior before a migration: ordering, null handling, optimistic concurrency, cascade expectations, transaction rollback, and generated identifiers. Retest query plans against the production database.

  1. Does the team reason primarily from aggregate object graphs or from SQL and tables?
  2. Is lazy loading an asset in this application, or an operational risk?
  3. Which database-specific features must remain first-class?
  4. Who owns the transaction when multiple repositories participate?
  5. Does the non-blocking path require a different session or repository model?

Start with JDBC vs R2DBC and transaction boundaries, then build one representative repository before choosing a broad migration.