MyBatis-Plus 快速入门

介绍 

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

官网:MyBatis-Plus (baomidou.com)

1.引入MybatisPlus的起步依赖


MyBatisPlus官方提供了starter,其中集成了Mybatis和MybatisPlus的所有功能,并且实现了自动装配效果。
因此我们可以用MybatisPlus的starter代替Mybatis的starter:

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3.1</version>
</dependency>

2.定义Mapper
 

为了简化单表CRUD,MybatisPlus提供了一个基础的BaseMapper接口,其中已经实现了单表的CRUD:

因此我们自定义的Mapper只要实现了这个BaseMapper,就无需自己实现单表CRUD了。 修改mp-demo中的com.itheima.mp.mapper包下的UserMapper接口,让其继承BaseMapper

代码如下:

package com.itheima.mp.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.mp.domain.po.User;

public interface UserMapper extends BaseMapper<User> {
}

MybatisPlus就是根据PO实体的信息来推断出表的信息,从而生成SQL的。默认情况下:

  • MybatisPlus会把PO实体的类名驼峰转下划线作为表名

  • MybatisPlus会把PO实体的所有变量名驼峰转下划线作为表的字段名,并根据变量类型推断字段类型

  • MybatisPlus会把名为id的字段作为主键

但很多情况下,默认的实现与实际场景不符,因此MybatisPlus提供了一些注解便于我们声明表信息。

常见注解

@TableName

说明:

  • 描述:表名注解,标识实体类对应的表

  • 使用位置:实体类

示例:

@TableName("user")
public class User {
    private Long id;
    private String name;
}

TableName注解除了指定表名以外,还可以指定很多其它属性:

属性

类型

必须指定

默认值

描述

value

String

""

表名

schema

String

""

schema

keepGlobalPrefix

boolean

false

是否保持使用全局的 tablePrefix 的值(当全局 tablePrefix 生效时)

resultMap

String

""

xml 中 resultMap 的 id(用于满足特定类型的实体类对象绑定)

autoResultMap

boolean

false

是否自动构建 resultMap 并使用(如果设置 resultMap 则不会进行 resultMap 的自动构建与注入)

excludeProperty

String[]

{}

需要排除的属性名 @since 3.3.1

@TableId

说明:

  • 描述:主键注解,标识实体类中的主键字段

  • 使用位置:实体类的主键字段

示例:

 
@TableName("user")
public class User {
    @TableId
    private Long id;
    private String name;
}

TableId注解支持两个属性:

属性

类型

必须指定

默认值

描述

value

String

""

表名

type

Enum

IdType.NONE

指定主键类型

IdType支持的类型有:

描述

AUTO

数据库 ID 自增

NONE

无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)

INPUT

insert 前自行 set 主键值

ASSIGN_ID

分配 ID(主键类型为 Number(Long 和 Integer)或 String)(since 3.3.0),使用接口IdentifierGenerator的方法nextId(默认实现类为DefaultIdentifierGenerator雪花算法)

ASSIGN_UUID

分配 UUID,主键类型为 String(since 3.3.0),使用接口IdentifierGenerator的方法nextUUID(默认 default 方法)

ID_WORKER

分布式全局唯一 ID 长整型类型(please use ASSIGN_ID)

UUID

32 位 UUID 字符串(please use ASSIGN_UUID)

ID_WORKER_STR

分布式全局唯一 ID 字符串类型(please use ASSIGN_ID)

这里比较常见的有三种:

  • AUTO:利用数据库的id自增长

  • INPUT:手动生成id

  • ASSIGN_ID:雪花算法生成Long类型的全局唯一id,这是默认的ID策略

@TableField

说明:

描述:普通字段注解

示例:

 
@TableName("user")
public class User {
    @TableId
    private Long id;
    private String name;
    private Integer age;
    @TableField("isMarried")
    private Boolean isMarried;
    @TableField("concat")
    private String concat;
}

一般情况下我们并不需要给字段添加@TableField注解,一些特殊情况除外:

  • 成员变量名与数据库字段名不一致

  • 成员变量是以isXXX命名,按照JavaBean的规范,MybatisPlus识别字段时会把is去除,这就导致与数据库不符。

  • 成员变量名与数据库一致,但是与数据库的关键字冲突。使用@TableField注解给字段名添加转义字符:``

支持的其它属性如下:

属性

类型

必填

默认值

描述

value

String

""

数据库字段名

exist

boolean

true

是否为数据库表字段

condition

String

""

字段 where 实体查询比较条件,有值设置则按设置的值为准,没有则为默认全局的 %s=#{%s},参考(opens new window)

update

String

""

字段 update set 部分注入,例如:当在version字段上注解update="%s+1" 表示更新时会 set version=version+1 (该属性优先级高于 el 属性)

insertStrategy

Enum

FieldStrategy.DEFAULT

举例:NOT_NULL insert into table_a(<if test="columnProperty != null">column</if>) values (<if test="columnProperty != null">#{columnProperty}</if>)

updateStrategy

Enum

FieldStrategy.DEFAULT

举例:IGNORED update table_a set column=#{columnProperty}

whereStrategy

Enum

FieldStrategy.DEFAULT

举例:NOT_EMPTY where <if test="columnProperty != null and columnProperty!=''">column=#{columnProperty}</if>

fill

Enum

FieldFill.DEFAULT

字段自动填充策略

select

boolean

true

是否进行 select 查询

keepGlobalFormat

boolean

false

是否保持使用全局的 format 进行处理

jdbcType

JdbcType

JdbcType.UNDEFINED

JDBC 类型 (该默认值不代表会按照该值生效)

typeHandler

TypeHander

类型处理器 (该默认值不代表会按照该值生效)

numericScale

String

""

指定小数点后保留的位数

MybatisPlus是如何获取实现CRUD的数据库表信息的?
默认以类名驼峰转下划线作为表名
默认把名为id的字段作为主键
默认把变量名驼峰转下划线作为表的字段名MybatisPlus的常用注解有哪些?@TableName:指定表名称及全局配置@Tableld:指定id字段及相关配置@TableField:指定普通字段及相关配置
IdType的常见类型有哪些?AUTO、ASSIGN ID、INPUT使用@TableField的常见场景是?
成员变量名与数据库字段名不一致
成员变量名以is开头,且是布尔值
成员变量名与数据库关键字冲突
成员变量不是数据库字段 

常见配置

MybatisPlus也支持基于yaml文件的自定义配置,详见官方文档:

使用配置 | MyBatis-Plus (baomidou.com)

大多数的配置都有默认值,因此我们都无需配置。但还有一些是没有默认值的,例如:

  • 实体类的别名扫描包

  • 全局id类型

    mybatis-plus:
      type-aliases-package: com.itheima.mp.domain.po
      global-config:
        db-config:
          id-type: auto # 全局id类型为自增长

    需要注意的是,MyBatisPlus也支持手写SQL的,而mapper文件的读取地址可以自己配置:

mybatis-plus:
  mapper-locations: "classpath*:/mapper/**/*.xml" # Mapper.xml文件地址,当前这个是默认值。

可以看到默认值是classpath*:/mapper/**/*.xml,也就是说我们只要把mapper.xml文件放置这个目录下就一定会被加载。

核心功能

刚才的案例中都是以id为条件的简单CRUD,一些复杂条件的SQL语句就要用到一些更高级的功能了。

1.条件构造器

除了新增以外,修改、删除、查询的SQL语句都需要指定where条件。因此BaseMapper中提供的相关方法除了以id作为where条件以外,还支持更加复杂的where条件。

参数中的Wrapper就是条件构造的抽象类,其下有很多默认实现,继承关系如图: 

 

Wrapper的子类AbstractWrapper提供了where中包含的所有条件构造方法: 

而QueryWrapper在AbstractWrapper的基础上拓展了一个select方法,允许指定查询字段:

而UpdateWrapper在AbstractWrapper的基础上拓展了一个set方法,允许指定SQL中的SET部分:

QueryWrapper

无论是修改、删除、查询,都可以使用QueryWrapper来构建查询条件。接下来看一些例子: 查询:查询出名字中带o的,存款大于等于1000元的人。代码如下:

@Test
void testQueryWrapper() {
    // 1.构建查询条件 where name like "%o%" AND balance >= 1000
    QueryWrapper<User> wrapper = new QueryWrapper<User>()
            .select("id", "username", "info", "balance")
            .like("username", "o")
            .ge("balance", 1000);
    // 2.查询数据
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}

 更新:更新用户名为jack的用户的余额为2000,代码如下:

@Test
void testUpdateByQueryWrapper() {
    // 1.构建查询条件 where name = "Jack"
    QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("username", "Jack");
    // 2.更新数据,user中非null字段都会作为set语句
    User user = new User();
    user.setBalance(2000);
    userMapper.update(user, wrapper);
}

UpdateWrapper

基于BaseMapper中的update方法更新时只能直接赋值,对于一些复杂的需求就难以实现。 例如:更新id为1,2,4的用户的余额,扣200,对应的SQL应该是:

UPDATE user SET balance = balance - 200 WHERE id in (1, 2, 4)

SET的赋值结果是基于字段现有值的,这个时候就要利用UpdateWrapper中的setSql功能了: 

@Test
void testUpdateWrapper() {
    List<Long> ids = List.of(1L, 2L, 4L);
    // 1.生成SQL
    UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
            .setSql("balance = balance - 200") // SET balance = balance - 200
            .in("id", ids); // WHERE id in (1, 2, 4)
        // 2.更新,注意第一个参数可以给null,也就是不填更新字段和数据,
    // 而是基于UpdateWrapper中的setSQL来更新
    userMapper.update(null, wrapper);
}

LambdaQueryWrapper

无论是QueryWrapper还是UpdateWrapper在构造条件的时候都需要写死字段名称,会出现字符串魔法值。这在编程规范中显然是不推荐的。 那怎么样才能不写字段名,又能知道字段名呢?

其中一种办法是基于变量的gettter方法结合反射技术。因此我们只要将条件对应的字段的getter方法传递给MybatisPlus,它就能计算出对应的变量名了。而传递方法可以使用JDK8中的方法引用Lambda表达式。 因此MybatisPlus又提供了一套基于Lambda的Wrapper,包含两个:

  • LambdaQueryWrapper

  • LambdaUpdateWrapper

分别对应QueryWrapper和UpdateWrapper

其使用方式如下:

@Test
void testLambdaQueryWrapper() {
    // 1.构建条件 WHERE username LIKE "%o%" AND balance >= 1000
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.lambda()
            .select(User::getId, User::getUsername, User::getInfo, User::getBalance)
            .like(User::getUsername, "o")
            .ge(User::getBalance, 1000);
    // 2.查询
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}

2.自定义SQL

在演示UpdateWrapper的案例中,我们在代码中编写了更新的SQL语句:

这种写法在某些企业也是不允许的,因为SQL语句最好都维护在持久层,而不是业务层。就当前案例来说,由于条件是in语句,只能将SQL写在Mapper.xml文件,利用foreach来生成动态SQL。 这实在是太麻烦了。假如查询条件更复杂,动态SQL的编写也会更加复杂。

所以,MybatisPlus提供了自定义SQL功能,可以让我们利用Wrapper生成查询条件,再结合Mapper.xml编写SQL

基本用法

以当前案例来说,我们可以这样写:

@Test
void testCustomWrapper() {
    // 1.准备自定义查询条件
    List<Long> ids = List.of(1L, 2L, 4L);
    QueryWrapper<User> wrapper = new QueryWrapper<User>().in("id", ids);

    // 2.调用mapper的自定义方法,直接传递Wrapper
    userMapper.deductBalanceByIds(200, wrapper);
}

然后在UserMapper中自定义SQL:

package com.itheima.mp.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.mp.domain.po.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;
import org.apache.ibatis.annotations.Param;

public interface UserMapper extends BaseMapper<User> {
    @Select("UPDATE user SET balance = balance - #{money} ${ew.customSqlSegment}")
    void deductBalanceByIds(@Param("money") int money, @Param("ew") QueryWrapper<User> wrapper);
}

 这样就省去了编写复杂查询条件的烦恼了。

多表关联

理论上来讲MyBatisPlus是不支持多表查询的,不过我们可以利用Wrapper中自定义条件结合自定义SQL来实现多表查询的效果。 例如,我们要查询出所有收货地址在北京的并且用户id在1、2、4之中的用户 要是自己基于mybatis实现SQL,大概是这样的:

<select id="queryUserByIdAndAddr" resultType="com.itheima.mp.domain.po.User">
      SELECT *
      FROM user u
      INNER JOIN address a ON u.id = a.user_id
      WHERE u.id
      <foreach collection="ids" separator="," item="id" open="IN (" close=")">
          #{id}
      </foreach>
      AND a.city = #{city}
  </select>

可以看出其中最复杂的就是WHERE条件的编写,如果业务复杂一些,这里的SQL会更变态。

但是基于自定义SQL结合Wrapper的玩法,我们就可以利用Wrapper来构建查询条件,然后手写SELECT及FROM部分,实现多表查询。

查询条件这样来构建:

@Test
void testCustomJoinWrapper() {
    // 1.准备自定义查询条件
    QueryWrapper<User> wrapper = new QueryWrapper<User>()
            .in("u.id", List.of(1L, 2L, 4L))
            .eq("a.city", "北京");

    // 2.调用mapper的自定义方法
    List<User> users = userMapper.queryUserByWrapper(wrapper);

    users.forEach(System.out::println);
}

然后在UserMapper中自定义方法:

@Select("SELECT u.* FROM user u INNER JOIN address a ON u.id = a.user_id ${ew.customSqlSegment}")
List<User> queryUserByWrapper(@Param("ew")QueryWrapper<User> wrapper);

当然,也可以在UserMapper.xml中写SQL:

<select id="queryUserByIdAndAddr" resultType="com.itheima.mp.domain.po.User">
    SELECT * FROM user u INNER JOIN address a ON u.id = a.user_id ${ew.customSqlSegment}
</select>

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/415852.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

UE5 UE4 不同关卡使用Sequence动画

参考自&#xff1a;关于Datasmith导入流程 | 虚幻引擎文档 (unrealengine.com) 关卡中的Sequence动画序列&#xff0c;包含特定关卡中的Actor的引用。 将同一个Sequcen动画资源放入其他关卡&#xff0c;Sequence无法在新关卡中找到相同的Actor&#xff0c;导致报错。 Sequen…

成人年龄判断(个人学习笔记黑马学习)

结合前面学习的input输入语句&#xff0c;完成如下案例: 1.通过input语句&#xff0c;获取键盘输入&#xff0c;为变量age赋值。(注意转换成数字类型) 2.通过if判断是否是成年人&#xff0c;满足条件则输出提示信息&#xff0c;如下&#xff1a; 欢迎来到黑马儿童游乐场&#x…

上拉电阻与下拉电阻、电容的作用

上拉电阻与下拉电阻 在单片机电路中&#xff0c;上拉电阻和下拉电阻都是常见的电路元件&#xff0c;它们在数字电路设计中扮演着重要的角色。它们的作用如下&#xff1a; 1. **上拉电阻**&#xff1a; - **作用**&#xff1a;当一个引脚没有外部信号时&#xff0c;上拉电阻…

前端css、js、bootstrap、vue2.x、ajax查漏补缺(1)

学到的总是忘&#xff0c;遇到了就随手过来补一下 1.【JS】innerHTML innerHTML属性允许更改HTML元素的内容可以解析HTML标签 2.【CSS】display: none 设置元素不可见&#xff0c;不占空间&#xff0c;约等于将元素删除一样&#xff0c;只是源代码还存在 3.【CSS】行内样式 4.【…

加密与安全_深入了解哈希算法

文章目录 Pre概述哈希碰撞常用的哈希算法Codejava.security.MessageDigestMD5SHA-1SHA-256MessageDigest支持算法 哈希算法的用途彩虹表攻击基本原理攻击过程 防御彩虹表攻击基本原理用途 小结 Pre PKI - 01 散列(Hash)函数 概述 哈希算法&#xff08;Hash&#xff09;又称摘…

【探索AI】十一 深度学习之第1周:深度学习概述与基础

深度学习概述与基础 深度学习的发展历史与现状神经网络的基本原理前向传播与反向传播算法常见的激活函数与优化算法深度学习框架&#xff08;如TensorFlow或PyTorch&#xff09;进行基础操作 深度学习的发展历史与现状 深度学习的发展历史可以追溯到上世纪40年代&#xff0c;当…

H12-821_85

85.如图所示,三台一层交换机与一台 HUB 互联,交换机均开启 STP 功能,交换机 STP 的桥 ID 设置请参考下图,其它均是默认配置。以下描述中中错误的是哪一选项? A.SWA 的两个端口都为指定端口,处于转发状态。 B.SWC 的 G0/0/1口为 Alternate 端口,处于阻塞状态 C.SWA 的 G0/0/2…

第二周opencv

一、边缘检测算子 边缘检测算子是用于检测图像中物体边界的工具。边缘通常表示图像中灰度值或颜色发生显著变化的地方。边缘检测有助于识别图像中的物体形状、轮廓和结构。这些算子通过分析图像的灰度或颜色梯度来确定图像中的边缘。 1、Roberts 算子 通过局部差分计算检测边缘…

李沐动手学习深度学习——3.2练习

以下是个人理解&#xff0c;希望进行讨论求解。 练习 1. 如果我们将权重初始化为零&#xff0c;会发生什么。算法仍然有效吗&#xff1f; 根据SGD算法公式如上&#xff0c;第一次迭代的值可知w只与b相关&#xff0c;而对于b的迭代更新&#xff0c;只是与b的初始值相关&#x…

langchain加载模型时出现ConnectionError: (ProtocolError(‘Connection aborted.‘的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

使用vue把一周的时间划分为可以选择多个阶段

提示&#xff1a;使用vue把一周的时间划分为可以选择多个阶段段&#xff08;亲测有效&#xff09; 一、直接复制以下代码 代码如下&#xff08;示例&#xff09;&#xff1a; <template><div class"byted-weektime" mousedown"dian" mousemove&qu…

理解计算着色器中glsl语言的内置变量

概要 本文通过示例的方式&#xff0c;着重解释以下几个内置变量&#xff1a; gl_WorkGroupSizegl_NumWorkGroupsgl_LocalInvocationIDgl_WorkGroupIDgl_GlobalInvocationID 基本概念 局部工作组与工作项 一个3x2x1的局部工作组示例如下&#xff0c;每个小篮格子表示一个工作项…

Web前端---图层嵌套与层叠三行三列效果

1.图层的嵌套设计 <!doctype html> <html> <head> <meta charset"utf-8"> <title>图层嵌套</title><style type"text/css">.inline_div{display:inline-block;}#wrap{width400px;height250px;border:2px solid…

IO多路复用-select模型

IO多路复用&#xff08;IO Multiplexing&#xff09;是一种高效的网络编程模型&#xff0c;可以同时监控多个文件描述符&#xff08;包括套接字等&#xff09;&#xff0c;并在有数据可读或可写时进行通知。其中&#xff0c;select模型是最常用和最早引入的一种IO多路复用模型。…

09-认证-自研微服务框架

认证 1. 开启https支持 func (e *Engine) RunTLS(addr, certFile, keyFile string) {err : http.ListenAndServeTLS(addr, certFile, keyFile, e.Handler())if err ! nil {log.Fatal(err)} }1.1 测试 证书生成&#xff1a; 安装openssl 网站下载&#xff1a;http://slproweb…

k8s.gcr.io/pause:3.2镜像丢失解决

文章目录 前言错误信息临时解决推荐解决onetwo 前言 使用Kubernetes&#xff08;k8s&#xff09;时遇到了镜像拉取的问题&#xff0c;导致Pod沙盒创建失败。错误显示在尝试从k8s.gcr.io拉取pause:3.2镜像时遇到了超时问题&#xff0c;这通常是因为网络问题或者镜像仓库服务器的…

【Go语言】Go语言中的指针

Go语言中的指针 变量的本质是对一块内存空间的命名&#xff0c;我们可以通过引用变量名来使用这块内存空间存储的值&#xff0c;而指针则是用来指向这些变量值所在内存地址的值。 注&#xff1a;变量值所在内存地址的值不等于该内存地址存储的变量值。 Go语言中&#xff0c;…

[CSS]文字旁边的竖线以及布局知识

场景&#xff1a;文字前面常见加竖线。 .center-title { 常见内容color: #FFF;font-family: "Source Han Sans CN";font-size: 50px;font-style: normal;font-weight: 700;line-height: normal;position: relative; 要定位left: 16px; 这里是想拉开间距margin-b…

vmware中Numlock和caplock一直闪烁(更新时间24/2/28)

问题复现&#xff1a; 分析原因是&#xff1a;宿主机和vm虚拟机的这两个键未同步导致的异常 解决方法:将鼠标移动到点击虚拟机窗口以外的地方&#xff0c;按这两个键将其设置为打开状态即可解决

科技赋能,MTW400A为农村饮水安全打通“最后一公里”

日前&#xff0c;山东省政府纵深推进国家省级水网先导区建设&#xff0c;持续深化“水网”行动&#xff0c;着力构筑水安全保障网、水民生服务网、水生态保护网&#xff0c;建设水美乡村示范带、内河航运示范带、文旅融合示范带、绿色发展示范带&#xff0c;推动形成“三网四带…