栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

MybatisPlus:SQL语句打印、SQL分析、自定义主键值策略填充(IdType.INPUT)、动态表名、多租户、枚举、类型处理器、连表自定义SQL(使用wrapper)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

MybatisPlus:SQL语句打印、SQL分析、自定义主键值策略填充(IdType.INPUT)、动态表名、多租户、枚举、类型处理器、连表自定义SQL(使用wrapper)

文章目录
    • 1. 简单使用以及配置 - 带分页配置
    • 2. 用法
      • 2.0 Wrapper属性
      • 2.1 @TableId - 自定义主键生成策略
      • 2.2 @TableField - 自定义字段值填充
      • 2.3 锁 - 乐观锁、悲观锁
        • 2.3.1 @Version - 乐观锁
      • 2.4 DML、DQL操作
        • 2.4.1 DML - 增删改 - 物理
        • 2.4.2 DML - 逻辑删除 - @TableLogic - 仅对注入MyBatisPlus注入的SQL生效,自定义SQL不生效
        • 2.4.3 DQL - 查
        • 2.4.4 条件构造器
      • 2.5 性能分析、SQL打印插件(p6spy) - 支持SQL格式化
      • 2.6 代码生成器
      • 2.7 自定义主键值策略填充(IdType.INPUT)
      • 2.8 动态表名-根据入参决定执行哪个表-会影响手写的SQL语句且手写SQL记得写表别名 - 手写SQL中所有的表名都会经过这层
        • 内置SQL
        • 手写SQL - 必须起表别名因为动态表单插件仅改from表名,where属性的表名不会改
      • 2.9 多租户 - 手写SQL中所有的表名都会经过这层 - 主表的租户字段加在where上,连表的租户字段加在on上
      • 2.10 枚举@EnumValue
      • 2.11 类型处理器BaseTypeHandler - 必须开启TableName.autoResultMap=true
      • 2.12 连表自定义SQL - 配合wrapper使用

1. 简单使用以及配置 - 带分页配置

pom.xml - 依赖



        
        
            org.springframework.boot
            spring-boot-starter-web
        

        
        
            mysql
            mysql-connector-java
            runtime
        

        
        
            org.projectlombok
            lombok
        

        
        
            com.baomidou
            mybatis-plus-boot-starter
            3.3.2
        


        
        
            org.springframework.boot
            spring-boot-devtools
        


        
        
            org.springframework.boot
            spring-boot-starter-test
            test
            
                
                    org.junit.vintage
                    junit-vintage-engine
                
            
        
    

 
 

application.yml

spring:
  application:
    name: mybatisplusStudy
  datasource:
    data-password: root
    password: root
    username: 123456
    url: jdbc:mysql://localhost:3306/interview?serverTimezone=UTC&useSSL=false
    driver-class-name: com.mysql.cj.jdbc.Driver

server:
  servlet:
    context-path: /mybatisplus
  port: 80


mybatis-plus:

  # mapper文件的位置
  mapper-locations: classpath:/mappers
    @Override
    public String formatMessage(int connectionId, String now, long elapsed, String category, String prepared, String sql, String url) {
        //格式化SQL
        String sqlContent = sql.replaceAll("[\s]+", " ");
        sqlContent = SqlUtil.formatSql(sqlContent);

        return StringUtils.isNotBlank(sql) ? " Consume Time:" + elapsed + " ms " + now +
                "n Execute SQL:n" + sqlContent + "n" : "";

    }
}

 
 
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
# 自定义日志打印
# 官网的SQL没有格式化
#logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
# SQL格式化输出
logMessageFormat=work.linruchang.mybatisplussamplestest.config.mybatisplus.EnhaceP6SpyLogger
#日志输出到控制台
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

 
 
测试

    @Test
    public void test10() {
        HashMap map = new HashMap();
        map.put("sport", "泡妞");

        List students = studentDao.selectByMap(map);

        students.forEach(System.out::println);
    }

格式化前-配置官网的com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger

格式化后:配置增强的EnhaceP6SpyLogger

 
 

2.6 代码生成器
    
        
            org.springframework.boot
            spring-boot-starter-web
        

        
            mysql
            mysql-connector-java
            runtime
        


        
        
            com.baomidou
            mybatis-plus-boot-starter
            3.2.0
        

        
        
            com.baomidou
            mybatis-plus-generator
            3.3.2
        
        
        
        
        
            io.springfox
            springfox-swagger2
            2.9.2
        

        
            io.springfox
            springfox-swagger-ui
            2.9.2
        

        
        
            org.apache.velocity
            velocity-engine-core
            2.0
        

        
        
            org.springframework.boot
            spring-boot-starter-thymeleaf
        


        
            org.springframework.boot
            spring-boot-starter-test
            test
            
                
                    org.junit.vintage
                    junit-vintage-engine
                
            
        
    

 
 

 @Test
    public void test16() {


        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();

        // 1. 全局配置
        GlobalConfig gc = new GlobalConfig();

        //生成文件的输入目录
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("LinRuChang");

        //生成文件后不打开文件管理器级【我的电脑】
        gc.setOpen(false);
        //添加swagger注解在生成文件中
        gc.setSwagger2(true);

        //设置文件生成日期注释所使用的日期类
        gc.setDateType(DateType.ONLY_DATE);

        //设置插入记录时,ID主键生成的值
        gc.setIdType(IdType.ASSIGN_UUID);

        //生成的文件不覆盖已有的文件
        gc.setFileOverride(false);

        //service类,mapper类即dao类,mapper的xml文件的名字  %s表示表名
        gc.setServiceName("%sService");
        gc.setMapperName("%sDao");
        gc.setXmlName("%sMapper");
        mpg.setGlobalConfig(gc);

        // 2. 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/book?serverTimezone=UTC&useSSL=false&characterEncoding=utf8");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("root");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);

        // 3. 包配置
        PackageConfig pc = new PackageConfig();

        //将该模块的文件放在哪个包里
        pc.setParent("top.linruchang.springbootmybatisautogenerator");
        //模块名 - 可以不配 -
        pc.setModuleName("");
        //控制层、实体类层、service层,dao层的包名设置
        pc.setController("controller");
        pc.setEntity("dto");
        pc.setService("service");
        pc.setMapper("dao");
        mpg.setPackageInfo(pc);

        // 3. 策略配置 - 实体类、controller类生成规则
        StrategyConfig strategy = new StrategyConfig();

        //实体类名的设置 - 表名转驼峰命名
        strategy.setNaming(NamingStrategy.underline_to_camel);

        //实体类的字段名的设置 - 字段名名转驼峰命名
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);

        //是否使用lombok注解在实体类中
        strategy.setEntityLombokModel(true);

        //控制器中的含有@RestController注解 - 常用于前后端分离
        strategy.setRestControllerStyle(true);

        // 写于父类中的公共字段 - 实体类中的公共字段
        strategy.setSuperEntityColumns("id", "version", "deleted");
        //注意这个类需要自己写哦 - 即写上面定义的字段
        strategy.setSuperEntityClass(BaseDTO.class);

        //TODO 表名 - 准备映射的表
        strategy.setInclude("class_type", "student");
        strategy.setControllerMappingHyphenStyle(true);
        //逻辑删除字段
        strategy.setLogicDeleteFieldName("deleted");
        //乐观锁字段
        strategy.setVersionFieldName("version");

        //字段值填充
        TableFill createTime = new TableFill("create_time", FieldFill.INSERT);
        TableFill updateTime = new TableFill("update_time", FieldFill.INSERT_UPDATE);
        strategy.setTableFillList(Arrays.asList(createTime, updateTime));

        mpg.setStrategy(strategy);
        
        
        //执行代码生成
        mpg.execute();


    }

 
 

2.7 自定义主键值策略填充(IdType.INPUT)

注意: 一旦设置了keyGenerator,如果没有全局指定ID的生成策略或在基类中的@TableId中指定主键的生成策略,则默认为IdType.INPUT,则全部的主键值都由keyGenerator进行生成

ArticleTag.java

@Data
@ToString(callSuper=true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@TableName("article_tag")
//用于com.baomidou.mybatisplus.core.incrementer.IKeyGenerator#executeSql的入参
@KeySequence("ArticleTag")
public class ArticleTag extends BaseDB implements Serializable {


    
    @TableId(type= IdType.INPUT)
    private String id;

    
    private static final long serialVersionUID = -8898734581249698571L;


    
    private String tagName;

    
    private String userId;

    
    private Integer isViolation;

}

 

MyBatisPlusConfig.java

@Configuration
public class MyBatisPlusConfig {

    
    @Bean
    public IKeyGenerator keyGenerator() {
        return new IKeyGenerator(){

            @Override
            public String executeSql(String incrementerName) {
                incrementerName = StrUtil.nullToEmpty(StrUtil.trim(incrementerName));
                return StrUtil.format("select CONCAt('{}',SUBSTr(REPLACE(uuid(),'-',''),1,32-LENGTH('{}')))",incrementerName,incrementerName );
            }

            @Override
            public DbType dbType() {
                return DbType.MYSQL;
            }
        };
    }
    
}

 

ArticleTagDao.java

@Mapper
public interface ArticleTagDao extends BaseMapper {
}

 

ArticleTagService.java

@Service
public class ArticleTagService extends ServiceImpl {
}

 

ArticleTagServiceTest.java

@SpringBootTest
class ArticleTagServiceTest {

    @Autowired
    ArticleTagService articleTagService;

    @Test
    public void keySequenceTest1() {
        ArticleTag articleTag = new ArticleTag();
        articleTag.setTagName("你好");

        boolean insert = articleTag.insert();
        Console.log("插入情况:{}", insert);
        Console.log("实体情况:{}", articleTag);

    }

}

 
 

2.8 动态表名-根据入参决定执行哪个表-会影响手写的SQL语句且手写SQL记得写表别名 - 手写SQL中所有的表名都会经过这层 内置SQL

MyBatisPlusConfig.java

@Configuration
public class MyBatisPlusConfig {



    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();


        //防全表更新与删除插件
        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());

        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
        paginationInnerInterceptor.setDbType(DbType.MYSQL);
        interceptor.addInnerInterceptor(paginationInnerInterceptor);

        //乐观锁,字段添加version注解
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());

        //动态表单
        interceptor.addInnerInterceptor(new DynamicTableNameInnerInterceptor(() -> {
            Console.log("动态表单执行成功");
        }, (sql, tableName) -> {
            Console.log("当前执行的SQL:{}n 表名:{}", sql,tableName);

            Map requestData = RequestDataHelper.getRequestData();

            String tableNum = null;
            if(CollUtil.isNotEmpty(requestData)) {
                String mapValueContent = requestData.values()
                        .stream()
                        .map(Object::toString)
                        .reduce(StrUtil.EMPTY, (t1, t2) -> {
                            return StrUtil.join(StrUtil.DOT, t1, t2);
                        });
                int mapHashValue = HashUtil.fnvHash(mapValueContent);
                int modulus = mapHashValue % 3;
                tableNum = String.valueOf(modulus);
                Console.log("当前参数的Hash值:{}: 取模后:{}",mapHashValue,modulus);
            }


            String resultTableName = tableName;
            if(StrUtil.isNotBlank(tableNum)) {
                resultTableName = StrUtil.format("{}_{}", tableName, tableNum);
            }

            RequestDataHelper.setRequestData(null);
            return resultTableName;
        }));


        return interceptor;
    }


}

 
 
SysUser.java

@Data
@ToString(callSuper=true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@TableName("sys_user")
public class SysUser extends BaseDB implements Serializable {
    private static final long serialVersionUID = 2095940921263481761L;


    
    private String nickName;

    
    private String headUrl;

    
    private String loginName;

    
    private String phoneNumber;

    
    private String email;

    
    private Integer gender;

    @TableField("gender")
    private GenderEnum genderEnum;

    
    private String personalMotto;

    //@ApiModelProperty("我的分类专栏,逗号分隔 - article_category表的id")
    //private String myArticleCategory;


    
    private String lastLoginTime;


    
    private Integer loginStatus;


    
    private Integer disabledStatus;

    
    private String password;


}

 
 
SysUserDao.java

@Mapper
public interface SysUserDao extends MyBaseMapper {
}

 
 
SysUserService.java

@Service
public class SysUserService extends ServiceImpl {
}

 
 
SysUserServiceTest.java

@SpringBootTest
class SysUserServiceTest {

    @Test
    public void test12() {

        List nicknames = null;

        Console.log("n========sys_user=========n");
        RequestDataHelper.setRequestData(null);
        List sysUsers = sysUserService.listByIds(Arrays.asList("c08d391e02bc11eb9416b42e99ea3e69", "dd9918601ef8071877cd75b6f1a21ad2"));
        nicknames = sysUsers.stream()
                .map(SysUser::getNickName)
                .collect(Collectors.toList());
        Console.log(sysUsers);
        Console.log(nicknames);


        Console.log("n========sys_user_0=========n");
        RequestDataHelper.setRequestData(new Dict()
                .set("id","4"));
        sysUsers = sysUserService.listByIds(Arrays.asList("c08d391e02bc11eb9416b42e99ea3e69", "dd9918601ef8071877cd75b6f1a21ad2"));
        nicknames = sysUsers.stream()
                .map(SysUser::getNickName)
                .collect(Collectors.toList());
        Console.log(sysUsers);
        Console.log(nicknames);


        Console.log("n========sys_user_1=========n");
        RequestDataHelper.setRequestData(new Dict()
                .set("id","8"));
        sysUsers = sysUserService.listByIds(Arrays.asList("c08d391e02bc11eb9416b42e99ea3e69", "dd9918601ef8071877cd75b6f1a21ad2"));
        nicknames = sysUsers.stream()
                .map(SysUser::getNickName)
                .collect(Collectors.toList());
        Console.log(sysUsers);
        Console.log(nicknames);




        Console.log("n========sys_user_2=========n");
        RequestDataHelper.setRequestData(new Dict()
                .set("id","5"));
        sysUsers = sysUserService.listByIds(Arrays.asList("c08d391e02bc11eb9416b42e99ea3e69", "dd9918601ef8071877cd75b6f1a21ad2"));
        nicknames = sysUsers.stream()
                .map(SysUser::getNickName)
                .collect(Collectors.toList());
        Console.log(sysUsers);
        Console.log(nicknames);
    }

}

 
 

执行结果

手写SQL - 必须起表别名因为动态表单插件仅改from表名,where属性的表名不会改

 
MyBatisPlusConfig.java

@Configuration
public class MyBatisPlusConfig {

    
    @Bean
    public IKeyGenerator keyGenerator() {
        return new IKeyGenerator() {

            @Override
            public String executeSql(String incrementerName) {
                incrementerName = StrUtil.nullToEmpty(StrUtil.trim(incrementerName));
                return StrUtil.format("select CONCAt('{}',SUBSTr(REPLACE(uuid(),'-',''),1,32-LENGTH('{}')))", incrementerName, incrementerName);
            }

            @Override
            public DbType dbType() {
                return DbType.MYSQL;
            }
        };
    }


    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

        //动态表单
        interceptor.addInnerInterceptor(new DynamicTableNameInnerInterceptor(() -> {
            Console.log("动态表单执行成功");
        }, (sql, tableName) -> {
            Console.log("当前执行的SQL:{}n 表名:{}", sql, tableName);

            Map requestData = RequestDataHelper.getRequestData();

            String tableNum = null;
            if (CollUtil.isNotEmpty(requestData)) {
                String mapValueContent = requestData.values()
                        .stream()
                        .map(Object::toString)
                        .reduce(StrUtil.EMPTY, (t1, t2) -> {
                            return StrUtil.join(StrUtil.DOT, t1, t2);
                        });
                int mapHashValue = HashUtil.fnvHash(mapValueContent);
                int modulus = mapHashValue % 3;
                tableNum = String.valueOf(modulus);
                Console.log("当前参数的Hash值:{}: 取模后:{}", mapHashValue, modulus);
            }


            String resultTableName = tableName;
            if (StrUtil.isNotBlank(tableNum)) {
                resultTableName = StrUtil.format("{}_{}", tableName, tableNum);
                RequestDataHelper2.setRequestData(resultTableName, TableInfoHelper.getTableInfo(tableName));
            }

            return resultTableName;
        }));

        //乐观锁,字段添加version注解
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());


        //防全表更新与删除插件
        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());

        //租户模式
        interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {

            
            @Override
            public Expression getTenantId() {
                Object tenant = RequestDataHelper.getRequestData("tenant");
                return tenant == null ? null : new StringValue(tenant.toString());
            }

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


            
            @Override
            public boolean ignoreTable(String tableName) {

                //无租户值则直接忽略拼接租户字段
                Object tenant = RequestDataHelper.getRequestData("tenant");
                boolean tenantValueFlag = ObjectUtil.isNotEmpty(tenant);

                //根据实体是否定义tenant属性来评判是否需要租户条件 == 经过动态表单的表名可能非com.baomidou.mybatisplus.annotation.TableName.value定义的表名
                TableInfo tableInfo = ObjectUtil.defaultIfNull(TableInfoHelper.getTableInfo(tableName),(TableInfo)RequestDataHelper2.getRequestData(tableName));
                boolean tanantEntityFlag = tableInfo.getFieldList().stream()
                        .anyMatch(tableFieldInfo -> {
                            return StrUtil.equals(tableFieldInfo.getColumn(), "tenant");
                        });


                //需要租户的表
                return !tanantEntityFlag && (!tenantValueFlag || !Arrays.asList(SysUser.class, FriendlyLink.class).stream()
                        .map(TableInfoHelper::getTableInfo)
                        .map(TableInfo::getTableName)
                        .anyMatch(t -> StrUtil.contains(tableName,t)));
            }
        }));


        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
        paginationInnerInterceptor.setDbType(DbType.MYSQL);
        interceptor.addInnerInterceptor(paginationInnerInterceptor);




        return interceptor;
    }


    
    @Bean
    public ISqlInjector enhanceISqlInjector() {
        return new DefaultSqlInjector() {
            @Override
            public List getMethodList(Class mapperClass, TableInfo tableInfo) {
                List methodList = super.getMethodList(mapperClass, tableInfo);

                methodList.add(new DeleteByIdPhysical());
                methodList.add(new DeleteByIdLogic());


                return methodList;
            }
        };
    }

}

 
SysUser.java

@Data
@ToString(callSuper=true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@TableName("sys_user")
public class SysUser extends BaseDB implements Serializable {
    private static final long serialVersionUID = 2095940921263481761L;


    
    private String nickName;

    
    private String headUrl;

    
    private String loginName;

    
    private String phoneNumber;

    
    private String email;

    
    private Integer gender;

    @TableField("gender")
    private GenderEnum genderEnum;

    
    private String personalMotto;

    //@ApiModelProperty("我的分类专栏,逗号分隔 - article_category表的id")
    //private String myArticleCategory;


    
    private String lastLoginTime;


    
    private Integer loginStatus;


    
    private Integer disabledStatus;

    
    private String password;


    
    private String tenant;
}

 
FriendlyLink.java

@Data
@ToString(callSuper=true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@TableName("friendly_link")
public class FriendlyLink extends BaseDB{

    private static final long serialVersionUID = -4714261187453073302L;

    
    private String userId;

    
    private String name;

    
    private String url;

    
    private Integer sort;

    
    private String type;

    
    private String jumpFlag;

    
    private String params;

    
    private String tenant;

}

 
SysUserDao.java

public interface SysUserDao extends MyBaseMapper {

    SysUser findOneById(String id);

    Map findOneByIdAndFriendLink(String id);
    Map findOneByIdAndFriendLink2(String id);

}

 
SysUserMapper.xml




    
        select *
            from sys_user
                left join friendly_link  on friendly_link.user_id = sys_user.id
            where sys_user.id = #{id}
    

    
        select *
        from sys_user as sys_user
                 left join friendly_link as friendly_link on friendly_link.user_id = sys_user.id
        
            ${ew.sqlSegment}
        

    

 
SysUserServiceTest.java

@SpringBootTest
class SysUserServiceTest {
    @Test
    public void test18() {
        RequestDataHelper.setRequestData(new Dict().set("tenant", 1));

        Page pageInfo = new Page<>(2, 1);

        LambdaQueryWrapper wrapper = Wrappers.query()
                .like("sys_user.id", "1536530866313883649")
                .eq("friendly_link.name", "Gitee")
                .or().lambda()
                .like(SysUser::getNickName, "嗯嗯**");


        IPage dataPage = sysUserDao.findOneByIdAndFriendLink2Page(pageInfo, wrapper);
        dataPage.getRecords().stream().forEach(Console::log);
    }

}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/978951.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号