mybatis-plus-3(MP)-进阶

mybatis-plus-3(MP)-进阶

一、准备

1、数据库脚本

-- 创建用户表
CREATE TABLE user (
    id BIGINT(20) PRIMARY KEY NOT NULL COMMENT '主键',
    name VARCHAR(30) DEFAULT NULL COMMENT '姓名',
    age INT(11) DEFAULT NULL COMMENT '年龄',
    email VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
    manager_id BIGINT(20) DEFAULT NULL COMMENT '直属上级id',
    create_time DATETIME DEFAULT NULL COMMENT '创建时间',
	update_time DATETIME DEFAULT NULL COMMENT '修改时间',
	version INT(11) DEFAULT '1' COMMENT '版本',
	deleted INT(1) DEFAULT '0' COMMENT '逻辑删除标识(0.未删除,1.已删除)',
    CONSTRAINT manager_fk FOREIGN KEY (manager_id)
        REFERENCES user (id)
)  ENGINE=INNODB CHARSET=UTF8;

-- 初始化数据:
INSERT INTO user (id, name, age, email, manager_id
	, create_time)
VALUES (1087982257332887553, '大boss', 40, 'boss@baomidou.com', NULL
		, '2019-01-11 14:20:20'),
	(1088248166370832385, '王天风', 25, 'wtf@baomidou.com', 1087982257332887553
		, '2019-02-05 11:12:22'),
	(1088250446457389058, '李艺伟', 28, 'lyw@baomidou.com', 1088248166370832385
		, '2019-02-14 08:31:16'),
	(1094590409767661570, '张雨琪', 31, 'zjq@baomidou.com', 1088248166370832385
		, '2019-01-14 09:15:15'),
	(1094592041087729666, '刘红雨', 32, 'lhm@baomidou.com', 1088248166370832385
		, '2019-01-14 09:48:16');

2、实体类

@Data
public class User {
    
    private Long id;  // 主键
    private String name;  // 姓名
    private Integer age;  // 年龄
    private String email;  // 邮箱
    private Long managerId;  // 直属上级
    private LocalDateTime createTime;  // 创建时间
    private LocalDateTime updateTime;  // 创建时间
    private Integer version;	// 版本
    private Integer deleted;	// 逻辑删除。0:未删除,1:已删除
}

3、Mapper

public interface UserMapper extends BaseMapper<User> {
    
}

4、启动类

@MapperScan("com.demo.dao")
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

5、application.yml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/mp?useSSL=false&serverTimezone=GMT%2B8
    username: root
    password: root

二、高级功能

1、逻辑删除

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/mp?useSSL=false&serverTimezone=GMT%2B8
    username: root
    password: root
mybatis-plus:
  global-config:
    db-config:
      logic-not-delete-value: 0 # 全局配置。如需局部配置可在实体类@TableLogic注解后配置
      logic-delete-value: 1
@Configuration
public class MybatisPlusConfiguration{
    
    // V3.1.1及一下版本需要配置这个Bean
    // v3.1.2不需要配置
    @Bean
    public ISqlInjector sqlInjector(){
        // 已经过时
        return new LogicSqlInjector();
    }
}
@Data
public class User {
    
    private Long id;  // 主键
    private String name;  // 姓名
    private Integer age;  // 年龄
    private String email;  // 邮箱
    private Long managerId;  // 直属上级
    private LocalDateTime createTime;  // 创建时间
    private LocalDateTime updateTime;  // 创建时间
    private Integer version;	// 版本
    @TableLogic	// 全局逻辑删除
    @TableField(select=false) // 查询时,不查询该字段
    private Integer deleted;	// 逻辑删除。0:未删除,1:已删除
}
public interface UserMapper extends BaseMapper<User> {    
    @Select("select * from user ${ew.customSqlSegment}")
    List<User> mySelectList(@Param(Constants.WRAPPER) Wrapper<User> wrapper);  
}

测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class SimpleTest{
    
    @Autowired
    private UserMapper userMapper;
    
    @Test
    public void deleteById(){
        // 返回影响行数
       int rows = userMapper.deleteById(id);
       System.out.println("影响行数:" + rows);
    }
    
    // 查询时加入了 where deleted = 0
    // 查询时会将deleted查询出来。解决办法是在实体类注解@TableField(select=false)
    @Test
    public void select(){
       List<User> users = userMapper.selectList(null);
       users.forEach(System.out::println);
    }
    
    // 更新时加入了 where deleted = 0
    @Test
    public void updateById(){
       User user = new User();
       user.setId(id);
       user.setAge(26);
       int rows = userMapper.updateById(user);
       System.out.println("影响记录数:" + rows);
    }
    
    // 在Mapper自定义的查询中,查询时不会出现where deleted = 0
    // 解决:
    // 		方法一:如下面方法。
    //		方法二:在Mapper的select中加入条件
    @Test
    public void mySelect(){
       List<User> users = userMapper.mySelectList(
           // age > 25 and deleted = 0
           Wrappers.<User>lambdaQuery().gt(User::getAge,25).eq(User::getDeleted,0)
       );
    }
}

2、自动填充

@Data
public class User {
    
    private Long id;  // 主键
    private String name;  // 姓名
    private Integer age;  // 年龄
    private String email;  // 邮箱
    private Long managerId;  // 直属上级
    @TableField(fill=FieldFill.INSERT)
    private LocalDateTime createTime;  // 创建时间
    @TableField(fill=FieldFill.UPDATE)
    private LocalDateTime updateTime;  // 创建时间
    private Integer version;	// 版本
    private Integer deleted;	// 逻辑删除。0:未删除,1:已删除
}
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    
    @Override
    public void insertFill(MetaObject metaObject){
        // 优化1
        // 有些实体没有createTime。每次都自动填充,浪费了性能
        // 判断一下是否有createTime再自动填充
        boolean hasSetter = metaObject.hasSetter("createTime");
        if(hasSetter){
            // 优化2
            // 达到效果。已设置值,要已设置的值。未设置值,自动填充
            Object val = getFieldValByName("createTime",metaObject);
            if(val == null){
                System.out.println("insertFill~~");
                // createTime 为实体参数的名称
                setInsertFieldValByName("createTime",LocalDateTime.now(),metaObject);
            }
        }
    }
    
    @Override
    public void updateFill(MetaObject metaObject){
        System.out.println("updateFill~~");
        // createTime 为实体参数的名称
        setUpdateFieldValByName("updateTime",LocalDateTime.now(),metaObject);
    }
}

测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class SimpleTest{
    
    @Autowired
    private UserMapper userMapper;
    
    @Test
    public void insert(){
        User user = new User();
        user.setName("小明");
        user.setAge("23");
        user.setManagerId("1");
        int rows = userMapper.insert(user);
       System.out.println("影响行数:" + rows);
    }
    
    @Test
    public void updateById(){
       User user = new User();
       user.setId(id);
       user.setAge(26);
       int rows = userMapper.updateById(user);
       System.out.println("影响记录数:" + rows);
    }
}

3、乐观锁插件

@Configuration
public class MybatisPlusConfig {
	/**
	 *   乐观锁
	 */
	@Bean 
	public OptimisticLockerInterceptor optimisticLockerInterceptor() {
	    return new OptimisticLockerInterceptor();
	}
}
@Data
public class User {
    
    private Long id;  // 主键
    private String name;  // 姓名
    private Integer age;  // 年龄
    private String email;  // 邮箱
    private Long managerId;  // 直属上级
    private LocalDateTime createTime;  // 创建时间
    private LocalDateTime updateTime;  // 创建时间
    @Version // 支持的数据类型:int Integer long Long Date Timestamp LocalDateTime
    private Integer version;	// 版本
    private Integer deleted;	// 逻辑删除。0:未删除,1:已删除
}

测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class SimpleTest{
    
    @Autowired
    private UserMapper userMapper;
    
    @Test
    public void updateById(){
       int version = 1;
        
       User user = new User();
       user.setId(id);
       user.setAge(26);
       user.setVersion(version);
        // 查询后版本号+1
       int rows = userMapper.updateById(user);
       System.out.println("影响记录数:" + rows);
    }
}

4、性能分析插件

@Configuration
public class MybatisPlusConfig {
    @Bean
    @Profile({"dev","test"})// 设置 dev test 环境开启
	public PerformanceInterceptor performanceInterceptor() {
        PerformanceInterceptor performanceInterceptor =  new PerformanceInterceptor();
        performanceInterceptor.setFormat(true);// 格式化语句
        //performanceInterceptor.setMaxTime(5);// 执行时间超过多少秒会抛出异常
        return  performanceInterceptor;
}

4.1、执行sql分析打印

参考文档

<dependency>
  <groupId>p6spy</groupId>
  <artifactId>p6spy</artifactId>
  <version>3.8.2</version>
</dependency>
spring:
  datasource:
    driver-class-name: com.p6spy.engine.spy.P6SpyDriver
    url: jdbc:p6spy:h2:mem:test

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

5、多租户SQL解析器

项目

@Configuration
public class MybatisPlusConfig {
    /**
     * mybatis-plus分页插件<br>
     * 文档:http://mp.baomidou.com<br>
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        /*
         * 【测试多租户】 SQL 解析处理拦截器<br>
         * 这里固定写成住户 1 实际情况你可以从cookie读取,因此数据看不到 【 麻花藤 】 这条记录( 注意观察 SQL )<br>
         */
        List<ISqlParser> sqlParserList = new ArrayList<>();
        TenantSqlParser tenantSqlParser = new TenantSqlParser();
        tenantSqlParser.setTenantHandler(new TenantHandler() {
            @Override
            public Expression getTenantId() {
                return new LongValue(1L);
            }

            @Override
            public String getTenantIdColumn() {
                return "tenant_id";
            }

            @Override
            public boolean doTableFilter(String tableName) {
                // 这里可以判断是否过滤表
                /*if ("user".equals(tableName)) {
                    return true;
                }*/
                return false;
            }
        });

        sqlParserList.add(tenantSqlParser);
        paginationInterceptor.setSqlParserList(sqlParserList);
//        paginationInterceptor.setSqlParserFilter(new ISqlParserFilter() {
//            @Override
//            public boolean doFilter(MetaObject metaObject) {
//                MappedStatement ms = PluginUtils.getMappedStatement(metaObject);
//                // 过滤自定义查询此时无租户信息约束【 麻花藤 】出现
//                if ("com.baomidou.springboot.mapper.UserMapper.selectListBySQL".equals(ms.getId())) {
//                    return true;
//                }
//                return false;
//            }
//        });
        return paginationInterceptor;
    }
}

6、动态表名 SQL 解析器

项目

@Configuration
@MapperScan("com.baomidou.mybatisplus.samples.dytablename.mapper")
public class MybatisPlusConfig {

    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        DynamicTableNameParser dynamicTableNameParser = new DynamicTableNameParser();
        dynamicTableNameParser.setTableNameHandlerMap(new HashMap<String, ITableNameHandler>(2) {{
            put("user", (metaObject, sql, tableName) -> {
                // metaObject 可以获取传入参数,这里实现你自己的动态规则
                String year = "_2018";
                int random = new Random().nextInt(10);
                if (random % 2 == 1) {
                    year = "_2019";
                }
                return tableName + year;
            });
        }});
        paginationInterceptor.setSqlParserList(Collections.singletonList(dynamicTableNameParser));
        return paginationInterceptor;
    }
}

7、Sql 注入器

/**
 * 删除全部
 */
public class DeleteAll extends AbstractMethod {

    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, 
                                                 Class<?> modelClass, TableInfo tableInfo) {
        /* 执行 SQL ,动态 SQL 参考类 SqlMethod */
        String sql = "delete from " + tableInfo.getTableName();
        /* mapper 接口方法名一致 */
        String method = "deleteAll";
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        return this.addDeleteMappedStatement(mapperClass, method, sqlSource);
    }
}
@Component
public class MySqlInjector extends DefaultSqlInjector{
    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass){
        List<AbstractMethod> methodList = super.getMethodList(mapperClass);
        methodList.add(new DeleteAll());
        return methodList;
    }
}
public interface UserMapper extends BaseMapper<User> {
    int deleteAll(); // 返回影响行数
}

以上学习自 慕课网 -- MyBatis-Plus进阶

视频地址

posted @ 2020-07-06 23:31  Lucky丶zhu  阅读(264)  评论(0编辑  收藏  举报