Spring Data JPA 与 MyBatis 的一些心得
引言
之前一直使用 MyBatis ,习惯了自己写增删改查的 SQL 。在入职新公司后,公司是用的是 Spring Data JPA ,半年过去了,由于公司本身是互联网行业,开发和迭代快速,比较深刻的体会到了 Spring Data JPA 和 MyBatis 的优缺点。
先下结语,互联网行业,开发和迭代快速,如果没有 Spring Data JPA / Hibernate 的大牛,或者技术文档没有维护的想法,不建议使用 Spring Data JPA ,哪怕 Spring Data JPA 具有初始的开发速度优势。
Spring Data JPA 与 MyBatis
由于 Spring Data JPA 默认使用 Hibernate 作为 ORM 实现,Spring Data JPA 与 MyBatis 对比,其实也就是 Hibernate 与 MyBatis 的对比。
-
Spring Data JPA 与 MyBatis 的查询构建
Spring Data JPA 中,如果一个 DAO 查询类继承了 JpaRepository,即
public interface classADAO extends JpaRepository<classA,Integer>{ }
那么查询所有的 classA 有多简单呢:
List<classA> list =classADAO.findAll();
核心在于什么, Spring Data JPA (Hibernate) 是面向对象的,MyBatis 是面向关系的。
但面向对象麻烦的地方也就在这里,如果有级联呢?
它把表看做一个对象,那表A是一个对象 classA 。如果表A通过中间表B关联了表C,那么毫无意义的表B也成了一个对象。注意,这些都是要写入类中的。
public class ClassA implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) Integer id; @OneToMany(mappedBy = "classA", cascade = {CascadeType.ALL}, fetch = FetchType.LAZY) private List<classB> listB; }
public class ClassB implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) Integer id; @OneToMany(mappedBy = "classB", cascade = {CascadeType.ALL}, fetch = FetchType.LAZY) private List<classC> listC; }
仔细想想,从 classA 到 classC 查询实现会比较复杂。
所以要用好 Spring Data JPA 的查询,一定要有一位大牛,对数据库设计时的表关系进行取舍,尽可能的避免上文这种情况出现。当然肯定还有其他需要考虑的地方,我这方面经验尚缺。
但是用好了,就可以很方便的查询,不用 SQL 写来写去,对吧,而 MyBatis 的 SQL 是绕不开的。
-
Spring Data JPA 与 MyBatis 的文档维护
由上一点就可以延伸到这一点,我之前提到过, Spring Data JPA 具有初始的开发速度优势。
为什么,这里可能有些疑问,因为就算你简单的 SQL 能避免,但是总有多表条件查询啊。是的,Spring Data JPA 还有一样利器 Specification ,我贴下基本的结构,你就明白了
public interface ClassASpec { static Specification<ClassA> spec(ClassAQuery classAQuery, Boolean checkUnique) { return (root, query, cb) -> { List<Predicate> predicates = new ArrayList<>(); if (classAQuery.getId() != null) { if (checkUnique != null && checkUnique) { predicates.add(cb.notEqual(root.get("id"), classAQuery.getId())); } else { predicates.add(cb.equal(root.get("id"), classAQuery.getId())); } } return cb.and(predicates.toArray(new Predicate[predicates.size()])); }; } }
明白了吧,这就是不用写 SQL 的原因所在,所有的查询以此种方式从对象映射为关系,自动生成 SQL。
那问题来了,Spring Data JPA 的优势在这里处理不好就会变成劣势。
处于互联网行业,开发是快速的,迭代是快速的,数据库中的表关系会层层叠加,表字段会层层叠加,如果不注意维护文档,一段时间,你可以想象得到,这个查询类 Specification 会变成何种怪物。
有朋友就问了,我不能通过看 SQL 来看懂查询逻辑吗。可以,极费时间,你可以通过日志查看自动生成的 SQL ,但一是由于级联的存在,二是由于 Specification 自动生成的 SQL 会加载每个表及每个表的所有字段,且会给每个字段生成别名,相信我,你看到 SQL 会爆炸的。
我拿一个实际中常见的多表联合条件查询 SQL 举例(来源于网络):
SELECT CATENTRY.PARTNUMBER, CATENTRY.CATENTRY_ID, CATENTDESC.NAME, MASSOCCECE.MASSOCCECE_ID FROM CATENTDESC, CATENTRY, CATGROUP, CATGRPREL, CATGPENREL, MASSOCCECE WHERE CATENTDESC.LANGUAGE_ID = 44 AND CATENTRY.MARKFORDELETE = 0 AND CATENTDESC.CATENTRY_ID = CATENTRY.CATENTRY_ID AND CATENTRY.CATENTRY_ID = MASSOCCECE.CATENTRY_ID_TO AND MASSOCCECE.CATENTRY_ID_FROM = 299811 -- AND CATGPENREL.CATALOG_ID = 10201 AND MASSOCCECE.MASSOCTYPE_ID = 'EDITORIAL' AND CATGPENREL.CATENTRY_ID = MASSOCCECE.CATENTRY_ID_TO AND CATGRPREL.CATGROUP_ID_CHILD = CATGPENREL.CATGROUP_ID AND CATGRPREL.CATGROUP_ID_PARENT = CATGROUP.CATGROUP_ID AND CATGRPREL.CATALOG_ID = CATGPENREL.CATALOG_ID AND CATGROUP.FIELD1 = 'EDITORIAL' GROUP BY CATENTRY.PARTNUMBER, CATENTRY.CATENTRY_ID, CATENTDESC.NAME, MASSOCCECE.MASSOCCECE_ID ORDER BY MASSOCCECE.MASSOCCECE_ID WITH UR
如果没有文档,MyBatis 的语句好歹是直观的。哪怕没文档、排版乱,格式化后,是看得懂的。升级直接改 SQL ,还可以拜托 DBA 优化。
要是用 Spring Data JPA 做的话,查询类会极度复杂。没有文档,开发人员一换,后边的开发人员头都得想破。谈到升级和优化,DBA 无从下手,必须有个 Spring Data JPA / Hibernate 的大牛。
结语
- 在简单数据库逻辑查询(单表)下 Mybatis 开发效率会比 Spring Data JPA 低,但不会低多少
- 在复杂数据库逻辑查询(多表、联合)下,随着表关系和表字段的迭代, Spring Data JPA 查询的效率和优化比 Mybatis 困难得多
所以,互联网行业,因为开发和迭代快速,如果没有 Spring Data JPA / Hibernate 的大牛,或者技术文档没有维护的想法,不建议使用 Spring Data JPA ,哪怕 Spring Data JPA 具有初始的开发速度优势。