MybatisPlus多表查询之零sql编写实现

news/2024/7/9 4:13:24

1.前言

       年初节奏还没有快起来,适合做做技术前瞻,无论是对个人还是团队都是好事。真要说分享,其实感觉也没啥好分享的,就像接手新项目一样,代码都是自己看,别人讲的再多,不看,不用,不踩坑都不会刻骨铭心。
       今天要跟大家分享的mybatisPlus的join查询也是临时起意,早上有同事觉得在mybatisPlus的xml文件里配置子查询不好,嫌弃慢(其实是偷懒了没有正确使用,resultMap没有根据业务自己用自己的,避免多余的子查询),建议在代码层面实现,这里就分享这个join查询如你所愿。

2.分享内容

       MybatisPlus的join查询工具包,这个几乎可以满足零xml的sql编写。实现这一工具包的有2个人,一个是河南的,一个是北京的。个人偏向使用北京小哥实现的,有boot-starter可以支持配置文件,功能更强点。

3.使用方法

3.1北京小哥的join工具包

3.1.1源码与帮助介绍

Git地址:https://github.com/yulichang/mybatis-plus-join.git
使用帮助:https://ylctmh.com/

3.1.2使用集成

1、依赖引入

<!-- mybatis-plus-join依赖 -->
<dependency>
    <groupId>com.github.yulichang</groupId>
    <artifactId>mybatis-plus-join-boot-starter</artifactId>
    <version>1.4.2.2</version>
</dependency>

2、配置
       因为提供了boot-starter,所以可以直接使用配置文件,具体可以查看在线使用帮助。我这里是在多数据源场景下使用的,就不用配置文件了。
配置类
A、 MbatisPlus的配置类增加绑定配置

import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.github.yulichang.injector.MPJSqlInjector;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Collections;


/**
 * 分页插件配置
 *
 * @author zhengwen
 */
@Slf4j
@Configuration
public class MyBatisPlusConfig {

    /**
     * 分页插件 3.5.X
     */
    @Bean
    public PaginationInnerInterceptor paginationInnerInterceptor() {
        log.debug("注册分页插件");
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
        /* 只负责注册分页插件,不具体设置DbType、方言等
        // 设置最大单页限制数量,默认 500 条,-1 不受限制
        paginationInnerInterceptor.setMaxLimit(-1L);
        paginationInnerInterceptor.setDbType(DbType.TDENGINE);
        // 开启 count 的 join 优化,只针对部分 left join
        paginationInnerInterceptor.setOptimizeJoin(true);
        */
        return paginationInnerInterceptor;
    }

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.setInterceptors(Collections.singletonList(paginationInnerInterceptor()));
        return mybatisPlusInterceptor;
    }

    /**
     * GlobalConfig 配置 MPJSqlInjector
     * 绑定MPJSqlInjector到全局配置
     */
    @Bean
    public GlobalConfig globalConfig(MPJSqlInjector mpjSqlInjector) {
        GlobalConfig config = new GlobalConfig();
        //绑定 mpjSqlInjector到全局配置
        config.setSqlInjector(mpjSqlInjector);
        //其他配置 略
        return config;
    }


}

B、 Mysql数据源配置注入全局配置
如果不是多数据源其实可以直接在MybatisPlus里的SqlSessionFactory注入全局配置。我这里是在Mysql数据源配置类注入配置。
MysqlServerConfig配置类的SqlSessionFactory增加配置

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;
import java.util.Collections;


/**
 * mysql配置类
 *
 * @author zhengwen
 */
@Configuration
@MapperScan(basePackages = {"com.xiaotian.datatrans.mapper.mysql"}, sqlSessionTemplateRef = "mysqlSqlSessionTemplate")
public class MysqlServerConfig {

    @Autowired
    private PaginationInnerInterceptor paginationInnerInterceptor;

    private static final String MAPPER_LOCATION = "classpath:mapper/mysql/*.xml";

    @Bean(name = "mysqlDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.mysql-service")
    @Primary
    public DataSource mysqlDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "mysqlSqlSessionFactory")
    @Primary
    public SqlSessionFactory mysqlSqlSessionFactory(@Qualifier("mysqlDataSource") DataSource dataSource, GlobalConfig globalConfig) throws Exception {
        MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATION));

        //关联SqlSessionFactory与GlobalConfig
        bean.setGlobalConfig(globalConfig);

        //不在这里注入分页插件,会失效,(MyBatisPlusConfig只负责注册分页插件)
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        paginationInnerInterceptor.setMaxLimit(-1L);
        paginationInnerInterceptor.setDbType(DbType.MYSQL);
        // 开启 count 的 join 优化,只针对部分 left join
        paginationInnerInterceptor.setOptimizeJoin(true);
        mybatisPlusInterceptor.setInterceptors(Collections.singletonList(paginationInnerInterceptor));
        bean.setPlugins(mybatisPlusInterceptor);

        return bean.getObject();
    }

    @Bean(name = "mysqlTransactionManager")
    @Primary
    public DataSourceTransactionManager mysqlTransactionManager(@Qualifier("mysqlDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "mysqlSqlSessionTemplate")
    @Primary
    public SqlSessionTemplate mysqlSqlSessionTemplate(@Qualifier("mysqlSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

3、Mapper变更
业务mapper原先是继承MybatisPlus的BaseMapper,这里改为继承MPJBaseMapper,点击进去实际上这个接口最终也有继承了BaseMapper的。


/**
 * <p>
 * 设备推送信息表 Mapper 接口
 * </p>
 *
 * @author zhengwen
 * @since 2023-02-03
 */
@DS("mysql-service")
public interface DeviceRecordMapper extends MPJBaseMapper<DeviceRecord> {


    /**
     * 测试
     *
     * @param page
     * @param deviceRecord
     * @return
     */
    IPage<DeviceRecordDto> selectListBy(Page page, @Param("deviceRecord") DeviceRecord deviceRecord);
}

4、service实现使用

/**
 * <p>
 * 设备推送信息表 服务实现类
 * </p>
 *
 * @author zhengwen
 * @since 2023-02-03
 */
@Service
public class DeviceRecordServiceImpl extends ServiceImpl<DeviceRecordMapper, DeviceRecord> implements DeviceRecordService {

    @Autowired
    private DeviceRecordMapper deviceRecordMapper;

    @Override
    public IPage<DeviceRecordDto> selectPage(Page page, DeviceRecord customQueryParams) {
        QueryWrapper<DeviceRecord> qw = new QueryWrapper<>();
        IPage<DeviceRecordDto> records = deviceRecordMapper.selectPage(page, qw);
        //IPage<Student> records = deviceRecordMapper.selectStudentPage(page, customQueryParams);

        return records;

    }

    @Override
    public IPage<DeviceRecordDto> getPageUseJoin(Page page, DeviceRecord customQueryParams) {
        //分页查询
        IPage<DeviceRecordDto> list = deviceRecordMapper.selectJoinPage(page, DeviceRecordDto.class,
                new MPJLambdaWrapper<DeviceRecord>()
                        .selectAll(DeviceRecord.class)
                        //子查询
                        .selectCollection(DeviceRecordInfo.class, DeviceRecordDto::getRecordInfoList)
                        .leftJoin(DeviceRecordInfo.class, DeviceRecordInfo::getRecordId, DeviceRecord::getId));

        return list;
    }
}

3.1.3效果查看

1、2张表的sql
设备推送信息表:device_record
设备上报信息详情:device_record_info

CREATE TABLE `device_record` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `device_no` varchar(100) DEFAULT NULL COMMENT '设备编码',
  `record_time` datetime DEFAULT NULL COMMENT '上报时间',
  `device_type_no` varchar(10) DEFAULT NULL COMMENT '设备类型编码',
  `device_name` varchar(200) DEFAULT NULL COMMENT '设备名称',
  `record_type` varchar(10) DEFAULT NULL COMMENT '上报数据类型',
  `service_id` varchar(20) DEFAULT NULL COMMENT '服务id',
  `record_content` varchar(4000) DEFAULT NULL COMMENT '上报原始内容',
  `create_by` int(11) DEFAULT NULL COMMENT '创建人',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='设备推送信息表';
CREATE TABLE `device_record_info` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `tag_name` varchar(100) DEFAULT NULL COMMENT '字段名称',
  `tag_key` varchar(50) DEFAULT NULL COMMENT '字段key',
  `tag_val` varchar(50) DEFAULT NULL COMMENT '字段值',
  `record_id` bigint(20) DEFAULT NULL COMMENT '记录id',
  `tag_unit` varchar(30) DEFAULT NULL COMMENT '单位',
  `tag_val_name` varchar(100) DEFAULT NULL COMMENT '字段值释意',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='设备上报信息详情';

2、2张表关系说明
Device_record的主键关联device_record_info的record_id字段:
3、接口请求效果
A、执行的sql
在这里插入图片描述

B、返回的数据
在这里插入图片描述

3.1.4使用总结

1、这里做了个简单示例,它的使用确实应该是可以做到零xml的sql编写的,就看你愿意不。
2、MPJLambdaWrapper增加过滤条件就跟MybatisPlus的QueryWrapper一致。
3、还有很多使用方法,可以自行去在线帮助文档查看,也可以自己在mapper后面“.“的看。
4、个人认为还是挺香的

3.2河南小哥的join工具包

3.2.1源码与帮助介绍

Git地址:https://gitee.com/mhb0409/mybatis-plus-join
使用帮助:就是项目的README.md里介绍

3.2.2使用集成

1、依赖引入

<dependency>
    <groupId>icu.mhb</groupId>
    <artifactId>mybatis-plus-join</artifactId>
    <version>1.3.4</version>
</dependency>

2、配置
A、配置类
MyBatisPlusConfig需要继承JoinDefaultSqlInjector,并且需要重写getMethodList方法

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import icu.mhb.mybatisplus.plugln.injector.JoinDefaultSqlInjector;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Collections;
import java.util.List;


/**
 * 分页插件配置
 *
 * @author zhengwen
 */
@Slf4j
@Configuration
public class MyBatisPlusConfig extends JoinDefaultSqlInjector {

    /**
     * 分页插件 3.5.X
     */
    @Bean
    public PaginationInnerInterceptor paginationInnerInterceptor() {
        log.debug("注册分页插件");
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
        /* 只负责注册分页插件,不具体设置DbType、方言等
        // 设置最大单页限制数量,默认 500 条,-1 不受限制
        paginationInnerInterceptor.setMaxLimit(-1L);
        paginationInnerInterceptor.setDbType(DbType.TDENGINE);
        // 开启 count 的 join 优化,只针对部分 left join
        paginationInnerInterceptor.setOptimizeJoin(true);
        */
        return paginationInnerInterceptor;
    }

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.setInterceptors(Collections.singletonList(paginationInnerInterceptor()));
        return mybatisPlusInterceptor;
    }

    
    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
        List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);
        return methodList;
    }
}

B、Mysql数据源配置类
全局配置绑定join的Injector

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import icu.mhb.mybatisplus.plugln.injector.JoinDefaultSqlInjector;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;
import java.util.Collections;


/**
 * mysql配置类
 *
 * @author zhengwen
 */
@Configuration
@MapperScan(basePackages = {"com.xiaotian.datatrans.mapper.mysql"}, sqlSessionTemplateRef = "mysqlSqlSessionTemplate")
public class MysqlServerConfig {

    @Autowired
    private PaginationInnerInterceptor paginationInnerInterceptor;

    private static final String MAPPER_LOCATION = "classpath:mapper/mysql/*.xml";

    @Bean(name = "mysqlDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.mysql-service")
    @Primary
    public DataSource mysqlDataSource() {
        return DataSourceBuilder.create().build();
    }


    @Bean
    public GlobalConfig globalConfig(JoinDefaultSqlInjector joinDefaultSqlInjector) {
        GlobalConfig config = new GlobalConfig();
        //绑定 mpjSqlInjector到全局配置
        config.setSqlInjector(joinDefaultSqlInjector);
        //其他配置 略
        return config;
    }

    @Bean(name = "mysqlSqlSessionFactory")
    @Primary
    public SqlSessionFactory mysqlSqlSessionFactory(@Qualifier("mysqlDataSource") DataSource dataSource, GlobalConfig globalConfig) throws Exception {
        MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATION));

        //关联SqlSessionFactory与GlobalConfig
        bean.setGlobalConfig(globalConfig);

        //不在这里注入分页插件,会失效,(MyBatisPlusConfig只负责注册分页插件)
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        paginationInnerInterceptor.setMaxLimit(-1L);
        paginationInnerInterceptor.setDbType(DbType.MYSQL);
        // 开启 count 的 join 优化,只针对部分 left join
        paginationInnerInterceptor.setOptimizeJoin(true);
        mybatisPlusInterceptor.setInterceptors(Collections.singletonList(paginationInnerInterceptor));
        bean.setPlugins(mybatisPlusInterceptor);

        return bean.getObject();
    }

    @Bean(name = "mysqlTransactionManager")
    @Primary
    public DataSourceTransactionManager mysqlTransactionManager(@Qualifier("mysqlDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "mysqlSqlSessionTemplate")
    @Primary
    public SqlSessionTemplate mysqlSqlSessionTemplate(@Qualifier("mysqlSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

3、springBoot3的启动类还要增加import
否则,查询结果不能成功映射到MybatisPlus的IPage对象的records
启动类上增加注解@Import({JoinInterceptor.class, JoinInterceptorConfig.class})

import icu.mhb.mybatisplus.plugln.interceptor.JoinInterceptor;
import icu.mhb.mybatisplus.plugln.interceptor.JoinInterceptorConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Import;

/**
 * @author zhengwen
 */

@Import({JoinInterceptor.class, JoinInterceptorConfig.class})
@EntityScan("com.xiaotian")
@SpringBootApplication
public class DataGeniusApplication {

    public static void main(String[] args) {
        SpringApplication.run(DataGeniusApplication.class, args);
        System.out.println("--数据精灵启动成功--");
    }

}

4、Mapper变更
业务mapper原先是继承MybatisPlus的BaseMapper,这里改为继承JoinBaseMapper,点击进去实际上这个接口最终也继承了BaseMapper的。


/**
 * <p>
 * 设备推送信息表 Mapper 接口
 * </p>
 *
 * @author zhengwen
 * @since 2023-02-03
 */
@DS("mysql-service")
public interface DeviceRecordMapper extends JoinBaseMapper<DeviceRecord> {


    /**
     * 测试
     *
     * @param page
     * @param deviceRecord
     * @return
     */
    IPage<DeviceRecordDto> selectListBy(Page page, @Param("deviceRecord") DeviceRecord deviceRecord);
}

5、service实现类使用

/**
 * <p>
 * 设备推送信息表 服务实现类
 * </p>
 *
 * @author zhengwen
 * @since 2023-02-03
 */
@Service
public class DeviceRecordServiceImpl extends ServiceImpl<DeviceRecordMapper, DeviceRecord> implements DeviceRecordService {

    @Autowired
    private DeviceRecordMapper deviceRecordMapper;

    @Override
    public IPage<DeviceRecordDto> selectPage(Page page, DeviceRecord customQueryParams) {
        QueryWrapper<DeviceRecord> qw = new QueryWrapper<>();
        IPage<DeviceRecordDto> records = deviceRecordMapper.selectPage(page, qw);
        //IPage<Student> records = deviceRecordMapper.selectStudentPage(page, customQueryParams);

        return records;

    }

    @Override
    public IPage<DeviceRecordDto> getPageUseJoin(Page page, DeviceRecord customQueryParams) {
         //分页查询
        JoinLambdaWrapper<DeviceRecord> joinLambdaWrapper = new JoinLambdaWrapper<>(DeviceRecord.class);
        joinLambdaWrapper.leftJoin(DeviceRecordInfo.class, DeviceRecordInfo::getRecordId, DeviceRecord::getId)
                .manyToManySelect(DeviceRecordDto::getRecordInfoList,DeviceRecordInfo.class)
                //下面的这个end一定不能少哦,否在sql不会出现 left join xxx
                .end();
        IPage<DeviceRecordDto> list = deviceRecordMapper.joinSelectPage(page, joinLambdaWrapper, DeviceRecordDto.class);

        return list;
    }
}

3.2.3使用效果查看

       效果与3.1.3一致

3.2.4使用总结

1、总体感觉基本与3.1.4一致
2、没有boot-starter感觉没有北京小哥的高大上
3、在线文档没有专门的网址
4、配置略比北京小哥的复杂一丢丢

4.总体感想

  • 首先应该确实可以做到零XML的sql编写了
  • 零sql编写应该是优缺点各一半吧
  • 有没有觉得有了这个,JPA真的是没有必要用了呢?
  • 这2各包在Maven中心库,看到发布第一版时间是同年同月,不过走的方向不一样,一个走插件方式,一个走重写。
  • 2个作者都还在不断更新兼容新版本的MybatisPlus,点赞
  • 2个作者,我也联系到人了,都很友好。就喜欢技术人这种面向问题的交流方式。
           如果有需要,可以去我CSDN的gitCode下载demo,地址:https://gitcode.net/zwrlj527/data-trans.git。这个还是集成上次的多数据源做的集成。更多使用姿势,大家可以自行去他们的帮助文档查看并解锁使用。
           希望可以帮到大家,uping!!!

http://lihuaxi.xjx100.cn/news/801304.html

相关文章

互联网摸鱼日报(2023-02-03)

互联网摸鱼日报&#xff08;2023-02-03&#xff09; InfoQ 热门话题 管理不好软件复杂度&#xff0c;大厦倾颓随时可能发生 | 独家对话《代码大全》作者 又一个让马斯克害怕的“推特杀手”出现了 2022 中国开源年度报告 更高性能表现&#xff0c;一文解读高精度计算数据类型…

图解最常用的 10 个机器学习算法

在机器学习领域&#xff0c;有种说法叫做“世上没有免费的午餐”&#xff0c;简而言之&#xff0c;它是指没有任何一种算法能在每个问题上都能有最好的效果&#xff0c;这个理论在监督学习方面体现得尤为重要。 举个例子来说&#xff0c;你不能说神经网络永远比决策树好&#…

uniapp APP分享;判断用户是否安装APP,已安装直接打开,未安装跳转下载页;uniapp 在外部打开APP(schemes)

场景&#xff1a; A将某商品分享给B&#xff0c;B点击后判断是否安装APP&#xff0c;若安装直接打开&#xff0c;没有安装则跳转下载页&#xff1b; 知识点&#xff1a; uniapp APP分享&#xff1b;判断用户是否安装APP&#xff0c;已安装直接打开&#xff0c;未安装跳转下载…

NoClassDefFoundError错误解决

NoClassDefFoundError 类型报错 NoClassDefFoundError与ClassNotFoundException略有区别&#xff0c;从两者的异常类型可以发现&#xff0c;前者属于Error&#xff0c;后者属于Exception&#xff0c;发生了Error往往会导致程序直接崩溃或者无法启动运行。 NoClassDefFoundErro…

SMB2协议特性之oplock与lease(下

前期回顾上篇文章我们介绍了oplock/lease的相关概念及其基本工作原理&#xff0c;由于间隔时间较长&#xff0c;忘记的读者可以先去回顾一下。本篇文章带大家了解一下&#xff0c;在实际场景中&#xff0c;oplock/lease是如何工作的。实际场景分析在一些警匪影视剧中&#xff0…

uniapp时间格式化处理

应用需求分析:前台页面有时需要展示YYYY-MM-DD格式,但后台却返回给我们YYYY-MM-DD hh:mm:ss、或者是一串字符 //格式化处理 方式一: dateFormat(time) { let date = new Date(time); let year = date.getFullYear(); …

使用蚁群优化 (ACO) 解决背包问题(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 背包问题&#xff08;Knapsack problem&#xff09;是一种组合优化的NP完全&#xff08;NP-Complete&#xff0c;NPC&#xff0…

电脑休眠唤醒后会出现屏幕闪烁问题怎么彻底解决?

电脑休眠唤醒后会出现屏幕闪烁问题怎么彻底解决&#xff1f;有的用户在电脑待机休眠之后&#xff0c;重新去唤醒电脑使用&#xff0c;这个时候电脑屏幕就会出现验证的屏幕闪烁&#xff0c;导致无法进行正常的使用。这个情况是电脑系统不兼容导致的。如果想要彻底解决问题&#…