前言JPA和Mybatis的争论由来已久。还记得2年前我在spring4all社区发表过关于哪个更好哪个更差这个话题的看法。当时我是支持JPA的。当然,这与我对JPA的熟悉程度有关,但也有更深层次的原因,就是JPA的设计理念符合领域驱动设计的思想,可以很好地指导我们进行设计数据库交互接口。在这两年的工作中,逐渐接触到了一些使用Mybatis的项目,对它有了一定的新的认识。据说认知是一个螺旋式的过程。随着经验的积累,人们很容易推翻过去。两年后的今天,我也有了新的看法。本文不是告诉大家JPA和Mybatis谁更好,而是尝试求同存异,甚至在项目中同时使用JPA和Mybatis。什么?有必要同时使用两个ORM框架吗?不要急着抱怨我。希望大家在看完这篇文章后,也可以考虑在某些场合同时使用这两个框架。附言。本文所讨论的JPA特指spring-data-jpa。建模@Entity@Table(name="t_order")publicclassOrder{@IdprivateStringoid;@EmbeddedprivateCustomerVocustomer;@OneToMany(cascade={CascadeType.ALL},orphanRemoval=true,fetch=FetchType.LAZY,mappedBy="order")privateListorderItems;}JPA最大的特点就是sqlless。如上实体定义,将数据库表与Java中的类型关联起来。JPA可以根据@Entity注解自动创建表结构;基于此实体实现Repository接口使JPA用户可以轻松实现数据CRUD。因此,在使用JPA的项目中,人们很少提及“数据库设计”,人们更关注领域建模而不是数据建模。Mybatis用户逆向工程用得比较多。比如mybatis-generator插件可以根据上面的xml配置直接将表结构翻译成mapper文件和实体文件。codefirst和tablefirst在结果上没有区别。区别在于流程,所以不会因为这个区别就判断系统设计的好不好,但是从指导的角度来说,无疑是在设计系统的时候,应该考虑的是实体与实体之间的关联,实体和值对象,以及域边界的划分,而不是首先关注数据库表结构的设计。从建模的角度来看,JPA的领域建模思想更胜一筹。数据更新、数据库聊天自然离不开CRUD。我们先来看一下增删改查的数据更新操作,看看这两个框架的一般习惯是什么。JPA提倡的数据更新只有一种范式,分为三个步骤:首先findOne映射到一个实体,修改内存中的实体,将实体作为一个整体保存。你可能会争辩说@Query也有nativeQuery和JPQL用法,但这不是主流用法。JPA特别强调“整体保存”的思想,这与领域驱动设计强调的状态性密不可分,即认为修改不应该针对某个领域:“updatetableseta=bwherecolomonA=xx",而是应该反映实体的变化,save代表实体状态的最终持久化。先查找后保存显然也适用于Mybatis,而Mybatis的灵活性使得它的数据更新方式更加百花齐放。路人A可以认为JPA死板不懂得适应,而Mybatis不羁放纵,热爱自由;路人B也可以认为JPA格式规范易于维护,Mybatis并不完善。关于这一点我不想做太多的判断,留给后人去说吧。就个人习惯而言,我还是更喜欢先找到再整体保存的习惯。不是说这是JPA的专利,Mybatis没有;但是JPA的强制性让我养成了这个习惯。从数据更新的角度,JPA强制使用find+save,mybatis也可以做到,赢家:无。数据查询JPA提供的查询方式主要分为两种简单查询:findBy+属性名复杂查询:JpaSpecificationExecutor简单查询在一些简单的业务场景中提供了极大的方便,findBy+属性名可以自动翻译成SQL,如果你会写更少的代码,谁不愿意?复杂查询是JPA为了解决复杂查询场景而提供的解决方案。它只是将数据库的一些聚合函数和连接操作转换成Java方法。虽然它实现了sqlless,但是写出来的代码又臭又长,而且不一定好读好维护。这是我最不喜欢JPA的事情之一,但没有其他方法可以解决复杂的查询。而Mybatis可以执行任意查询sql,灵活性是JPA所望尘莫及的。数据库新手搜索最多的两个问题:如何做数据库分页和如何做条件查询Mybatis都能轻松解决。不要拒绝复杂查询:聚合查询、连接查询等场景。让JPA用户发疯的最简单方法是给他一个复杂查询的案例。selecta,b,c,sum(a)wherea=xxandd=xxgroupbya,b,c;来吧,表演。或许JPA确实可以完成上述sql的转义,但是要知道并不是所有的开发者都是JPA高手。没有人关心你用JPA解决了多么复杂的查询语句。更多人关心的是能不能把这个复杂的查询做完,早点回家。让我们回到复杂数据查询需求本身的分析。我们假设这些要求是合理的。毕竟工程的复杂度是难以估计的。可能有1000个数据查询需求JPA可以轻松实现,但是JPA无法hold住的复杂查询只有十几个。这时候只能乖乖的写sql了。如果这时候再出现一个条件查询的场景,如果出现else,说明你连@Query都用不了了,你就彻底退化到JdbcTemplate时代了。那么为什么不用Mybatis呢?Mybatis用户从不为复杂的查询而苦恼,它简直就是为之而生。现在很多Mybatis插件也可以帮助用户快速生成基本方法。虽然sql还是要写的,但是对于开发者来说并不难。不要质疑高并发下JOIN操作和聚合函数的可能性。在数据查询场景中,Mybatis胜出。性能从本质上讲,ORM框架不存在一定程度的性能差异化,因为最终转化为SQL交给数据库引擎执行,ORM层面的性能损失几乎可以忽略不计。但是从实用的角度来说,Mybatis为开发者提供了更高的sql自由度,所以在一些需要sql调优的场景下会更加灵活。可维护性前面我们提到JPA相比Mybatis失去了SQL的自由,一切都要权衡。从另一个角度看,它提供了高层抽象,试图用统一的模型来解决数据层面的问题。.同时sqlless也屏蔽了数据库的实现,屏蔽了数据库高低版本的兼容性问题,为可能的数据库迁移和数据库升级提供了极大的便利。两者同时使用的其他细节我就不分析了。我相信有很多点可以用来比较,但我认为主要的点应该在上面说到。以上维度的对比并不是我写这篇文章的初衷,更多的是从实际开发的角度为大家使用这两个框架提供一些参考建议。大多数场景下,我习惯使用JPA。例如,在设计领域对象时,得益于JPA的前向模型,我会优先考虑实体与值对象之间的关联以及领域上下文的边界,而不会过多关注如何设计表结构;另外,删除、修改和简单的查询场景,JPA提供的API已经是刻在我DNA里的范式了,用起来很舒服。在复杂的查询场景,比如没有域关联的join查询,包含多个聚合函数的复杂查询,以及其他JPA难以实现的查询,我会选择使用Mybatis,有点像使用Mybatis作为数据库视图生成器。坚定的JPA爱好者可能会质疑这些场景的真实性,是否是设计漏洞,但根据经验,即使是短期的解决方案,这些场景也是客观存在的,所以听我说,试着拥抱Mybatis吧。随着各种存储中间件的流行,比如mongodb、ES等,已经取代了数据库的一部分地位,经过反思,本质上是在用专业的工具来解决特定场景下的问题,最终目的还是为了解放生产力。数据库作为最古老、最基础的存储组件,确实承载了很多不该承载的东西,何必让一个工具、一个框架成为限制我们想象的沟壑呢?这两个框架其实并不重要。在springboot的支持下,引入几行配置就可以实现两者共存。我在我最近的项目中都使用过,遵循本文前面讨论的规范,我向你推荐它,所以试一试。