Spring Data JPA 与 MyBatis 的一些心得

jpa mysql numeric 转成什么类型 jpa改mybatis_Data

引言

之前一直使用 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 的对比。

  1. 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 是绕不开的。

  1. 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 的大牛。

结语

  1. 在简单数据库逻辑查询(单表)下 Mybatis 开发效率会比 Spring Data JPA 低,但不会低多少
  2. 在复杂数据库逻辑查询(多表、联合)下,随着表关系和表字段的迭代, Spring Data JPA 查询的效率和优化比 Mybatis 困难得多

所以,互联网行业,因为开发和迭代快速,如果没有 Spring Data JPA / Hibernate 的大牛,或者技术文档没有维护的想法,不建议使用 Spring Data JPA ,哪怕 Spring Data JPA 具有初始的开发速度优势。