MyBatisPlus

0 快速开始

1.1 pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring-boot-starter-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>2.3.4.RELEASE</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.example</groupId>
    <artifactId>mybatis-plusDemo</artifactId>
    <version>1.0-SNAPSHOT</version>

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



    </dependencies>



</project>

1.2 application.yml

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mybatis-plus-demo
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver

1.3 userMapper的编写

package com.xiaodidi.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xiaodidi.demo.User;
import org.apache.ibatis.annotations.Mapper;


public interface UserMapper extends BaseMapper<User> {

}

1.4 主程序

package com.xiaodidi;

import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.annotation.MapperScans;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.xiaodidi.mapper")
public class MainApplication {
    public static void main(String[] args) {
        SpringApplication.run(MainApplication.class, args);

    }


}

1.5 测试

package com.xiaodidi;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xiaodidi.demo.User;
import com.xiaodidi.mapper.UserMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestDemo {
    @Resource
    UserMapper userMapper;
    @Test
    public void save() {
        List<User> users = userMapper.selectList(null);
        for (User user : users) {
            System.out.println(user);
        }
    }
}

2.1 mybatis的主键(默认识别表中id字段为主键)

只要mybatis识别到主键,同时检测出来插入id为null,默认就自动使用了雪花算法,转换了然后在插入到数据库

有时数据库中的主键不是id也可以使用@TableId(“id”)指定主键

@TableId("id")//里面的id代表数据库中的字段
private Long uid;

image-20210430181723271

2.2 TableName

用来指定数据库中的名字,当数据库大的时候,通常使用t_user等标签来执行不同的服务,这是就需要通过TableName来执行表的名字,用来定位

2.3 @TableField

@TableField("name")

name代表数据库中的字段,用来进行对应,不过mybatisplus默认把user_number 转换成userNumber,可以自动进行识别

2.4 myBatisPlus设置createTime

第一种方式

通过更改数据库中的

image-20210306103005780

更改默认为当地的时间戳

(一般不用,不允许更改数据库)

第二种方式

image-20210502101949751

@TableField(fill = FieldFill.INSERT)
private Date createTime;

//更新时间
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
package com.atguigu.boot.config;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.Date;

@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    //插入时候的填充策略
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill ....");  //日志
        //设置字段的值(String fieldName字段名,Object fieldVal要传递的值,MetaObject metaObject)
        this.fillStrategy(metaObject, "createTime", new Date());
        this.fillStrategy(metaObject, "updateTime", new Date());

        
        //this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)
       // this.fillStrategy(metaObject, "createTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug请升级到之后的版本如`3.3.1.8-SNAPSHOT`)
        /* 上面选其一使用,下面的已过时(注意 strictInsertFill 有多个方法,详细查看源码) */
        //this.setFieldValByName("operator", "Jerry", metaObject);
        //this.setInsertFieldValByName("operator", "Jerry", metaObject);
    }

    //更新时间的填充策略
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill ....");
        this.fillStrategy(metaObject, "updateTime", new Date());
        
        
        //this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)
       // this.fillStrategy(metaObject, "updateTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug请升级到之后的版本如`3.3.1.8-SNAPSHOT`)
        /* 上面选其一使用,下面的已过时(注意 strictUpdateFill 有多个方法,详细查看源码) */
        //this.setFieldValByName("operator", "Tom", metaObject);
        //this.setUpdateFieldValByName("operator", "Tom", metaObject);
    }
}

测试

@ResponseBody
@RequestMapping("/saveRole")
public String saveRole() {
    Role role = new Role();
    role.setName("小");
    roleService.save(role);
    return "成功";



}

//自动会加入时间戳

注意:这里面所有的操作都需要使用mybatisBaseMapper里面自带的方法,因此不能使用自己写的方法使用createTime

2.5 乐观锁

面试中经常会问到乐观锁,悲观锁

乐观锁:顾名思义十分乐观,它总是被认为不会出现问题,无论干什么都不去上锁!如果出现了问题,再次更新测试

悲观锁:顾名思义十分悲观,它总是出现问题,无论干什么都会上锁!再去操作!

乐观锁实现方式

取出记录是,获取当前version 更新时,带上这个version 执行更新事,set version=newVersion where version =oldVersion 如果version不对,就更新失败 乐观锁: 1、先查询,获得版本号 version=1

--A
update user set name ="shuishui" ,version =version+1
where id =2 and version=1

--B 如果线程抢先完成,这个时候version=2,会导致A修改失败
update user set name ="shuishui" ,version =version+1
where id =2 and version=1

使用乐观锁

1 在数据库中加入version字段

image-20210306140312911

2 在pojo类中加入version字段
@Version
private String version;
3 加入配置
//开启事务
@EnableTransactionManagement
@Configuration
public class MybatisPlusConfig {

    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor();
    }
}

2.6 查询操作

// 根据 ID 查询
T selectById(Serializable id);
// 根据 entity 条件,查询一条记录
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

// 查询(根据ID 批量查询)
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// 根据 entity 条件,查询全部记录
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 查询(根据 columnMap 条件)
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
// 根据 Wrapper 条件,查询全部记录
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

// 根据 entity 条件,查询全部记录(并翻页)
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录(并翻页)
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询总记录数
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

condition

@Test
public void queryTest() {
    QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
    //查询所有名字中存在o的,同时年龄在16到20中的,如果不写就不进行判断
    String likeName = "o";
    Integer maxAge = 20;
    Integer minAge = 16;
    //如果第一个参数返回的是true就加入,如果返回的false那么就不进行加入
    userQueryWrapper.like(StringUtils.isNotBlank(likeName), "name", likeName)
            .gt(minAge!=null,"age",minAge)
            .lt(maxAge!=null,"age",maxAge);
    for (User user : userMapper.selectList(userQueryWrapper)) {
        System.out.println(user);
    }

}

优先级

@Test
public void queryTest2() {
    QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
    //查润name中带o的,同时查找年龄大于16或者name=xiaodidi
    userQueryWrapper.like("name","o").and(userQueryWrapper1 -> userQueryWrapper1.gt("age",16).or().eq("name","xiaodidi"));
    List<User> users = userMapper.selectList(userQueryWrapper);
    for (User user : users) {
        System.out.println(user);
    }


}

and中用消费型接口

LambdaQueryWrapper

@Test
public void queryTest2() throws IOException {
    LambdaQueryWrapper<User> userQueryWrapper = new LambdaQueryWrapper<>();
    //查润name中带o的,同时查找年龄大于16或者name=xiaodidi
    userQueryWrapper.like((User::getUsername), "o").and(userQueryWrapper1 -> userQueryWrapper1.gt(User::getAge,16).or().eq(User::getUsername,"xiaodidi"));
    List<User> users = userMapper.selectList(userQueryWrapper);
    for (User user : users) {
        System.out.println(user);
    }



}

image-20210502200617063

模拟实现

package com.xiaodidi;


import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import lombok.Data;
import net.minidev.json.JSONUtil;

import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;

@Data
public class LambdaTest {

    private String fieldA;

    public static void main(String[] args) throws Exception {
        SerializedLambda serializedLambda = doSFunction(LambdaTest::getFieldA);
        System.out.println("方法名:" + serializedLambda.getImplMethodName());
        System.out.println("类名:" + serializedLambda.getImplClass());

    }
/*SerializedLambda对象了。 实际上序列化的对象是通过writeReplace方法产生的,那么我们要获取SerializedLambda对象没必要真的序列化和反序列化一遍。 反射调用writeReplace方法就可以了。*/
    private static <T, R> java.lang.invoke.SerializedLambda doSFunction(SFunction<T, R> func) throws Exception {
        // 直接调用writeReplace
        Method writeReplace = func.getClass().getDeclaredMethod("writeReplace");
        writeReplace.setAccessible(true);
       //反射调用
        Object sl = writeReplace.invoke(func);
        java.lang.invoke.SerializedLambda serializedLambda = (java.lang.invoke.SerializedLambda) sl;
        return serializedLambda;
    }
}

2.7 myBatis-plus自带分页查询

放入分页的组件

@Bean
public PaginationInterceptor paginationInterceptor() {
    PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
    //指定数据库类型,以防每次都要去适配数据库
    paginationInterceptor.setDbType(DbType.MYSQL);
    return paginationInterceptor;
}
@ResponseBody
@RequestMapping("/pageTest")
public List<Role> pageTest() {
    Page<Role> rolePage = new Page<>(1, 5);
    roleMapper.selectPage(rolePage, null);
    return rolePage.getRecords();
}

自添加分页

IPage<User> getUsersgtAge(Page<User> page, @Param("age") Integer age);
<select id="getUsersgtAge" resultMap="userResultMap">
    select * from user where age > #{age}



</select>

这样,也可以像别的分页一样进行操作

2.8 删除操作

// 根据 entity 条件,删除记录
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
// 删除(根据ID 批量删除)
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// 根据 ID 删除
int deleteById(Serializable id);
// 根据 columnMap 条件,删除记录
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

2.9 配置逻辑删除

在表中添加一个字段

image-20210306155422737

同时在类中也加入相同

private String deleted;

mybatis3.3以前

image-20210306155233852

myBatis 3.3 以后

global-config:
  db-config:
    logic-delete-field: deleted
    logic-delete-value: 1
    logic-not-delete-value: 0

使用logic-delete-field:deleted 来指定全局的逻辑删除指标 相当于deleted

如果某个类全局逻辑指标要自己定义的时候添加

@TableLogic
private String deleted;

默认先从注解开始寻找,如果注解没有指定逻辑删除指标的时候,在从全局配置文件中寻找,如果都没有,那么就没有逻辑删除

注意:只针对myBatis-plus创建的sql有效,自己创建sql没有效果

2.10 代码自动生成器

代码自动生成器
daopojoconrtrollerservice自动生成

AutoGenerator 是 MyBatis-Plus 的代码生成器通过 AutoGenerator 可以快速生成 Entity MapperMapper XMLServiceController 等各个模块的代码极大的提升了开发效率

package com.kuang;
import com.baomidou.mybatisplus.annotation.DbType; 
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType; 
import com.baomidou.mybatisplus.annotation.TableField;
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.po.TableFill;
import com.baomidou.mybatisplus.generator.config.rules.DateType; 
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;

import java.util.ArrayList;
// 代码自动生成器 
public class KuangCode {
     public static void main(String[] args) {       
         // 需要构建一个 代码自动生成器 对象        
         AutoGenerator mpg = new AutoGenerator();   
         // 配置策略
         // 1、全局配置        
         GlobalConfig gc = new GlobalConfig();    
         String projectPath = System.getProperty("user.dir"); 
         gc.setOutputDir(projectPath+"/src/main/java");    
         gc.setAuthor("狂神说");     
         gc.setOpen(false);      
         gc.setFileOverride(false);  // 是否覆盖     
         gc.setServiceName("%sService"); // 去Service的I前缀      
         gc.setIdType(IdType.ID_WORKER);  
         gc.setDateType(DateType.ONLY_DATE);  
         gc.setSwagger2(true);    
         mpg.setGlobalConfig(gc);
         
          //2、设置数据源  
         DataSourceConfig dsc = new DataSourceConfig();  
         dsc.setUrl("jdbc:mysql://localhost:3306/kuang_community? useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8");       
         dsc.setDriverName("com.mysql.cj.jdbc.Driver");   
         dsc.setUsername("root");      
         dsc.setPassword("123456");  
         dsc.setDbType(DbType.MYSQL);  
         mpg.setDataSource(dsc);
 
          //3、包的配置     
         PackageConfig pc = new PackageConfig();  
         pc.setModuleName("blog");     
         pc.setParent("com.kuang");    
         pc.setEntity("entity");  
         pc.setMapper("mapper");     
         pc.setService("service");   
         pc.setController("controller");    
         mpg.setPackageInfo(pc);
 
          //4、策略配置      
         StrategyConfig strategy = new StrategyConfig();       
  strategy.setInclude("blog_tags","course","links","sys_settings","user_record"," user_say"); // 设置要映射的表名
         strategy.setNaming(NamingStrategy.underline_to_camel);   
         strategy.setColumnNaming(NamingStrategy.underline_to_camel);  
         // 自动lombok
         strategy.setEntityLombokModel(true);
         strategy.setLogicDeleteFieldName("deleted");  
         // 自动填充配置      
         TableFill gmtCreate = new TableFill("gmt_create", FieldFill.INSERT); 
         TableFill gmtModified = new TableFill("gmt_modified", FieldFill.INSERT_UPDATE);    
         ArrayList<TableFill> tableFills = new ArrayList<>(); 
         tableFills.add(gmtCreate);    
         tableFills.add(gmtModified);     
         strategy.setTableFillList(tableFills);   
         // 乐观锁      
         strategy.setVersionFieldName("version");
         strategy.setRestControllerStyle(true); 
         strategy.setControllerMappingHyphenStyle(true);
         // localhost:8080/hello_id_2     
         mpg.setStrategy(strategy);
         mpg.execute(); 
         //执行   
     } 
}

————————————————
版权声明本文为CSDN博主?Handsome?的原创文章遵循CC 4.0 BY-SA版权协议转载请附上原文出处链接及本声明
原文链接https://blog.csdn.net/zdsg45/article/details/105138493/