Mybatis-plus

本质上是对于Mybatis的增强,不会对Mybatis原有的功能进行改变。首先实现快速的在SpringBoot中使用Mybatis-plus,第一步导入的依赖,然后根据数据库的实际情况写好pojo和mapper接口

  1. <dependency>
  2. <groupId>com.baomidou</groupId>
  3. <artifactId>mybatis-plus-boot-starter</artifactId>
  4. <version>3.5.1</version>
  5. </dependency>

配置好后,按照Mybatis的做法是面向接口编程,在Mybatis-plus中框架为我们提供了对于单表操作的接口,且已经为我们实现了对应的接口编程,所以如果单独的对一个表中数据进行操作,那么只需要我们当前的接口继承BaseMapper接口,并为这个接口指定要操作的pojo类型就可以了,代码如下

  1. package com.zjl.mybatisplus.mapper;
  2. import com.baomidou.mybatisplus.core.conditions.Wrapper;
  3. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  4. import com.zjl.mybatisplus.pojo.user;
  5. import org.springframework.stereotype.Repository;
  6. import java.util.List;
  7. @Repository
  8. //注册了个接口当作bean???
  9. public interface usermapper extends BaseMapper<user> {
  10. //接口继承了接口,方法里面的内容是在哪里写的?
  11. //如果是Mybatis是写在mapper.xml中的,这里是不是basemapper已经写完了这些?我们既然继承了接口,也就继承了xml?
  12. List<user> selectuserbyname(String name);
  13. }

注意我们在这里是不用去重写任何父类方法,但是如果有其他的操作,则需要在当前接口编写,按照Mybatis 的方法进行xml文件配置就行了,下面展示如何进行自定义方法的面向接口编程;

首先在resources文件写创建一个mapper文件夹,这是Springboot中默认Mybatis-plus中的mapper.xml文件所在的路径,然后在配置xml文件时,一定要确保.xml和.java文件的名称是一样的,其他按照Mybatis的书写规则进行编写就行了。

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace="com.zjl.mybatisplus.mapper.usermapper">
  6. <!-- List<user> selectuserbyname();-->
  7. <select id="selectuserbyname" resultType="com.zjl.mybatisplus.pojo.user">
  8. select * from user where name = #{name}
  9. </select>
  10. </mapper>

因为是在Spring框架中,所以我们需要做一件事,就是在启动类上添加注释MapperScan(“包名”),虽然不知道这样的用意,但是大致猜测,只有这么写,才能够精准的定为到指定的mapper吧,因为并没有声明为什么mapper的注释,所以不知道扫描的意义

在测试类中我们可以通过bean 的自动装配来获得mapper对象,然后直接调用其方法就能完成数据库的相应操作了;

  1. package com.zjl.mybatisplus;
  2. import com.zjl.mybatisplus.mapper.usermapper;
  3. import com.zjl.mybatisplus.pojo.user;
  4. import org.junit.jupiter.api.Test;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.boot.test.context.SpringBootTest;
  7. import java.util.List;
  8. @SpringBootTest
  9. class MybatisplusApplicationTests {
  10. @Autowired
  11. //这里是自动装配了,我能想到,但是Spring 可以装配接口???
  12. com.zjl.mybatisplus.mapper.usermapper usermapper;
  13. @Test
  14. void contextLoads() {
  15. List<user> users = usermapper.selectList(null);
  16. for (user user : users) {
  17. System.out.println(user);
  18. }
  19. }
  20. @Test
  21. public void diysql(){
  22. List<user> jack = usermapper.selectuserbyname("jack");
  23. for (user user : jack) {
  24. System.out.println(user);
  25. }
  26. }
  27. }

BaseMapper也自带了一系列的增删改查功能,不过都是单表的;这里也就不进行一一列举了。

主键问题,如果说我们当前的pojo类所对应的数据库的主键属性名并不是id的情况下,(Mybatis-plus是不能够直接知道主键是什么的,所以一般默认为id这个属性名作为数据库的主键。)我们需要手动去声明pojo类中的哪个属性是对应的主键。相应的注释是@TableId;这个注释还有2个属性值,1个是value 这是用来调配数据库中字段和属性名的映射的,而TableId是用来调配BaseMapper和pojo中属性名的映射的;另一个是type属性,这个属性的用处是来设置主键的自增,如果我们不去设置那就会默认的使用雪花算法给我们传入主键值,但是如果我们自己去设置成auto ,那就会以自增的方式来处理主键。

对于其他的pojo类的属性如果和数据库中字段不能够一一对应,需要使用@TableField的注解来进行声明。

好心的Myabtis-plus不仅写了mapper层,还完善了service层,在这层官方定义了一个Iservice接口 ,然后官方实现了一个service类。如果我们想使用官方的service,但是同时还想自己写点东西出来,那么就可以同时实现这个接口,然后继承这个官方的类(妈的,真烦啊)

  1. @Service
  2. public class userserviceImpl extends ServiceImpl<usermapper,user> implements userservice{
  3. //属于是不用在声明mapper了?
  4. @Override
  5. public int countnums() {
  6. return baseMapper.selectList(null).size();
  7. }
  8. }

在这个过程中,我们也传入了service层需要的mapper实体类,在ServiceImpl的底层我们可以看到

它会自动装配我们的mapper ,所以我们可以直接拿来使用。比如调用我们usermapper中的一些自定义或者官方配置的方法等等;系统自带的方法就不说了,用的上再说!

同时发现了一个问题,MP并不会把我们通过Mybatis.xml配置的pojo 的属性名与字段进行匹配,我们还需要通过resultmap来配置,这也很正常。(我能理解);

@TableLogic 这是一个逻辑删除的注释,使用它,在MP进行查询,只会去查询逻辑上存在的,会自动的添加where方法进行查询;

  1. @TableLogic(value = "yes" , delval = "no")
  2. private String readstatue;

例如上面的代码是在说,我们只查询readstatue中状态是yes的数据;

wrapper

什么是wrapper,其实就是MP中用来传递where条件的一个类,他下面有4个子类,我这里主要围绕2个展开描述,分别是QueryWrapper和UpdateWrapper 其实他们就是用来去写sql语句后面的where 语句的

乐观锁

这里需要题一个@Version注解,它就是MP中的乐观锁设置,我们通过他可以完成灾多线程中的一些读取数据的处理,举个例子,我在飞猪上买机票时,在支付宝和客户端同时进行机票的查看,然后在支付宝发起了订单提交,但是在飞猪上进行的支付,这样我在回到支付宝进行支付是不失败的,这就是乐观锁。在每次查看数据的时候,会带着这个version数据,当我们对这个对象进行提交操作的时候,就会返回这个version ,并对他进行修改,所以即便是有另一个还可以进行提交修改的请求,也是不被允许的,因为version是不能够对应的上得,这就是乐观锁;
提起乐观锁,我们必须先提一提MP中的BaseMapper中自带的查询和更新方法
select方法
image.png
例如selectById,selectList,selectPage这些都是比较常用的参数一般就是个比较器,这里主要说说这个selectByMap 和 selectBatchIds。

  1. //这个方法的功能就是sql中的in,我们通过集合的方式把ids传给MP,它
  2. //会自动帮我们组装好sql,具体的sql如下:
  3. /*
  4. **==> Preparing: SELECT uuid AS tid,name,age,email,readstatue,version FROM user WHERE uuid IN ( ? , ? , ? ) AND readstatue='yes'
  5. **==> Parameters: 1(Integer), 3(Integer), 2(Integer)
  6. */
  7. @Test
  8. public void lock(){
  9. ArrayList<Integer> ids = new ArrayList();
  10. ids.add(1);
  11. ids.add(3);
  12. ids.add(2);
  13. List<user> users = usermapper.selectBatchIds(ids);
  1. //这就是利用了map的特性,key作为字段,value作为要查询的值,所有关系也能保证是and的关系,所以想查什么
  2. //直接按照把条件写入map,就可以了,不需要在新建什么user对象;
  3. @Test
  4. public void selectbymap(){
  5. HashMap<String, Object> map = new HashMap<>();
  6. map.put("name","杨过");
  7. map.put("age",18);
  8. usermapper.selectByMap(map);
  9. }

QueryWrapper

这个类就是用来设置我们的MP中的查询语句的,相当于where后面的部分,内置了很多条件判断语句;
1.alleq( ) 这个方法中我们需要传入的是map集合,相当于我们在sql后面加上了where k = v and k = v这样的语句
2.eq( ) 传入的参数是数据库中的字段和我们查询的值,也相当于加上了where K = V 和 eq 类似的方法还有
ne (<>) gt(>=) ge(<=) lt(>) le(<)
3.between notbetween 这样的使用方法都类似与传入K V 的方式,按照合理的方式去思考不难知道如何使用 4.like( ) notlike( ) ,在使用模糊查询的时候,我们不需要手动的传入%%,只需要传入自己想得到的参数就行。
5.insql( ) 这是一个子查询的操作,我们在insql中传入2个参数 ,第一个参数是字段,另一个参数是对应的子查询语句;
6.condition 在使用上述的众多方法中,我们会注意到有些方法可以传入一个boolean类型的数据,其实这个设定就类似于动态sql,我们可以对注入的字段进行判定,根据是否满足条件在写入成sql

  1. @Test
  2. public void condition(){
  3. String name = "杨过";
  4. int age = 18;
  5. String email = "";
  6. QueryWrapper<user> userQueryWrapper = new QueryWrapper<>();
  7. userQueryWrapper.eq(!StringUtils.isBlank(name),"name",name).eq(age>=0,"age",age);
  8. List<user> users = usermapper.selectList(userQueryWrapper);
  9. }

在MP中的具体实现:

数据库

在MP中我发现了我们在数据库中建立表格的时候,一般还要有4个表,分别是 create_time update_time is_delete ,以及version 这四个字段会帮助我们更好的设计程序,拓展更多的功能

代码实现:

MP中的自动补全代码

MP中的分页插件?

众所周知,在业务逻辑中分页的操作是必不可少的,所以我们在MP中进行单个表格的查询的时候,可以直接利用MP中的分页插件进行代码的编写
具体的实现流程:核心要素只有一个就是MP中的拦截器:
1.配置一个config类,然后在配置类中注册一个bean , 这个bean就是MybatisPlus 的 拦截器,具体代码如下:
当然我们可以在这个拦截器中配置其他的插件,这样我们就完成了拦截器的配置,这样配置完毕全局变量之后,就可以在BaseMapper中的自带的查询中进行分页了;

  1. package com.zjl.mybatisplus.config;
  2. import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
  3. import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
  4. import org.mybatis.spring.annotation.MapperScan;
  5. import org.springframework.context.annotation.Bean;
  6. import org.springframework.context.annotation.Configuration;
  7. @Configuration
  8. @MapperScan("com/zjl/mybatisplus/mapper")
  9. public class mpconfig {
  10. @Bean
  11. MybatisPlusInterceptor mybatisPlusInterceptor(){
  12. MybatisPlusInterceptor Interceptor = new MybatisPlusInterceptor();
  13. Interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
  14. return Interceptor;
  15. }
  16. }

2.Test中的代码如下所示:

  1. @Test
  2. public void page(){
  3. //经过测试这个userpage 和 userpage1 是一个对象;
  4. Page<user> userPage = new Page<>(1,3);
  5. Page<user> userPage1 = usermapper.selectPage(userPage, null);
  6. System.out.println(userPage1.getSize());
  7. System.out.println(userPage1.getRecords());
  8. }

2.我们不仅仅可以在BaseMapper中自带的方法中使用分页插件,也可以在自定义的方法中使用分页插件,只需要给我写入的方法加入一个Ipage类对象,这样就就可以自动的完成分页,我们甚至不需要在Mapper.xml文件中写上任何的limit相关的语句,具体代码如下:

  1. public interface usermapper extends BaseMapper<user> {
  2. IPage<user> pageselect(Page p,int age);
  3. }
  1. <resultMap id="user" type="com.zjl.mybatisplus.pojo.user">
  2. <result property="tid" column="uuid"></result>
  3. </resultMap>
  4. <select id="pageselect" resultMap="user">
  5. select * from user where age = #{age}
  6. </select>
  1. @Test
  2. public void pagetest(){
  3. Page<user> userPage = new Page<>(1, 2);
  4. IPage<user> pageselect = usermapper.pageselect(userPage, 18);
  5. System.out.println(pageselect.getRecords());
  6. }

通用枚举

这个方法主要的用处还是前后端进行业务的分离,比如说性别这个东西,我们前端肯定是要用字符去表示,但是后端的话,可以用数字来存储标识。在MP中的具体体现如下:
核心的配置 主要就是2处,一处是@EnumValue 这个用来声明枚举类中的哪一个属性会用来写进数据库中,然后
还要做的一件事就是在Springboot中配置枚举注解扫描

  1. mybatis-plus:
  2. configuration:
  3. log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  4. type-enums-package: com.zjl.mybatisplus.pojo
  1. public enum sex {
  2. MAN("男",1),WOMAN("女",2);
  3. //比如说数据库中的性别男为1, 女为2 ,但是在前端我们用字符表示
  4. @JsonValue
  5. private String pagesex;
  6. @EnumValue
  7. private int dbsex;
  8. private sex(String pagesex,int dbsex){
  9. this.dbsex = dbsex;
  10. this.pagesex = pagesex;
  11. }
  12. }
  1. @Data
  2. public class user {
  3. //主要功能还是为了完成basemapper中的id与主键的映射;
  4. @TableId(value = "uuid",type = IdType.ASSIGN_ID)
  5. private Long tid;
  6. private String name;
  7. private sex sex;
  8. private Integer age;
  9. private String email;
  10. @TableLogic(value = "yes" , delval = "no")
  11. private String readstatue;
  12. @Version
  13. private Integer version;
  14. }
  1. @Test
  2. public void enuminsert(){
  3. //如果是前端怎么用呢?
  4. user user = new user();
  5. user.setEmail("qerr@qq.com");
  6. user.setSex(sex.WOMAN);
  7. user.setAge(22);
  8. user.setName("小红");
  9. int insert = usermapper.insert(user);
  10. System.out.println(insert);
  11. }

配置时出现的问题

1.在建表格的时候,一定要确保字段不是关键字,有关键字直接报错,例如desc describle 这种会直接报错的!
2.注册bean 一定要记得注册bean ,不论是配置类,还是service ,或者 dao 层,这写地方不配置是无法直接装配的,有些配置在我们使用时并不会用到,因为框架会自动给我们写好,所以不用不代表不需要注册!
3.如何设置自动填充,首先在要进行自动填充的属性上声明我们是在什么时候会进行自动填充,就比如时间,有创建时间,有更新时间,这些是需要进行自动填充的,我们就不需要去每次进行填写了,设置自动填充的方法

  1. package com.zjl.bookshop.config;
  2. import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
  3. import org.apache.ibatis.reflection.MetaObject;
  4. import org.springframework.stereotype.Component;
  5. import java.util.Date;
  6. @Component
  7. public class MyMetaObjectHandler implements MetaObjectHandler {
  8. //用来设置自动填充功能;
  9. @Override
  10. public void insertFill(MetaObject metaObject) {
  11. this.setFieldValByName("createTime",new Date(),metaObject);
  12. this.setFieldValByName("updateTime",new Date(),metaObject);
  13. }
  14. @Override
  15. public void updateFill(MetaObject metaObject) {
  16. this.setFieldValByName("updateTime",new Date(),metaObject);
  17. }
  18. }