Mybatis-Plus学习笔记

1. Mybatis-plus简单概括

  Mybatis-plus是基于mybatis的,核心功能就是简化mybatis开发,提高效率。是国产的。

2. 快速入门

建表语句

create table `user`(
	id int PRIMARY key auto_increment,
	name varchar(32),
	age int
)
insert into db_user values(null,"小米",23),(null,"小刚",21)

创建一个springboot项目,maven坐标如下

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.3.1.tmp</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

在resources文件夹下新建application.yml,添加如下内容

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8
    username: root
    password: 123456
mybatis-plus:
  configuration:
    # 在控制台打印sql语句的
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

编写User实体类

package com.cht.entity;

import lombok.Data;

@Data
public class User {
    private Integer id;
    private String name;
    private Integer age;
}

再编写UserMapper类

package com.cht.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cht.entity.User;

public interface UserMapper extends BaseMapper<User> {
    
}

在springboot启动类上加上如下注解

@MapperScan("com.cht.mapper") //添加@MapperScan("com.cht.mapper")注解以后,com.cht.mapper包下面的接口类,在运行时,会通过动态代理生成相应的实现类

测试

package com.cht;

import com.cht.entity.User;
import com.cht.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
class MybatisplusApplicationTests {

    @Autowired
    private UserMapper mapper;

    @Test
    void contextLoads() {
        List<User> users = mapper.selectList(null);
        for (User u:users) {
            System.out.println(u);
        }
    }
}

3. 常用注解

  让我们对实体类增加几个属性以及注解,如下:

@Data
@TableName(value = "db_user")
public class User {
    @TableId(type = IdType.AUTO)
    private Integer id;
    private String name;
    private Integer age;
    @TableField(exist = false)
    private String gender;
    @TableField(select = false)
    private String nation;
    @TableField(value = "address")
    private String dizhi;
    @TableField(fill = FieldFill.INSERT)
    private Date createDate;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateDate;
}

对数据库里的user表修改表名并增加三个字段,如下:

alter table user rename AS db_user;
alter table db_user add address VARCHAR(32);
alter table db_user add create_date datetime;
alter table db_user add update_date datetime;

@TableName

  该注解用来映射数据库的表名,解决实体类名跟数据库表名不一致的情况,否则它会去数据库找类名首字母小写的表。
  加在实体类上,如下:

@TableName(value = "对应的表名")

@TableField

  映射表中的非主键字段,比如数据库中有个字段叫name,实体类User也有个属性叫name,这种情况是能够对应上的,但是数据库有个字段叫address,我实体类User有吗?没有,是不是只有一个叫dizhi的属性,而事实上dizhi就是要跟address对应的,所以,我们应该怎么让其对应,如上,是不是就在属性dizhi上加上@TableField(value = “address”)注解呀,这样不就可以对应上了吗?同时dizhi这个属性就成了address的别名。
  看属性gender,它被注解@TableField(exist = false)标识了,那么这个exist表示什么意思?它表示是否存在,假设我们当前的这个属性gender,在表中对应的字段应该也是gender,但是在表中是不存在gender的,不存在就会报错,针对这种情况有两种解决办法,一种是在表中增加gender字段,另一种是我们确实不需要gender这个属性参与表中映射,那么我们可以把gender这个属性删掉,或者不删,就给它在注解上加上exist属性即可,值为false,表示不参与映射。
  下一个,也就是nation属性,被@TableField(select = false)标识,看select,值有true和false,如果为false,表示不查询该属性。
  最后一个,就是create_date和update_date了,两个分别代表创建时间和更新时间,看注解,fill表示是否自动填充,将对象存入数据库的时候,由MyBatis Plus自动给某些字段赋值,而触发填充的时机又是什么样的呢?如果是FieldFill.INSERT,表示在添加的时候就更新该字段,如果是FieldFill.INSERT_UPDATE,就表示在添加或者更新的时候更新该字段。既然知道自动填充的时机,那么填充啥内容?是不是就是时间呀,我们可以写一个自动填充的处理器,如下:

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        this.setFieldValByName("createTime",new Date(),metaObject);
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }
}

@TableId

  跟@TableField意思一样,只不过@TableId服务于主键。如下:

@TableId(value = "id")

  它还有另一个属性,叫type,表示主键的生成策略,我们知道,主键是有唯一性的,但不一定用自增的方式来保证唯一性,也可以用某种算法,比如雪花算法(snowflake算法),或者用UUID,或者是用redis来生成等等,都是可以用来生成主键的,我们可以通过IdType这个类来查看,有如下:

public enum IdType {
    AUTO(0),
    NONE(1),
    INPUT(2),
    ASSIGN_ID(3),
    ASSIGN_UUID(4),
    /** @deprecated */
    @Deprecated     /** 过时 */
    ID_WORKER(3),
    /** @deprecated */
    @Deprecated
    ID_WORKER_STR(3),
    /** @deprecated */
    @Deprecated
    UUID(4);
}

重点掌握前5个,后三个都过时了,不管,如下:

描述
AUTO数据库自增(以当前表的最大id为准,加1),无需手动赋值,手动赋值反而无效
NONE未设置主键
INPUT如果开发者没有手动赋值,则数据库通过自增的方式给主键赋值,如果开发者手动赋值,则存入该值。
ASSIGN_ID主键类型为Long,Integer或String,默认采用雪花算法
ASSIGN_UUID主键的数据类型必须是String,自动生成UUID进行赋值

@Version

  标记乐观锁,通过version字段来保证数据的安全性,当修改数据的时候,会以version作为条件,当条件成立的时候才会修改成功。
  什么意思呢?也就是它可以防止多个线程同时修改某一条数据,我们可以在数据库表中增加一条version字段,如下:

alter table db_user add version int DEFAULT 1;

  然后在实体类User下增加一个字段,如下:

@Version
private Integer version;

  还没完,还要写一个配置类,如下:

@Configuration
public class MyBatisPlusConfig {

  //返回一个乐观锁的拦截器
  @Bean
  public OptimisticLockerInterceptor optimisticLockerInterceptor(){
      return new OptimisticLockerInterceptor();
  }
}

  这次就可以进行修改操作了,比如我要修改id为6的记录,那么一旦修改成功,字段version原本不是1吗?修改后就变为2了。后台sql语句如下:

UPDATE db_user SET name=?, age=?, create_date=?, update_date=?, version=? WHERE id=? AND version=? 

  也就是说,如果有两条线程同时获取同一条记录,那么两条线程拿到的version可能就是一样的,都是1,那么如果有一条线程最先修改成功,version变为2,但是后一条线程拿到的version还是原来的1,所以where后的第二个条件不成立,不成立就修改不成功咯。

@EnumValue

  通用枚举类注解,将数据库字段映射成实体类的枚举类型成员变量。
  我们可以在数据库表中再增加一个字段叫status,值的话要么1,要么0。然后实体类这边也要加上status属性,如下:

private StatusEnum status;

  StatusEnum类如下:

package com.cht.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
public enum StatusEnum {

    WORK(1,"上班"),
    REST(0,"休息");

    @EnumValue
    private Integer code;
    private String msg;

    StatusEnum(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }
}

  别忘了yml,如下:

mybatis-plus:
  configuration:
   ...
  type-enums-package:
    com.cht.enums

  如果数据库里的值是1,那么查出来的status就是WORK,为0就是REST。
  StatusEnum类的另一种写法,就是不用@EnumValue注解,用实现接口的方式,如下:

public enum StatusEnum implements IEnum<Integer> {

    WORK(1,"上班"),
    REST(0,"休息");

    @EnumValue
    private Integer code;
    private String msg;

    StatusEnum(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    @Override
    public Integer getValue() {
        return this.code;
    }
}

@TableLogic

  映射逻辑删除。所谓的逻辑删除不是真的删除,而是有一个字段,该字段有两个取值,分别是0或者1,0就代表未删除,1就代表删除。
  所以,让我们在数据表中再增加一个字段,就叫deleted吧,如下:

alter table db_user add deleted int DEFAULT 0;

  再在实体类上加上该字段,如下:

@TableLogic
private Integer deleted;

  在yml下增加几句话,如下:

mybatis-plus:
  ...
  global-config:
    db-config:
      logic-not-delete-value: 0
      logic-delete-value: 1

4. CRUD详解

查询

@Test
void findAll() {
    List<User> users = mapper.selectList(null); //null表示不加任何条件,也就是查询全部数据
    for (User u:users) {
        System.out.println(u);
    }
}
@Test
void select1() {
	//查询name等于小飞的人
    QueryWrapper wrapper = new QueryWrapper();
    wrapper.eq("name","小飞");
    List<User> users = mapper.selectList(wrapper);
    for (User u:users) {
        System.out.println(u);
    }
}
@Test
void select2() {
    QueryWrapper wrapper = new QueryWrapper();
    //多条件查询,查询name等于小路,age等于3的人
    Map<String,Object> map = new HashMap<>();
    map.put("name","小路");
    map.put("age",3);
    wrapper.allEq(map);
    List<User> users = mapper.selectList(wrapper);
    for (User u:users) {
        System.out.println(u);
    }
}
@Test
void select3() {
    QueryWrapper wrapper = new QueryWrapper();
    //查询年龄大于18岁的人
    wrapper.gt("age",18); //还有像ne就是不等,ge就是大于等于,lt就是小于
    List<User> users = mapper.selectList(wrapper);
    for (User u:users) {
        System.out.println(u);
    }
}
 @Test
void select4() {
    QueryWrapper wrapper = new QueryWrapper();
    //模糊查询,查名字里边带小的
    wrapper.like("name","小");//还有像likeLeft表示%小,likeRight表示小%
    List<User> users = mapper.selectList(wrapper);
    for (User u:users) {
        System.out.println(u);
    }
}
@Test
void select5() {
    QueryWrapper wrapper = new QueryWrapper();
    wrapper.inSql("id","select id from db_user where id<4");
    List<User> users = mapper.selectList(wrapper);
    for (User u:users) {
        System.out.println(u);
    }
}
@Test
void select6() {
    QueryWrapper wrapper = new QueryWrapper();
    wrapper.orderByAsc("age");//对age进行升序排列
    List<User> users = mapper.selectList(wrapper);
    for (User u:users) {
        System.out.println(u);
    }
}
@Test
void select7() {
 //  System.out.println(mapper.selectById(6));
   for (User user : mapper.selectBatchIds(Arrays.asList(3, 4, 5))) {
       System.out.println(user);
   }
}
@Test
void select8() {
    Map<String,Object> map = new HashMap<>();
    map.put("id",7);
    mapper.selectByMap(map);//查询id等于7的记录
}
@Test
void select9() {
    QueryWrapper queryWrapper = new QueryWrapper();
    queryWrapper.gt("id",1);
    System.out.println(mapper.selectCount(queryWrapper));
}
@Test
void select10() {
    //将查询的结果封装到map里面,注意它跟selectByMap的区别
    QueryWrapper queryWrapper = new QueryWrapper();
    queryWrapper.gt("id",1);
    List<Map> list = mapper.selectMaps(queryWrapper);
    for (Map map : list) {
        System.out.println(map);
    }
}
@Test
void select11() {
	// 分页查询
    Page<User> page = new Page<>(1,2);//查询第1页,每一页查询2条记录
    Page<User> userPage = mapper.selectPage(page, null);
    System.out.println(userPage.getSize());//当前查出来的记录数
    System.out.println(userPage.getTotal());//总记录数
    List<User> records = userPage.getRecords();
    for (User user:records){
        System.out.println(user);
    }
}
//注意以上要在配置类MyBatisPlusConfig写上这么一个方法才行,如下:
@Bean
public PaginationInterceptor paginationInterceptor(){
    return new PaginationInterceptor();
}
@Test
void select12() {
    Page<Map<String,Object>> page = new Page<>(1,2);
    List<Map<String, Object>> records = mapper.selectMapsPage(page, null).getRecords();
    for (Map<String,Object> map:records){
        for(String key:map.keySet()){
            System.out.println("key:"+key+" "+"Value:"+map.get(key));
        }
    }
}
@Test
void select13() {
    //查询所有的主键
    List<Object> objects = mapper.selectObjs(null);
    for (Object object:objects){
        System.out.println(object);
    }
}
@Test
void select14() {
    QueryWrapper queryWrapper = new QueryWrapper();
    queryWrapper.eq("id",6);
    System.out.println(mapper.selectOne(queryWrapper));
}

自定义SQL(多表关联查询)

create table product(
	category int ,
	count int,
	description varchar(32),
	user_id int,
	foreign key (user_id) references db_user(id)
)
insert into product values(1,10,"手机",3);
insert into product values(1,2,"电脑",4);
insert into product values(2,100,"电视",5);
insert into product values(2,200,"冰箱",6);

  再创建一个ProductVo类,如下:

package com.cht.entity;

import lombok.Data;

@Data
public class ProductVO {
    private Integer category;
    private Integer count;
    private String description;
    private Integer userId;
    private String userName;
}

  再看UserMapper接口类,加上这么一句话,如下:

@Select("select p.*,u.name userName from product p,db_user u where p.user_id = u.id and u.id = #{id}")
List<ProductVO> productList(Integer id);

  测试,如下:

@Test
void select15() {
    List<ProductVO> productVOS = mapper.productList(6);
    System.out.println(productVOS);
}

添加

@Test
void save(){
     User user = new User();
     user.setName("小b");
     user.setAge(23);
     mapper.insert(user);
     System.out.println(user);//注意,id会自动回填
}

删除

@Test
void delete1() {
    mapper.deleteBatchIds(Arrays.asList(7,8));
}
...

5. MyBatisPlus自动生成

  根据数据表自动生成实体类,Mapper,Service,ServiceImpl,Controller。
  首先,要想让它自动生成,先在pom.xml中导入依赖,如下:

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.3.1.tmp</version>
</dependency>
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity</artifactId>
    <version>1.7</version>
</dependency>
package com.cht;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;

public class Main {
    public static void main(String[] args) {
        // 创建generator对象
        AutoGenerator autoGenerator = new AutoGenerator();
        // 数据源
        DataSourceConfig dataSourceConfig = new DataSourceConfig();
        dataSourceConfig.setDbType(DbType.MYSQL);
        dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8");
        dataSourceConfig.setUsername("root");
        dataSourceConfig.setPassword("123456");
        dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver");
        autoGenerator.setDataSource(dataSourceConfig);
        //全局配置
        GlobalConfig globalConfig = new GlobalConfig();
        globalConfig.setOutputDir(System.getProperty("user.dir")+"/src/main/java");
        globalConfig.setOpen(false);
        globalConfig.setAuthor("wudongchenxu");
        globalConfig.setServiceImplName("%sService");//因为生成出来的service接口类它的名字前面默认带有I,如果不想要,可以加上这一行
        autoGenerator.setGlobalConfig(globalConfig);
        //包信息
        PackageConfig packageConfig = new PackageConfig();
        packageConfig.setParent("com.cht");
        packageConfig.setModuleName("generator");
        packageConfig.setController("controller");
        packageConfig.setService("services");
        packageConfig.setServiceImpl("services.impl");
        packageConfig.setMapper("mapper");
        packageConfig.setEntity("entity");
        autoGenerator.setPackageInfo(packageConfig);
        //配置策略
        StrategyConfig strategyConfig = new StrategyConfig();
        strategyConfig.setEntityLombokModel(true);
        strategyConfig.setNaming(NamingStrategy.underline_to_camel);
        strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);//将数据库表里的下划线转为驼峰命名发,在生成实体类字段的时候
        autoGenerator.setStrategy(strategyConfig);
        autoGenerator.execute();
    }
}

  但要注意,yml里的数据源信息也要修改一下,如果一样就不用修改了,反正要与上对应。然后测试的时候别忘了加@MapperScan注解,包括实体类中以驼峰命名的属性加上@TableField与数据库里的字段对应,因为字段是下划线的,而实体类的属性是驼峰式的。
  如果只是想生成部分表,那么在配置策略下加上这么一句话,如下:

strategyConfig.setInclude("db_user"); //写上你要生成的表名,注意它是可变参数,如果还有表,逗号隔开即可

6. 性能分析插件

  MybatisPlus也提供了性能分析插件,如果超过这个时间就会停止运行,主要针对慢sql,用于输出每条sql语句及其执行时间。

maven坐标

<dependency>
  <groupId>p6spy</groupId>
  <artifactId>p6spy</artifactId>
  <version>3.8.2</version>
</dependency>

application.yml

spring:
  datasource:
    driver-class-name: com.p6spy.engine.spy.P6SpyDriver
    url: jdbc:p6spy:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8

spy.properties

#3.2.1以上使用
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
#3.2.1以下使用或者不配置
#modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系统记录 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 设置 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL前缀
useprefix=true
# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 实际驱动可多个
#driverlist=org.h2.Driver
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值