Java ORM 哪家强?10个ORM框架测试对比与选型建议

前言:

Java 领域的ORM(Object-Relational Mapping)框架有很多,各家的性能和使用体验如何?本文将对比体验以下的Java ORM框架,包括Spring JDBC、Spring Data JPA + Hibernate、QueryDSL、jOOQ、GraphQL、MyBatis、MyBatis-dynamic-sql、MyBatis-plus、Fluent-mybatis、MyBatis-flex,以帮助开发者选型。

一、性能测试对比

测试背景:

我们以diboot的操作日志表为基准,字段十几个,测试表中数据量约2万条。分别测试对比以下ORM框架:Spring JdbcTemplate、Spring JdbcClient、Spring data JPA、Mybatis、Mybatis-plus、Mybatis-flex。*(其他框架会在使用体验章节介绍) *

测试以下步骤:

  • 循环执行 1000 次读LIKE 查询,重复多次
  • 循环执行 1000 次写插入,重复多次测试

测试结果:

(注:因Mybatis-plus 和 Mybatis-flex 存在冲突无法共存,所以我们拆分为2个测试用例分别测试)

总结:

  • Spring JdbcTemplate 是最接近原生JDBC的性能,以此为基准,Spring 最新的JdbcClient是在JdbcTemplate的基础上在对象映射上做了轻量封装,二者的读写性能都非常优秀。
  • Mybatis 的读写性能仅次于Spring JdbcClient,非常优秀。
  • Mybatis-plus 的读性能较好,在Mybatis的基础上损耗较小,优于Mybatis-flex(二者都是通过Lambda构建查询)。写性能上二者相差不大。
  • JPA的读写性能不如Mybatis-plus(与Mybatis-flex混合测试还会导致无法写入)。

二、使用体验对比

Spring JdbcTemplate、JdbcClient

优点: 接近原生JDBC的性能 缺点: 仅提供了基础的对象映射转换处理,需要在Java中写大量的SQL语句,表多的话很难维护 示例:

java 复制代码
// 查询用法示例
List<IamOperationLog> dataList = jdbcClient
                .sql("select * from iam_operation_log where business_obj LIKE ?")
                .param("%"+keyword+"%")
                .query(IamOperationLog.class)
                .list();

Spring Data JPA + Hibernate

优点: 有Spring体系的支持,关注对象模型不太关注SQL的简单场景用起来比较容易 缺点: 过度抽象,隐藏了SQL实现,背后的Hibernate驾驭起来也比较困难,复杂SQL条件构建与扩展都不方便 示例:

java 复制代码
// 查询用法示例
IamOperationLog iamOperationLog = new IamOperationLog().setBusinessObj(keyword);
List<IamOperationLog> dataList = jpaRepository.findAll(Example.of(iamOperationLog));

QueryDSL

优点: 支持APT自动生成构建SQL所需的DSL类;可以作为JPA方案的补充,扩展完善其查询条件构建等能力 缺点: 国内比较小众,复杂SQL实现繁琐 示例:

java 复制代码
// 查询用法示例
List<IamOperationLog> dataList = queryFactory.selectFrom(iamOperationLog)
    .where(iamOperationLog.businessObj.like('%'+keyword+'%'))
    .fetch();

jOOQ

优点: SQL构建方式相对优雅,写法接近原生SQL 缺点: 数据库支持少(开源版仅支持MySQL、PostgreSQL、SQLite等),国内比较小众 示例:

java 复制代码
// 查询用法示例
// 不使用APT生成DSL辅助类,写起来还是挺繁琐的
Query query = create.select(field("BOOK.TITLE"),field("AUTHOR.FIRST_NAME"),field("AUTHOR.LAST_NAME"))
.from(table("BOOK")).join(table("AUTHOR")).on(field("BOOK.AUTHOR_ID").eq(field("AUTHOR.ID")))
.where(field("BOOK.PUBLISHED_IN").eq(2008));
// 使用APT生成DSL辅助类,写起来相对顺畅
Query query = create.select(BOOK.TITLE, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME)
    .from(BOOK).join(AUTHOR)
    .on(BOOK.AUTHOR_ID.eq(AUTHOR.ID))
    .where(BOOK.PUBLISHED_IN.eq(2008)); 

List<Object> bindValues = query.getBindValues();

GraphQL

优点: 设计思路新颖 缺点: 只热过一阵子,把查询构建(业务逻辑)交给前端注定过于挑战开发者习惯,尤其是前后端分离场景下

javascript 复制代码
// GraphQL 查询,注意是:前端构建查询请求
author(id: "7") {
  id
  name
  avatarUrl
  articles(limit: 2) {
    name
    urlSlug
  }
}

MyBatis

优点: 性能优秀,使用简单,复杂SQL可以写在XML中方便统一维护,复杂项目更可控 缺点: 手写SQL过多,缺失通用Mapper、联表SQL手写复杂

xml 复制代码
<!-- 查询用法示例 -->
<select id="getMatchedLog" resultType="com.diboot.ormpk.entity.IamOperationLog">
    select *
    from iam_operation_log
    where business_obj LIKE #{keyword,jdbcType=VARCHAR}
</select>

MyBatis-dynamic-sql

优点: Mybatis官方出品的支持多表查询的动态SQL构建解决方案 缺点: 缺少APT自动生成方案,手写代码过多,使用起来缺失一点优雅

xml 复制代码
// 查询用法示例
SelectStatementProvider selectStatement = select(id, animalName, bodyWeight, brainWeight)
            .from(animalData)
            .where(id, isIn(1, 5, 7))
            .and(bodyWeight, isBetween(1.0).and(3.0))
            .orderBy(id.descending(), bodyWeight)
            .build().render(RenderingStrategies.MYBATIS3);
List<AnimalData> animals = mapper.selectMany(selectStatement);

MyBatis-plus

优点: Mybatis的扩展框架,性能较好,支持通用Mapper等,单表CRUD、查询条件构建写起来比较优雅 缺点: 缺少联表查询方案(可使用 Diboot core 内核实现)

java 复制代码
// 查询用法示例
LambdaQueryWrapper<IamOperationLog> queryWrapper = new LambdaQueryWrapper<IamOperationLog>()
        .like(IamOperationLog::getBusinessObj, keyword);
List<IamOperationLog> logList = iamOperationLogMPMapper.selectList(queryWrapper);

Fluent-mybatis

优点: 借鉴了jOOQ的实现思路 缺点: 已停更

java 复制代码
// 查询用法示例
StudentQuery query = new StudentQuery().where.userName().eq("u2").end();
List<StudentEntity> users = mapper.listEntity(query);

MyBatis-flex

优点: Mybatis的扩展框架,支持通用Mapper,支持APT生成DSL辅助类,支持多表,动态SQL构建也相对优雅。像是借鉴了众多ORM的优势实现的一个既要又要的解决方案。 缺点: 查询性能还有优化空间(v1.8.x版本),稳定性还有待验证

java 复制代码
// 查询用法示例
// 1. 不使用APT生成DSL辅助类,Lambda写起来类似Mybatis-plus
QueryWrapper queryWrapper = QueryWrapper.create().like(IamOperationLog::getBusinessObj, keyword);
List<IamOperationLog> logList = iamOperationLogMFMapper.selectListByQuery(queryWrapper);

// 2. 使用APT生成DSL辅助类,写起来类似jOOQ
queryWrapper = QueryWrapper.create().from(IAM_OPERATION_LOG).and(IAM_OPERATION_LOG.BUSINESS_OBJ.like(keyword));
logList = iamOperationLogMFMapper.selectListByQuery(queryWrapper);

三、总结与选型建议

Java ORM虽然很多,综合下来目前主流的还是JPA(Hibernate)和Mybatis两大阵营。

  • Mybatis拥有良好的性能和应对复杂场景的能力,国内使用更广泛是有理由的,个人建议首选站队Mybatis,除非你的项目非常简单。
  • 针对Mybatis的不足,如果你追求稳健,可以使用 MyBatis-plus + Diboot 。如果你的开发场景能够接受尝鲜,可以使用 Mybatis-flex(Diboot 后期也可能会适配)。
相关推荐
大牧师5 天前
TypeORM 入门教程
后端·sql·mysql·orm·nest·typeorm
大牧师9 天前
TypeORM 学习教程
数据库·sql·学习·node.js·orm·nest.js·typeorm
FungLeo10 天前
成为全栈·Node 后端篇·数据迁移:schema 变更如何不弄脏线上数据
sqlite·orm·schema·d1·node 后端·drizzle
沐沐师10 天前
TypeORM 学习教程
orm
触底反弹17 天前
🚀 Next.js 全栈项目数据库连接实战:Drizzle ORM + Supabase 从零到跑通
postgresql·orm·next.js
姚杨17 天前
聊了三年 DDD,代码里全是贫血模型:老陈一句话点破,落地先过这几关
后端·orm
Darling噜啦啦18 天前
从零搭建单词管理系统:Next.js + Supabase + Drizzle ORM 全栈实战
数据库·orm·next.js
不好听61318 天前
ORM:让你不用再手写SQL语句
orm
柒和远方18 天前
V077:Next.js 后台的认证与权限防线:首个超级管理员的事务锁初始化、会话令牌哈希,与四道管理员保护规则
orm·next.js
__zRainy__20 天前
Node系列 · ORM:mysql 驱动程序
数据库·后端·mysql·node.js·orm