Sessionless architecture
JPA is architecture such that the entity beans must be attached
to the PersistenceContext
in order for the entity beans to be persisted
.
That is, JPA mandates attach/detach
semantics. This has a number of consequences including:
- Developers can't easily take control and persist selected objects
- All attached beans must be in a valid state to flush
- Flush implicitly flushes all dirty state (so dirty state created anywhere flushes implicitly)
- Flush can reorder statements in unexpected ways (causing deadlocks)
- The EntityManager scope must now be managed along with the transaction scope
Ebean architecture
- No Entity manager, only manage transactions
- Dirty state on each entity bean
- Persisting (save,delete) does not require the PersistenceContext
- PersistenceContext attached to the Transaction
- Partially populated beans always expected
Persist features
JDBC batch control
With JPA there is:
- No control over JDBC batch size
- No ability to turn off getGeneratedKeys to optimise large inserts
- No ability to turn off cascade behavior (to take full control)
Using JDBC batch is important so that Ebean can get optimal performance when persisting. With Ebean there is a JDBC batch buffer attached to the transaction and can seamlessly batch up persistence calls using JDBC batch.
Ebean provides full control over JDBC batch via the transaction including the
batch size
, getGeneratedKeys
, and cascade behavior
.
Unlike JPA with Ebean it is easy to fully control and optimise batch processing.
Transaction
Additional to full control over JDBC batch Ebean provides the ability on a transaction to:
- Register transaction callbacks (Post commit callback etc)
- Set and get user defined objects
- Specify if L2 cache should be skipped for the transaction
Raw JDBC
Ebean provides access to the underlying java.sql.Connection
for performing
raw JDBC if needed along with transaction.addModification(table, ...)
method to
tell Ebean what tables were modified for L2 cache invalidation.
Ebean also has built in SqlUpdate
and CallableSql
for easy SQL bulk
statements and calling stored procedures.
Transaction Isolation level
Ebean lets you start a transaction at a higher Isolation level. JPA does not support this.
Stateless updates
Ebean allows us to populate a bean (or object graph) and update()
without loading
the object object. This is useful in Rest like API's where we want to populate an entity object graph
from JSON and perform an update.
Query features
findCount
Ebean has built in findCount
which takes a copy of the query and optimises it for executing
a count (by removing fetch, ordering etc). In JPA we would use the count() function
and can't
use the same query to for a findList
type query.
findPagedList
JPA has no support for a PagedList
query that supports finding the "total count" for the
query as well as a page of results. (Spring Data JPA helps fill this gap somewhat).
findEach
JPA has no support for executing large queries. Ebean has findEach
for executing large queries (cursors / streaming) that takes into account the scope of the persistence context
, JDBC driver fetchSize and MySql specific treatment (because MySql has specific issues to deal with).
findMap
JPA has no equivalent to Ebean's findMap
which returns the objects mapped by a property.
Async queries
Ebean has built in support for executing queries in the background returning Futures
with findFutureList(), findFutureCount() and findFutureIds().
This provides an easy way to execute queries that can be cancelled.
Internally findFutureCount() is used as part of PagedList
query such that the
total count query can be executed in parallel to the findList() query.
As of history query
JPA has no support for SQL2011 AS OF
queries yet.
Versions between history query
JPA has no support for SQL2011 VERSIONS BETWEEN
queries yet.
Generics
JPA missed using generics with it's initial API and hence has both Query
and TypedQuery
which is not ideal.
JPQL
JPQL
has both poor support for partial objects
and poor support for optimising complex queries for N + 1
by defining
what part of the object graph should be fetched.
In it's current state JPQL
is a poor language for optimising ORM queries. JPA 2.1 added
in support for fetch groups
as a query hint but there are a number of issues with this:
being rather inelegant to use, only a query hint with relatively poor support and ultimately missing
some important features for controlling object graph construction from mixed sources (L2, L3 and DB).
In JPA the emphasis on annotations FetchType.Eager
and FetchType.Lazy
and limitations in JPQL has put JPA in a bad position for optimising ORM queries and it will be
interesting if they can get to the level of control over object graph construction that Ebean supports.
Type safe queries
You could argue there is a bit more "ceremony" with JPA Criteria queries which makes the query more verbose and harder to read.
Ebean "Query beans"
LocalDate today = new LocalDate();
List<Customer> customers =
new QCustomer()
.birthday.equalTo(today)
.createdAt.before(today.minusYears(2))
.findList();
JPA Criteria
LocalDate today = new LocalDate();
EntityManager em = ...;
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Customer> query = builder.createQuery(Customer.class);
Root<Customer> root = query.from(Customer.class);
Predicate hasBirthday = builder.equal(root.get(Customer_.birthday), today);
Predicate isLongTermCustomer = builder.lessThan(root.get(Customer_.createdAt), today.minusYears(2);
query.where(builder.and(hasBirthday, isLongTermCustomer));
List<Customer> customers = em.createQuery(query.select(root)).getResultList();
Java annotation processing is used by both Ebean (to generate "Query beans") and JPA (to generate JPA Query Meta model classes in order to provide type safe query construction and execution.
QueryDSL
I suspect many JPA users look to use QueryDSL as a preference over the standard JPA Criteria meta model objects.
Mapping
Constructor
Unlike JPA Ebean does not require a default constructor.
@View
Ebean has built in support for entities based on database views.
Entities without @Id
Ebean does not require entities to have an @Id property. These entities are considered "read only" and automatically bypass the persistence context. This entities are typically used for reporting purposes.
Naming convention
The JPA spec naming convention includes both mixed case and underscores - it is odd. Ebean's default naming
convention of UnderscoreNamingConvention
matches Hibernates ImprovedNamingStrategy
and not the JPA spec naming convention.
Auditing & @History
Ebean has built in support for @WhenCreated
, @WhenModified
, @WhoCreated
, @WhoModified
and full SQL2011 @History support.
JSON in DB
Ebean includes mapping support for Postgres JSONB, JSON and similar types for Oracle, MySql (and shortly SQL Server).
DB ARRAY
Ebean includes mapping support for Postgres ARRAY type.
@SoftDelete
Ebean includes support for @SoftDelete
with associated
delete permanent, cascading behavior and query support.
@Draftable
Ebean includes support for @Draftable
which provides
"live" and "editing" capability with publish()
and restore()
function.