跳转到主内容
极星编程网:以代码为星,赴技术山海!

MyBatis-Plus实用篇超完整教程

目录

一、基础组件

1、BaseMapper接口API

2、IService接口API

3、创建Service层操作数据

二、常用注解

1、@TableName

2、@TableId

2.1、value属性

2.2、type属性

3、@TableField

3.1、value属性

3.2、exist属性

3.3、select属性

3.4、condition属性

3.5、update属性

3.6、fill属性

3.7、insertStrategy、updateStrategy、whereStrategy属性

3.8、typeHandler属性

4、@TableLogic

三、条件构造器

1、wapper介绍

2、构造器常用方法

3、组装条件示例

四、扩展功能

1、链式查询与修改

2、静态工具类Db

4、自定义sql

4.1、自定义xml分页sql

4.2、@Select注解自定义sql

4.3、连表left join和inner join的分页查询区别

4.4、连表查询条件Wrapper和响应IPage的泛型

5、IPage的泛型转换(entity转换为vo)

6、SimpleQuery工具类

7、ActiveRecord

五、插件

1、Mybatis插件

2、MyBatisX插件

2.1、生成逆向工程

2.2、快速生成CRUD

一、基础组件

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

1、BaseMapper接口API

BaseMapper是MyBatis-Plus提供的模板mapper,其中包含了基本的CRUD方法,泛型为操作的实体类型Mapper继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能BaseMapper接口,增删改返回影响数据条数的Integer

BaseMapper中提供的CRUD方法增加:insert

删除:delete

修改:update update方法:entity实体对象某属性为null,则不会修改此属性

查询:select

selectObjs方法:只返回第一个字段的值selectPage方法:分页需要添加分页插件,否则不生效

2、IService接口API

封装IService接口,进一步封装CRUD采用get查询单行、remove删除、list查询集合、page分页前缀命名方式IService接口,增删改返回是否操作成功的boolean泛型 T 为任意实体对象

IService中提供的CRUD方法增加:save

删除:remove

修改:update

新增或修改:主键存在则根据主键修改,主键不存在则新增

查询:单个get,集合listgetOne方法,多个抛出异常,第二个参数throwEx设置为false则获取第一条数据

查询记录数:count

分页查询:page

集合查询:listlistObjs方法,可以将查询结果T类型转换成V类型返回List

3、创建Service层操作数据

Mapper接口:创建UserMapper对象并继承BaseMapper@Mapper public interface UserMapper extends BaseMapper {}Service接口:创建UserService并继承IService/*** UserService继承IService模板提供的基础功能*/ public interface UserService extends IService {}Service实现类:创建UserService的实现类并继承ServiceImpl/*** ServiceImpl实现了IService,提供了IService中基础功能的实现* 若ServiceImpl无法满足业务需求,则可以使用自定的UserService定义方法,并在实现类中实现*/@Service public class UserServiceImpl extends ServiceImpl implements UserService{}

二、常用注解

1、@TableName

在实体类类型上添加@TableName(“t_user”),标识实体类对应的表不加注解,默认驼峰转下划线则是表明,如实体OrderInfo,则默认对应表名order_info@Data@TableName("t_user") public class User {private Long id;private String name;private Integer age;private String email;}为实体类所对应的表名设置默认的前缀,那么就不需要在每个实体类上通过@TableName标识实体类对应的表mybatis-plus:global-config:db-config:# 设置实体类所对应的表的统一前缀table-prefix: t_

2、@TableId

2.1、value属性

不加注解,默认情况Long id就是主键如果想其他字段作为主键,且实体与数据库字段不一致,则添加@TableId(value = “tid”)@Data public class User {@TableId(value = "tid") private Long uid;private String name;private Integer age;private String email;}

2.2、type属性

常用主键策略:

枚举值描述IdType.AUTO数据库ID自增;该类型请确保数据库设置了ID自增,否则无效IdType.INPUT用户输入ID;该类型可以通过自己注册自动填充插件进行填充IdType.ASSIGN_ID(默认)主键类型为number或string的雪花算法;只有当插入对象ID 为空,才自动填充IdType.ASSIGN_UUID主键类型为string,UUID.replace(“-”,“”);只有当插入对象ID 为空,才自动填充IdType.NONE没有设置主键类型;跟随全局;全局的主键策略如果没有设置,默认是雪花算法@Data public class User {@TableId(type = IdType.AUTO) private Long uid;private String name;private Integer age;private String email;}Mybatis-plus中的内置雪花算法在某些情况下,我们想提前获取这个ID,调用com.baomidou.mybatisplus.core.toolkit.IdWorker.getId()方法即可

3、@TableField

3.1、value属性

解决对象中字段名和数据库不匹配(没有遵循小驼峰或者完全不匹配)

//指定数据库字段名称@TableField(value = "email") private string mail解决关键字报错// 执行的sql语句就变成`关键字`,这样sql就不会报错了@TableField(value = "`desc`") private string desc

3.2、exist属性

解决对象中的属性字段在表中不存在的问题@TableField(exist = false) private Boolean checked;

3.3、select属性

在查询操作中某个字段值不想被查询展示出来(比如密码password),可使用此注解@TableField(select = false) private String password;

3.4、condition属性

预处理where查询条件// 实体注解-实现模糊查询@TableField(condition = SqlCondition.LIKE) private String name;// 业务代码User user = new User();user.setName("张");userService.lambdaQuery(user).list();// 打印sql SELECT id,user_name,age FROM user WHERE name LIKE CONCAT('%',?,'%')链式查询不生效(没有模糊查询)

userService.lambdaQuery().eq(User::getName,"张").list();

3.5、update属性

预处理update set 部分方式一:@TableField(update="%s+1") update 表 set 字段=字段+1 where …

方式二:@TableField(update="now()") 使用数据库时间,update 表 set 字段=now() where …

// 方式三:// 实体注解-实现修改字段时候,前后添加“**” 其中 %s 会填充为字段@TableField(update = "CONCAT('**',%s,'**')") private String name;// 业务代码User user = new User();user.setId(1);user.setName("张三");userService.updateById(user);// 打印sql UPDATE user SET name=CONCAT('**',name,'**') WHERE id=?链式查询不生效(前后没有填充**)

userService.lambdaQuery().eq(User::getName,"张").list();

3.6、fill属性

解决每个数据库表都有create_time 和 update_time字段,我们可以使用自动填充功能维护这两个字段// 实体字段注解@TableField(fill = FieldFill.INSERT) private Date createTime;@TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime;// 枚举类public enum FieldFill {/*** 默认不处理*/ DEFAULT,/*** 插入填充字段*/ INSERT,/*** 更新填充字段*/ UPDATE,/*** 插入和更新填充字段*/ INSERT_UPDATE}创建MyMetaObjectHandler配置类,实现MetaObjectHandler接口@Component public class MyMetaObjectHandler implements MetaObjectHandler {Date date = new Date();@Override public void insertFill(MetaObject metaObject) {this.strictInsertFill(metaObject, "createTime", Date.class, date);this.strictInsertFill(metaObject, "updateTime", Date.class, date);}@Override public void updateFill(MetaObject metaObject) {this.strictUpdateFill(metaObject, "updateTime", Date.class, date);}}3.7、insertStrategy、updateStrategy、whereStrategy属性insertStrategy:当insert操作时,该字段拼接insert语句时的策略updateStrategy:当更新操作时,该字段拼接set语句时的策略whereStrategy:表示该字段在拼接where条件时的策略public enum FieldStrategy {/*** 忽略判断,所有字段都进行更新和插入* 无论什么值,直接拼接,没有值,则拼接null*/ IGNORED,/*** 只更新和插入非NULL值* 相当于添加if判断,不为null才操作* column=#{columnProperty}*/ NOT_NULL,/*** 只更新和插入非NULL值且非空字符串* 相当于添加if判断,不为null而且不是空字符串才操作* column=#{columnProperty}*/ NOT_EMPTY,/*** 默认NOT_NULL*/ DEFAULT,/*** 永远不进行更新和插入*/ NEVER}

3.8、typeHandler属性

类型处理器,设置存入数据库的类型设置mysql数据库字段info为json类型,@TableField(typeHandler = JacksonTypeHandler.class)表示将UserInfo对象转为json对象入库此时user出现对象嵌套List

,需要设置resultMap响应对象,@TableName(autoResultMap = true)表示自动映射resultMap

4、@TableLogic

物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除的数据逻辑删除:假删除,将对应数据中代表是否被删除字段的状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录实现逻辑删除数据库中创建逻辑删除状态列,设置默认值为0

实体类中添加逻辑删除属性

测试删除功能,真正执行的是修改public void testDeleteById(){int result = userMapper.deleteById(1527472864163348482L);System.out.println(result > 0 ? "删除成功!" : "删除失败!");System.out.println("受影响的行数为:" + result);}

此时执行查询方法,查询的结果为自动添加条件is_deleted=0public void testSelectList(){List users = userMapper.selectList(null);}

三、条件构造器

1、wapper介绍

Wrapper:条件构造抽象类,最顶端父类AbstractWrapper:用于查询条件封装,生成sql的where条件QueryWrapper:查询条件封装UpdateWrapper:update条件封装AbstractLambdaWrapper:使用Lambda语法LambdaQueryWrapper:用于Lambda语法使用的查询Wrapper LambdaUpdateWrapper:用于Lambda语法更新Wrapper

2、构造器常用方法

函数名说明说明/例子eq等于=例:eq(“name”,“zhangsan”) —> name = ‘zhangsan’ne不等于<>例:ne(“name”,“zhangsan”) —> name <> ‘zhangsan’gt大于>例:gt(“age”,18) —> age > 18 ge大于等于>=例:ge(“age”,18) —> age >= 18 lt小于<例:lt(“age”,18) —> age < 18 le小于等于<=例:le(“age”,18) —> age <= 18 between between 值1 and 值2例:between(“age”,10,20) —> age between 10 and 20 notBetween not between 值1 and 值2例:notBetween(“age”,10,20) —> age not between 10 and 20 like like ‘%值%’例:like(“name”,“强”) —> name like ‘%强%’notLike not like ‘%值%’例:notLike(“name”,“强”) —> name not like ‘%强%’likeLeft like ‘%值’例:like(“name”,“飞”) —> name like ‘%强’likeRight like ‘值%’例:like(“name”,“王”) —> name like ‘王%’isNull字段 is null例:isNull(“emal”) —> email is null isNotNull字段 is not null例:isNotNull(“emal”) —> email is not null in字段 in (值1,值2…)例:in(“age”,{10,18,30}) —> age in (10,18,30) notIn字段 not in (值1,值2…)例:notIn(“age”,{10,18,30}) —> age not in (10,18,30) inSql字段 in ( sql语句 ) inSql(“id”, “select id from table where name like ‘%J%’”)

—> id in (select id from table where name like ‘%J%’) notInSql字段 not in ( sql语句 ) notInSql(“id”, “select id from table where name like ‘%J%’”)

—> id not in (select id from table where name like ‘%J%’) groupBy分组 group by 字段,…

例:groupBy(“id”,“name”) —> group by id,name

⚠️注意:如果不查询聚合函数,默认返回第一条数据orderBy排序 ordery by 字段,…

例:orderBy(true,true,“id”,“name”) —> order by id asc,name asc orderByAsc升排序 order by 字段,… asc例:orderByAsc(“id”,“name”) —> order by id,name orderByDesc降排序 order by 字段,… desc例:orderByDesc(“id”,“name”) —> order by id desc,name desc having having (sql语句) having(“sum(age) > {0}”,18) —> having sum(age) > 18 or拼接or例:eq(“id”,1).or().eq(“name”,“老王”) —> id =1 or name = ‘老王’and and 嵌套例:and(i -> i.eq(“name”,“李白”).ne(“status”,“活着”))

—> and (name = ’李白‘ and status <> ‘活着’) apply拼接sql**例:apply(“date_format(date_time,‘%Y-%m-%d’) = {0}”,“2002-08-08”)

—> date_fromat(date_time,‘%Y-%m-%d’) = ‘2008-08-08’

⚠️注意:动态入参对应{index}部分,直接写sql语句,有sql注入风险last拼接到sql的最后例:last(“limit 5”)

注意事项:只能调用一次,多次调用以最后一次为准,有sql注入风险exists拼接exists (sql语句)例:exists(“select id from table where age = 1”) not exists拼接not exists (sql语句)例:not exists(“select id from table where age = 1”) nested正常嵌套 不带and和or例:nested(i-> i.eq(“name”,“李华”).gt(“age”,20))

—> (name = “李华” and age > 20)

3、组装条件示例

条件的优先级sql语句规则:and和or一起用,and优先级高 /** 组装修改条件 将(年龄大于20并且用户名中包含有a)或邮箱为null的用户信息修改 **/@Test public void test01() {/*UPDATE t_user SET user_name=?, email=?age > ? AND user_name LIKE ? OR email IS NOT NULL*/ QueryWrapper queryWrapper = new QueryWrapper<>();queryWrapper.gt("age", 20).like("user_name", "a").or().isNotNull("email");User user = new User();user.setName("小明");user.setEmail("test@atguigu.com");int result = userMapper.update(user, queryWrapper);System.out.println("result = " + result);}/** 条件优先级 将用户名中包含a并且(年龄大于20或邮箱为null)的用户信息修改 **/@Test public void test02() {// lambda中条件优先级/*UPDATE t_user SET user_name=?, email=?WHERE user_name LIKE ? AND (age > ? OR email IS NULL)*/ QueryWrapper queryWrapper = new QueryWrapper<>();queryWrapper.like("user_name", "a").and(i -> i.gt("age", 20).or().isNull("email"));User user = new User();user.setName("小红");user.setEmail("test@atguigu.com");int result = userMapper.update(user, queryWrapper);System.out.println("result = " + result);}Lambda表达式@Test public void test3() {/*SELECT uid AS id,user_name AS name,age,email,is_deleted FROM t_user WHERE user_name LIKE ? AND age <= ?*/ String username = "a";Integer ageBegin = null;Integer ageEnd = 30;//组装set子句LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();//避免使用字符串表示字段,防止运行时错误queryWrapper.like(StringUtils.isNotBlank(username), User::getName, username).gt(ageBegin != null, User::getAge, ageBegin).le(ageEnd != null, User::getAge, ageEnd);List list = userMapper.selectList(queryWrapper);list.forEach(System.out::println);}@Test public void test4() {/*UPDATE t_user SET user_name=?,email=?WHERE user_name LIKE ? AND (age > ? OR email IS NULL)*/ LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>();updateWrapper.like(User::getName, "a")//lambda表达式内的逻辑优先运算.and(i -> i.gt(User::getAge, 20).or().isNull(User::getEmail));updateWrapper.set(User::getName, "小黑").set(User::getEmail, "abc@atguigu.com");int result = userMapper.update(null, updateWrapper);System.out.println("result = " + result);}

四、扩展功能

1、链式查询与修改

IService接口中提供链式查询:可以串着写多个条件.one()结尾:查询单条数据.list()结尾:查询多条数据.count()结尾:查询记录数

@Test public void test1(){// 单个查询,多个报错User user = userService.lambdaQuery().eq(User::getName, "Tom").one();// 集合查询List userList = userService.lambdaQuery().like(User::getName, "J").eq(User::getAge,20).list();// 记录数查询Integer count = userService.lambdaQuery().like(User::getAge, 20).select(User::getName, User::getAge).count();}链式修改:可以串着写多个条件.update()结尾:修改set的字段(只修改set的字段,fill属性更新填充属性不会修改)

.update(entity)结尾:修改entity数据的字段.remove()结尾:删除数据

@Test public void test2() {// set修改某些属性userService.lambdaUpdate().eq(User::getName, "Tom").set(User::getId, 110).set(User::getAge, 15).update();// 修改整个对象User user = new User();user.setId(110L);user.setAge(15);userService.lambdaUpdate().eq(User::getName, "Tom").update(user);// 根据名称删除userService.lambdaUpdate().eq(User::getName, "Jack").remove();}

2、静态工具类Db

Service之间也会相互调用,为了避免出现循环依赖问题,MybatisPlus提供一个静态工具类:Db

@Test void testDbGet() {User user = Db.getById(1L, User.class);System.out.println(user);}​@Test void testDbList() {// 利用Db实现复杂条件查询List list = Db.lambdaQuery(User.class).like(User::getUsername, "o").ge(User::getBalance, 1000).list();}​@Test void testDbUpdate() {Db.lambdaUpdate(User.class).set(User::getBalance, 2000).eq(User::getUsername, "Rose");}这样就可以在任意serviceImpl中都可以使用任意的service,而不需要依赖注入了

4、自定义sql

4.1、自定义xml分页sql

UserMapper中定义接口方法page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,必须放在第一位/*** 根据年龄查询用户列表,分页显示*/ Page selectPageVo(@Param("page") Page page,@Param("age") Integer age);UserMapper.xml中编写SQL测试方法xml中没有分页语句,mybatisplus自动分页前提必须有分页插件,否则没有分页效果@Test public void testSelectPageVo(){ //设置分页参数Page page = new Page<>(1, 5);userMapper.selectPageVo(page, 20);//获取分页数据List list = page.getRecords();list.forEach(System.out::println);System.out.println("当前页:"+page.getCurrent());System.out.println("每页显示的条数:"+page.getSize());System.out.println("总记录数:"+page.getTotal());System.out.println("总页数:"+page.getPages());System.out.println("是否有上一页:"+page.hasPrevious());System.out.println("是否有下一页:"+page.hasNext());}

4.2、@Select注解自定义sql

普通sql@Select("select * from student where age = #{age} and user_name = #{userName}") Student getStudent( @Param("age") Integer age, @Param("userName") String userName);带Wrapper条件sql@Param("ew"):固定写法,可以参照BaseMapper接口里的方法${ew.customSqlSegment}:固定写法,在sql后面拼接条件,不需要写where关键字@Param("page") Page page:Wrapper条件和分页可以共用,但是分页需要写在最前面// 单表查询@Select("select * from student ${ew.customSqlSegment}") Student getStudentById( @Param("ew") QueryWrapper wrapper);// 多表查询@Select("SELECT u.* FROM student u INNER JOIN address a ON u.id = a.student_id ${ew.customSqlSegment}") List queryStudentListByWrapper(@Param("page") Page page, @Param("ew")QueryWrapper wrapper);4.3、连表left join和inner join的分页查询区别left join查询通过Page page参数自动分页时候,查询总条数COUNT(*)时候会将left join去掉,也就是只查主表的数据此时如果有副表的字段作为查询条件会报错:Unknown column ‘xxx’ in ‘where clause’(主表找不到xxx字段)

// 情况1:如果查询条件中有address的字段,比如模糊查询地址名称// 情况2:如果查询条件的字段,两个表都有,这里会有问题// 自动分页的查询count(*)时候,就会抛出以上异常@Select("SELECT u.* FROM student u LEFT JOIN address a ON u.id = a.student_id ${ew.customSqlSegment}") List queryStudentListByWrapper(@Param("page") Page page, @Param("ew")QueryWrapper wrapper);解决方式:嵌套一层select * from (连表复杂sql)as result ${ew.customSqlSegment}@Select("SELECT * FROM (SELECT u.* FROM student u LEFT JOIN address a ON u.id = a.student_id) as result ${ew.customSqlSegment}") List queryStudentListByWrapper(@Param("page") Page page, @Param("ew")QueryWrapper wrapper);inner join查询通过Page page参数自动分页时候,查询总条数COUNT(*)时候不会将inner join去掉,所以一般不需要嵌套

4.4、连表查询条件Wrapper和响应IPage的泛型

查询条件Wrapper和响应IPage的泛型非表实体也行,只有符合数据库字段的驼峰命名即可(需要创建实体对象)

如果查询条件同时需要多个表字段,也可以@Param("ew") Wrapper queryWrapper(不需要创建实体对象)

5、IPage的泛型转换(entity转换为vo)

// 分页查询Page userPage = super.lambdaQuery().page(new Page<>(request.getCurrent(), request.getSize()));// entity转换为vo IPage infoResPage = userPage.convert(item -> Convert.convert(UserVO.class, item));

6、SimpleQuery工具类

Simplequery可以对selectList查询后的结果用stream流进行了一些封装,使其可以返回一些指定结果,简洁api的调用示例获取Student对象集合的名字集合// 方式一List nameList = SimpleQuery.list(new LambdaQueryWrapper().like(Student::getUserName, "张"),Student::getUserName);// 方式二List list = studentService.list();List nameList2 = SimpleQuery.list2List(list, Student::getUserName);先将Student对象的名称前后添加**再获取名字集合List nameList = SimpleQuery.list(new LambdaQueryWrapper().like(Student::getUserName, "李"),Student::getUserName,new Consumer() {@Override public void accept(Student student) {student.setUserName("**"+student.getUserName()+"**");}});返回key=名字,value=年龄的mapMap map = SimpleQuery.map(new LambdaQueryWrapper<>(),Student::getUserName,Student::getAge);group分组,key位名字,value相同名字的Student对象集合Map> group = SimpleQuery.group(new LambdaQueryWrapper(),Student::getUserName);

7、ActiveRecord

ActiveRecord(活动记录,简称 AR),是一种领域模型模式,特点是一个模型类对应关系型数据库中的一个表,而模型类的一个实例对应表中的一行记录ActiveRecord 一直广受解释型动态语言( PHP 、 Ruby 等)的喜爱,通过围绕一个数据对象进行CRUD操作仅仅需要让实体类继承Model类且实现主键指定方法@Data@TableName(value = "student") public class Student extends Model {/*** 主键id*/ private Long id;/*** 姓名*/ private String userName;/*** 年龄*/ private Integer age;}@Test void activeRecordAddQ{Student student = new Student();student.setUserName("李四");student.setAge(12);student.insert();}Model抽象类方法

注意: 必须存在对应的原始mapper并继承baseMapper并且可以使用

五、插件

1、Mybatis插件

MybatisPlusInterceptor分页插件@Configuration public class MybatisPlusConfig {@Bean public MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();//注意使用哪种数据库interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return interceptor;}}

2、MyBatisX插件

2.1、生成逆向工程

找到我们需要生成的表点击右键

填写完信息以后下一步

继续填写信息

结果展示

2.2、快速生成CRUD

MyBaitsX可以根据我们在Mapper接口中输入的方法名【alt+Enter】快速帮我们生成对应的sql语句

到此这篇关于MyBatis-Plus实用篇超完整教程的文章就介绍到这了,更多相关MyBatis-Plus实用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:Spring Boot 中整合 MyBatis-Plus详细步骤(最新推荐) Mybatis-plus对单表操作的封装实现MyBatis-Plus忽略多租户隔离自定义注解MyBatis-Plus流式查询的实现示例SpringBoot+MyBatis-Plus实现分页的项目实践

相关文章