mybatis实现分页的几种方式

本文目录

  • 借助数组进行分页
  • 借助Sql语句进行分页
  • 拦截器分页
  • RowBounds实现分页

借助数组进行分页

原理:进行数据库查询操作时,获取到数据库中所有满足条件的记录,保存在应用的临时数组中,再通过List的subList方法,获取到满足条件的所有记录。

实现
首先在dao层,创建StudentMapper接口,用于对数据库的操作。在接口中定义通过数组分页的查询方法,如下所示:

 List<Student> queryStudentsByArray();

方法很简单,就是获取所有的数据,通过list接收后进行分页操作。

创建StudentMapper.xml文件,编写查询的sql语句:

 <select id="queryStudentsByArray"  resultMap="studentmapper">select * from student</select>

可以看出再编写sql语句的时候,我们并没有作任何分页的相关操作。这里是查询到所有的学生信息。

接下来在service层获取数据并且进行分页实现:

定义IStuService接口,并且定义分页方法:

List<Student> queryStudentsByArray(int currPage, int pageSize);

通过接收currPage参数表示显示第几页的数据,pageSize表示每页显示的数据条数。

创建IStuService接口实现类StuServiceIml对方法进行实现,对获取到的数组通过currPage和pageSize进行分页:

 @Overridepublic List<Student> queryStudentsByArray(int currPage, int pageSize) {List<Student> students = studentMapper.queryStudentsByArray();
//        从第几条数据开始int firstIndex = (currPage - 1) * pageSize;
//        到第几条数据结束int lastIndex = currPage * pageSize;return students.subList(firstIndex, lastIndex);}

通过subList方法,获取到两个索引间的所有数据。

最后在controller中创建测试方法:

  @ResponseBody@RequestMapping("/student/array/{currPage}/{pageSize}")public List<Student> getStudentByArray(@PathVariable("currPage") int currPage, @PathVariable("pageSize") int pageSize) {List<Student> student = StuServiceIml.queryStudentsByArray(currPage, pageSize);return student;}

通过用户传入的currPage和pageSize获取指定数据。

测试:

首先我们来获取再没实现分页效果前获取到的所有数据,如下所示:
在这里插入图片描述
接下来在浏览器输入http://localhost:8080/student/student/array/1/2测试实现了分页后的数据。获取第一页的数据,每页显示两条数据。

结果如下
在这里插入图片描述
输出的是指定的从第0-2条数据,可见我们通过数组分页的功能是成功的。(这里因为用到了关联查询,所以看起来数据可能比较多)

缺点:数据库查询并返回所有的数据,而我们需要的只是极少数符合要求的数据。当数据量少时,还可以接受。当数据库数据量过大时,每次查询对数据库和程序的性能都会产生极大的影响。

借助Sql语句进行分页

在了解到通过数组分页的缺陷后,我们发现不能每次都对数据库中的所有数据都检索。然后在程序中对获取到的大量数据进行二次操作,这样对空间和性能都是极大的损耗。所以我们希望能直接在数据库语言中只检索符合条件的记录,不需要在通过程序对其作处理。这时,Sql语句分页技术横空出世。

实现:通过sql语句实现分页也是非常简单的,只是需要改变我们查询的语句就能实现了,即在sql语句后面添加limit分页语句。

首先还是在StudentMapper接口中添加sql语句查询的方法,如下:

List<Student> queryStudentsBySql(Map<String,Object> data);

然后在StudentMapper.xml文件中编写sql语句通过limiy关键字进行分页:

 <select id="queryStudentsBySql" parameterType="map" resultMap="studentmapper">select * from student limit #{currIndex} , #{pageSize}
</select>

接下来还是在IStuService接口中定义方法,并且在StuServiceIml中对sql分页实现。

List<Student> queryStudentsBySql(int currPage, int pageSize)
 @Overridepublic List<Student> queryStudentsBySql(int currPage, int pageSize) {Map<String, Object> data = new HashedMap();data.put("currIndex", (currPage-1)*pageSize);data.put("pageSize", pageSize);return studentMapper.queryStudentsBySql(data);}

sql分页语句如下:select * from table limit index, pageSize;

所以在service中计算出currIndex:要开始查询的第一条记录的索引。

测试:

在浏览器输入http://localhost:8080/student/student/sql/1/2获取第一页的数据,每页显示两条数据。

结果:
在这里插入图片描述
从输出结果可以看出和数组分页的结果是一致的,因此sql语句的分页也是没问题的。

缺点:虽然这里实现了按需查找,每次检索得到的是指定的数据。但是每次在分页的时候都需要去编写limit语句,很冗余。而且不方便统一管理,维护性较差。所以我们希望能够有一种更方便的分页实现。

拦截器分页

上面提到的数组分页和sql语句分页都不是我们今天讲解的重点,今天需要实现的是利用拦截器达到分页的效果。自定义拦截器实现了拦截所有以ByPage结尾的查询语句,并且利用获取到的分页相关参数统一在sql语句后面加上limit分页的相关语句,一劳永逸。不再需要在每个语句中单独去配置分页相关的参数了。。

首先我们看一下拦截器的具体实现,在这里我们需要拦截所有以ByPage结尾的所有查询语句,因此要使用该拦截器实现分页功能,那么再定义名称的时候需要满足它拦截的规则(以ByPage结尾),如下所示:

package com.cbg.interceptor;import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;import java.sql.Connection;
import java.util.Map;
import java.util.Properties;/*** Created by chenboge on 2017/5/7.* <p>* Email:baigegechen@gmail.com* <p>* description:*//*** @Intercepts 说明是一个拦截器* @Signature 拦截器的签名* type 拦截的类型 四大对象之一( Executor,ResultSetHandler,ParameterHandler,StatementHandler)* method 拦截的方法* args 参数*/
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class MyPageInterceptor implements Interceptor {//每页显示的条目数private int pageSize;
//当前现实的页数private int currPage;private String dbType;@Overridepublic Object intercept(Invocation invocation) throws Throwable {//获取StatementHandler,默认是RoutingStatementHandlerStatementHandler statementHandler = (StatementHandler) invocation.getTarget();//获取statementHandler包装类MetaObject MetaObjectHandler = SystemMetaObject.forObject(statementHandler);//分离代理对象链while (MetaObjectHandler.hasGetter("h")) {Object obj = MetaObjectHandler.getValue("h");MetaObjectHandler = SystemMetaObject.forObject(obj);}while (MetaObjectHandler.hasGetter("target")) {Object obj = MetaObjectHandler.getValue("target");MetaObjectHandler = SystemMetaObject.forObject(obj);}//获取连接对象//Connection connection = (Connection) invocation.getArgs()[0];//object.getValue("delegate");  获取StatementHandler的实现类//获取查询接口映射的相关信息MappedStatement mappedStatement = (MappedStatement) MetaObjectHandler.getValue("delegate.mappedStatement");String mapId = mappedStatement.getId();//statementHandler.getBoundSql().getParameterObject();//拦截以.ByPage结尾的请求,分页功能的统一实现if (mapId.matches(".+ByPage$")) {//获取进行数据库操作时管理参数的handlerParameterHandler parameterHandler = (ParameterHandler) MetaObjectHandler.getValue("delegate.parameterHandler");//获取请求时的参数Map<String, Object> paraObject = (Map<String, Object>) parameterHandler.getParameterObject();//也可以这样获取//paraObject = (Map<String, Object>) statementHandler.getBoundSql().getParameterObject();//参数名称和在service中设置到map中的名称一致currPage = (int) paraObject.get("currPage");pageSize = (int) paraObject.get("pageSize");String sql = (String) MetaObjectHandler.getValue("delegate.boundSql.sql");//也可以通过statementHandler直接获取//sql = statementHandler.getBoundSql().getSql();//构建分页功能的sql语句String limitSql;sql = sql.trim();limitSql = sql + " limit " + (currPage - 1) * pageSize + "," + pageSize;//将构建完成的分页sql语句赋值个体'delegate.boundSql.sql',偷天换日MetaObjectHandler.setValue("delegate.boundSql.sql", limitSql);}
//调用原对象的方法,进入责任链的下一级return invocation.proceed();}//获取代理对象@Overridepublic Object plugin(Object o) {//生成object对象的动态代理对象return Plugin.wrap(o, this);}//设置代理对象的参数@Overridepublic void setProperties(Properties properties) {
//如果项目中分页的pageSize是统一的,也可以在这里统一配置和获取,这样就不用每次请求都传递pageSize参数了。参数是在配置拦截器时配置的。String limit1 = properties.getProperty("limit", "10");this.pageSize = Integer.valueOf(limit1);this.dbType = properties.getProperty("dbType", "mysql");}
}

上面即是拦截器功能的实现,在intercept方法中获取到select标签和sql语句的相关信息,拦截所有以ByPage结尾的select查询,并且统一在查询语句后面添加limit分页的相关语句,统一实现分页功能。

重点详解:
StatementHandler是一个接口,而我们在代码中通过StatementHandler statementHandler = (StatementHandler) invocation.getTarget();获取到的是StatementHandler默认的实现类RoutingStatementHandler。而RoutingStatementHandler只是一个中间代理,他不会提供具体的方法。那你可能会纳闷了,拦截器中基本上是依赖statementHandler获取各种对象和属性的,没有具体属性和方法怎么行??接着看下面代码:

private final StatementHandler delegate;public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {switch(RoutingStatementHandler.SyntheticClass_1.$SwitchMap$org$apache$ibatis$mapping$StatementType[ms.getStatementType().ordinal()]) {case 1:this.delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;case 2:this.delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;case 3:this.delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);break;default:throw new ExecutorException("Unknown statement type: " + ms.getStatementType());}}

原来它是通过不同的MappedStatement创建不同的StatementHandler实现类对象处理不同的情况。这里的到的StatementHandler实现类才是真正服务的。看到这里,你可能就会明白MappedStatement mappedStatement = (MappedStatement) MetaObjectHandler.getValue(“delegate.mappedStatement”);中delegate的来源了吧。至于为什么要这么去获取,后面我们会说道。

拿到statementHandler后,我们会通过MetaObject MetaObjectHandler = SystemMetaObject.forObject(statementHandler);去获取它的包装对象,通过包装对象去获取各种服务。

MetaObject:mybatis的一个工具类,方便我们有效的读取或修改一些重要对象的属性。四大对象(ResultSetHandler,ParameterHandler,Executor和statementHandler)提供的公共方法很少,要想直接获取里面属性的值很困难,但是可以通过MetaObject利用一些技术(内部反射实现)很轻松的读取或修改里面的数据。

接下来说说:MappedStatement mappedStatement = (MappedStatement) MetaObjectHandler.getValue(“delegate.mappedStatement”);

上面提到为什么要这么去获取MappedStatement对象??在RoutingStatementHandler中delegate是私有的(private final StatementHandler delegate;),有没有共有的方法去获取。所以这里只有通过反射来获取啦。

MappedStatement是保存了xxMapper.xml中一个sql语句节点的所有信息的包装类,可以通过它获取到节点中的所有信息。在示例中我们拿到了id值,也就是方法的名称,通过名称区拦截所有需要分页的请求。

通过StatementHandler的包装类,不光能拿到MappedStatement,还可以拿到下面的数据:

    public abstract class BaseStatementHandler implements StatementHandler {protected final Configuration configuration;protected final ObjectFactory objectFactory;protected final TypeHandlerRegistry typeHandlerRegistry;protected final ResultSetHandler resultSetHandler;protected final ParameterHandler parameterHandler;protected final Executor executor;protected final MappedStatement mappedStatement;protected final RowBounds rowBounds;protected BoundSql boundSql;protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {this.configuration = mappedStatement.getConfiguration();this.executor = executor;this.mappedStatement = mappedStatement;this.rowBounds = rowBounds;this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();this.objectFactory = this.configuration.getObjectFactory();if(boundSql == null) {this.generateKeys(parameterObject);boundSql = mappedStatement.getBoundSql(parameterObject);}this.boundSql = boundSql;this.parameterHandler = this.configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);this.resultSetHandler = this.configuration.newResultSetHandler(executor, mappedStatement, rowBounds, this.parameterHandler, resultHandler, boundSql);}

上面的所有数据都可以通过反射拿到。

几个重要的参数:
Configuration:所有配置的相关信息。
ResultSetHandler:用于拦截执行结果的组装。
ParameterHandler:拦截执行Sql的参数的组装。
Executor:执行Sql的全过程,包括组装参数、组装结果和执行Sql的过程。
BoundSql:执行的Sql的相关信息。

接下来我们通过如下代码拿到请求时的map对象(反射)。

 //获取进行数据库操作时管理参数的handlerParameterHandler parameterHandler = (ParameterHandler) MetaObjectHandler.getValue("delegate.parameterHandler");//获取请求时的参数Map<String, Object> paraObject = (Map<String, Object>) parameterHandler.getParameterObject();//也可以这样获取//paraObject = (Map<String, Object>) statementHandler.getBoundSql().getParameterObject();

拿到我们需要的currPage和pageSize参数后,就是组装分页查询的sql语句’limitSql‘了。

最后通过MetaObjectHandler.setValue(“delegate.boundSql.sql”, limitSql);将原始的sql语句替换成我们新的分页语句,完成偷天换日的功能,接下来让代码继续执行。

编写好拦截器后,需要注册到项目中,才能发挥它的作用。在mybatis的配置文件中,添加如下代码:

    <plugins><plugin interceptor="com.cbg.interceptor.MyPageInterceptor"><property name="limit" value="10"/><property name="dbType" value="mysql"/></plugin></plugins>

如上所示,还能在里面配置一些属性,在拦截器的setProperties方法中可以获取配置好的属性值。如项目分页的pageSize参数的值固定,我们就可以配置在这里了,以后就不需要每次传入pageSize了,读取方式如下:

 //读取配置的代理对象的参数@Overridepublic void setProperties(Properties properties) {String limit1 = properties.getProperty("limit", "10");this.pageSize = Integer.valueOf(limit1);this.dbType = properties.getProperty("dbType", "mysql");}

到这里,有关拦截器的相关知识就讲解的差不多了,接下来就需要测试,是否我们这样写真的有效??

首先还是添加dao层的方法和xml文件的sql语句配置,注意项目中拦截的是以ByPage结尾的请求,所以在这里,我们的方法名称也以此结尾:

//方法
List<Student> queryStudentsByPage(Map<String,Object> data);
<!--xml文件的select语句--><select id="queryStudentsByPage" parameterType="map" resultMap="studentmapper">select * from student</select>

可以看出,这里我们就不需要再去手动配置分页语句了。

接下来是service层的接口编写和实现方法:

//方法:
List<Student> queryStudentsByPage(int currPage,int pageSize);
//实现:@Overridepublic List<Student> queryStudentsByPage(int currPage, int pageSize) {Map<String, Object> data = new HashedMap();data.put("currPage", currPage);data.put("pageSize", pageSize);return studentMapper.queryStudentsByPage(data);}

这里我们虽然传入了currPage和pageSize两个参数,但是在sql的xml文件中并没有使用,直接在拦截器中获取到统一使用。

最后编写controller的测试代码:

 @ResponseBody@RequestMapping("/student/page/{currPage}/{pageSize}")public List<Student> getStudentByPage(@PathVariable("currPage") int currPage, @PathVariable("pageSize") int pageSize) {List<Student> student = StuServiceIml.queryStudentsByPage(currPage, pageSize);return student;}

测试:
在浏览器输入:http://localhost:8080/student/student/page/1/2

结果:
在这里插入图片描述
可见和上面两种分页的效果是一样的。

RowBounds实现分页

原理:通过RowBounds实现分页和通过数组方式分页原理差不多,都是一次获取所有符合条件的数据,然后在内存中对大数据进行操作,实现分页效果。只是数组分页需要我们自己去实现分页逻辑,这里更加简化而已。

存在问题:一次性从数据库获取的数据可能会很多,对内存的消耗很大,可能导师性能变差,甚至引发内存溢出。

适用场景:在数据量很大的情况下,建议还是适用拦截器实现分页效果。RowBounds建议在数据量相对较小的情况下使用。

简单介绍:这是代码实现上最简单的一种分页方式,只需要在dao层接口中要实现分页的方法中加入RowBounds参数,然后在service层通过offset(从第几行开始读取数据,默认值为0)和limit(要显示的记录条数,默认为java允许的最大整数:2147483647)两个参数构建出RowBounds对象,在调用dao层方法的时,将构造好的RowBounds传进去就能轻松实现分页效果了。

具体操作如下:

dao层接口方法:

//加入RowBounds参数
public List<UserBean> queryUsersByPage(String userName, RowBounds rowBounds);

然后在service层构建RowBounds,调用dao层方法:

  @Override@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.SUPPORTS)public List<RoleBean> queryRolesByPage(String roleName, int start, int limit) {return roleDao.queryRolesByPage(roleName, new RowBounds(start, limit));}

RowBounds就是一个封装了offset和limit简单类,如下所示:

public class RowBounds {public static final int NO_ROW_OFFSET = 0;public static final int NO_ROW_LIMIT = 2147483647;public static final RowBounds DEFAULT = new RowBounds();private int offset;private int limit;public RowBounds() {this.offset = 0;this.limit = 2147483647;}public RowBounds(int offset, int limit) {this.offset = offset;this.limit = limit;}public int getOffset() {return this.offset;}public int getLimit() {return this.limit;}
}

只需要这两步操作,就能轻松实现分页效果了,是不是很神奇。但却不简单,内部是怎么实现的??给大家提供一个简单的思路:RowBounds分页简单原理

结论:从上面四种sql分页的实现方式可以看出,通过RowBounds实现是最简便的,但是通过拦截器的实现方式是最优的方案。只需一次编写,所有的分页方法共同使用,还可以避免多次配置时的出错机率,需要修改时也只需要修改这一个文件,一劳永逸。而且是我们自己实现的,便于我们去控制和增加一些逻辑处理,使我们在外层更简单的使用。同时也不会出现数组分页和RowBounds分页导致的性能问题。当然,具体情况可以采取不同的解决方案。数据量小时,RowBounds不失为一种好办法。但是数据量大时,实现拦截器就很有必要了。

到这里,mybatis的分页原理和全部实现过程都完成了,还有不清楚的可以自己去看一下mybatis的源码,按照这个思路去阅读还是比较清晰的。

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

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

相关文章

【分布式应用】ELK企业级日志分析系统

一、ELK 简介 ELK平台是一套完整的日志集中处理解决方案&#xff0c;将 ElasticSearch、Logstash 和 Kiabana 三个开源工具配合使用&#xff0c; 完成更强大的用户对日志的查询、排序、统计需求。 1.1 ELK各组件介绍 ElasticSearch&#xff1a; 是基于Lucene&#xff08;一个…

Rust每日一练(Leetday0016) 全排列I\II、旋转图像

目录 46. 全排列 Permutations &#x1f31f;&#x1f31f; 47. 全排列 II Permutations II &#x1f31f;&#x1f31f; 48. 旋转图像 Rotate Image &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专…

win10操作系统官网如何下载ios境像文件安装操作系统

1.打开官网 2.立即下载工具 3.正在准备进行工作 4.接收条款 5.根据需求选择安装合适的位置 6.等待创建成功 7.右键选择装载 8.双击安装setup.exe文件 8 使用微Pe安装过程中发现需要联网更新解决 winR 然后cmd 输入 OOBE\BYPASSNRO 9 或者关闭此进程 电脑不要插网线&#xff…

虚拟机系统iso镜像下载_如何下载正版系统镜像

许多网友想装系统&#xff0c;可是不会装(后期会推装系统的文章)&#xff0c;而且网上的系统有的装有全家桶&#xff0c;有的又有捆绑软件&#xff0c;严重的还有病毒。那么&#xff0c;在哪里去下载正版&#xff0c;安全的系统镜像呢 "MSDN&#xff0c;我告诉你"这个…

自定义IOS系统弹框

写在之前 系统的弹框虽然很好看&#xff0c;但是有时候无法完全满足UI设计的需求&#xff0c;比如说中间要显示一个输入框&#xff0c;或者要放置一张图片&#xff0c;这里介绍一个很早之前的自定义弹框库&#xff0c;这个自定义弹框sdk写的很细致&#xff0c;定制性很强&…

华为vivo小米魅族oppo荣耀安卓手机刷IOS苹果系统

现在的刷机教程层出不穷&#xff0c;但是没有哪个大神可以开发出一款安卓刷IOS的工具&#xff0c;使得安卓手机或者安卓平板变成IOS苹果系统&#xff0c;这个工具除了可以刷安卓手机也可以刷安卓平板&#xff0c;网友实测&#xff0c;如果设备不支持的话软件会提醒不支持&#…

android高仿ios11系统,安卓仿ios11桌面全套文件

安卓手机想要苹果11的桌面的话&#xff0c;安卓仿ios11桌面全套文件app就可以帮您实现哦。安卓仿ios11桌面全套文件app是一款超级优质的手机桌面工具软件&#xff0c;平台上面拥有海量的壁纸资源&#xff0c;大家可以任意选取哦。 应用说明 修改负一屏效果 修改dock栏模糊度 修…

软件配置 | ios系统Clion下载、安装、配置环境

软件配置 软件配置 | ios系统Clion下载、安装、配置环境 软件配置下载、安装gClion 调试参考链接下载、安装调试 本文总结ios系统下Clion下载、安装、配置环境过程和可能碰到的问题。 因为是在私人电脑&#xff08;ios&#xff09;上配置的Clion&#xff0c;我还不太熟悉自己的…

iOS 13.0 至 13.7 平刷系统教程

来自 : 某QQ群分享文件&#xff0c;整理后发布 如侵权告知删除 关于 Succession 平刷插件信息&#xff1a; 插件&#xff1a;Succession 版本&#xff1a;1.4.15 开发者&#xff1a;Samg_is_a_Ninja 系统支持&#xff1a;iOS 10 至 13.7 用途&#xff1a;平刷当前系统 补充&am…

ios添加邮件收件服务器,iOS 系统邮件的基础使用

尽管有好多人的主力邮件是 Gmail,客户端的首选也是 Gmail,但是在国内的互联网环境中不可避免的要使用到本土的一些邮件服务。对于这些轻量级的服务我选择用系统自带的邮件应用进行统一的管理。本文主要讲解在系统自带邮件中,如何使用国内常用的邮件服务:如网易邮箱、QQ 邮箱…

关闭app服务器系统,ios12系统服务哪些关掉

类型&#xff1a;ios系统工具大小&#xff1a;38.7M语言&#xff1a;中文 评分&#xff1a;10.0 标签&#xff1a; 立即下载 ios12更新后&#xff0c;很多服务功能都开启了&#xff0c;是比较费电的&#xff0c;用户可以前往设置进行关闭来省电等&#xff0c;那ios12系统服务哪…

ios android 系统占用空间,iOS 系统占用了 20G 储存空间?别担心,教你快速解决!...

如果你曾经查看过自己 iOS 设备的储存空间使用情况&#xff0c;那么你可能见到过下面这种情形&#xff1a; 你可能发现了&#xff0c;系统所占据的空间太大了&#xff0c;远超过了正常的范围&#xff0c;这是怎么回事呢&#xff1f;下面就为大家解释一下出现这类情形的几种解决…

如何关闭iOS系统自动更新?最新屏蔽升级描述文件下载

新老司机朋友们晚上好&#xff0c;隔壁老王又来和大家见面了&#xff0c;鸽了有一段时间了&#xff0c;非常抱歉~ 众所周知&#xff0c;iOS 系统一直保持着较高的更新频率&#xff0c;但并不是所有用户都喜欢将自己的设备升级到最新版本&#xff0c;尤其是一些旧型号的 iPhone…

如何下载和安装 iOS开发教程

步骤 1: 在App Uploader网站&#xff0c;点击“Windows版本”下载 。 步骤 2: 在弹出框中&#xff0c;点击“下载”。 步骤 3&#xff1a;在弹出框中&#xff0c;点击“文件夹”打开。 步骤:4&#xff1a;右键选择“解压全部文件”。 步骤5&#xff1a;在弹出框中&#xff0c…

ios系统安装包下载_iOS在后台自动升级?一招教你屏蔽iOS更新

苹果产品一直被用户喜爱的最大的特点就是因为iOS系统&#xff0c;iOS系统有着流畅的体验以及完善的生态环境&#xff0c;因此历年新款iPhone都处于畅销状态。但是随着苹果旗下产品发布的越来越多&#xff0c;比如到目前为止苹果已经发布了15款iPhone产品&#xff0c;从最早的第…

ios系统安装包下载_iOS 屏蔽系统升级,描述文件版本已复活,无需越狱,请速度下载!...

沉寂已久的老方法,利用描述文件屏蔽 iOS 系统更新,目前已经正式恢复,再也不担心会自动下载更新包啦。 以往都是安装 tvOS 系列的描述文件,绕过苹果验证更新,从而达到阻止系统升级的目的,但是自 iOS 13 开始,安装 tvOS 12 会提示已过期或者失效。 而在当时,安装 tvOS 13…

如何下载和安装iOS 15公测版【附更新建议】

苹果在WWDC2021开发者大会上正式发布了iOS 15和iPadOS 15系统&#xff0c;推出了许多新功能&#xff0c;例如FaceTime共享、专注模式、实时文本、离线Siri、全新设计的通知等等。iOS 15和iPadOS 15的第一个公开测试版已于6月30日发布。 相信不少果粉已经迫不及待想要升级体验。…

氟化物超标怎么处理

项目基本信息 工艺及产品信息 甲方 / 采用工艺 过滤系统螯合树脂除氟系统 工程公司 / 工艺原理 废水除氟&#xff0c;耐受氯离子、硫酸根等阴离子的干扰 开始时间 2020/11/12 工艺特点 再生周期长 结束时间 2020/11/30 型号 CH-87 项目周期及项目地 15天 江…

lecory 波形二进制文件头(.trc)定义

1.文件头&#xff0c;从0字节开始 byte[] lecroy_trc_header new byte[]{0x23,0x39,0x30,0x30,0x32,0x30,0x30,0x30,0x31,0x34,0x34,0x57,0x41,0x56,0x45,0x44,0x45,0x53,0x43,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x4C,0x45,0x43,0x52,0x4F,0x59,0x5F,0x32,0x5F,0x33,0x…

playwright - 剧作家, 端对端测试

本文基于 playwright v1.34.3, node v18.16.0 注意&#xff1a;playwright 版本随 node lts 版本更新而更新&#xff0c;所以请确保 node 版本与 playwright 版本匹配。 写在前面 前端开发阶段&#xff0c;少不了测试&#xff0c;一般包含两类测试&#xff1a;单元测试、端对端…