【Mybatis系列】Mybatis常见的分页方法以及源码理解

news2024/11/16 15:53:41

Mybatis-Plus的selectPage

  1. 引入依赖
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>
  1. 添加分页插件
@Configuration
public class MybatisConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
        return interceptor;

    }
}
  1. Mapper类和实体类

Mapper类

public interface UserMapper extends BaseMapper<UserInfo> {

}

实体类

@Data
@EqualsAndHashCode(callSuper = false)
@TableName(value = "cls_user", autoResultMap = true)
@NoArgsConstructor
public class UserInfo {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    @TableField("user_name")
    private String userName;

    @TableField("password")
    private String password;
}
  1. 使用方法
    @Override
    public Page<UserInfo> pageQuery(String name) {
        return userMapper.selectPage(new Page<>(1,10), new QueryWrapper<>());
    }
  1. 查询示例结果
{
	"records": [
		{
			"id": 1,
			"userName": "cc",
			"password": "12345"
		},
		{
			"id": 2,
			"userName": "cc",
			"password": "12345"
		},
		{
			"id": 3,
			"userName": "cc",
			"password": "12345"
		}
	],
	"total": 11,
	"size": 3,
	"current": 1,
	"orders": [],
	"optimizeCountSql": true,
	"searchCount": true,
	"countId": null,
	"maxLimit": null,
	"pages": 4
}

源码解析

MybatisPlusInterceptor#intercept进行查询拦截,遍历其中的InnerInterceptor,核心方法是InnerInterceptor#willDoQueryInnerInterceptor#beforeQuery

    public Object intercept(Invocation invocation) throws Throwable {
        Object target = invocation.getTarget();
        Object[] args = invocation.getArgs();
        if (target instanceof Executor) {
            Executor executor = (Executor)target;
            Object parameter = args[1];
            boolean isUpdate = args.length == 2;
            MappedStatement ms = (MappedStatement)args[0];
            if (!isUpdate && ms.getSqlCommandType() == SqlCommandType.SELECT) {
                RowBounds rowBounds = (RowBounds)args[2];
                ResultHandler resultHandler = (ResultHandler)args[3];
                BoundSql boundSql;
                if (args.length == 4) {
                    boundSql = ms.getBoundSql(parameter);
                } else {
                    boundSql = (BoundSql)args[5];
                }

                Iterator var11 = this.interceptors.iterator();

                while(var11.hasNext()) {
                    InnerInterceptor query = (InnerInterceptor)var11.next();
                    if (!query.willDoQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql)) {
                        return Collections.emptyList();
                    }

                    query.beforeQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                }

                CacheKey cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
                return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
            }

            if (isUpdate) {
                Iterator var8 = this.interceptors.iterator();

                while(var8.hasNext()) {
                    InnerInterceptor update = (InnerInterceptor)var8.next();
                    if (!update.willDoUpdate(executor, ms, parameter)) {
                        return -1;
                    }

                    update.beforeUpdate(executor, ms, parameter);
                }
            }
        } else {
            StatementHandler sh = (StatementHandler)target;
            if (null == args) {
                Iterator var14 = this.interceptors.iterator();

                while(var14.hasNext()) {
                    InnerInterceptor innerInterceptor = (InnerInterceptor)var14.next();
                    innerInterceptor.beforeGetBoundSql(sh);
                }
            } else {
                Connection connections = (Connection)args[0];
                Integer transactionTimeout = (Integer)args[1];
                Iterator var18 = this.interceptors.iterator();

                while(var18.hasNext()) {
                    InnerInterceptor innerInterceptor = (InnerInterceptor)var18.next();
                    innerInterceptor.beforePrepare(sh, connections, transactionTimeout);
                }
            }
        }

        return invocation.proceed();
    }

PaginationInnerInterceptor#willDoQuery,核心逻辑就是根据参数中的Page对象构造查询总数的sql,进行sql查询,存放到Page对象中。

    public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        IPage<?> page = (IPage)ParameterUtils.findPage(parameter).orElse((Object)null);
        if (page != null && page.getSize() >= 0L && page.searchCount()) {
            MappedStatement countMs = this.buildCountMappedStatement(ms, page.countId());
            BoundSql countSql;
            if (countMs != null) {
                countSql = countMs.getBoundSql(parameter);
            } else {
                countMs = this.buildAutoCountMappedStatement(ms);
                String countSqlStr = this.autoCountSql(page, boundSql.getSql());
                MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);
                countSql = new BoundSql(countMs.getConfiguration(), countSqlStr, mpBoundSql.parameterMappings(), parameter);
                PluginUtils.setAdditionalParameter(countSql, mpBoundSql.additionalParameters());
            }

            CacheKey cacheKey = executor.createCacheKey(countMs, parameter, rowBounds, countSql);
            List<Object> result = executor.query(countMs, parameter, rowBounds, resultHandler, cacheKey, countSql);
            long total = 0L;
            if (CollectionUtils.isNotEmpty(result)) {
                Object o = result.get(0);
                if (o != null) {
                    total = Long.parseLong(o.toString());
                }
            }

            page.setTotal(total);
            return this.continuePage(page);
        } else {
            return true;
        }
    }

PaginationInnerInterceptor#beforeQuery,对原有的查询语句进行封装改造,增加limit ?,?

    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        IPage<?> page = (IPage)ParameterUtils.findPage(parameter).orElse((Object)null);
        if (null != page) {
            boolean addOrdered = false;
            String buildSql = boundSql.getSql();
            List<OrderItem> orders = page.orders();
            if (CollectionUtils.isNotEmpty(orders)) {
                addOrdered = true;
                buildSql = this.concatOrderBy(buildSql, orders);
            }

            Long _limit = page.maxLimit() != null ? page.maxLimit() : this.maxLimit;
            if (page.getSize() < 0L && null == _limit) {
                if (addOrdered) {
                    PluginUtils.mpBoundSql(boundSql).sql(buildSql);
                }

            } else {
                this.handlerLimit(page, _limit);
                IDialect dialect = this.findIDialect(executor);
                Configuration configuration = ms.getConfiguration();
                DialectModel model = dialect.buildPaginationSql(buildSql, page.offset(), page.getSize());
                MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);
                List<ParameterMapping> mappings = mpBoundSql.parameterMappings();
                Map<String, Object> additionalParameter = mpBoundSql.additionalParameters();
                model.consumers(mappings, configuration, additionalParameter);
                mpBoundSql.sql(model.getDialectSql());
                mpBoundSql.parameterMappings(mappings);
            }
        }
    }

PageHelper.startPage(int pageNum, int pageSize);

  1. 引入依赖
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <!-- 特别注意版本问题 -->
            <version>1.4.5</version>
        </dependency>
            
         <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>
  1. Mapper类和实体类

Mapper类

public interface UserMapper extends BaseMapper<UserInfo> {

}

实体类

@Data
@EqualsAndHashCode(callSuper = false)
@TableName(value = "cls_user", autoResultMap = true)
@NoArgsConstructor
public class UserInfo {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    @TableField("user_name")
    private String userName;

    @TableField("password")
    private String password;
}
  1. 使用方法
    public PageInfo search() {
        PageHelper.startPage(1, 3);
        QueryWrapper<UserInfo> wrapper = new QueryWrapper();
        List<UserInfo> testEntityList = userMapper.selectList(wrapper);
        return new PageInfo<UserInfo>(testEntityList);
    }
  1. 运行结果
{
	"total": 11,
	"list": [
		{
			"id": 1,
			"userName": "cc",
			"password": "12345"
		},
		{
			"id": 2,
			"userName": "cc",
			"password": "12345"
		},
		{
			"id": 3,
			"userName": "cc",
			"password": "12345"
		}
	],
	"pageNum": 1,
	"pageSize": 3,
	"size": 3,
	"startRow": 1,
	"endRow": 3,
	"pages": 4,
	"prePage": 0,
	"nextPage": 2,
	"isFirstPage": true,
	"isLastPage": false,
	"hasPreviousPage": false,
	"hasNextPage": true,
	"navigatePages": 8,
	"navigatepageNums": [
		1,
		2,
		3,
		4
	],
	"navigateFirstPage": 1,
	"navigateLastPage": 4
}

源码解析

  1. 引入pagehelper-spring-boot-starter,Springboot会扫描jar包中的spring.factories,自动装配机制可以看我这篇文章,原理讲的透透的,SpringBoot自动装配的实现原理。在pagehelper-spring-boot-autoconfigure的jar中有spring.factories,会引入PageHelperAutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.github.pagehelper.autoconfigure.PageHelperAutoConfiguration
  1. PageHelperAutoConfiguration该类实现了InitializingBean,在启动的时候创建该对象之后会执行afterPropertiesSet,Spring的Bean的生命周期,详细了解可以看这篇文章。该类主要是添加了PageInterceptor进行拦截。
public class PageHelperAutoConfiguration implements InitializingBean {

    @Override
    public void afterPropertiesSet() throws Exception {
        PageInterceptor interceptor = new PageInterceptor();
        interceptor.setProperties(this.properties);
        for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
            org.apache.ibatis.session.Configuration configuration = sqlSessionFactory.getConfiguration();
            if (!containsInterceptor(configuration, interceptor)) {
                configuration.addInterceptor(interceptor);
            }
        }
    }
}
  1. PageInterceptor#intercept,该方法是拦截查询语句,分别组装查询总数的sql和分页查询的sql。
    public Object intercept(Invocation invocation) throws Throwable {
        try {
            Object[] args = invocation.getArgs();
            MappedStatement ms = (MappedStatement) args[0];
            Object parameter = args[1];
            RowBounds rowBounds = (RowBounds) args[2];
            ResultHandler resultHandler = (ResultHandler) args[3];
            Executor executor = (Executor) invocation.getTarget();
            CacheKey cacheKey;
            BoundSql boundSql;
            //由于逻辑关系,只会进入一次
            if (args.length == 4) {
                //4 个参数时
                boundSql = ms.getBoundSql(parameter);
                cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
            } else {
                //6 个参数时
                cacheKey = (CacheKey) args[4];
                boundSql = (BoundSql) args[5];
            }
            checkDialectExists();
            //对 boundSql 的拦截处理
            if (dialect instanceof BoundSqlInterceptor.Chain) {
                boundSql = ((BoundSqlInterceptor.Chain) dialect).doBoundSql(BoundSqlInterceptor.Type.ORIGINAL, boundSql, cacheKey);
            }
            List resultList;
            //调用方法判断是否需要进行分页,如果不需要,直接返回结果
            if (!dialect.skip(ms, parameter, rowBounds)) {
                //开启debug时,输出触发当前分页执行时的PageHelper调用堆栈
                // 如果和当前调用堆栈不一致,说明在启用分页后没有消费,当前线程再次执行时消费,调用堆栈显示的方法使用不安全
                debugStackTraceLog();
                //判断是否需要进行 count 查询
                if (dialect.beforeCount(ms, parameter, rowBounds)) {
                    //查询总数
                    Long count = count(executor, ms, parameter, rowBounds, null, boundSql);
                    //处理查询总数,返回 true 时继续分页查询,false 时直接返回
                    if (!dialect.afterCount(count, parameter, rowBounds)) {
                        //当查询总数为 0 时,直接返回空的结果
                        return dialect.afterPage(new ArrayList(), parameter, rowBounds);
                    }
                }
                resultList = ExecutorUtil.pageQuery(dialect, executor,
                        ms, parameter, rowBounds, resultHandler, boundSql, cacheKey);
            } else {
                //rowBounds用参数值,不使用分页插件处理时,仍然支持默认的内存分页
                resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
            }
            return dialect.afterPage(resultList, parameter, rowBounds);
        } finally {
            if (dialect != null) {
                dialect.afterAll();
            }
        }
    }
  1. PageHelper.startPage,核心就是组装Page对象存放到线程变量中。那么会导致线程污染吗?拦截器中有remove的操作吗?看到线程变量,最关心的点应该就是使用ThreadLocal可能会导致内存泄露,线程变量ThreadLocal详解,该篇对ThreadLocal的相关知识点讲的明明白白。
    public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {
        Page<E> page = new Page<E>(pageNum, pageSize, count);
        page.setReasonable(reasonable);
        page.setPageSizeZero(pageSizeZero);
        //当已经执行过orderBy的时候
        Page<E> oldPage = getLocalPage();
        if (oldPage != null && oldPage.isOrderByOnly()) {
            page.setOrderBy(oldPage.getOrderBy());
        }
        setLocalPage(page);
        return page;
    }
  1. PageHelper#skip,PageHelper判断是否需要分页查询。如果Page对象是null,则选择跳过分页查询的逻辑
    public boolean skip(MappedStatement ms, Object parameterObject, RowBounds rowBounds) {
        Page page = pageParams.getPage(parameterObject, rowBounds);
        if (page == null) {
            return true;
        } else {
            //设置默认的 count 列
            if (StringUtil.isEmpty(page.getCountColumn())) {
                page.setCountColumn(pageParams.getCountColumn());
            }
            autoDialect.initDelegateDialect(ms, page.getDialectClass());
            return false;
        }
    }

PageParams#getPage,会从线程变量中获取Page对象,进行组装

    public Page getPage(Object parameterObject, RowBounds rowBounds) {
        Page page = PageHelper.getLocalPage();
        if (page == null) {
            if (rowBounds != RowBounds.DEFAULT) {
                if (offsetAsPageNum) {
                    page = new Page(rowBounds.getOffset(), rowBounds.getLimit(), rowBoundsWithCount);
                } else {
                    page = new Page(new int[]{rowBounds.getOffset(), rowBounds.getLimit()}, rowBoundsWithCount);
                    //offsetAsPageNum=false的时候,由于PageNum问题,不能使用reasonable,这里会强制为false
                    page.setReasonable(false);
                }
                if (rowBounds instanceof PageRowBounds) {
                    PageRowBounds pageRowBounds = (PageRowBounds) rowBounds;
                    page.setCount(pageRowBounds.getCount() == null || pageRowBounds.getCount());
                }
            } else if (parameterObject instanceof IPage || supportMethodsArguments) {
                try {
                    page = PageObjectUtil.getPageFromObject(parameterObject, false);
                } catch (Exception e) {
                    return null;
                }
            }
            if (page == null) {
                return null;
            }
            PageHelper.setLocalPage(page);
        }
        //分页合理化
        if (page.getReasonable() == null) {
            page.setReasonable(reasonable);
        }
        //当设置为true的时候,如果pagesize设置为0(或RowBounds的limit=0),就不执行分页,返回全部结果
        if (page.getPageSizeZero() == null) {
            page.setPageSizeZero(pageSizeZero);
        }
        if (page.getKeepOrderBy() == null) {
            page.setKeepOrderBy(keepOrderBy);
        }
        if (page.getKeepSubSelectOrderBy() == null) {
            page.setKeepSubSelectOrderBy(keepSubSelectOrderBy);
        }
        return page;
    }
  1. PageHelper#afterAll,清除线程变量。所以PageInterceptor已经帮我们把线程变量给清除了,不会产生线程污染。
    @Override
    public void afterAll() {
        //这个方法即使不分页也会被执行,所以要判断 null
        AbstractHelperDialect delegate = autoDialect.getDelegate();
        if (delegate != null) {
            delegate.afterAll();
            autoDialect.clearDelegate();
        }
        clearPage();
    }

Mybatisplus的PageHelper

  1. 引入依赖
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus</artifactId>
            <version>2.1.9</version>
        </dependency>
  1. 引入Mybatis配置
@Configuration
public class MybatisPlusConfig {

    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
    }
  1. 分页查询,PageHelper是com.baomidou.mybatisplus.plugins.pagination.PageHelper
	@RequestMapping("/list")
    public Pagination search() {
        Wrapper wrapper = new EntityWrapper();
        PageHelper.startPage(1, 10);
        List<TestEntity> testEntityList = testMapper.selectList(wrapper);
        Pagination pagination = PageHelper.getPagination();
        PageHelper.remove();
        return pagination;
    }

源码解析

  1. PaginationInterceptor#intercept,核心也是从PageHelper中获取线程变量,对象封装存储在线程变量中。
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler)PluginUtils.realTarget(invocation.getTarget());
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        this.sqlParser(metaObject);
        MappedStatement mappedStatement = (MappedStatement)metaObject.getValue("delegate.mappedStatement");
        if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {
            return invocation.proceed();
        } else {
            RowBounds rowBounds = (RowBounds)metaObject.getValue("delegate.rowBounds");
            if (rowBounds == null || rowBounds == RowBounds.DEFAULT) {
                if (!this.localPage) {
                    return invocation.proceed();
                }

                rowBounds = PageHelper.getPagination();
                if (rowBounds == null) {
                    return invocation.proceed();
                }
            }

            BoundSql boundSql = (BoundSql)metaObject.getValue("delegate.boundSql");
            String originalSql = boundSql.getSql();
            Connection connection = (Connection)invocation.getArgs()[0];
            DBType dbType = StringUtils.isNotEmpty(this.dialectType) ? DBType.getDBType(this.dialectType) : JdbcUtils.getDbType(connection.getMetaData().getURL());
            if (rowBounds instanceof Pagination) {
                Pagination page = (Pagination)rowBounds;
                boolean orderBy = true;
                if (page.isSearchCount()) {
                    SqlInfo sqlInfo = SqlUtils.getOptimizeCountSql(page.isOptimizeCountSql(), this.sqlParser, originalSql);
                    orderBy = sqlInfo.isOrderBy();
                    this.queryTotal(this.overflowCurrent, sqlInfo.getSql(), mappedStatement, boundSql, page, connection);
                    if (page.getTotal() <= 0) {
                        return invocation.proceed();
                    }
                }

                String buildSql = SqlUtils.concatOrderBy(originalSql, page, orderBy);
                originalSql = DialectFactory.buildPaginationSql(page, buildSql, dbType, this.dialectClazz);
            } else {
                originalSql = DialectFactory.buildPaginationSql((RowBounds)rowBounds, originalSql, dbType, this.dialectClazz);
            }

            metaObject.setValue("delegate.boundSql.sql", originalSql);
            metaObject.setValue("delegate.rowBounds.offset", 0);
            metaObject.setValue("delegate.rowBounds.limit", 2147483647);
            return invocation.proceed();
        }
    }

总结一下

  1. Mybatis-Plus的selectPage是我最喜欢用的,不需要引入额外的jar包;pagehelper-spring-boot-starter中的PageHelper.startPage,封装形式也很简单。

  2. Mybatisplus的PageHelper真的是大坑,在项目中我以为是pagehelper-spring-boot-starter类的PageHelper,结果查询结果中pages返回的数据一直是0,跟踪源码发现PageHelper获取的Page对象为null,不进行分页查询。跟踪设置环境变量的方法,发现原来是调用的MybatisPlus老版本的PageHelper,关键的是Mybatis-Plus使用PageHelper和pagehelper-spring-boot-starter使用PageHelper格式会如此的相似。这意味着不看包名,很容易搞混。这种出现一样的类名很容易出现你以为用的是A包,实际上用的是B包的情况。
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/389651.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ifm3dlib+Python实现摄像头点云数据保存

0. 起因&需求 现有一款摄像头 O3D303&#xff0c;通过网线将其连接到局域网后&#xff0c;同一局域网的电脑可以通过可视化软件查看到各项参数以及对应的点云图。 但是如果想定制化具体的需求&#xff0c;用官方的可视化软件无疑是不可取的。这时候就需要用到SDK&#xf…

【Java】JVM

一、介绍 1.什么是JVM? JVM是一种用于计算设备的规范&#xff0c;它是一个虚构出来的机器&#xff0c;是通过在实际的计算机上仿真模拟各种功能实现的。JVM包含一套字节码指令集&#xff0c;一组寄存器&#xff0c;一个栈&#xff0c;一个垃圾回收堆和一个存储方法域。JVM屏…

面向对象设计模式:创建型模式之抽象工厂模式

一、抽象工厂模式&#xff0c;Abstract Factory Pattern 1.1 Definition 定义 抽象工厂模式是围绕一个抽象工厂&#xff08;其他工厂的工厂&#xff09;创建其他工厂的创建型模式。 1.2 Intent 意图 Provide an interface for creating families of related or dependent o…

【AutoSAR】【MCAL】Dio

一、结构 二、功能介绍 DIO&#xff08;数字输入输出&#xff09;驱动模块主要是对端口&#xff08;Port&#xff09;&#xff0c;通道&#xff08;Channel&#xff09;和通道组&#xff08;ChannelGroup&#xff09;进行读写操作。 通道&#xff08;Channel&#xff09;&…

Tomcat服务器配置以及问题解决方案

文章目录01 Tomcat简介02 Tomcat的安装03 Tomcat的使用启动Tomcat服务器 &#xff08;解决一闪而过&#xff09;测试 Tomcat 是否启动Tomcat 服务器的关闭04 Tomcat的配置配置端口控制台配置&#xff08;乱码解决&#xff09;部署工程到Tomcat中01 Tomcat简介 Tomcat是一款开源…

Android Compose——一个简单的Bilibili APP

Bilibili移动端APP简介依赖效果登录效果WebView自定义TobRow的Indicator大小首页推荐LazyGridView使用Paging3热门排行榜搜索模糊搜索富文本搜索结果视频详情合集信息Coroutines进行网络请求管理&#xff0c;避免回调地狱添加suspendwithContextGit项目链接末简介 此Demo采用A…

Motor-DK (MM32SPIN05PF, MM32SPIN06PF, MM32SPIN07PF)

输入电压范围&#xff1a;12V - 30V 使用 60V / 40A N-MOS 管 使用内建&#xff08;MM32SPIN2x&#xff09;/外挂&#xff08;MM32SPIN05 / MM32SPIN06 / MM32SPIN07&#xff09;GBW 6MHz 高速运放 x 4 MCU 使用 5V 供电 支持 48 / 64 Pin MM32SPIN 系列 MCU 支持无霍尔&#x…

LearnDash测验报告如何帮助改进您的课程

某一个场景。Pennywell 大学有一门课程“Introduction to Linear Algebra”。上学期进行了两次测验。20% 的学生在第一次测验中不及格&#xff0c;而 80% 在第二次测验中不及格。在进一步评估中&#xff0c;观察到第一次测验不及格的学生在第二次测验中也不及格。在第二次测验中…

基于Linux系统-搭建Java Web开发环境

目录 1. 安装JDK 2.安装MySQL数据库 3.安装Tomcat 4.访问Tomcat 1. 安装JDK 1.执行以下命令&#xff0c;查看yum源中JDK版本。 yum list java* 2.执行以下命令&#xff0c;使用yum安装JDK1.8。 yum -y install java-1.8.0-openjdk* 3.执行以下命令&#xff0c;查看是否安…

【软件使用】MarkText下载安装与汉化设置 (markdown快捷键收藏)

一、安装与汉化 对版本没要求的可以直接选择 3、免安装的汉化包 1、下载安装MarkText MaxText win64 https://github.com/marktext/marktext/releases/download/v0.17.1/marktext-setup.exe 使用迅雷可以快速下载 2. 配置中文语言包 中文包下载地址&#xff1a;GitHub - chi…

TPU编程竞赛系列|算能赛道冠军SO-FAST团队获第十届CCF BDCI总决赛特等奖!

近日&#xff0c;第十届中国计算机学会&#xff08;CCF&#xff09;大数据与计算智能大赛总决赛暨颁奖典礼在苏州顺利落幕&#xff0c;算能赛道的冠军队伍SO-FAST从2万余支队伍中脱颖而出&#xff0c;获得了所有赛道综合评比特等奖&#xff01; 本届CCF大赛吸引了来自全国的2万…

【MySQL】查询访问方法

查询语句经过查询优化器生成 SQL 执行计划&#xff0c;在引入索引的情况下&#xff0c;MySQL 不可能让我们什么查询都是走全表扫描&#xff0c;那样效率太低了&#xff0c;所有需要有各种各样的执行计划 &#xff0c; MySQL 会根据经验为我们的查询语句生成它认为最优的执行计划…

mac安装nvm

1、nvm介绍 &#xff08;1&#xff09;什么是nvm&#xff1f;简单来说&#xff0c;nvm是一款可以用命令行快速切换node版本的工具&#xff01; &#xff08;2&#xff09;为什么要切换node版本&#xff1f;打个比方&#xff0c;你目前正在用node 14版本&#xff0c;现在出了nod…

Greenplum-主备同步机制

我们在学习Greenplum的架构时知道&#xff0c;Greenplum中主要有Master管理层和Segment计算层。在高可用方面&#xff0c;Master通过配置一个Standby来实现主备&#xff0c;Segment则通过对实例设置镜像的方式也实现主备高可用&#xff08;其中主实例称为Primary&#xff0c;备…

网络基础(二)

目录 应用层 再谈 "协议" 协议是一种 "约定". socket api的接口, 在读写数据时, 都是按 "字符串" 的方式来发送接收的. 如果我们要传输一些"结构化的数据" 怎么办呢? 为什么要转换呢&#xff1f; 如果我们将struct message里面…

传输线的物理基础(二):信号在传输线中的速度

铜中电子的速度信号在传输线上传输的速度有多快&#xff1f;如果人们经常错误地认为信号在传输线上的速度取决于导线中电子的速度。凭着这种错误的直觉&#xff0c;我们可能会想象降低互连的电阻会提高信号的速度。事实上&#xff0c;典型铜线中电子的速度实际上比信号速度慢约…

OpenWrt 软路由 IPV6 配置 DDNS

一、申请 dynv6 账号 1、去官网注册一个账号&#xff0c;不过人机验证那块需要 "梯子" 才能注册成功 Free dynamic DNS for IPv6 2、注册成功后&#xff0c;创建一个 Domain 3、这是我创建好的 4、获取 密码&#xff0c;后面需要用到 二、配置 DDNS 1、点击服务菜…

C++之单例模式

目录 1. 请设计一个类&#xff0c;只能在堆上创建对象 2. 请设计一个类&#xff0c;只能在栈上创建对象 3.请设计一个类&#xff0c;不能被拷贝 C98 C11 4. 请设计一个类&#xff0c;不能被继承 C98 C11 5. 请设计一个类&#xff0c;只能创建一个对象(单例模式) 设计…

在GoLand中编译cgo程序

GoLand的C使用问题为什么会出现fyne之类的包无法在GoLand中运行安装gcc安装make安装Choco安装指令使用Choco安装make将make加入Path将GoLand的make路径进行替换测试&#xff0c;编译fyne程序问题 本人go萌新&#xff0c;正在学习golang的界面包fyen&#xff0c;但是在使用GoLa…

Android面试题——JVM

Android平台的虚拟机是基于栈的吗&#xff1f; JVM运行时数据区 运行时栈 基于栈的虚拟机 第0行表示将一个Int型的1推送至操作数栈栈顶&#xff0c;程序计数器指向第0行。第一行字节码表示将栈顶的int型数值存入第一个本地变量&#xff0c;这两行代码就进行了给局部变量赋值的操…