GitHub - bwajtr/java-persistence-frameworks-comparison: Comparison of non-JPA SQL mapping frameworks for Java (Jooq, Spring JDBCTemplate, MyBatis, EBean, JDBI, Speedment, sql2o)
该项目比较了 Java 的非 JPA SQL 映射(持久性)框架(jOOQ、Spring JDBCTemplate 等)的使用情况。我们用它来找出在https://www.spintrace.com的开发过程中哪个 DB 层最好
我不是在比较性能,而是在比较这些框架如何用于日常任务。
我准备了一些常见的场景,您通常需要这些场景来实现以数据为中心的应用程序,然后我使用各种非 JPA DB 层框架来实现这些场景。这个项目应该服务
- 在决定使用 SQL 映射框架时作为参考点
- 作为常见框架使用场景的模板(见下文场景)
- 记录此类常见用法的最佳实践(欢迎评论!)
随意使用存储库中的代码(MIT 许可证)
比较框架
对于我选择考虑的框架,我有以下(主观评估:))条件:
- 框架应该包含——而不是隐藏——我们正在使用的 SQL 语言和 RDBMS
- 该框架必须足够成熟以供“企业级”使用。
- 可以使用 JPA 注释,但不能是完整的 JPA 实现(请参阅下面的“为什么只有非 JPA?”部分)
在此条件下,比较了以下框架:
我试图在每个框架中找到最佳(== 最易读的)实现,但欢迎发表评论!代码中有很多注释解释了为什么我选择了这样的实现以及一些我不喜欢的地方的 FIXME,但不能以不同的方式实现或者我有困难改进......
此外,我甚至考虑过(并尝试实施)以下框架,但结果证明它们不符合条件:
- Speedment - 过多地隐藏 SQL 语言并尝试用流操作代替;不是所有的场景都可以在里面实现;截至 2016 年 11 月 30 日和 3.0.1 版,GitHub 上的文档非常薄弱
- sql2o - 它根本不支持 Spring 事务管理(测试版本 1.6.0-RC3),这是一个显示停止器 - 在问题 #7中跟踪这个
实施的方案
这些是场景:
- 根据主键获取单个实体
- 根据条件获取实体列表
- 保存新的单个实体并返回主键
- 批量插入多个相同类型的实体并返回生成的键
- 更新单个现有实体 - 一次更新实体的所有字段
- 获取多对一关系(公司为部门)
- 获取一对多关系(公司的部门)
- 更新实体一对多关系(公司中的部门) - 添加两项,更新两项并删除一项 - 一次
- 复杂选择 - 基于一些布尔条件构造选择条件 + 加入一些 JOIN
- 调用存储过程/函数并处理结果
- 使用 JDBC 简单语句(不是 PreparedStatement)执行查询
- 根据主键删除单个实体
每个场景在 Scenarios 类中都有它的实现。有关每个场景的更详细描述,请参阅场景方法的Javadoc 。
使用的模型
如何
- 克隆存储库
- 在application.properties中配置 PostgreSQL 连接详细信息
- 通过运行create-script.sql创建表和数据
- 通过运行register_employee.sql创建一个存储过程
- 从 Gradle 构建执行时,JUnit 测试将通过。如果您希望测试通过您的 IDE,然后为您的 IDE 设置 EBean 增强器
- 通过运行其中一个测试类对场景进行测试并享受 :)
为什么只有非 JPA?
好吧,我和我的同事一直试图在我们的项目中“坚持标准”,所以我们过去使用 JPA,但是在使用了多年 JPA(主要是 Hibernate)之后,我们意识到它适得其反。在我们的大多数项目中,它引起的问题多于它帮助解决的问题——尤其是在大型项目中(有很多表和关系)。这些失败的原因有很多——但最大的问题是 JPA 实现只是变成了膨胀软件。内部发生了很多奇怪的魔法,而且复杂性如此之高,以至于每个团队都需要一个高级 Hibernate “超级专家”,这样应用程序才能真正显示出一些性能并且代码易于管理......
所以我们完全放弃了 JPA,开始使用 JDBCTemplate 并发现我们可以更快地交付应用程序(这有点令人惊讶),它们更快(感谢有效使用 DB)并且更健壮......这真的很放松,而且我们根本不打算返回 JPA...(是的,即使是 CRUD 应用程序!)
该项目旨在探索 SQL 映射领域中的其他选项,而不仅仅是 JDBCTemplate。
结论/注释
请注意,以下评论非常主观、固执己见,不一定适用于您。
我会选择什么
- 如果项目经理可以接受额外的许可证费用,或者项目使用开源数据库之一(如 PostgreSQL),那么一定要使用jOOQ。
- 如果您的项目使用 Oracle、DB2、MSSQL 或任何其他商业数据库,并且 jOOQ 许可的额外费用是不可接受的,那么请使用JDBCTemplate(就我个人而言,它的成熟度和文档胜过其他选择)。
每个框架的主观优缺点
JDBCTemplate
- 优点
- 感觉你和 JDBC 本身很接近
- 实施了所有场景,没有更大的问题 - 没有隐藏的惊喜
- 非常简单的批处理操作
- 易于设置
- 缺点
- JDBCDataRepositoryImpl 中的方法可读性不强——那是因为您必须在 Java 代码中内联 SQL。如果 Java 支持多行字符串会更好。
- 调试日志记录可能会更好
JOOQ
- 优点
- 很流畅,很容易写新的查询,代码可读性很强
- 设置好后非常容易使用,非常适合简单查询
- 很棒的记录器调试输出
- 缺点
- 某些数据库的付费许可证 - 很难说服经理这是值得的 :)
- 对于大型查询不太有用 - 最好使用本机 SQL(参见场景 9。)
- 批处理操作的奇怪语法(如果您不使用 UpdatableRecord)。但这不是什么大问题...
MyBatis
- 优点
- 在 XML 映射器文件中编写 SQL 语句感觉很好——使用参数很容易。
- 缺点
- 单个 DAO 实现的很多文件(MyBatisDataRepositoryImpl、DataRepositoryMapper 和 DataRepositoryMapper.xml),尽管导航并不是什么大问题
- 在 3.4.0 版本无法使用 Java8 DateTime 类型(LocalDate 等),可以通过 3rd 方库(mybatis-types)支持,请参阅 mybatis-config.xml 中的 build.gradle 和配置
- 不能在单个 SqlSession 中运行批处理和非批处理操作,而是必须创建全新的 SqlSession(参见 DbTestsApplication 类中的配置)。令人惊讶的是,这并不一定意味着批处理和非批处理操作将在不同的事务中执行(如我们所料),所以最后这并不是一个完全的缺点,而只是不便
- 预计 localCacheScope=STATEMENT 是默认的 MyBatis 行为,这不是......我知道这是有问题的缺点,但这对我来说有点惊讶,请参阅 mybatis-config.xml
EBean
- 优点
- 一切看起来都很好——所有的场景都是由可读性很强的代码实现的
- 超级简单的批处理操作(实际上只是使用正确的方法:))
- 尽管有一些方法可以使 CRUD 操作和查询变得超级简单,但仍然有一些方法可以执行普通的 SQL,甚至还有一种方法可以获取基本的 JDBC Transaction 对象,您可以将其用于核心 JDBC 内容。那真的很好。
- 缺点
- 编写实体的必要性(我的意思是@Entity 类) - 有一些生成器会很酷
- 实体“增强”的必要性——这让我很惊讶——但实际上它基本上只是关于正确的环境设置(IDE 插件和 Gradle 插件),然后你不必考虑它
- 在线文档非常薄弱(截至 2016 年 12 月 1 日)。很多东西都隐藏在视频中,你必须用谷歌搜索细节或进入 JavaDocs……然而,JavaDoc 非常好,我通常没有问题在 JavaDoc 中找到我需要的东西。此外,API 也很容易理解……总而言之,薄弱的在线文档并不是什么大不了的事。
- 日志记录可能会更好
- 允许 JPA OneToMany 和 ManyToOne 关系建模和“延迟获取”这些关系的可能性 - 实际上,我根本不喜欢这个概念,因为它可能导致潜在的非常无效的代码。根据互联网上几个人的文档和经验,以这种方式,EBean 的行为比完整的 JPA 实现更好,但是您仍然可能会遇到 N+1 问题和所有性能陷阱,这些都是惰性获取带来的......
JDBI(2.77 版)
- 优点
- 我喜欢创建语句和绑定参数的流畅风格——我希望在 JDBC 模板中看到类似的东西
- 代码通常比 jdbc 模板更具可读性
- 相当简单易懂的批处理操作
- 缺点
- 极弱的日志记录:(
- 非常薄弱的文档(截至 5.12.2016,版本 2.77)
- 我不太喜欢为每个 DAO 方法打开和关闭句柄的必要性 -> 我有点不清楚是否应该为每种方法打开句柄,或者是否可以为每个 HTTP 请求打开一个句柄......文档不多清楚这个...