无会话架构
JPA 是这样的架构,实体 Bean 必须附加
到PersistenceContext
,才能使实体 Bean 持久化
。
也就是说,JPA 强制要求附加/分离
语义。这会产生许多后果,包括
- 开发人员无法轻松控制和持久化选定的对象
- 所有附加的 Bean 都必须处于有效状态才能刷新
- 刷新会隐式刷新所有脏状态(因此任何地方创建的脏状态都会隐式刷新)
- 刷新可能会以意外的方式重新排序语句(导致死锁)
- 现在必须与事务范围一起管理 EntityManager 范围
Ebean 架构
- 没有实体管理器,只管理事务
- 每个实体 Bean 上的脏状态
- 持久化(保存、删除)不需要 PersistenceContext
- PersistenceContext 附加到事务
- 始终预期部分填充的 Bean
持久化功能
JDBC 批处理控制
JPA 中有
- 无法控制 JDBC 批处理大小
- 无法关闭 getGeneratedKeys 以优化大规模插入
- 无法关闭级联行为(以获得完全控制权)
使用 JDBC 批处理非常重要,以便 Ebean 在持久化时获得最佳性能。Ebean 附加到事务的 JDBC 批处理缓冲区,并且可以使用 JDBC 批处理无缝地批处理持久化调用。
Ebean 通过事务提供对 JDBC 批处理的完全控制,包括批处理大小
、getGeneratedKeys
和级联行为
。与 Ebean 中的 JPA 不同,可以轻松地完全控制和优化批处理。
事务
除了对 JDBC 批处理的完全控制之外,Ebean 还提供了在事务中
- 注册事务回调(提交后回调等)
- 设置和获取用户定义的对象
- 指定是否应为事务跳过 L2 缓存
原始 JDBC
Ebean 提供对底层 java.sql.Connection
的访问,以便在需要时执行原始 JDBC,以及 transaction.addModification(table, ...)
方法,以告诉 Ebean 哪些表已修改,以便使 L2 缓存失效。
Ebean 还内置了 SqlUpdate
和 CallableSql
,以便轻松地执行 SQL 批量语句和调用存储过程。
事务隔离级别
Ebean 允许您在更高的隔离级别启动事务。JPA 不支持此操作。
无状态更新
Ebean 允许我们填充 bean(或对象图)并 update()
,而无需加载对象对象。这在类似于 REST 的 API 中很有用,在这些 API 中,我们希望从 JSON 填充实体对象图并执行更新。
查询功能
findCount
Ebean 内置了 findCount
,它会复制查询并对其进行优化,以便执行计数(通过删除提取、排序等)。在 JPA 中,我们将使用 count() 函数
,并且无法对 findList
类型查询使用相同的查询。
findPagedList
JPA 不支持 PagedList
查询,该查询支持查找查询的“总数”以及一页结果。(Spring Data JPA 在一定程度上填补了这一空白)。
findEach
JPA 不支持执行大型查询。Ebean 具有 findEach
,用于执行大型查询(游标/流),它会考虑持久性上下文的范围、JDBC 驱动程序的 fetchSize 以及特定的 MySql 处理(因为 MySql 有特定的问题需要处理)。
findMap
JPA 没有等效于 Ebean 的 findMap
,后者返回由属性映射的对象。
异步查询
Ebean 内置了对在后台执行查询的支持,并使用 findFutureList()、findFutureCount() 和 findFutureIds()
返回 Futures
。这提供了一种简单的方法来执行可以取消的查询。
在内部,findFutureCount() 用作 PagedList
查询的一部分,以便可以并行于 findList() 查询执行总数查询。
历史查询
JPA 还不支持 SQL2011 AS OF
查询。
历史查询之间的版本
JPA 还不支持 SQL2011 VERSIONS BETWEEN
查询。
泛型
JPA 最初的 API 中缺少泛型,因此同时具有 Query
和 TypedQuery
,这并不理想。
JPQL
JPQL
对 部分对象
和 优化 N + 1 复杂查询
的支持都很差,无法通过定义要获取的对象图的哪一部分来进行优化。
在当前状态下,JPQL
是一种用于优化 ORM 查询的糟糕语言。JPA 2.1 中添加了对 获取组
的支持作为查询提示,但存在许多问题:使用起来相当不优雅,只是一个支持相对较差的查询提示,并且最终缺少一些重要的功能来控制来自混合源(L2、L3 和 DB)的对象图构建。
在 JPA 中,对注释 FetchType.Eager
和 FetchType.Lazy
的强调以及 JPQL 中的限制使 JPA 处于优化 ORM 查询的不利位置,如果他们能够达到 Ebean 支持的对象图构建控制级别,将会很有趣。
类型安全查询
你可以争辩说 JPA Criteria 查询中有一些“仪式”,这使得查询更加冗长且难以阅读。
Ebean “查询 bean”
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();
Ebean(用于生成“查询 bean”)和 JPA(用于生成 JPA 查询元模型类)都使用 Java 注释处理,以便提供类型安全查询构建和执行。
QueryDSL
我怀疑许多 JPA 用户倾向于使用 QueryDSL,而不是标准 JPA Criteria 元模型对象。
映射
构造函数
与 JPA 不同,Ebean 不需要默认构造函数。
@View
Ebean 已经内置了对基于数据库视图的实体的支持。
没有 @Id 的实体
Ebean 不要求实体具有 @Id 属性。这些实体被视为“只读”,并自动绕过持久性上下文。这些实体通常用于报告目的。
命名约定
JPA 规范命名约定包括混合大小写和下划线 - 这是很奇怪的。Ebean 的默认命名约定 UnderscoreNamingConvention
匹配 Hibernate 的 ImprovedNamingStrategy
,而不是 JPA 规范命名约定。
审计和 @History
Ebean 已经内置了对 @WhenCreated
、@WhenModified
、@WhoCreated
、@WhoModified
和完整的 SQL2011 @History 支持 的支持。
数据库中的 JSON
Ebean 包含对 Postgres JSONB、JSON 和 Oracle、MySql(以及即将推出的 SQL Server)类似类型的映射支持。
DB 数组
Ebean 包含对 Postgres ARRAY 类型的映射支持。
@SoftDelete
Ebean 包含对 @SoftDelete
的支持,并具有关联的永久删除、级联行为和查询支持。
@Draftable
Ebean 包含对 @Draftable
的支持,该支持通过 publish()
和 restore()
函数提供“实时”和“编辑”功能。