Mybatis-Plus 框架

前面我们体验了JPA带来的快速开发体验,但是我们发现,面对一些复杂查询时,JPA似乎有点力不从心,反观稍微麻烦一点的Mybatis却能够手动编写SQL,使用起来更加灵活,那么有没有一种既能灵活掌控逻辑又能快速完成开发的持久层框架呢?答案是肯定的,这就是Mybatis-Plus框架。

1. Mybatis-Plus简介

MyBatis-Plus(简称 MP)是一个 MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

官方网站地址:https://baomidou.com

MybatisPlus具有以下特性:

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
  • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
  • 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
  • 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

2. Mybatis-Plus 快速上手

  1. 跟之前一样,按照官方步骤即可,首先添加依赖:
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
    <version>3.5.9</version>
</dependency>
  1. 配置数据源:
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/book?useSSL=false
    username: root
    password:
    driver-class-name: com.mysql.jdbc.Driver
  1. 创建实体类:
package com.demo.mybaitisplus.eneity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

/**
 * 酒店实体类
 * 
 * 该类用于映射数据库中的酒店信息表,通过MyBatis Plus注解简化配置,
 * 并利用Lombok的@Data注解自动生成getter和setter方法,减少boilerplate代码。
 */
@Data
@TableName("hotel")
public class Hotel {

    /**
     * 酒店ID
     * 
     * 使用自动增长策略作为主键ID。
     */
    @TableId(type = IdType.AUTO)
    private Integer id;
    
    /**
     * 酒店名称
     * 
     * 映射到数据库表的name字段。
     */
    @TableField("name")
    private String name;
    
    /**
     * 酒店地址
     * 
     * 映射到数据库表的address字段。
     */
    @TableField("address")
    private String address;
    
    /**
     * 房间租金
     * 
     * 映射到数据库表的rent字段,表示每晚的租金价格。
     */
    @TableField("rent")
    private Double rent;
}
  1. 创建Mapper接口:
package com.demo.mybaitisplus.mapper;


import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.demo.mybaitisplus.eneity.Hotel;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface HotelMapper extends BaseMapper<Hotel> {
    //使用方式与JPA极其相似,同样是继承一个基础的模版Mapper
    //这个模版里面提供了预设的大量方法直接使用,跟JPA如出一辙
}
  1. 添加测试类,进行功能测试
	@Resource
	HotelMapper hotelMapper;

	@Test
	void contextLoads() {
		System.out.println(hotelMapper.selectById(3));
	}

输出

Hotel(id=3, name=吉果酒店, address=太原市迎泽区万邦国际1205, rent=78.4)

通过以上几个简单的步骤,我们就实现了 User 表的 CRUD 功能,甚至连 XML 文件都不用编写!从以上步骤中,我们可以看到集成 MyBatis-Plus 非常的简单,只需要引入 starter 依赖,简单进行配置即可使用。但 MyBatis-Plus 的强大远不止这些功能。

3. 条件构造器

MyBatis-Plus 提供了一套强大的条件构造器(Wrapper),用于构建复杂的数据库查询条件。Wrapper 类允许开发者以链式调用的方式构造查询条件,无需编写繁琐的 SQL 语句,从而提高开发效率并减少 SQL 注入的风险。

在 MyBatis-Plus 中,Wrapper 类是构建查询和更新条件的核心工具。以下是主要的 Wrapper 类及其功能:

  • AbstractWrapper:这是一个抽象基类,提供了所有 Wrapper 类共有的方法和属性。它定义了条件构造的基本逻辑,包括字段(column)、值(value)、操作符(condition)等。所有的 QueryWrapper、UpdateWrapper、LambdaQueryWrapper 和 LambdaUpdateWrapper 都继承自 AbstractWrapper。

  • QueryWrapper:专门用于构造查询条件,支持基本的等于、不等于、大于、小于等各种常见操作。它允许你以链式调用的方式添加多个查询条件,并且可以组合使用 and 和 or 逻辑。

  • UpdateWrapper:用于构造更新条件,可以在更新数据时指定条件。与 QueryWrapper 类似,它也支持链式调用和逻辑组合。使用 UpdateWrapper 可以在不创建实体对象的情况下,直接设置更新字段和条件。

  • LambdaQueryWrapper:这是一个基于 Lambda 表达式的查询条件构造器,它通过 Lambda 表达式来引用实体类的属性,从而避免了硬编码字段名。这种方式提高了代码的可读性和可维护性,尤其是在字段名可能发生变化的情况下。

  • LambdaUpdateWrapper:类似于 LambdaQueryWrapper,LambdaUpdateWrapper 是基于 Lambda 表达式的更新条件构造器。它允许你使用 Lambda 表达式来指定更新字段和条件,同样避免了硬编码字段名的问题。

下面以查询酒店信息为例,演示如何使用条件构造器进行查询:

	@Test
	void contextLoads() {
		// 创建QueryWrapper对象,用于构建查询条件
		QueryWrapper<Hotel> queryWrapper = new QueryWrapper<>();
		
		// 设置查询条件
		queryWrapper
				.select("id", "name", "address") // 选择要查询的字段
				.eq("name", "吉果酒店"); // 指定酒店名称为“吉果酒店”
		System.out.println(hotelMapper.selectOne(queryWrapper));
	}

也就相当于

SELECT id,name,address FROM hotel WHERE name = '吉果酒店'

开启sql语句日志打印

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

输出

==>  Preparing: SELECT id,name,address FROM hotel WHERE (name = ?)
==> Parameters: 吉果酒店(String)
<==    Columns: id, name, address
<==        Row: 3, 吉果酒店, 太原市迎泽区万邦国际1205
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1cb44a2f]
Hotel(id=3, name=吉果酒店, address=太原市迎泽区万邦国际1205, rent=null)

可以看到,使用QueryWrapper可以灵活构造复杂查询条件,并自动生成sql语句。其中使用到了一些方法,在官方文档中都有详细介绍,所以不再介绍。

接下来演示如何使用条件构造器进行更新:

	@Test
	void UpdateWrapper(){
		UpdateWrapper<Hotel> updateWrapper = new UpdateWrapper<>();
		updateWrapper.eq("id",3)
				.set("name","彼岸酒店");
		System.out.println(hotelMapper.update(null, updateWrapper));
	}

也就相当于

UPDATE hotel SET name = '彼岸酒店' WHERE id = 3

输出

==>  Preparing: UPDATE hotel SET name=? WHERE (id = ?)
==> Parameters: 彼岸酒店(String), 3(Integer)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2ecdcfe3]
1

4. IService

IService 是 MyBatis-Plus 提供的一个通用 Service 层接口,它封装了常见的 CRUD 操作,包括插入、删除、查询和分页等。通过继承 IService 接口,可以快速实现对数据库的基本操作,同时保持代码的简洁性和可维护性。简单来说,我们的业务中,实际上很多时候也是一样的工作,都是去简单调用底层的Mapper做一个很简单的事情,那么能不能干脆把Service也给弄个模版,于是就有了Iservice。

IService 接口中的方法命名遵循了一定的规范,如 get 用于查询单行,remove 用于删除,list 用于查询集合,page 用于分页查询,这样可以避免与 Mapper 层的方法混淆。

下面以 HotelService 为例,演示如何使用 IService 接口进行 CRUD 操作:

  1. 创建 HotelService 接口,继承 IService 接口:
public interface IHotelService extends IService<Hotel> {
    //除了继承模版,我们也可以把它当成普通Service添加自己需要的方法

    //自定义的方法
    void test();
}
  1. 创建 HotelServiceImpl 实现类,继承 ServiceImpl 类,并实现 IHotelService 接口:
//需要继承ServiceImpl才能实现那些默认的CRUD方法
@Service
public class HotelServiceImpl extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {

    //实现自定义的方法
    @Override
    public void test() {
        baseMapper.selectById(1);
    }
}
  1. 测试输出
	@Resource
	IHotelService hotelService;

	@Test
	void testService(){
		System.out.println(hotelService.getById(3));
	}

输出

Hotel(id=3, name=彼岸酒店, address=太原市迎泽区万邦国际1205, rent=78.4)

关于 IService 接口的详细演示,官方文档已经提供了细致的说明,这里不再深入演示。

5. 代码生成器

MyBatis-Plus 提供了一个代码生成器,可以根据实体类生成 Mapper 接口、Model 类、Service 接口、ServiceImpl 类、Controller 类等代码,并自动完成相关配置。

使用代码生成器的步骤如下:

  1. 引入依赖:
		<dependency>
			<groupId>org.apache.velocity</groupId>
			<artifactId>velocity-engine-core</artifactId>
			<version>2.3</version>
		</dependency>
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-generator</artifactId>
			<version>3.5.9</version>
		</dependency>
  1. 编写自动生成器代码
	@Resource
	DataSource dataSource;

	@Test
	void contextLoads() {
		FastAutoGenerator.create(new DataSourceConfig.Builder( dataSource ))
				.globalConfig(builder -> {
					 builder.author("十一月") // 设置作者
					.outputDir("src/main/java"); // 指定输出目录
				})
				//打包设置,这里设置一下包名就行,注意跟我们项目包名设置为一致的
      			.packageConfig(builder -> builder.parent("com.demo.plus"))
				.strategyConfig(builder -> {
					//设置为所有Mapper添加@Mapper注解
					builder
							.mapperBuilder()
							.mapperAnnotation(Mapper.class)
							.build();
				})
				.execute();

	}
  1. 运行项目,代码生成器会自动生成相关代码,并自动完成相关配置。
    代码生成器.jpg

  2. 随便调用个方法,测试一下生成的代码

	@Test
	void test(){
		System.out.println("hotelService.list() = " + hotelService.list());
	}

输出

==>  Preparing: SELECT id,address,name,rent FROM hotel
==> Parameters: 
<==    Columns: id, address, name, rent
<==        Row: 3, 太原市迎泽区万邦国际1205, 彼岸酒店, 78.4
<==        Row: 4, 太原市迎泽区万邦国际2102, 红苹果酒店, 88.5
<==      Total: 2
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4d0b276e]
hotelService.list() = [Hotel{id = 3, address = 太原市迎泽区万邦国际1205, name = 彼岸酒店, rent = 78.4}, Hotel{id = 4, address = 太原市迎泽区万邦国际2102, name = 红苹果酒店, rent = 88.5}]