广告位联系
返回顶部
分享到

Spring Data JPA与MyBatisPlus的比较介绍

java 来源:互联网 作者:佚名 发布时间:2024-09-01 08:50:20 人浏览
摘要

JPA(Java Persistence API)和MyBatis Plus是两种不同的持久化框架,它们具有不同的特点和适用场景。 JPA是Java官方的持久化规范,它提供了一种基于对象的编程模型,可以通过注解或XML配置来实现对

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

1

2

3

4

<dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-data-jpa</artifactId>

</dependency>

  • MPP

1

2

3

4

<dependency>

    <groupId>com.baomidou</groupId>

    <artifactId>mybatis-plus-boot-starter</artifactId>

</dependency>

3 Entity定义

  • JPA

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

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

1

2

3

4

5

6

import org.springframework.data.jpa.repository.JpaRepository;

import org.springframework.stereotype.Repository;

 

@Repository

public interface DeptRepository extends JpaRepository<Dept, Long> {

}

  • MPP

1

2

3

4

5

6

import org.apache.ibatis.annotations.Mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;

 

@Mapper

public interface DeptMapper extends BaseMapper&lt;Dept&gt; {

}

4.1 基类主要方法

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

4.2 Example、Specification VS Wrapper

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

4.2.1 JPA Example

1

2

3

4

5

6

Dept dept = new Dept();

dept.setCode("100");

dept.setName("Dept1");

 

// select * from dept where code = '100' and name = 'Dept1';

List<Dept> deptList = deptRepository.findAll(Example.of(dept));

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

1

2

3

4

5

6

7

8

Dept dept = new Dept();

dept.setCode("100");

dept.setName("Dept1");

 

// select * from dept where code like '100%' and name like '%Dept1%';

List<Dept> 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接口来完成:

1

2

3

4

5

6

7

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<Dept, Long>, JpaSpecificationExecutor<Dept>  {

}

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

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

使用示例:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

Dept dept = new Dept();

dept.setCode("100");

dept.setName("Dept1");

 

// select * from dept where code like '100%' and name like '%Dept1%';

Specification<Dept> spec = new Specification<Dept>() {

   @Override

   public Predicate toPredicate(Root<Dept> root, CriteriaQuery<?> query, CriteriaBuilder cb) {

       List<Predicate> 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<Dept> deptList = deptRepository.findAll(Example.of(dept));

除了equal、notEqual, 针对日期、数字类型,还有gt、ge、lt、le等常用比较符。

4.2.3 MPP Wrpper

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

1

2

3

4

5

6

7

Dept dept = new Dept();

dept.setCode("100");

dept.setName("Dept1");

 

// select * from dept where code = '100' and name = 'Dept';

Wrapper<Dept> wrapper = Wrappers.lambdaQueryWrapper(detp);

List<Dept> deptList = deptRepository.selectList(wrapper);

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

1

2

3

4

5

6

7

8

9

Dept dept = new Dept();

dept.setCode("100");

dept.setName("Dept1");

 

// select * from dept where code like '100%' and name like '%Dept1%';

Wrapper<Dept> wrapper = Wrappers.<Dept>lambdaQueryWrapper()

                        .likeRight(Dept::getCode, dept.getCode)

                        .like(Dept::getName, dept.getName);

List<Dept> 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在进行方法解析的时候会把前缀取掉,然后对剩下部分进行解析。例如:

1

2

3

4

5

6

@Repository

public interface DeptRepository extends JpaRepository<Dept, Long> {

     

    // 调用此方法时,会自动生成 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的接口默认实现来达到同样的目的,只不过需要编写少量的代码:

1

2

3

4

5

6

7

8

9

import org.apache.ibatis.annotations.Mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;

 

@Mapper

public interface DeptMapper extends BaseMapper<Dept> {

    default Dept getByCode(String code) {

        return selectOne(Wrappers.<Dept>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:

1

2

3

4

5

6

7

8

9

10

11

12

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

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

 

@Repository

public interface DeptRepository extends JpaRepository<Dept, Long> {

    @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

1

2

3

4

5

6

7

8

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

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

 

@Repository

public interface DeptRepository extends JpaRepository<Dept, Long> {

    @Query(value = "SELECT * FROM dept WHERE name = ?1", countQuery = "SELECT count(*) FROM dept WHERE name = ?1", nativeQuery = true)

    Page<Dept> findByName(@Param("name") String name, Pageable pageable);

}

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

1

2

3

4

5

6

<named-query name="Dept.getByCode">

  <query> select d from Dept d where d.code = ?1</query>

</named-query>

<named-native-query name="Dept.deleteByCode">

  <query> DELETE FROM dept WHERE code = ?1</query>

</named-native-query>

6.2 MyBatis

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

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<Dept> {

    @Select(value = "SELECT * FROM dept WHERE code = #[code]")

    Dept getByCode(@Param("code") String code);

 

    @Delete("DELETE FROM dept WHERE code = #[code]")

    int deleteByCode(@Param("code") String code);

     

    @Select(value = "SELECT * FROM dept WHERE name = #{name}")

    IPage<Dept> findByName(@Param("name") String name, IPage<Dept> page);

}

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

1

2

3

4

5

6

7

8

9

10

11

<mapper namespace="DeptMapper">

    <select id = "getByCode", resultType = "Dept">

        SELECT * FROM dept WHERE code = #[code]

    </select>

    <delete id = "deleteByCode">

        DELETE FROM dept WHERE code = #[code]

    </select>

    <select id = "findByName">

        SELECT * FROM dept WHERE name = #{name}

    </select>

</mapper>

7 表关联

待补充

8 其他

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

比较点 JPA MPP
成熟度 JPA毕竟是javax标准,成熟度自然高 MyBatis成熟度也很高,但是MPP毕竟是国内个人维护,质量和成熟度相对还是比较低的,但是使用起来更加适配国内开发者的习惯
自动DDL JPA可以根据Entity的定义自动更新实际数据库的DDL, 使用起来比较便利 利用MPP的脚本自动维护或Flyway进行SQL脚本的自动执行
实体关系 使用@OneToMany、@OneToOne、@ManyTo@Many注解描述表与表之间的关联,查询时自动进行表的关联,并且支持更新和删除时自动级联到关联的实体 使用<association>和<collection>标签以及@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的转变是需要一个过程的,就跟面向过程开发到面向对象的开发,是需要一个大的开发思维一个转变,可能需要在项目的实践中不断体会和适应。


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 :
相关文章
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计