ebean-orm/ebean
> A Java/Kotlin ORM that looks like JPA but rejects the persistence context — stateless finders, bytecode enhancement, and SQL you would have hand-written.
GitHub repo · Official website · License: Apache-2.0
Overview
Ebean is a persistence framework for Java and Kotlin that reads a subset of the JPA mapping annotations (@Entity, @Id, @ManyToOne) but is not a JPA provider1. There is no EntityManager, no persistence context, no JPQL, no persistence.xml. Persistence is expressed through a stateless, session-less API (DB.find(...), DB.save(...)) plus type-safe query beans. The project originated as "Avaje Ebean" and has been developed primarily by Rob Bygrave since well before the GitHub repo was created in 20122.
The defining decision is the absence of a first-level cache / unit-of-work session. In Hibernate/JPA, entities live inside a persistence context that tracks them, batches writes at flush, and can silently re-order or defer SQL. Ebean has no such session: a save is a save, a query is a query, and the SQL Ebean emits maps closely to what you asked for. This removes a large class of "why did it flush here / why is this entity detached" confusion, at the cost of losing the automatic identity map and write-behind batching that JPA users rely on (Ebean offers explicit transaction batching instead).
The second defining decision is bytecode enhancement. Entity classes are rewritten at build time (Maven/Gradle plugin) or load time (a -javaagent) to implement Ebean's EntityBean interface, adding the interception field that powers lazy loading, dirty checking, and partial objects3. When enhancement does not run, these features silently degrade — the most common source of Ebean support questions.
Getting Started
Maven coordinate (group io.ebean) plus the enhancement plugin:
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>15.8.1</version>
</dependency>
<!-- plus io.ebean:ebean-maven-plugin (or the Gradle plugin) to enhance entities -->
@Entity
@Table(name = "customer")
public class Customer {
@Id long id;
String name;
@ManyToOne Country country;
// getters/setters, or use Lombok
}
// Static default database — no EntityManager, no session to open/close
Customer c = new Customer();
c.setName("Rob");
DB.save(c);
Customer found = DB.find(Customer.class)
.where().eq("name", "Rob")
.findOne();
Type-safe query beans (QCustomer is generated by an annotation processor)4:
List<Customer> list = new QCustomer()
.name.startsWith("R")
.country.code.eq("NZ")
.orderBy().name.asc()
.findList();
Architecture / How It Works
Enhancement. @Entity/@Embeddable/transactional classes are byte-code enhanced. Entity beans gain an _ebean_intercept interceptor that records which properties are loaded and which are dirty. This is what lets Ebean issue partial-object SELECTs, lazy-load associations on access, and generate an UPDATE touching only changed columns. Enhancement is a build step (recommended) or a runtime agent; getting it wired correctly in every module and in the IDE/test run is the main setup hurdle.
Query beans. A javac/KAPT annotation processor (querybean-generator) emits a Q-prefixed companion per entity. These give IDE autocomplete, compile-time checked property paths, and a fluent DSL. They are optional — the string-based find(...).where().eq("name", …) API works without them — but they are Ebean's headline feature and require APT configuration.
Query layering. Three levels coexist5: ORM queries (object graph, autotuned), DTO queries (map arbitrary SQL/result rows onto plain DTOs), and raw SqlQuery/JDBC. You work at the highest level that fits and drop down when needed.
N+1 and autotune. A shared "load context" batches lazy loads so that iterating a list and touching an association fetches the association for the whole batch rather than per row. Autotune can profile which properties an ORM query's object graph actually uses and rewrite the fetch plan to select only those columns.
Migrations. Ebean can diff the entity model against the last generated model and emit DDL migration scripts (init, normal, repeatable, and "rebase")6, run by a companion runner. This is separate from Flyway/Liquibase but conceptually similar.
Caching. A built-in L2 bean/query cache reduces database load, queries can mix DB and L2 cache, and optional Elasticsearch integration exists for search / L3. Since Ebean 13 the codebase ships module-info (JPMS modules) and requires Java 11+; the README documents the build friction this causes with Eclipse and with test runners that default to the module path.
Production Notes
- Enhancement is a hard prerequisite, and its failure is quiet. If the Maven/Gradle plugin
or agent doesn't run, lazy loading and dirty checking don't work — you get fully-eager or broken behavior with no exception. Verify enhancement in test runs and in the IDE, not just the CI build. This is the recurring footgun.
- JPA annotations, non-JPA semantics. Reusing
jakarta.persistenceannotations tempts teams
to expect JPA behavior. There is no JPQL, no Criteria API, no EntityManager, no cascade semantics identical to a JPA provider. Ebean-specific behavior lives under io.ebean.annotation.
- No implicit flush ordering. Writes happen when you call them. Batch mode
(transaction.setBatchMode(true)) must be turned on explicitly to get JDBC batching; it is not the automatic write-behind that JPA gives you.
- Static
DBvs injectedDatabase. The staticDBfacade is convenient but couples code to
a global default server; testable/multi-tenant setups should inject the Database instance. (Ebean/EbeanServer are the older names for DB/Database — old tutorials still use them.)
- Smaller ecosystem. Far fewer Stack Overflow answers, blog posts, and third-party
integrations than Hibernate. Spring Data does not target Ebean; there is a separate ebean-spring/Spring Boot starter. Hiring for Ebean experience is harder.
- Kotlin works but adds KAPT weight. Query-bean generation via KAPT slows Kotlin builds;
data classes and nullability need care with enhancement.
When to Use / When Not
Use when:
- You want an ORM but find the JPA persistence-context model a net negative, and prefer explicit
save/find with predictable SQL.
- You value type-safe query beans and compile-time-checked query paths.
- You want built-in DB migration generation and an L2 cache without assembling them.
- Your team is comfortable owning the bytecode-enhancement build wiring.
Avoid when:
- You need JPA compliance, JPQL, or the broad Spring Data JPA ecosystem and tooling.
- You want the largest possible hiring pool and community answer base.
- You prefer SQL-first with no ORM abstraction (use a SQL DSL or mapper instead).
- You cannot tolerate a build step that silently changes runtime behavior if misconfigured.
Alternatives
- hibernate/hibernate-orm — the reference JPA provider; use it when JPA compliance, JPQL, or Spring Data JPA integration is required.
- jOOQ/jOOQ — type-safe SQL DSL generated from your schema; use it when you want SQL-first control and no ORM identity/lazy semantics.
- mybatis/mybatis-3 — SQL mapper with explicit statements; use it when you want to hand-write SQL but avoid JDBC boilerplate.
- JetBrains/Exposed — Kotlin-first DSL + lightweight DAO; use it in Kotlin projects that want an idiomatic SQL DSL over annotation-driven mapping.
- spring-projects/spring-data-jdbc — aggregate-oriented, no lazy loading or proxies; use it when you want a simpler, more explicit persistence model inside Spring.
History
| Version | Date | Notes |
|---|---|---|
| — | 2012-09-13 | GitHub repo created; project predates it as "Avaje Ebean"2. |
| 11 | ~2017 | Package rename com.avaje.ebean → io.ebean; Maven group io.ebean. |
| 12 | ~2019 | Ebean/EbeanServer facade renamed to DB/Database. |
| 13 | ~2021 | JPMS module-info; Java 11 minimum7. |
| 14–15 | 2023–2026 | Jakarta namespace, GraalVM native-image support, ongoing platform coverage. |
References
- ^ Ebean documentation — introduction and query levels. https://ebean.io/docs/
- ^ GitHub repository
ebean-orm/ebean(created 2012-09-13); originally "Avaje Ebean." https://github.com/ebean-orm/ebean - ^ Ebean docs — bytecode enhancement / entity enhancement. https://ebean.io/docs/intro/setup/enhancement
- ^ Ebean docs — type-safe query beans. https://ebean.io/docs/query/query-beans
- ^ Ebean docs — ORM, DTO, and SqlQuery levels. https://ebean.io/docs/intro/queries/orm-query
- ^ Ebean docs — DB migrations. https://ebean.io/docs/db-migrations/
- ^
ebean-orm/ebeanREADME — "Ebean 13 uses Java modules with module-info"; JDK 11+ to build. https://github.com/ebean-orm/ebean
Tags
java, kotlin, orm, jdbc, sql, database, persistence, jpa-alternative, query-beans, bytecode-enhancement, db-migrations