IT俱乐部 Java 浅谈Spring Data JPA与MyBatisPlus的比较

浅谈Spring Data JPA与MyBatisPlus的比较

1 前言

JPA(Java Persistence API)和MyBatis Plus是两种不同的持久化框架,它们具有不同的特点和适用场景。

JPA是Java官方的持久化规范,它提供了一种基于对象的编程模型,可以通过注解或XML配置来实现对象与数据库的映射关系。JPA的优点是可以对数据库进行更高级的操作,如查询、更新、删除等,同时也支持事务管理和缓存机制,能够更好地支持复杂的业务逻辑。

MyBatis Plus (MPP) 是在MyBatis基础上进行封装的增强版本,它提供了更简单易用的API和更高效的性能。MyBatis Plus通过XML或注解的方式来配置数据库映射关系,并提供了丰富的查询、更新、删除操作的方法。相对于JPA,MyBatis Plus配置简单、易于上手,同时也灵活性较高,能够更好地满足项目的特定需求。

如果只是针对单表的增删改查,两者十分相似,本质上都算ORM框架,那么到底什么时候适合用JPA,什么时候用MyBatisPlus,下面做下这两者的详细对比。

2 POM依赖

  • JPA
org.springframework.bootspring-boot-starter-data-jpa
  • MPP
com.baomidoumybatis-plus-boot-starter

3 Entity定义

  • JPA
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.GeneratedValue;

@Entity
@Table(name = "dept")
public class Dept {
	@Id
	@Column(name = "id")
	@GeneratedValue(strategy = GenerationType.AUTO)
	private Long id;
	
	@Column(name = "code")
	private String code;
	
	@Column(name = "name")
	private String name;
}
  • MPP
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;

@TableName(value = "dept")
public class Dept {
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    @TableField(value = "code")
    private String code;

    @TableField(value = "name")
    private String name;
}

4 DAO基类

  • JPA
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface DeptRepository extends JpaRepository {
}
  • MPP
import org.apache.ibatis.annotations.Mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

@Mapper
public interface DeptMapper extends BaseMapper<Dept> {
}

4.1 基类主要方法

方法 JpaRepository MPP BaseMapper
插入一条记录 save(T entity) insert(T entity)
插入多条记录 saveAll(Iterable entities) insertBatchSomeColumn(List entityList)
根据 ID 删除 deleteById(ID id) deleteById(Serializable id)
根据实体(ID)删除 delete(T entity) deleteById(T entity)
根据条件删除记录 delete(Wrapper queryWrapper)
删除(根据ID或实体 批量删除) deleteAllById(Iterable extends ID> ids) deleteBatchIds(Collection> idList)
根据 ID 修改 save(T entity) updateById(T entity)
根据条件更新记录 update(Wrapper updateWrapper)
根据 ID 查询 findById(ID id) selectById(Serializable id)
查询(根据ID 批量查询) findAllById(Iterable ids) selectBatchIds(Collection extends Serializable> idList)
根据条件查询一条记录 selectOne(Wrapper queryWrapper)
根据条件判断是否存在记录 exists(Example example) exists(Wrapper queryWrapper)
根据条件查询总记录数 count(Example example) selectCount(Wrapper queryWrapper)
根据条件查询全部记录 findAll(Example example, Sort sort) selectList(Wrapper queryWrapper)
根据条件查询分页记录 findAll(Example example, Pageable pageable) selectPage(P page, Wrapper queryWrapper)

4.2 Example、Specification VS Wrapper

JPA使用Example和Specification 类来实现范本数据的查询,而MPP使用QueryWrapper来设置查询条件

4.2.1 JPA Example

Dept dept = new Dept();
dept.setCode("100");
dept.setName("Dept1");

// select * from dept where code = '100' and name = 'Dept1';
List deptList = deptRepository.findAll(Example.of(dept)); 

默认是生成的条件都是 “=”,如果要设置其他比较符,需要使用ExampleMatcher

Dept dept = new Dept();
dept.setCode("100");
dept.setName("Dept1");

// select * from dept where code like '100%' and name like '%Dept1%';
List deptList = deptRepository.findAll(Example.of(dept, ExampleMatcher.matching()
                .withMatcher("code", ExampleMatcher.GenericPropertyMatchers.startsWith())
                .withMatcher("name", ExampleMatcher.GenericPropertyMatchers.contains()))); 

4.2.2 JPA Specification

Example仅能实现对字符串类型的匹配模式,如果要设置其他类型的字段,可以实现JpaSpecificationExecutor接口来完成:

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;

@Repository
public interface DeptRepository extends JpaRepository, JpaSpecificationExecutor  {
}

增加以上接口后,会增加以下查询方法:

  • findOne(Specification spec)
  • findAll(Specification spec)
  • findAll(Specification spec, Pageable pageable)
  • count(Specification spec)
  • exists(Specification spec)

使用示例:

Dept dept = new Dept();
dept.setCode("100");
dept.setName("Dept1");

// select * from dept where code like '100%' and name like '%Dept1%';
Specification spec = new Specification() {
   @Override
   public Predicate toPredicate(Root root, CriteriaQuery> query, CriteriaBuilder cb) {
       List predicates = new ArrayList();
       predicates.add(cb.like(root.get("code"), dept.getCode() + "%"));
       predicates.add(cb.like(root.get("code"), '%' + dept.getCode() + "%"));
       return query.where(predicates.toArray(new Predicate[predicates.size()])).getRestriction();
    }
};
List deptList = deptRepository.findAll(Example.of(dept)); 

除了equalnotEqual, 针对日期、数字类型,还有gtgeltle等常用比较符。

4.2.3 MPP Wrpper

MPP Wrapper类似于JPA的CriteriaBuilder,不过用法上更加便捷:

Dept dept = new Dept();
dept.setCode("100");
dept.setName("Dept1");

// select * from dept where code = '100' and name = 'Dept';
Wrapper wrapper = Wrappers.lambdaQueryWrapper(detp);
List deptList = deptRepository.selectList(wrapper); 

默认是生成的条件都是 “=”,如果要设置其他比较符,需要单独设置Wrapper:

Dept dept = new Dept();
dept.setCode("100");
dept.setName("Dept1");

// select * from dept where code like '100%' and name like '%Dept1%';
Wrapper wrapper = Wrappers.lambdaQueryWrapper()
                        .likeRight(Dept::getCode, dept.getCode)
                        .like(Dept::getName, dept.getName);
List deptList = deptRepository.selectList(wrapper); 

4.2.4 JPA Specification 与 MPP Wrpper的方法汇总

方法 JPA Specification MPP Wrpper
等于 = equal eq
不等于 notEqual ne
大于 > greaterThan, gt gt
大于等于 >= greaterThanOrEqualTo, ge ge
小于 lessThan, lt lt
小于等于 lessThanOrEqualTo, le le
BETWEEN 值1 AND 值2 between between
NOT BETWEEN 值1 AND 值2 notBetween
LIKE ‘%值%’ like like
NOT LIKE ‘%值%’ notLike notLike
LIKE ‘%值’ like likeLeft
LIKE ‘值%’ like likeRight
NOT LIKE ‘%值’ notLike notLikeLeft
NOT LIKE ‘值%’ notLike notLikeRight
字段 IS NULL isNull isNull
字段 IS NOT NULL isNotNull isNotNull
字段 = true isTrue
字段 = false isFalse
字段 IN (v0, v1, …) in in
字段 NOT IN (v0, v1, …) notIn
排序:ORDER BY 字段, … ASC asc orderByAsc
排序:ORDER BY 字段, … DESC desc orderByDesc
排序:ORDER BY 字段, … orderBy(CriteriaQuery) orderBy
拼接 OR or or
AND 嵌套 and and
正常嵌套 不带 AND 或者 OR nested
拼接 sql apply
无视优化规则直接拼接到 sql 的最后 last
拼接 EXISTS ( sql语句 ) exists exists
拼接 NOT EXISTS ( sql语句 ) notExists
去重 distinct(CriteriaQuery)
设置查询字段 select, multiselect(CriteriaQuery) select
分组:GROUP BY 字段, … groupBy(CriteriaQuery) groupBy
SQL SET 字段 set
设置 SET 部分 SQL setSql
字段自增变量 val 值 setIncrBy
字段自减变量 val 值 setDecrBy
条件判断 selectCase
平均值 avg
加和 sum, sumAsLong, sumAsDouble
计数 count, countDistinct
最大值 max, greatest
最小值 min, least
取反 neg
绝对值 abs
Product prod
差值 diff
求商 quot
取模 mod
开根号 sqrt
转换类型 toLong, toInteger, toFloat, toDouble, toBigDecimal, toBigInteger, toString
集合是否为空 isEmpty, isNotEmpty
集合大小 size
是否包含 isMember, isNotMember
键值对 keys, values
字符串拼接 concat
字符串分隔 substring
去空白 trim
大小写转换 upper, lower
字符串长度 length
空处理 nullif, coalesce

5 DAO子类

5.1 JPA Repository方法命名规范

JPA支持接口规范方法名查询,一般查询方法以 find、findBy、read、readBy、get、getBy为前缀,JPA在进行方法解析的时候会把前缀取掉,然后对剩下部分进行解析。例如:

@Repository
public interface DeptRepository extends JpaRepository {
    
    // 调用此方法时,会自动生成 where code = ? 的条件
	Dept getByCode(String code);
}

常用的方法命名有:

关键字 方法命名 sql条件
Distinct findDistinctByLastnameAndFirstname select distinct …​ where x.lastname = ?1 and x.firstname = ?2
And findByNameAndPwd where name= ? and pwd =?
Or findByNameOrSex where name= ? or sex=?
Is,Equals findById, findByIdIs, findByIdEquals where id= ?
Between findByIdBetween where id between ? and ?
LessThan findByIdLessThan where id
LessThanEquals findByIdLessThanEquals where id
GreaterThan findByIdGreaterThan where id > ?
GreaterThanEquals findByIdGreaterThanEquals where id > = ?
After findByIdAfter where id > ?
Before findByIdBefore where id
IsNull findByNameIsNull where name is null
isNotNull,NotNull findByNameNotNull where name is not null
Like findByNameLike where name like ?
NotLike findByNameNotLike where name not like ?
StartingWith findByNameStartingWith where name like ‘?%’
EndingWith findByNameEndingWith where name like ‘%?’
Containing findByNameContaining where name like ‘%?%’
OrderBy findByIdOrderByXDesc where id=? order by x desc
Not findByNameNot where name ?
In findByIdIn(Collection> c) where id in (?)
NotIn findByIdNotIn(Collection> c) where id not in (?)
True findByEnabledTue where enabled = true
False findByEnabledFalse where enabled = false
IgnoreCase findByNameIgnoreCase where UPPER(name)=UPPER(?)
First,Top findFirstByOrderByLastnameAsc order by lastname limit 1
FirstN,TopN findTop3ByOrderByLastnameAsc order by lastname limit 3

5.2 MPP自定义方法 + 接口默认实现

MyBatisPlus没有JPA那样可以根据接口的方法名自动组装查询条件,但是可以利用Java8的接口默认实现来达到同样的目的,只不过需要编写少量的代码:

import org.apache.ibatis.annotations.Mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

@Mapper
public interface DeptMapper extends BaseMapper {
    default Dept getByCode(String code) {
		return selectOne(Wrappers.lambdaWrapper().eq(Dept::getCode, code));
	}
}

6 自定义SQL

JPA支持通过@Query注解和XML的形式实现自定义SQL,而MyBatis支持通过@Select、@Delete、@Update、@Script注解和XML的形式实现自定义SQL。

6.1 JPA

JPA的自定义SQL分为JPQL(Java Persistence Query Language Java 持久化查询语言)和原生SQL两种。
JPQL:

import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

@Repository
public interface DeptRepository extends JpaRepository {
    @Query(value = "select d from Dept d where d.code = ?1")
	Dept getByCode(String code);

    @Modifying
    @Query(value = "delete from Dept d where d.code = :code")
    int deleteByCode(@Param("code") String code);
}

原生SQL

import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

@Repository
public interface DeptRepository extends JpaRepository {
    @Query(value = "SELECT * FROM dept WHERE name = ?1", countQuery = "SELECT count(*) FROM dept WHERE name = ?1", nativeQuery = true)
    Page findByName(@Param("name") String name, Pageable pageable);
}

XML形式:
/resource/META-INFO/orm.xml

 select d from Dept d where d.code = ?1 DELETE FROM dept WHERE code = ?1

6.2 MyBatis

JPA的自定义SQL分为注解形式和XML形式
注解形式:

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;

@Mapper
public interface DeptMapper extends BaseMapper {
    @Select(value = "SELECT * FROM dept WHERE code = #")
	Dept getByCode(@Param("code") String code);

    @Delete("DELETE FROM dept WHERE code = #")
    int deleteByCode(@Param("code") String code);
    
    @Select(value = "SELECT * FROM dept WHERE name = #{name}")
    IPage findByName(@Param("name") String name, IPage page);
}

XML形式:
/resource/mapper/DeptMapper.xml

		SELECT * FROM dept WHERE code = #
	
		DELETE FROM dept WHERE code = #
	
	
		SELECT * FROM dept WHERE name = #{name}
	

7 表关联

待补充

8 其他

对于简单的CRUD操作,JPA和MPP都提供了丰富的API简化开发人员的操作,但是有些差异化的地方需要总结下:

比较点 JPA MPP
成熟度 JPA毕竟是javax标准,成熟度自然高 MyBatis成熟度也很高,但是MPP毕竟是国内个人维护,质量和成熟度相对还是比较低的,但是使用起来更加适配国内开发者的习惯
自动DDL JPA可以根据Entity的定义自动更新实际数据库的DDL, 使用起来比较便利 利用MPP的脚本自动维护Flyway进行SQL脚本的自动执行
实体关系 使用@OneToMany、@OneToOne、@ManyTo@Many注解描述表与表之间的关联,查询时自动进行表的关联,并且支持更新和删除时自动级联到关联的实体 使用标签以及@One、@Many注解来映射结果集和Java对象,只支持查询,不支持更新和删除, 另外还有一个MyBatis-Plus-Join项目, 可以实现Java中表Join的操作。
复杂SQL查询 不太方便 使用xml结构化语言 + 动态SQL 标签 可以实现非常复杂的SQL场景
数据库差异 使用自带的API和JPQL的话,是不用关心具体用什么数据库,但是用原生SQL的话无法解决数据库的差异 使用自带API的话,基本上不需要关注数据库的差异,如果切换了不同类型的数据库,通过配置databaseIdProvider 就可以根据当前使用数据库的不同选择不同的SQL脚本
学习曲线 较为难,主要是思路的转变,使用JPA更加关注的是实体间的关系,表的结构会根据实体关系自动维护 对于传统的关系型数据库的操作,MyBatisPlus可以与JQuery操作DOM元素那么顺手

9 个人建议

目前对比下来整体的感觉是JPA侧重数据建模,关注数据一致性,屏蔽SQL操作,MyBatis侧重构建灵活的SQL,而MyBatisPlus在MyBatis的基础上较少了日常的CRUD操作,JPA更适合事务性系统,MyBatisPlus更适合做分析型系统。

个人是从SQL -> MyBatis -> MyBatisPlus的路线过来的,所以更习惯与用MPP解决数据的问题,在使用MPP的过程中,越来越发现自定义SQL用到越来越少,大部分场景下是可以用MPP的API组合来实现的,即便是MPP不支持多表关联,通过抽象视图的形式,也能达到单表查询的效果,只有在极限、特别复杂的情况下才会写SQL。

这么看来,其实JPA也是能满足日常的开发需求的。但是从传统SQL向JPA的转变是需要一个过程的,就跟面向过程开发到面向对象的开发,是需要一个大的开发思维一个转变,可能需要在项目的实践中不断体会和适应。

到此这篇关于浅谈Spring Data JPA与MyBatisPlus的比较的文章就介绍到这了,更多相关Spring Data JPA与MyBatisPlus内容请搜索IT俱乐部以前的文章或继续浏览下面的相关文章希望大家以后多多支持IT俱乐部!

本文收集自网络,不代表IT俱乐部立场,转载请注明出处。https://www.2it.club/code/java/13206.html
上一篇
下一篇
联系我们

联系我们

在线咨询: QQ交谈

邮箱: 1120393934@qq.com

工作时间:周一至周五,9:00-17:30,节假日休息

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部