当前位置: 首页 > news >正文

从零开始的 MyBatis 拦截器之旅:实战经验分享

文章目录

    • MyBatis拦截器可以做什么?
    • Mybatis核心对象介绍
    • 四大核心对象
    • 如何实现?接口讲解
      • Interceptor接口
      • intercept方法
      • plugin方法
      • setProperties
    • 完整SQL打印拦截器实战
      • 拦截器实现
      • 拦截器注册

MyBatis拦截器可以做什么?

MyBatis拦截器是MyBatis框架提供的扩展机制,它可以在执行SQL语句的过程中拦截和干预,用于对SQL语句进行增强或修改。

MyBatis拦截器可以做以下几件事情:

  1. 拦截SQL语句的执行:拦截器可以在SQL语句执行前后进行拦截,可以在SQL语句执行之前对参数进行处理,也可以在SQL语句执行之后对结果进行处理。
  2. 修改SQL语句:拦截器可以对原始的SQL语句进行修改,可以增加、删除或修改SQL语句的部分内容,以满足一些特定需求。比如可以在SQL语句前后添加额外的条件或修改排序方式。
  3. 记录日志:拦截器可以用于记录SQL语句的执行日志,包括SQL语句的执行时间、执行结果等。这对于系统的性能监控和调优非常有帮助。
  4. 实现分页功能:拦截器可以在执行原始SQL语句之前,根据传入的参数进行分页处理,将查询结果限制在指定的页数和每页的记录数范围内。
  5. 实现缓存功能:拦截器可以在执行SQL语句之前,先检查缓存中是否存在对应的结果,如果存在则直接返回缓存结果,避免不必要的数据库查询操作。
  6. 在很多时候,对表中的数据都需要记录插入时间,修改时间,插入人和修改人,若每次都在插入或修改代码中去设置这些信息,就显得有些冗余。那么此时可以通过Mybatis提供的拦截器加上我们自定义的拦截器实现对在需要记录的操作人信息sql执行前,自动补充这些信息,也就是所谓的对Mybatis的核心对象进行增强。这里只拦截Executor对象,给更新的sql语句动态的增加参数。(可参考https://www.cnblogs.com/zys2019/p/16966866.html )

具体例子: 我们常用的分页插件Pagehelper其实就是一个拦截器实现

Mybatis核心对象介绍

MyBatis的主要的核心部件有以下几个:

  • Configuration:初始化基础配置,比如MyBatis的别名等,一些重要的类型对象,如插件,映射器,ObjectFactory和typeHandler对象,MyBatis所有的配置信息都维持在Configuration对象之中。
  • SqlSessionFactory:SqlSession工厂。
  • SqlSession:作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要的数据库增删改查功能。
  • Executor:MyBatis的内部执行器,它负责调用StatementHandler操作数据库,并把结果集通过ResultSetHandler进行自动映射,另外,它还处理二级缓存的操作。
  • StatementHandler:MyBatis直接在数据库执行SQL脚本的对象。另外它也实现了MyBatis的一级缓存。
  • ParameterHandler:负责将用户传递的参数转换成JDBC Statement所需要的参数。是MyBatis实现SQL入参设置的对象。
  • ResultSetHandler:负责将JDBC返回的ResultSet结果集对象转换成List类型的集合。是MyBatis把ResultSet集合映射成POJO的接口对象。
  • TypeHandler:负责Java数据类型和JDBC数据类型之间的映射和转换。
  • MappedStatement:MappedStatement维护了一条<select|update|delete|insert>节点的封装。
  • SqlSource :负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回。
  • BoundSql:表示动态生成的SQL语句以及相应的参数信息。

Mybatis(四):MyBatis核心组件介绍原理解析和源码解读- 夏威夷8080 - 博客园

img

四大核心对象

在Mybatis中,Executor、StatementHandler、ParameterHandler和ResultSetHandler是核心组件,它们分别负责不同的任务。

  1. Executor:统筹全局
    Executor是Mybatis中的执行器,它负责处理数据库的操作。它的职责是接收并执行SQL语句,管理事务的提交和回滚,以及处理缓存。 在Mybatis中,有三种类型的Executor:SimpleExecutor、ReuseExecutor和BatchExecutor,它们分别提供了不同的执行策略。
  2. StatementHandler:执行SQL
    StatementHandler负责处理SQL语句的操作,它是Executor的一个重要组成部分。它的主要职责是创建PreparedStatement对象,设置参数,并执行SQL语句。 StatementHandler可以根据不同的数据库厂商提供的驱动,生成不同的Statement对象,如PreparedStatement、CallableStatement等。
  3. ParameterHandler:参数封装
    ParameterHandler负责处理SQL语句中的参数。它的主要职责是将Java对象中的属性值映射到SQL语句中的参数位置。ParameterHandler可以根据参数的类型,将Java对象的属性值转换为数据库可以接受的类型,并设置到PreparedStatement对象中。
  4. ResultSetHandler:返回结果映射
    ResultSetHandler负责处理SQL语句的结果集。它的主要职责是将查询结果集中的数据映射到Java对象中。ResultSetHandler会根据映射规则,将数据库中的每一行数据转换为Java对象,并将这些对象放入一个集合中返回给调用者。

img

这四个组件在Mybatis中协同工作,完成了从数据库操作到Java对象映射的整个过程。Executor负责整体的控制和协调,StatementHandler负责处理SQL语句的操作,ParameterHandler负责处理参数,ResultSetHandler负责处理结果集。它们各自分工明确,相互配合,共同完成数据库操作和对象映射的任务。

如何实现?接口讲解

  • 写一个实现org.apache.ibatis.plugin.Interceptor接口的拦截器类,并实现其中的方法。
  • 添加@Intercepts注解,写上需要拦截的对象和方法,以及方法参数。
  • Spring项目注意添加@Component注解即可,使其成为Spring管理的一个Bean。

Interceptor接口

@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}),@Signature(type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class}),@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
public class MyInterceptor implements Interceptor {

MyBatis拦截器默认可以拦截的类型只有四种,即四种接口类型ExecutorStatementHandlerParameterHandlerResultSetHandler。对于我们的自定义拦截器必须使用MyBatis提供的@Intercepts注解来指明我们要拦截的是四种类型中的哪一种接口。

注解描述
@Intercepts标志该类是一个拦截器
@Signature指明该拦截器需要拦截哪一个接口的哪一个方法

@Signature注解的参数:

参数描述
type四种类型接口中的某一个接口,如Executor.class
method对应接口中的某一个方法名,比如Executorquery方法。
args对应接口中的某一个方法的参数,比如Executorquery方法因为重载原因,有多个,args就是指明参数类型,从而确定是具体哪一个方法。

MyBatis拦截器默认会按顺序拦截以下的四个接口中的所有方法:

org.apache.ibatis.executor.Executor  //拦截执行器方法
org.apache.ibatis.executor.statement.StatementHandler  //拦截SQL语法构建处理
org.apache.ibatis.executor.parameter.ParameterHandler  //拦截参数处理
org.apache.ibatis.executor.resultset.ResultSetHandler  //拦截结果集处理

具体是拦截这四个接口对应的实现类:

org.apache.ibatis.executor.CachingExecutor
org.apache.ibatis.executor.statement.RoutingStatementHandler
org.apache.ibatis.scripting.defaults.DefaultParameterHandler
org.apache.ibatis.executor.resultset.DefaultResultSetHandler

img

intercept方法

进行拦截的时候要执行的方法。该方法参数Invocation类中有三个字段:

  private final Object target;private final Method method;private final Object[] args;

可通过这三个字段分别获取下面的信息:

Object target = invocation.getTarget();//被代理对象
Method method = invocation.getMethod();//代理方法
Object[] args = invocation.getArgs();//方法参数

plugin方法

插件用于封装目标对象的,通过该方法我们可以返回目标对象本身,也可以返回一个它的代理,可以决定是否要进行拦截进而决定要返回一个什么样的目标对象,官方提供了示例:return Plugin.wrap(target, this);可以在这个方法中提前进行拦截对象类型判断,提高性能:

    @Overridepublic Object plugin(Object target) {//只对要拦截的对象生成代理if(target instanceof StatementHandler){//调用插件return Plugin.wrap(target, this);}return target;}

MyBatis拦截器用到责任链模式+动态代理+反射机制;
所有可能被拦截的处理类都会生成一个代理类,如果有N个拦截器,就会有N个代理,层层生成动态代理是比较耗性能的。而且虽然能指定插件拦截的位置,但这个是在执行方法时利用反射动态判断的,初始化的时候就是简单的把拦截器插入到了所有可以拦截的地方。所以尽量不要编写不必要的拦截器。另外我们可以在调用插件的地方添加判断,只要是当前拦截器拦截的对象才进行调用,否则直接返回目标对象本身,这样可以减少反射判断的次数,提高性能。

setProperties

如果我们拦截器需要用到一些变量参数,而且这个参数是支持可配置的,类似Spring中的@Value("${}")application.properties文件获取自定义变量属性,这个时候我们就可以使用这个方法。

private String property1;
private int property2;@Overridepublic void setProperties(Properties properties) {// 从配置文件中获取属性值this.property1 = properties.getProperty("property1");this.property2 = Integer.parseInt(properties.getProperty("property2"));}

setProperties方法中,我们可以通过传入的Properties对象获取配置文件中的属性值,并进行相应的处理。

然后,在MyBatis的配置文件中注册自定义的拦截器:

<configuration><!-- 其他配置 --><plugins><plugin interceptor="com.example.CustomInterceptor"><property name="property1" value="value1" /><property name="property2" value="2" /></plugin></plugins>
</configuration>

完整SQL打印拦截器实战

拦截器实现

@Slf4j
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),@Signature(type = Executor.class,method = "query",args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class,BoundSql.class}
), @Signature(type = Executor.class,method = "query",args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
)})
public class SqlPrintInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {try {// 获取xml中的一个select/update/insert/delete节点,是一条SQL语句MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];Object parameter = null;// 获取参数,if语句成立,表示sql语句有参数,参数格式是map形式if (invocation.getArgs().length > 1) {parameter = invocation.getArgs()[1];log.info("SQL打印拦截器参数 = " + parameter);}// 获取到节点的id, 即sql语句的idString sqlId = mappedStatement.getId();//log.info("sqlId = " + sqlId);// BoundSql就是封装myBatis最终产生的sql类BoundSql boundSql = mappedStatement.getBoundSql(parameter);// 获取节点的配置Configuration configuration = mappedStatement.getConfiguration();// 获取到最终的sql语句String sql = getSql(configuration, boundSql, sqlId);log.info("SQL打印拦截器完整SQL = " + sql);} catch (Exception e) {e.printStackTrace();}// 执行完上面的任务后,不改变原有的sql执行过程return invocation.proceed();}// 封装了一下sql语句,使得结果返回完整xml路径下的sql语句节点id + sql语句private static String getSql(Configuration configuration, BoundSql boundSql, String sqlId) {String sql = showSql(configuration, boundSql);StringBuilder str = new StringBuilder(100);str.append(sqlId);str.append(":");str.append(sql);return str.toString();}// 如果参数是String,则添加单引号, 如果是日期,则转换为时间格式器并加单引号; 对参数是null和不是null的情况作了处理private static String getParameterValue(Object obj) {String value = null;if (obj instanceof String) {value = "'" + obj.toString() + "'";} else if (obj instanceof Date) {DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT,DateFormat.DEFAULT, Locale.CHINA);value = "'" + formatter.format(new Date()) + "'";} else {if (obj != null) {value = obj.toString();} else {value = "";}}return value;}// 进行?的替换private static String showSql(Configuration configuration, BoundSql boundSql) {// 获取参数Object parameterObject = boundSql.getParameterObject();List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();// sql语句中多个空格都用一个空格代替String sql = boundSql.getSql().replaceAll("[\\s]+", " ");if (CollectionUtils.isNotEmpty(parameterMappings) && parameterObject != null) {// 获取类型处理器注册器,类型处理器的功能是进行java类型和数据库类型的转换TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();// 如果根据parameterObject.getClass()可以找到对应的类型,则替换if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {sql = sql.replaceFirst("\\?",Matcher.quoteReplacement(getParameterValue(parameterObject)));} else {// MetaObject主要是封装了originalObject对象,提供了get和set的方法用于获取和设置originalObject的属性值,// 主要支持对JavaBean、Collection、Map三种类型对象的操作MetaObject metaObject = configuration.newMetaObject(parameterObject);for (ParameterMapping parameterMapping : parameterMappings) {String propertyName = parameterMapping.getProperty();if (metaObject.hasGetter(propertyName)) {Object obj = metaObject.getValue(propertyName);sql = sql.replaceFirst("\\?",Matcher.quoteReplacement(getParameterValue(obj)));} else if (boundSql.hasAdditionalParameter(propertyName)) {// 该分支是动态sqlObject obj = boundSql.getAdditionalParameter(propertyName);sql = sql.replaceFirst("\\?",Matcher.quoteReplacement(getParameterValue(obj)));} else {// 打印出缺失,提醒该参数缺失并防止错位sql = sql.replaceFirst("\\?", "缺失");}}}}return sql;}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {}
}

拦截器注册

主要是这一句bean.setPlugins(new Interceptor[]{ new SqlPrintInterceptor()});

@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {//设置分页的拦截器PageInterceptor pageInterceptor = new PageInterceptor();//创建插件需要的参数集合Properties properties = new Properties();//配置数据库方言 为oracleproperties.setProperty("helperDialect", "mysql");//配置分页的合理化数据properties.setProperty("reasonable", "true");pageInterceptor.setProperties(properties);SqlSessionFactoryBean bean = new SqlSessionFactoryBean();bean.setPlugins(new Interceptor[]{ new SqlPrintInterceptor()});bean.setDataSource(dataSource);bean.setMapperLocations(resolveMapperLocations());//重点2 指定包扫描,当前这个数据源对应的mapper.xml文件在哪个resource下的包里,这里路径就指定哪里return bean.getObject();}

完整注册配置类

@Slf4j
@Configuration
@MapperScan(basePackages = {"com.*.dao"}, sqlSessionTemplateRef = "sqlSessionTemplate")
//重点1
// 指定包扫描,当前这个数据源对应的mapper.java文件放在哪个包下,这里路径就指定哪里
public class MySQLDataSourceConfig {@Bean@Primary@ConfigurationProperties(prefix = "spring.datasource.mysql")//重点3 这里对应yml的当前数据源的前缀public DataSource dataSource() {return DataSourceBuilder.create().build();}@Bean@Primarypublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {//设置分页的拦截器PageInterceptor pageInterceptor = new PageInterceptor();//创建插件需要的参数集合Properties properties = new Properties();//配置数据库方言 为oracleproperties.setProperty("helperDialect", "mysql");//配置分页的合理化数据properties.setProperty("reasonable", "true");pageInterceptor.setProperties(properties);SqlSessionFactoryBean bean = new SqlSessionFactoryBean();//设置拦截器!!!!bean.setPlugins(new Interceptor[]{ new SqlPrintInterceptor()});bean.setDataSource(dataSource);bean.setMapperLocations(resolveMapperLocations());//重点2 指定包扫描,当前这个数据源对应的mapper.xml文件在哪个resource下的包里,这里路径就指定哪里return bean.getObject();}/*** 获取多个路径下的mapper** @return*/public Resource[] resolveMapperLocations() {ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();List<String> mapperLocations = new ArrayList<>();mapperLocations.add("classpath:com/mapper/**/*.xml");List<Resource> resources = new ArrayList();if (!CollectionUtils.isEmpty(mapperLocations)) {for (String mapperLocation : mapperLocations) {try {Resource[] mappers = resourceResolver.getResources(mapperLocation);resources.addAll(Arrays.asList(mappers));} catch (IOException e) {//log.error("Get myBatis resources happened exception", e);}}}return resources.toArray(new Resource[0]);}@Bean@Primarypublic DataSourceTransactionManager mysqlTransactionManager(DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}@Bean@Primarypublic SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) throws Exception {return new SqlSessionTemplate(sqlSessionFactory);}}

参考文章:
https://blog.csdn.net/wb1046329430/article/details/111501755

相关文章:

从零开始的 MyBatis 拦截器之旅:实战经验分享

文章目录 MyBatis拦截器可以做什么&#xff1f;Mybatis核心对象介绍四大核心对象如何实现&#xff1f;接口讲解Interceptor接口intercept方法plugin方法setProperties 完整SQL打印拦截器实战拦截器实现拦截器注册 MyBatis拦截器可以做什么&#xff1f; MyBatis拦截器是MyBatis…...

网络编程day05(IO多路复用)

今日任务&#xff1a; TCP多路复用的客户端、服务端&#xff1a; 服务端代码&#xff1a; #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <unistd.h> …...

人声分离网站,帮你快速提取视频中的人声和背景音乐

今天给大家带来一个可以分离人声的网站——音分轨&#xff0c;他运用人工智能算法可以将音频中的人声部分和音乐部分分离&#xff0c;使我们的视频制作过程可以更方便。 我们点击右下角“选择文件”上传一个音频&#xff0c;上传好音频后&#xff0c;人工智能就开始处理我们上传…...

计算机网络常见问题

1.谈一谈对OSI七层模型和TCP/IP四层模型的理解&#xff1f; 1.1.为什么要分层&#xff1f; 在计算机中网络是个复杂的系统&#xff0c;不同的网络与网络之间由于协议&#xff0c;设备&#xff0c;软件等各种原因在协调和通讯时容易产生各种各样的问题。例如&#xff1a;各物流…...

上PICO,沉浸式观看亚运直播,参与跨国界游戏竞技

备受瞩目的杭州第19届亚运会&#xff0c;将于9月23日正式开幕。据悉&#xff0c;这也是有史以来项目最多的一届亚运会&#xff0c;除部分传统奥运项目外&#xff0c;还包含武术、藤球、板球、克柔术、柔术等亚洲特色项目&#xff0c;以及霹雳舞、电子竞技等深受年轻人喜爱的新兴…...

无重复字符的最长子串 - 力扣(LeetCode)

3. 无重复字符的最长子串 - 力扣&#xff08;LeetCode&#xff09; 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 示例 1: 输入: s "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc"&#xff0c;所以其长…...

企业行政许可的种类有哪些?

从行政许可的性质、功能和适用条件的角度来说&#xff0c;大体可以划分为五类&#xff1a;普通许可、特许、认可、核准、登记。 1.普通许可 普通许可是一种允许符合特定条件的相对方行使某种权利的行为。在许多情况下&#xff0c;需要普通许可的活动都与国家安全、公共安全息…...

Flink--4、DateStream API(执行环境、源算子、基本转换算子)

星光下的赶路人star的个人主页 注意力的集中&#xff0c;意象的孤立绝缘&#xff0c;便是美感的态度的最大特点 文章目录 1、DataStream API1.1 执行环境&#xff08;Execution Environment&#xff09;1.1.1 创建执行环境 1.2 执行模式&#xff08;Execution Mode&#xff09;…...

#循循渐进学51单片机#指针基础与1602液晶的初步认识#not.11

1、把本节课的指针相关内容&#xff0c;反复学习3到5遍&#xff0c;彻底弄懂指针是怎么回事&#xff0c;即使是死记硬背也要记住&#xff0c;等到后边用的时候可以实现顿悟。学会指针&#xff0c;就是突破了C语言的一道壁垒。 2&#xff0c;1602所有的指令功能都应用一遍&#…...

Lua学习笔记:探究package

前言 本篇在讲什么 理解Lua的package 本篇需要什么 对Lua语法有简单认知 对C语法有简单认知 依赖Visual Studio工具 本篇的特色 具有全流程的图文教学 重实践&#xff0c;轻理论&#xff0c;快速上手 提供全流程的源码内容 ★提高阅读体验★ &#x1f449; ♠ 一级…...

【面试经典150 | 双指针】三数之和

文章目录 写在前面Tag题目来源题目解读解题思路方法一&#xff1a;暴力枚举方法二&#xff1a;双指针 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带一些对…...

现代卷积网络实战系列3:PyTorch从零构建AlexNet训练MNIST数据集

1、AlexNet AlexNet提出了一下5点改进&#xff1a; 使用了Dropout&#xff0c;防止过拟合使用Relu作为激活函数&#xff0c;极大提高了特征提取效果使用MaxPooling池化进行特征降维&#xff0c;极大提高了特征提取效果首次使用GPU进行训练使用了LRN局部响应归一化&#xff08…...

Django系列:Django应用(app)的创建与配置

Django系列 Django应用&#xff08;app&#xff09;的创建与配置 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://blog.csdn.net/qq_28550263/article…...

Linux查看程序和动态库依赖的动态库

一. 前言 在一些时候&#xff0c;我们需要知道一个程序或者动态库所依赖的动态库有哪些。比如&#xff0c;当我们运行一个程序的时候&#xff0c;发现可能会报错&#xff0c;提示找不到某个符号&#xff0c;这时我们就需要知道程序依赖了什么库&#xff0c;从而添加对应需要的动…...

vue3 无法使用pnpm安装依赖 或 Cannot find module preinstall.js

创建.npmrc文件在根目录 shamefully-hoisttrue auto-install-peerstrue strict-peer-dependenciesfalse删除 node_modules 和 pnpm-lock.yaml 文件 重新 pnpm i 就可以啦...

C/C++连接数据库,包含完整代码。

C/C连接数据库 本篇文章意在简洁明了的在linux环境下使用C/C连接远程数据库&#xff0c;并对数据库进行增删查改等操作。我所使用的环境是centos7&#xff0c;不要环境除环境配置外&#xff0c;代码是大同小异的。完整代码在最底部&#xff01;&#xff01;&#xff01; 1.前…...

AUTOSAR词典:CAN驱动Mailbox配置技术要点全解析

AUTOSAR词典&#xff1a;CAN驱动Mailbox配置技术要点全解析 前言 首先&#xff0c;请问大家几个小小问题&#xff0c;你清楚&#xff1a; AUTOSAR框架下的CAN驱动关键词定义吗&#xff1f;是不是有些总是傻傻分不清楚呢&#xff1f;CAN驱动Mailbox配置过程中有哪些关键配置参…...

C语言 coding style

头文件 The #define Guard #define的保护文件的唯一性&#xff0c;防止被多重包含 格式 : <PROJECT>_< FILE>_H_ PROJECT : XS FILE : MV_CTR 头文件的包含顺序 C System FilesOther LibrariesUser LibraryConditional include 作用域 局部变量 -变量定义时需要…...

Python办公自动化之PDF

Python操作PDF 1、Python操作PDF概述2、批量拆分3、批量合并4、提取内容(文字)5、提取内容(表格)6、提取图片7、PDF添加水印8、加密与解密1、Python操作PDF概述 Python操作PDF主要有两个库:PyPDF2和pdfplumber PyPDF2是一个用于处理PDF文件的Python第三方库 官网文档参考:…...

【每日一题Day331】LC2560打家劫舍 IV | 二分查找 + 贪心

打家劫舍 IV【LC2560】 沿街有一排连续的房屋。每间房屋内都藏有一定的现金。现在有一位小偷计划从这些房屋中窃取现金。 由于相邻的房屋装有相互连通的防盗系统&#xff0c;所以小偷 不会窃取相邻的房屋 。 小偷的 窃取能力 定义为他在窃取过程中能从单间房屋中窃取的 最大金额…...

JVM 参数详解

GC有两种类型&#xff1a;Scavenge GC 和Full GC 1、Scavenge GC 一般情况下&#xff0c;当新对象生成&#xff0c;并且在Eden申请空间失败时&#xff0c;就会触发Scavenge GC&#xff0c;堆的Eden区域进行GC&#xff0c;清除非存活对象&#xff0c;并且把尚且存活的对象移动到…...

uni-app获取地理位置

在uni-app中&#xff0c;可以通过uni.getLocation()方法获取地理位置。具体步骤如下&#xff1a; 在uni-app项目中的manifest.json文件中&#xff0c;添加需要获取地理位置的权限&#xff1a; {"mp-weixin": {"appid": "...","permission…...

Learn Prompt-Prompt 高级技巧:思维链 Chain of Thought Prompting

Jason Wei等作者对思维链的定义是一系列的中间推理步骤&#xff08; a series of intermediate reasoning steps &#xff09;。目的是为了提高大型语言模型&#xff08;LLM&#xff09;进行复杂推理的能力。 思维链通常是伴随着算术&#xff0c;常识和符号推理等复杂推理任务出…...

Vim编辑器使用入门

目录 一、Vim 编辑器基础操作 二、Vim 编辑器进阶操作 三、Vim 编辑器高级操作 四、Vim 编辑器文件操作 五、Vim 编辑器文件管理 六、Vim 编辑器进阶技巧 七、Vim 编辑器增强功能 Vim的三种工作模式 一、Vim 编辑器基础操作 1.移动光标 - 光标的移动控制 移动光标有两…...

早餐与风景

来吧&#xff0c;我用流水账描述下这一天。 时维九月&#xff0c;北京的早上有点冷&#xff0c;因为今天有个市场活动要去支撑&#xff0c;按照会议时间的要求&#xff0c;我需要在早上7点半就赶到会场&#xff0c;所以昨天晚上我加班到凌晨处理完了今天要给出去的材料&#xf…...

常用python代码串

记录新疆出差期间的一些代码 打开yaml文件python中的专有名词ctrlc 打开yaml文件 with open(/home/cyun/文档/cotton_ws/src/control/scripts/ControlParameter.yaml, r) as file:yaml_data yaml.load(file, Loaderyaml.FullLoader)后面发现像这种打开文件的最好是try一下 p…...

电脑桌面透明便签软件是哪个?

在现代快节奏的工作环境中&#xff0c;许多上班族都希望能够在电脑桌面上方便地记录工作资料、重要事项、工作流程等内容。为了解决这个问题&#xff0c;一款优秀的电脑桌面便签软件是必不可少的。在选择桌面便签软件时&#xff0c;许多用户也希望便签软件能够与电脑桌面壁纸相…...

Git创建干净分支,本地操作不依赖任何分支

clone远程项目: git clone gittest.git查看分支: git branch -a创建新分支: git checkout --orphan test, 返回Switched to a new branch test删除当前项目文件夹下所有文件: git rm -rf .提交变更: git commit -m "new branch for test"查看分支: git branch -a, 发…...

sqlmap tamper脚本编写

文章目录 tamper脚本是什么&#xff1f;指定tamper脚本运行sqlmap安全狗绕过tamper脚本 tamper脚本是什么&#xff1f; SQLMap 是一款SQL注入神器&#xff0c;可以通过tamper 对注入payload 进行编码和变形&#xff0c;以达到绕过某些限制的目的。但是有些时候&#xff0c;SQLM…...

5.5V-65V Vin同步降压控制器,具有线路前馈SCT82630DHKR

描述&#xff1a; SCT82630是一款65V电压模式控制同步降压控制器&#xff0c;具有线路前馈。40ns受控高压侧MOSFET的最小导通时间支持高转换比&#xff0c;实现从48V输入到低压轨的直接降压转换&#xff0c;降低了系统复杂性和解决方案成本。如果需要&#xff0c;在低至6V的输…...

网页设计素材电影/广州aso优化公司 有限公司

【实例简介】主题模型(Topic Model)LDA详解及其Matlab代码【实例截图】【核心代码】LDA讲解及matlab程序└── LDA讲解及matlab程序├── LDA实验│ ├── Matlab Topic Modeling Toolbox 1.docx│ └── topictoolbox│ ├── AssociationLDA2.m│ ├── Associ…...

汽车音响网站建设/2022网站快速收录技术

————— 第二天 —————————————————首先&#xff0c;我们来定义一个Product类&#xff1a;public class Product {ArrayList<String> parts new ArrayList<String>();public void add(String part) {parts.add(part);}public void show() {S…...

wordpress支付宝/360推广登录入口

最近研究基于 GeoServer 的开源 GIS 框架&#xff0c;其中构建 GIS database 需要使用 PostgreSQL PostGIS 插件。花了些时间学习&#xff0c;这里记录一下。如有错误&#xff0c;感谢不吝指正&#xff5e;&#xff5e;本文地址&#xff1a;https://segmentfault.com/a/1190000…...

电商网站建设公司哪家好/友链价格

前言 随着k8s 作为容器编排解决方案变得越来越流行&#xff0c;有些人开始拿 Docker 和 k8s进行对比&#xff0c;不禁问道&#xff1a;Docker 不香吗&#xff1f; k8s 是kubernets的缩写&#xff0c;’8‘代表中间的八个字符。 其实 Docker 和 k8s 并非直接的竞争对手&#xff…...

帮助中心网站源码/直通车推广计划方案

Python内置了整数、复数、浮点数三种数字类型。整数 整数是没有小数部分的数值&#xff0c;与数学上的一样&#xff1a; >>> 1 1 >>> -1 -1 整数没有大小限制&#xff0c;只要你的内存足够大&#xff0c;就可以创建任意大小的整数&#xff1a; >>> …...

成都网站设计新闻/百度关键词排名批量查询工具

1 发请求的各种方法 使用form标签&#xff08;会在当前页面刷新或者新开一个页面刷新&#xff09; <form action"" methodpost/get><input type"submit"></form> 使用a标签&#xff08;会在当前页面刷新或者新开一个页面刷新&#xff…...