mybatis分页实现总结

1.mybatis拦截器相关知识

1.作用

mybatis的拦截器是mybatis提供的一个拓展机制,允许用户在使用时根据各自的需求对sql执行的各个阶段进行干预。比较常见的如对执行的sql进行监控,排查sql的执行时间,对sql进行拦截拼接需要的场景,对最终结果进行如加密等处理。

2.拦截器分类

mybatis提供了四种拦截器供使用,按拦截的执行顺序展示
Executor->StatementHandler->ParameterHandler->ResultHandler

/*
用途:拦截执行器的方法,
可以用来修改执行sql的操作(query,update)
对事务进行操作(commit,rollback,getTransaction),
开启缓存(createCacaheKey,isCached),看方法上注释
*/
public interface Executor {ResultHandler NO_RESULT_HANDLER = null;// 执行insert update delete操作int update(MappedStatement var1, Object var2) throws SQLException;// 执行query操作<E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4, CacheKey var5, BoundSql var6) throws SQLException;<E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4) throws SQLException;<E> Cursor<E> queryCursor(MappedStatement var1, Object var2, RowBounds var3) throws SQLException;List<BatchResult> flushStatements() throws SQLException;// 提交事务void commit(boolean var1) throws SQLException;// 回滚事务void rollback(boolean var1) throws SQLException;CacheKey createCacheKey(MappedStatement var1, Object var2, RowBounds var3, BoundSql var4);boolean isCached(MappedStatement var1, CacheKey var2);void clearLocalCache();void deferLoad(MappedStatement var1, MetaObject var2, String var3, CacheKey var4, Class<?> var5);// 获取事务Transaction getTransaction();// 关闭事务void close(boolean var1);// 判断事务是否关闭boolean isClosed();void setExecutorWrapper(Executor var1);
}
/**
用途: 拦截sql执行的语句
*/
public interface StatementHandler {// sql预编译拦截,在这个阶段可以修改sql执行的语句Statement prepare(Connection var1, Integer var2) throws SQLException;// 设置参数时拦截void parameterize(Statement var1) throws SQLException;void batch(Statement var1) throws SQLException;// 进行insert,update,delete的sql执行前进行拦截int update(Statement var1) throws SQLException;// 进行query的sql执行前进行拦截<E> List<E> query(Statement var1, ResultHandler var2) throws SQLException;<E> Cursor<E> queryCursor(Statement var1) throws SQLException;BoundSql getBoundSql();ParameterHandler getParameterHandler();
}
/**
用途: 拦截sql语句的参数设置
*/
public interface ParameterHandler {// 获取设置的参数(注意使用这个的时候最好和@param注解合用指定具体名,不然只能按顺序去获取,没有具体名)Object getParameterObject();// 设置sql的参数void setParameters(PreparedStatement var1) throws SQLException;
}
/**
用途:对执行完后返回的结果进行拦截
*/
public interface ResultSetHandler {// 最常用的返回拦截<E> List<E> handleResultSets(Statement var1) throws SQLException;<E> Cursor<E> handleCursorResultSets(Statement var1) throws SQLException;void handleOutputParameters(CallableStatement var1) throws SQLException;
}

2.定义拦截器的相关注解@Intercepts与@Signature

// 用于指定该拦截器是拦截哪个环节的注解,可以配置多个
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Intercepts {Signature[] value();
}
// 指定具体的拦截环节
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Signature {// 即上面的四种拦截器:Executor,StatementHandler,ParameterHandler,ResultHandler Class<?> type();// 上面四种拦截器中的方法名,如指定的是StatementHandler,你需要具体到方法上,可以选择填写queryString method();// method指定方法上的形参,如按query将两个参数的类对象写好即可Class<?>[] args();
}

2.使用SQL_CALC_FOUND_ROWS和found_rows()进行分页

1.直接按要求实现

a.在配置mysql的url处开启返回多个结果集:allowMultiQueries=true

spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: rooturl: jdbc:mysql://192.168.175.155:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false&allowMultiQueries=true

b.编写mapper

// 注意要接收多个结果,所以需要List不指定泛型
List select2(@Param("pageNum") int pageNum, @Param("pageSize") int pageSize);

c.编写xml的sql

	<!-- 实体和字段的映射配置 --><resultMap id="BaseResultMap" type="com.example.demo.pagehelper.User"><id column="id" jdbcType="BIGINT" property="id"/><result column="name" jdbcType="VARCHAR" property="name"/><result column="age" jdbcType="BIGINT" property="age"/></resultMap><!-- 总行数的配置 --><resultMap id="recordCounts" type="java.lang.Integer"><result column="recordCounts" jdbcType="BIGINT"/></resultMap><select id="select2" resultMap="BaseResultMap, recordCounts">select SQL_CALC_FOUND_ROWS * from user limit #{pageNum}, #{pageSize};SELECT found_rows() AS recordCounts;</select>

d.返回结果的处理

  List<Object> lists = userMapper.select2(1,3);// 获取分页的数据List<User> users = (List<User>) lists.get(0);// 获取查询的总数List<Integer> nums = (List<Integer>) lists.get(1);Integer count = nums.get(0);

总结:
1.sql在写的时候都需要拼接SQL_CALC_FOUND_ROWS和SELECT found_rows() AS recordCounts;
2.处理结果时很麻烦一直强转再分别获取

2.使用自定义拦截器对上述实现进行一定程度优化(思路仅供参考)

a.定义两个拦截器:一个拦截sql执行,一个拦截结果处理

/*** @description: 在准备阶段对sql进行拦截,并处理结果* @author: zengwenbo* @date: 2024/4/11 18:22*/
@Intercepts(value = {@Signature(type = StatementHandler.class, method="prepare", args = {Connection.class, Integer.class} )
})
@Component
public class PagingInterceptor implements Interceptor {private static final String PAGE_NUM = "pageNum";private static final String PAGE_SIZE = "pageSize";private static final String SQL_CALC_FOUND_ROWS = "sql_calc_found_rows";private static final String FOUND_ROWS = "found_rows()";private static final String SEMICOLON = ";";@Overridepublic Object intercept(Invocation invocation) throws Throwable {StatementHandler target = (StatementHandler) invocation.getTarget();// 借助metaObject来修改执行的sqlMetaObject metaObject= SystemMetaObject.forObject(target);BoundSql boundSql = target.getBoundSql();List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();String sql = boundSql.getSql();// 只处理查询语句if (sql != null && sql.toLowerCase().trim().startsWith("select ")) {// 获取传递的参数所有名称List<String> paramsName = parameterMappings.stream().filter(item -> ParameterMode.IN.equals(item.getMode())).map(ParameterMapping::getProperty).collect(Collectors.toList());// 根据参数名称里面是否有指定两个参数来判断是否需要分页if (paramsName.contains(PAGE_NUM) && paramsName.contains(PAGE_SIZE)) {// 需要分页则对sql进行处理metaObject.setValue("delegate.boundSql.sql", dealSql(sql));} else {metaObject.setValue("delegate.boundSql.sql", sql);}}return invocation.proceed();}@Overridepublic Object plugin(Object o) {return Plugin.wrap(o, this);}@Overridepublic void setProperties(Properties properties) {}/*** 拼接需要的分页sql语句* @param oldSql* @return*/private String dealSql(String oldSql) {if (oldSql.toLowerCase().contains(SQL_CALC_FOUND_ROWS) || oldSql.toLowerCase().contains(FOUND_ROWS)) {return oldSql;}oldSql = oldSql.toLowerCase().replace("select ", "select SQL_CALC_FOUND_ROWS ");if(!oldSql.trim().endsWith(SEMICOLON)) {oldSql = oldSql.concat(SEMICOLON);}return oldSql.concat("select found_rows() AS recordCounts;");}
}
/*** @description: 对分页结果数据进行处理* @author: zengwenbo* @date: 2024/4/12 18:37*/
@Intercepts(value = {@Signature(type = ResultSetHandler.class, method="handleResultSets", args = {Statement.class} )
})
@Component
public class PagingResultInterceptor implements Interceptor {private static final String SQL_CALC_FOUND_ROWS = "sql_calc_found_rows";private static final String FOUND_ROWS = "found_rows()";@Overridepublic Object intercept(Invocation invocation) throws Throwable {Object proceed = invocation.proceed();DefaultResultSetHandler target = (DefaultResultSetHandler) invocation.getTarget();// 借助metaObject来获取执行的sqlMetaObject metaObject= SystemMetaObject.forObject(target);BoundSql boundSql = (BoundSql) metaObject.getValue("boundSql");String sql = boundSql.getSql();// 执行的是分页,则需要封装返回需要的对象if (null != sql && sql.toLowerCase().contains(SQL_CALC_FOUND_ROWS) && sql.toLowerCase().contains(FOUND_ROWS)) {if (proceed instanceof List) {List<Object> result = (List) proceed;if (result.size() == 2) {Object res1 = result.get(0);Object res2 = result.get(1);if (res1 instanceof List && res2 instanceof List) {List<Object> result1 = (List) res1;List<Object> result2 = (List) res2;MyPage<Object> page = new MyPage<>();page.setTotal((Integer) result2.get(0));page.addAll(result1);return page;}}}}return proceed;}
}
/*** @description: 封装分页数据对象* @author: zengwenbo* @date: 2024/4/11 20:14*/
@Data
public class MyPage<E> extends ArrayList<E> {private int total; // 总行数
}

b.编写mapper

// 注意上面对sql拦截需要用到pageNum和pageSize,所以这两个参数不能丢,必须用@Param注解绑定名
// MyPage来接收上面的分页返回结果
MyPage<User> select3(@Param("pageNum") int pageNum, @Param("pageSize") int pageSize);

c.编写xml的sql

    <resultMap id="BaseResultMap" type="com.example.demo.pagehelper.User"><id column="id" jdbcType="BIGINT" property="id"/><result column="name" jdbcType="VARCHAR" property="name"/><result column="age" jdbcType="BIGINT" property="age"/></resultMap><resultMap id="recordCounts" type="java.lang.Integer"><result column="recordCounts" jdbcType="BIGINT"/></resultMap><!-- 正常的写分页sql即可 --><select id="select3" resultMap="BaseResultMap, recordCounts">select * from user limit #{pageNum}, #{pageSize};</select>

d.返回结果的处理

// 直接获取结果数据
List<User> lists = userMapper.select3(1,3);
// 总数需要转换下
int total = ((MyPage<User>) lists).getTotal();

3.使用PageHelper第三方插件来进行分页

1.实现过程

a.导包

        <dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>1.4.1</version></dependency>

b.mapper和xmlsql的编写

    @Select("select * from user")List<User> select1();

c.调用处和结果的处理

// 调用前设置当前页和当前页数据大小PageHelper.startPage(1,3);List<User> lists = userMapper.select1();// 借助PageInfo来获取总数PageInfo<User> userPageInfo = new PageInfo<>(lists);long total = userPageInfo.getTotal();

2.实现原理

1.PageHelper.startPage(1,3):将pagNum和pageSize通过ThreadLocal存储起来;

    public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {Page<E> page = new Page(pageNum, pageSize, count);page.setReasonable(reasonable);page.setPageSizeZero(pageSizeZero);Page<E> oldPage = getLocalPage();if (oldPage != null && oldPage.isOrderByOnly()) {page.setOrderBy(oldPage.getOrderBy());}// 核心代码:将page对象存储到ThreadLocal中进行线程隔离setLocalPage(page);return page;}

2.自定义一个拦截器对相关实现进行拦截PageInterceptor,该拦截器实现分页的方式是进行了两次sql的执行,第一次组sql查询出总数,再按参数修改原sql进行分页,然后将数据封装成Page对象,最后将TreadLocal里面的参数进行移除。代码中有标出,追代码可以看如何操作的。

@Intercepts({@Signature(type = Executor.class,method = "query",args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
), @Signature(type = Executor.class,method = "query",args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}
)})
public class PageInterceptor implements Interceptor {private static final Log log = LogFactory.getLog(PageInterceptor.class);private volatile Dialect dialect;private String countSuffix = "_COUNT";protected Cache<String, MappedStatement> msCountMap = null;private String default_dialect_class = "com.github.pagehelper.PageHelper";public PageInterceptor() {String bannerEnabled = System.getProperty("pagehelper.banner");if (StringUtil.isEmpty(bannerEnabled)) {bannerEnabled = System.getenv("PAGEHELPER_BANNER");}if (StringUtil.isEmpty(bannerEnabled) || Boolean.parseBoolean(bannerEnabled)) {log.debug("\n\n,------.                           ,--.  ,--.         ,--.                         \n|  .--. '  ,--,--.  ,---.   ,---.  |  '--'  |  ,---.  |  |  ,---.   ,---.  ,--.--. \n|  '--' | ' ,-.  | | .-. | | .-. : |  .--.  | | .-. : |  | | .-. | | .-. : |  .--' \n|  | --'  \\ '-'  | ' '-' ' \\   --. |  |  |  | \\   --. |  | | '-' ' \\   --. |  |    \n`--'       `--`--' .`-  /   `----' `--'  `--'  `----' `--' |  |-'   `----' `--'    \n                   `---'                                   `--'                        is intercepting.\n");}}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) {boundSql = ms.getBoundSql(parameter);cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);} else {cacheKey = (CacheKey)args[4];boundSql = (BoundSql)args[5];}this.checkDialectExists();if (this.dialect instanceof Chain) {boundSql = ((Chain)this.dialect).doBoundSql(Type.ORIGINAL, boundSql, cacheKey);}List resultList;if (!this.dialect.skip(ms, parameter, rowBounds)) {if (this.dialect.beforeCount(ms, parameter, rowBounds)) {// 1.组sql获取总数的地方Long count = this.count(executor, ms, parameter, rowBounds, (ResultHandler)null, boundSql);if (!this.dialect.afterCount(count, parameter, rowBounds)) {Object var12 = this.dialect.afterPage(new ArrayList(), parameter, rowBounds);return var12;}}// 2.修改原sql进行分页查询并返回分页结果resultList = ExecutorUtil.pageQuery(this.dialect, executor, ms, parameter, rowBounds, resultHandler, boundSql, cacheKey);} else {resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);}// 3.组装Page对象进行返回Object var16 = this.dialect.afterPage(resultList, parameter, rowBounds);return var16;} finally {if (this.dialect != null) {// 4.核心是移除最开始设置到Threadlocal里面的参数this.dialect.afterAll();}}}private void checkDialectExists() {if (this.dialect == null) {String var1 = this.default_dialect_class;synchronized(this.default_dialect_class) {if (this.dialect == null) {this.setProperties(new Properties());}}}}private Long count(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {String countMsId = ms.getId() + this.countSuffix;MappedStatement countMs = ExecutorUtil.getExistedMappedStatement(ms.getConfiguration(), countMsId);Long count;if (countMs != null) {count = ExecutorUtil.executeManualCount(executor, countMs, parameter, boundSql, resultHandler);} else {if (this.msCountMap != null) {countMs = (MappedStatement)this.msCountMap.get(countMsId);}if (countMs == null) {countMs = MSUtils.newCountMappedStatement(ms, countMsId);if (this.msCountMap != null) {this.msCountMap.put(countMsId, countMs);}}count = ExecutorUtil.executeAutoCount(this.dialect, executor, countMs, parameter, boundSql, rowBounds, resultHandler);}return count;}public Object plugin(Object target) {return Plugin.wrap(target, this);}public void setProperties(Properties properties) {this.msCountMap = CacheFactory.createCache(properties.getProperty("msCountCache"), "ms", properties);String dialectClass = properties.getProperty("dialect");if (StringUtil.isEmpty(dialectClass)) {dialectClass = this.default_dialect_class;}try {Class<?> aClass = Class.forName(dialectClass);this.dialect = (Dialect)aClass.newInstance();} catch (Exception var4) {throw new PageException(var4);}this.dialect.setProperties(properties);String countSuffix = properties.getProperty("countSuffix");if (StringUtil.isNotEmpty(countSuffix)) {this.countSuffix = countSuffix;}}
}

因此可以看出使用PageHelper时,在用PageHelper调用startPage设置参数后,中间必须要紧跟当前的查询语句,如果两者之间存在其他的查询语句就会出问题,这点在使用时需要注意。

4.使用Mybatis-plus封装来进行分页

参考链接:mybatis-plus实现分页
其实现过程和pageHelper差不多,也是先算出总数在执行原sql

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

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

相关文章

DC-5渗透测试复现

DC-5渗透测试复现 目的&#xff1a; 获取最高权限以及5个flag 过程&#xff1a; 信息打点-文件包含漏洞-弹shell- scren-4.0.5提权 环境&#xff1a; 攻击机&#xff1a;kali(192.168.85.136) 靶机&#xff1a;DC_3(192.168.85.134) 复现&#xff1a; 一.信息收集 nma…

PlantUML 实战示例(使用 PlantUML 画用例图、类图、活动图、时序图)

目录 前言 需求场景 用例图 类图 活动图 时序图 前言 在软件开发的生命周期中&#xff0c;需要先进行设计&#xff0c;最后才是进行具体的编码和测试。设计时就需要画各种 UML 图&#xff0c;有专业的 UML 画图软件&#xff0c;也有很多在线的 UML 画图网站可以来画图&a…

哈希表函数 —— uthash的简单使用

目录标题 哈希表函数 —— uthash的简单使用添加头文件定义结构体哈希表的初始化哈希表的添加哈希表的添加哈希表的排序 哈希表函数 —— uthash的简单使用 添加头文件 在使用uthash函数实现哈希表前&#xff0c;要先添加头文件&#xff1a; #include "uthash.h"定…

Dubbo面试回答简单版

一、dubbo特性 超时重试机制地址缓存多版本负载均衡&#xff1a;随机、权重轮询、最少活跃调用、一致性哈希集群容错&#xff1a;失败重试、快速失败、失败安全、失败自动恢复、并行调用、广播服务降级&#xff1a;异常时返回mock 集群容错 FailOver 失败重试&#xff0c;读…

链表算法题总结(二十一天)

203. 移除链表元素 题目 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 输入&#xff1a;head [1,2,6,3,4,5,6], val 6 输出&#xff1a;[1,2,3,4,5]示例 2&#xff1a; 输入&…

正方形(c++题解)

题目描述 给定一组不同长度的木棍&#xff0c;是否有可能将它们端对端地连接起来形成 个正方形? 输入格式 第1行输入包含N&#xff0c;即测试数据的数量。 每组测试数据第一个数为 &#xff0c;即木棒的根数。之后有 个整数&#xff0c; 每个都给出了一根棍子的长度 。 …

求存款本息和(C语言)

一、运行结果&#xff1b; 二、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h> # include <math.h>int main() {//初始化变量值&#xff1b;double P 1000, r1 0.015, r2 0.021, r3 0.0275, r4 0.03, r5 0.0035;int judge 0;//…

富文本编辑器的下载安装使用

为什么选择vue-quill-editor&#xff1f; 在众多的富文本编辑器中&#xff0c;vue-quill-editor因其易用性、灵活性以及对Vue框架友好的特性而受到开发者的青睐。它基于Quill编辑器&#xff0c;Quill是一款现代的WYSIWYG&#xff08;所见即所得&#xff09;编辑器&#xff0c;…

【每日刷题】Day13

1. 1041. 困于环中的机器人 - 力扣&#xff08;LeetCode&#xff09; //思路&#xff1a;可以利用轴上四个方向的单位坐标表示机器人的运动。遍历字符串&#xff0c;根据字符串内容进行相应的行动。 bool isRobotBounded(char* instructions) { int arr[4][2] {{0,1},{1,0},{0…

Redis漏洞利用

未授权 可以利用超级弱口令工具来判断redis是否能未授权登录 telnet #尝试登录info #查看redis信息 RCE方法 写shell 需要写权限需要知道web网站路径 config set dir /var/www/html config set dbfilename redis.php set test "<?php phpinfo(); ?>" sa…

CF Educational Codeforces Round 164 Div.2 D. Colored Balls 题解

Colored Balls 题目描述 There are balls of n n n different colors; the number of balls of the i i i-th color is a i a_i ai​. The balls can be combined into groups. Each group should contain at most 2 2 2 balls, and no more than 1 1 1 ball of each c…

UI设计/交互设计/视觉设计项目汇报/作品集Figma/PPT模板

作为UI设计/交互设计/视觉设计师&#xff0c;创建作品集对于向潜在客户或雇主展示您的技能、创造力和风格至关重要。以下分步指南可帮助您创建令人印象深刻的作品集&#xff1a; 选择您的最佳作品&#xff1a;选择您最强大且最相关的设计项目&#xff0c;将其纳入您的作品集。…

AI人工智能讲师大模型培训讲师叶梓 大语言模型(LLM)在科学文献摘要领域的应用

大语言模型&#xff08;LLM&#xff09;在科学文献摘要领域的应用是一个前沿且迅速发展的技术趋势。通过结合GitHub上yobibyte的Compressor项目&#xff0c;我们可以深入探讨这一技术方案的潜力和实现方式。 技术背景 随着科学研究的快速发展&#xff0c;每天都有大量的科学文…

文件上传下载

文章目录 文件上传下载文件上传文件下载 文件上传下载 HTTP请求会包含一个请求头&#xff0c;其中"Content-Type"字段告诉服务器正在发送什么类型的数据。根据发送的数据类型&#xff0c;浏览器和服务器会采取适应的处理方式。 "multipart/form-data"是一…

基于8B/10BGT收发器的PHY层设计(1)

一、PHY层简介 PHY层&#xff08;Physical Layer&#xff09;是OSI模型中最低的一层&#xff0c;也是最基本的一层&#xff0c;PHY是物理接口收发器&#xff0c;它实现物理层。包括MII/GMII&#xff08;介质独立接口&#xff09;子层、PCS&#xff08;物理编码子层&#xff09…

王道汽车4S企业管理系统 SQL注入漏洞复现

0x01 产品简介 王道汽车4S企业管理系统(以下简称“王道4S系统”)是一套专门为汽车销售和维修服务企业开发的管理软件。该系统是博士德软件公司集10余年汽车行业管理软件研发经验之大成,精心打造的最新一代汽车4S企业管理解决方案。 0x02 漏洞概述 王道汽车4S企业管理系统…

SQL语言自用(持续更新)+实验记录

课本:《数据库原理及其应用教程》&#xff08;第四版&#xff09; (主编)黄德才&(副主编)陆亿红 实验&#xff1a;学校实验课材料 其他&#xff1a; [ ]表示可以被删除&#xff0c;也表示可以被替换&#xff0c;请自行判断。如果有一些截图或照片&#xff0c;是暂时懒得整…

5、LMDeploy 量化部署 LLMVLM实战(homework)

基础作业&#xff08;结营必做&#xff09; 完成以下任务&#xff0c;并将实现过程记录截图&#xff1a; 配置lmdeploy运行环境 由于环境依赖项存在torch&#xff0c;下载过程可能比较缓慢。InternStudio上提供了快速创建conda环境的方法。打开命令行终端&#xff0c;创建一…

富格林:专研正规技巧助力出金

富格林指出&#xff0c;现货黄金拥有诸多其他投资产品无法比拟的交易优势&#xff0c;如此一来吸引着越来越多投资者相继涌入现货黄金市场。但有不少投资者发现自己的出金效率与他人的有差异。为什么会这样呢&#xff1f;其实在现货黄金市场中需要有正规技巧的支持才可以更好地…

车牌校验正则表达式

1. 规则 传统车牌&#xff1a; 第一位为省份简称&#xff08;汉字&#xff09;&#xff0c;第二位为发牌机关代号&#xff08;A-Z的字母&#xff09;第3到第7位为序号&#xff08;由字母或数字组成&#xff0c;但不存在字母I和O&#xff0c;防止和数字1、0混淆&#xff0c;另外…