mybatis 分页详解、mybatis分页查询,mybatis分页拦截器使用、struts2下mybatis分页


mybatis默认是支持分页的,内部通过创建可滚动的ResultSet(ResultSet.TYPE_FORWARD_ONLY)对结果集指针进行跳转以达到分页控制的目的。实际使用,需要传入RowBounds类型参数来告知mybatis做分页控制,RowBounds构造器有两个参数:

RowBounds(int offset, int limit), offset,从第offset条开始查(起始于0),limit查询个数。如:

RowBounds(0, 11):第一页,显示十一条【0-10】、

RowBounds(11, 10):第二页,显示十一条【11-21】

。。。。

不过mybatis默认分页存在两个问题:1.通过ResultSet控制指针进行分页与数据库本身通过sql语句进行分页相比,查询性能欠佳。2.无法返回总记录数,通常情况下前端表格除了要显示第n页数据,也需要显示总记录数,通过内置RowBounds显然不能满足需求。

有些兄弟可能不知道怎么使用,没关系,我先做一个简单的示例,供参考。

问题:现有区号表一张,需要支持表分页查询:

1.定义区号javabean AreaCode.java:

AreaCode.java

public class AreaCode implements Serializable{//javabean与数据库字段一致private String provId;private String description;public AreaCode() {super();}public String getProvId() {return provId;}public void setProvId(String provId) {this.provId = provId;}public String getDescription() {return description;}public void setDescription(String description) {this.description = description;}}

2.定义mapper接口AreaCodeMapper.java(为了简单只定义一个查询接口):

public interface AreaCodeMapper {//这个方法不支持分页,是全查public List<AreaCode> list();//通过RowBounds参数告知mybatis此方法需要分页查询public List<AreaCode> list(RowBounds rowBounds);}

3.定义配置文件AreaCodeMapper.xml(配置仅供参考):

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="AreaCodeMapper"><select id="list" resultType="AreaCode">select provId, description from areacodecfg</select>
</mapper>

4.定义配置文件configuration.xml(配置仅供参考):

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><environments default="development"><environment id="development"><transactionManager type="JDBC" /><dataSource type="UNPOOLED"><!-- ${driver}为实际的数据库驱动名 --><property name="driver" value="${driver}" /><property name="url" value="${url}" /><property name="username" value="${username}" /><property name="password" value="${password}" /></dataSource></environment></environments><mappers><!-- 添加mapper文件 --><mapper resource="AreaCodeMapper.xml" /></mappers>
</configuration>

5.测试:

public static void main(String[] args) throws SQLException {Reader reader = null;SqlSessionFactory sf;SqlSession session = null;try {SqlSessionFactoryBuilder build = new SqlSessionFactoryBuilder();//根据配置文件生成SqlSessionFactoryreader = Resources.getResourceAsReader("test/configuration.xml");sf = build.build(reader);session = sf.openSession();AreaCodeMapper mapper = session.getMapper(AreaCodeMapper.class);//查询第一页,每页十条记录RowBounds rowBounds = new RowBounds(0, 10);List<AreaCode> list = mapper.list(rowBounds);} catch (IOException e) {e.printStackTrace();} finally {if (reader != null) {try {reader.close();} catch (IOException e) {}}if (session != null) {session.close();}}}

分页查询已搞定,但以上查询并不包含总记录数,这个比较麻烦,如果你愿意,再添加一个总记录数查询的接口,不愿意接着往下看。

下面通过扩展RowBounds,使其支持数据库分页与总记录数统计。

1.由于不同的数据库,分页sql语句略有不同,我们通过一个枚举类型DBType.java,用于区分不同数据库类型。假设只支持mysql和oracle两种数据库,要添加其他数据库,自己扩展:

public enum DBType {MYSQL, ORACLE
}

2.创建用于sql转换的接口IDialect.java。接口功能:可将普通查询sql,转换成与具体数据库相关的分页查询sql,IDialect.java:

public interface IDialect {//支持根据字段进行排序查询,留给读者实现吧String getSortSQL(String sql, List<String> sortFields, List<String> sortOrders);//根据原始查询sql生成与数据库相关的查询sql//原始sql就是上文提到的:select provId, description from areacodecfgString getLimitSQL(String sql, int offset, int limit);//根据原始的sql生成查询总记录数的sqlString getTotalSQL(String sql);
}

3.定义IDialect的抽象实现ADialect.java:

public abstract class ADialect implements IDialect {@Overridepublic String getTotalSQL(String sql) {//基本所有数据库通用StringBuffer totalSql = new StringBuffer(sql.length() + 100);totalSql.append("select count(0) from ( ").append(sql).append(" ) as _tmp_count");return totalSql.toString();}
}

4.定义mysql的实现:

public class MySqlDialect extends ADialect {@Overridepublic String getLimitSQL(String sql, int offset, int limit) {sql = sql.trim();StringBuffer newSql = new StringBuffer(sql.length() + 100);newSql.append("select * from (").append(sql).append(") as _tmp_query limit ").append(offset).append(",").append(limit);return newSql.toString();}
}

5.定义oracle的实现:

public class OracleDialect extends ADialect {@Overridepublic String getLimitSQL(String sql, int offset, int limit) {sql = sql.trim();StringBuffer pageSelect = new StringBuffer(sql.length() + 100);pageSelect.append("select * from ( select row_.*, rownum rownum_ from ( ");pageSelect.append(sql);pageSelect.append(" ) row_ ) where rownum_ > ").append(offset).append(" and rownum_ <= ").append(offset + limit);return pageSelect.toString();}
}

5.创建IDialect的实例化工厂RoutingDialect.java:

public class RoutingDialect implements IDialect {//实际委托给delegateprivate IDialect delegate;//用于缓存实例private static Map<DBType, IDialect> dialectMap = new HashMap<DBType, IDialect>();private RoutingDialect(DBType dbType) {switch (dbType) {case MYSQL:delegate = new MySqlDialect();break;case ORACLE:delegate = new OracleDialect();break;default:delegate = new MySqlDialect();}}//工厂方法,根据数据库类型返回相应的Dialectpublic static IDialect getDialect(DBType dbType) {IDialect dialect = null;dialect = dialectMap.get(dbType);if (dialect == null) {synchronized (dialectMap) {dialect = dialectMap.get(dbType);if (dialect == null) {dialect = new RoutingDialect(dbType);dialectMap.put(dbType, dialect);}}}return dialect;}@Overridepublic String getLimitSQL(String sql, int offset, int limit) {return delegate.getLimitSQL(sql, offset, limit);}@Overridepublic String getTotalSQL(String sql) {return delegate.getTotalSQL(sql);}}

6.创建SmartRowBounds扩展自RowBounds,支持数据库分页,与总记录数统计:

public class SmartRowBounds extends RowBounds {//内部参数:当前查询记录的偏移private int queryOffset = -1;//内部参数:当前查询记录的条数private int queryLimit = -1;//内部参数:用于保存总记录数private int totalCount;//是否使用数据库分页,还是使用mybatis的默认滚动分页private boolean isDbSupport = false;//内部标识,是否阻止默认分页,当isDbSupport=true时此标识必须设成trueprivate boolean preventDefaultRowBounds = false;public SmartRowBounds() {//无分页参数,不分页super(RowBounds.NO_ROW_OFFSET, RowBounds.NO_ROW_LIMIT);}public SmartRowBounds(int queryOffset, int queryLimit) {//将分页参数传递给父类,这一点很重要,可用于默认分页super(queryOffset, queryLimit);this.queryOffset = queryOffset;this.queryLimit = queryLimit;}public SmartRowBounds(int queryOffset, int queryLimit,boolean isDbSupport) {this(queryOffset, queryLimit);this.isDbSupport = isDbSupport;}//根据数据库类型,即原始的查询sql来生成数据库相关的分页sql,//委托IDialect进行转换public String getPageSql(DBType dbType, String rawSql) {IDialect dialet = RoutingDialect.getDialect(dbType);//只有使用数据库分页的情况下才会对sql进行数据库相关的转换if (isDbSupport && queryOffset >= 0 && queryLimit >= 0) {rawSql = dialet.getLimitSQL(rawSql, queryOffset, queryLimit);//如果使用了数据库分页,就必须阻止mybatis默认分页行为。//设置一个阻止默认分页的标识preventDefaultRowBounds=tuepreventDefaultRowBounds = true;}return rawSql;}public String getTotalSQL(DBType dbType, String rawSql) {return RoutingDialect.getDialect(dbType).getTotalSQL(rawSql);}public void setTotalCount(int totalCount) {this.totalCount = totalCount;}public int getTotalCount() {return totalCount;}@Overridepublic int getLimit() {//mysql会调用RowBounds的getLimit方法计算查询记录条数,//如果未使用数据库分页,你需要告诉mybatis查询的记录长度queryLimitif (!preventDefaultRowBounds) {return queryLimit;}//如果你使用了数据库分页,那么mybatis内部就不能再使用ResultSet滚动分页了//因此需要返回super.NO_ROW_LIMIT,告知mybatis不进行分页相关的结果集指针跳转return super.NO_ROW_LIMIT;}@Overridepublic int getOffset() {//mysql会调用RowBounds的getOffset方法计算查询偏移,if (!preventDefaultRowBounds) {return queryOffset;}//如果默认使用了数据库分页,那么mybatis内部就不能再使用ResultSet滚动分页了//因此返回NO_ROW_OFFSETreturn super.NO_ROW_OFFSET;}public void reset() {totalCount = 0;preventDefaultRowBounds = false;}}

7.创建mybatis分页拦截器类:

//注意注解,这里只对Connection创建的的preparedStatement进行拦截,固定写法,可以不深究
@Intercepts( { @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) })
public class PageStatementInterceptor implements Interceptor {private DBType dbType;public void setDBType(String dbTypeStr) {if (dbTypeStr != null && !"".equals(dbTypeStr.trim())) {try {this.dbType = DBType.valueOf(dbTypeStr.trim().toUpperCase());} catch (Exception e) {this.dbType = DBType.MYSQL;}}}//mybatis框架自动调用@Overridepublic void setProperties(Properties properties) {setDBType(properties.getProperty("DBType"));}@Overridepublic Object intercept(Invocation invocation) throws Throwable {try {StatementHandler statementHandler = (StatementHandler) invocation.getTarget();StatementHandler deleStatementHandler = (StatementHandler) getFieldValue(statementHandler, "delegate");RowBounds rowBounds = (RowBounds) getFieldValue(deleStatementHandler, "rowBounds");MappedStatement mappedStatement = (MappedStatement)getFieldValue(deleStatementHandler, "mappedStatement");//只针对select类型切分页参数是SmartRowBounds的查询进行sql改造if (rowBounds != null && mappedStatement != null && SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType()) && SmartRowBounds.class.isAssignableFrom(rowBounds.getClass())) {SmartRowBounds pageHandler = (SmartRowBounds) rowBounds;//获取原始sqlBoundSql boundSql = statementHandler.getBoundSql();if (boundSql != null) {//进行总记录数查询countTotal(pageHandler, (Connection) invocation.getArgs()[0], statementHandler.getParameterHandler(), boundSql.getSql());//将原始sql替换为支持分页的sqlsetFieldValue(boundSql, "sql", pageHandler.getPageSql(dbType, boundSql.getSql()));}}} catch (Exception e) {e.printStackTrace();}return invocation.proceed();}private Field getField(Object target, String fieldName) {Field field = null;for (Class<?> clazz = target.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {try {field = clazz.getDeclaredField(fieldName);break;} catch (NoSuchFieldException e) {// ignore}}if (field != null) {if (!field.isAccessible()) {try {field.setAccessible(true);} catch (Exception e) {// ignore}}}return field;}private Object getFieldValue(Object target, String fieldName) {try {Field field = getField(target, fieldName);if (field != null) {return field.get(target);}} catch (Exception e) {e.printStackTrace();}return null;}private void setFieldValue(Object target, String fieldName, Object value) {try {Field field = getField(target, fieldName);if (field != null) {field.set(target, value);}} catch (Exception e) {e.printStackTrace();}}private void countTotal(SmartRowBounds pageHandler, Connection con, ParameterHandler paramHandler, String rawSql) {ResultSet rs = null;PreparedStatement stmt = null;try {String totalSql = pageHandler.getTotalSQL(dbType, rawSql);if (totalSql != null && !totalSql.isEmpty()) {stmt = con.prepareStatement(totalSql);paramHandler.setParameters(stmt);rs = stmt.executeQuery();if (rs.next()) {pageHandler.setTotalCount(rs.getInt(1));}}} catch (SQLException e) {e.printStackTrace();} finally {try {if (rs != null) {rs.close();}} catch (SQLException e1) {// ignore}try {if (stmt != null) {stmt.close();}} catch (Exception e) {// ignore}}}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}}

8.configuration.xml添加拦截器配置:

<plugins><plugin interceptor="PageStatementInterceptor"><property name="DBType" value="mysql" /></plugin></plugins>

9.测试:

public class Test {public static void main(String[] args) throws SQLException {Reader reader = null;SqlSessionFactory sf;SqlSession session = null;try {SqlSessionFactoryBuilder build = new SqlSessionFactoryBuilder();reader = Resources.getResourceAsReader("test/configuration.xml");sf = build.build(reader);session = sf.openSession();AreaCodeMapper mapper = session.getMapper(AreaCodeMapper.class);SmartRowBounds rowBounds = new SmartRowBounds(0, 10, true);//分页记录与记录总数,使用一次接口调用搞定List<AreaCode> list = mapper.list(rowBounds);int total = rowBounds.getTotalCount();} catch (IOException e) {e.printStackTrace();} finally {if (reader != null) {try {reader.close();} catch (IOException e) {}}if (session != null) {session.close();}}}
}

以上介绍了拦截器分页的基本用法,下面简单介绍下,如果配合struts, spring进行分页查询

1.创建业务类AreaCodeService.java,正常情况下应该创建业务接口再创建实现类,为了简单起见,跳过接口创建:

@Service
public class AreaCodeService {//通过spring进行自动注入@Resourceprivate AreaCodeMapper mapper;public List<AreaCode> listAreaCode(RowBounds rowBounds) {//方法1return mapper.list(rowBounds));}public List<AreaCode> listAreaCode(int offset, int limit) {//方法2//直接创建SmartRowBounds//但是总记录数如何传递出去?return mapper.list(new SmartRowBounds(offset, limit));}
}

AreaCodeService很简单,基本就是委托mapper查询,唯一需要做的就是实例化RowBounds交给mapper。

上面代码定义了两个用于分页查询的方法,方法1:通过传入RowBounds类型参数,实现分页控制,好处是简单,实际的rowbounds对象的创建代码放到调用者(一般是structs的控制器对象)中,缺点是AreaCodeService与mybatis耦合太紧,如果以后使用其他orm框架如hibernate,那就需要修改service类。方法2:方法独立性比较好,不与任何特定框架耦合,它只关心查询偏移,与返回记录数,但问题是,方法的返回类型是List<AreaCode>, 如何将SmartRowBounds的总记录数返回给调用者对象?先不说,后面再谈!

2.创建Struts控制器的抽象Action, AbstractAction.java:

public class AbstractAction extends ActionSupport{public static final String JSON = "json";//querOffset与queryLimit是通过前台jsp传过来的分页相关参数//其实可以将这两个参数封装成Page对象,通过Page对象来取queryOffset与queryLimit//这里这样操作是为了偷懒,展示用protected int queryOffset = -1;protected int queryLimit = -1;//前台页表格加载需要的数据对象protected Object gridDataList;//将分页的查询的结果存放到gridDataList//最终通过struts.xml配置文件将gridDataList转换为json传到前台jsp//list为查询的结果, total为总记录数protected void setJsonGrid(List list, int total) {Map data = new HashMap();data.put("total", total);data.put("rows", list);this.gridDataList = data;}public int getQueryOffset() {return queryOffset;}public void setQueryOffset(int queryOffset) {this.queryOffset = queryOffset;}public int getGridDataList() {return gridDataList;}public void setGridDataList(Object gridDataList) {this.gridDataList= gridDataList;}public int getQueryLimit() {return queryLimit;}public void setQueryLimit(int queryLimit) {this.queryLimit = queryLimit;}
}

抽象action继承自Struts的ActionSupport,方法功能看注释,不废话了。

2.创建具体控制器AreaCodeAction.java:

public class AreaCodeAction extends AbstractAction{//通过spring将AreaCodeService注入进来@Resourceprivate AreaCodeService service;public String gridList() {SmartRowBounds rb = new SmartRowBounds(queryOffset, queryLimit, true);List<AreaCode> list = service.listAreaCode(rb);//使用AreaCodeService的方法1setJsonGrid(list, rb.getTotalCount());return JSON;}
}

只有一个gridList ()方法,通过前台页面传进来的分页参数创建SmartRowBounds交给service进行分页查询,查询完了会通过rb回取总记录数,一起交给父类的setJsonGrid()方法。

上文提到的,将控制器属性gridDataList转化为json对象传到前台jsp,需要在struct.xml中加一段配置,内容片段如下:

<global-results><result name="json" type="json"><param name="root">gridDataList</param></result>
</global-results>

spring中mybatis拦截器配置片段:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="xx" /><property name="plugins"><array><!--拦截器类全称--><bean class="PageStatementInterceptor"></bean></array></property>
</bean>

自此所有内容已叙完,最后继续探讨下前面的遗留问题,AreaCodeService的方法2,

如何将查询总数返回到控制器?

其实至少有两个方法:

1.List<AreaCode> listAreaCode(int offset, int limit)再增加第三个参数,然后将SmartRowBounds的记录总数放到第三个参数中,供控制器取,以下是模拟实现:

public List<AreaCode> listAreaCode(int offset, int limit, ResultHook hook) {//方法2SmartRowBounds rb = new SmartRowBounds(offset, limit);try {return mapper.list(new SmartRowBounds(offset, limit,true));} catch (Exception e) {e.printStackTrace();} finally {//将记录总数传给hook,通知到控制器去取hook.setResult(rb.getTotalCount());}}
}

2.借助线程本地上下文ThreadLocal,同一个线程总能共享相同的数据对象,创建RowBoundsHolder对象:

public class RowBoundsHolder {private static ThreadLocal<SmartRowBounds> rowBoundHolder = new ThreadLocal<SmartRowBounds>();public static SmartRowBoundsinstance(int offset, int limit) {SmartRowBounds rb = new SmartRowBounds(offset, limit);rowBoundHolder.set(rb);return rb;}public static SmartRowBounds instance(int offset, int limit, boolean supportDbPage) {SmartRowBounds rb = new SmartRowBounds(offset, limit, supportDbPage);rowBoundHolder.set(rb);return rb;}public static SmartRowBoundsget() {return rowBoundHolder.get();}
}

RowBoundsHolder的instance方法创建一个SmartRowBounds rb并设置到线程本地山下文中,在同一个线程中,调用任意对象的任意方法,总能访问此rb对象的引用。

改造AreaCodeService的方法2:

@Service
public class AreaCodeService {//通过spring进行自动注入@Resourceprivate AreaCodeMapper mapper;public List<AreaCode> listAreaCode(int offset, int limit) {//方法2return mapper.list(RowBoundsHolder.instance(offset, limit, true));}
}

改造AbstractAction添加新方法:

protected void setJsonGrid(List list) {int total = list == null ? 0 : list.size();SmartRowBounds rb = RowBoundsHolder.get();if (rb != null) {//通线程上下文获取总记录数total = rb.getTotalCount();}Map data = new HashMap();data.put("total", total);data.put("rows", list);this.gridDataList = data;}

修改AreaCodeAction.java:

public class AreaCodeAction extends AbstractAction{//通过spring将AreaCodeService注入进来@Resourceprivate AreaCodeService service;public String gridList() {List<AreaCode> list = service.listAreaCode(queryOffset, queryLimit);//使用AreaCodeService的方法2setJsonGrid(list);return JSON;}
}

All right,如果你觉得有“点”帮助,请点个“赞”哦,3q。

以上实现仅供参考,思路各异,满意就行。

原创博文,转载请注明出处。

一步步教你mybatis分页,mybatis分页拦截器 使用,mybatis拦截器分页相关推荐

  1. 一步步教你整合SSM框架(Spring MVC+Spring+MyBatis)详细教程重要

    前言 SSM(Spring+SpringMVC+Mybatis)是目前较为主流的企业级架构方案,不知道大家有没有留意,在我们看招聘信息的时候,经常会看到这一点,需要具备SSH框架的技能:而且在大部分教 ...

  2. SpingBoot中使用MyBatis和pagehelper实现数据的增删改查和分页

    文章目录 一.认识MyBatis CRUD注解 映射注解 高级注解 二.用MyBatis实现数据的增加.删除.修改.查询和分页 1.创建springboot项目并引入依赖 2.实现数据表的自动初始化 ...

  3. jsp mysql 分页插件_知识分享:Mybatis框架如何使用分页插件呢?

    分页插件使用的方式 修改 pom 文件,添加分页 jar 包依赖 修改 mybatis.xml 文件 UserDao 接口,UserMapper.xml 添加对应方法与实现 sql 对应 UserSe ...

  4. 分页插件PageHelper配置步骤(mybatis)

    原理: mybatis执行sql步骤: 通过sqlsessionFactory  sqlsession Exector  (执行器对象)mappedstatement(sql语句封装) 在执行mapp ...

  5. Java-Mybatis(二): Mybatis配置解析、resultMap结果集映射、日志、分页、注解开发、Mybatis执行流程分析

    Java-Mybatis-02 学习视频:B站 狂神说Java – https://www.bilibili.com/video/BV1NE411Q7Nx 学习资料:mybatis 参考文档 – ht ...

  6. 浅谈AJAX并实现使用pagehelper-5.1.10.jar分页插件实现异步从数据库中获取数据分页显示

    AJAX异步请求 什么是ajax 异步 JavaScript 同步和异步的区别 同步方式:正常情况下,浏览器与服务器之间是串行操作,类似于一个Java线程的操作. 异步方式:浏览器与服务器是并行操作, ...

  7. MyBatis多参数传递之Map方式示例——MyBatis学习笔记之十三

    前面的文章介绍了MyBatis多参数传递的注解.参数默认命名等方式,今天介绍Map的方式.仍然以前面的分页查询教师信息的方法findTeacherByPage为例(示例源代码下载地址:http://d ...

  8. 1、mybatis是什么?为什么要用mybatis?

    对于初学者,如果进行mybatis的学习呢?我总结了几点,会慢慢的更新出来.首先大家需要了解mybatis是什么.用mybatis来做什么.为什么要用mybatis.有什么优缺点:当知道了为什么的时候 ...

  9. mybatis是什么_深入解析:Mybatis接口没有实现类为什么可以执行增删改查?

    作者:小傅哥 链接:https://segmentfault.com/a/1190000022767561 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言介绍 MyBatis 是一款非常优秀的 ...

  10. 面试大厂被MyBatis问到“哑口无言”?这份MyBatis源码笔记助你吊打面试官!

    写在前面 随着手机.平板电脑等移动终端的广泛应用,移动互联网时代已经到来.在这个时代里,构建一个高效的平台并提供服务是移动互联网的基础,在众多的网站服务中,使用Java构建网站的不在少数,移动互联网的 ...

最新文章

  1. Bézier曲线 和 Bézier曲面 ( 贝塞尔曲线 和 贝塞尔曲面 )
  2. 使用Application.GetResourceStream从XAP安装包加载任意资源
  3. 忍一时得寸进尺, 退一步变本加厉。
  4. mysql语句 java变量_Java操作Mysql的方法
  5. 【模板】快速排序(洛谷-P1177)
  6. 怎么做app图标_App拉新:以老拉新活动怎么做?
  7. 苹果或在 WWDC 宣布放弃英特尔转向自研 5nm ARM 芯片,这次时机成熟了?
  8. 内存分配失败错误处理
  9. Java中必须了解的常用类
  10. 网站安全检测:推荐8款免费的 Web 安全测试工具
  11. 初学JAVA GUI
  12. 爬取古诗文网的推荐古诗
  13. 高速的C/C++编译工具——ccache
  14. 最佳的75个安全工具
  15. 两端分散对齐怎么设置_Word文档两端对齐.分散对齐如何设置
  16. hapi入门简介(入门实践)----净土小沙弥学hapi.js_第二篇
  17. 建筑八大员培训湖北施工员培训建筑施工企业员工流失的原因
  18. 《程序员的自我修养》阅读笔记
  19. mysql翻译逗号隔开的字典_【求大神】现在有1000个string与一个MYSQL字典,查询这1000个string在MYSQL中的id并输出结果...
  20. dNet图像处理组件学习总结

热门文章

  1. (已解决)wps交叉引用更新所有域后出现“错误!未定义书签。”
  2. 01-02istio架构概念了解
  3. Windows 11 - 打开操作安全中心提示 “需要使用新应用以打开此 Windowsdefender 链接“ 解决方案(电脑更新了 Win11 系统版本后,想关闭病毒防护盾牌的时候提示打不开)
  4. “以赛助教”进校园,CCF大数据与计算智能大赛成高校人才探索实践AI技术的第二课堂!
  5. matlab数值积分变步长,关于MATLAB数值积分及算例.ppt
  6. Golang中的深拷贝与浅拷贝
  7. 蒲慕明:今人眼中的大脑之美 | 书评
  8. Wireshark中的名字解析
  9. 将Navicat右键表【对象信息】-【DDL】的内容转换为JavaBean
  10. springboot实现excel文件下载的功能