MyBatis拦截器终极指南:从原理到企业级实战
在本篇文章中,我们将深入了解如何编写一个 MyBatis 拦截器,并通过一个示例来展示如何在执行数据库操作(如插入或更新)时,自动填充某些字段(例如 createdBy
和 updatedBy
)信息。本文将详细讲解拦截器的工作原理、代码示例及其在 MyBatis 项目中的应用。
一、为什么需要数据库操作拦截器?
在典型的企业级应用开发中,我们经常会遇到这样的需求:
-
需要自动记录数据行的创建时间和修改时间
-
需要跟踪记录数据操作人
-
需要实现全局的软删除逻辑
-
需要对敏感数据进行自动加密
-
需要实现SQL执行监控和慢查询统计
传统做法是在每个Mapper方法中手动添加这些逻辑,但这样会导致大量重复代码。MyBatis拦截器(Interceptor)正是为了解决这类问题而生,它可以在SQL执行的各个阶段插入自定义逻辑,实现横切关注点的统一管理。
二、MyBatis拦截器核心原理
如果对于MyBatis核心组件功能还不了解的小伙伴们建议先跳到最后一章节:第五节去了解MyBatis核心组件功能再倒回来继续学习!
2.1 拦截器架构
MyBatis采用责任链模式实现拦截器机制,主要拦截点包括:
拦截接口 | 拦截时机 | 典型应用场景 |
---|---|---|
Executor | SQL执行前后(增删改查操作) | 事务管理、分页处理 |
StatementHandler | SQL语句构建时 | SQL改写、危险操作拦截 |
ParameterHandler | 参数处理时 | 参数加密、参数校验 |
ResultSetHandler | 结果集处理时 | 结果解密、数据脱敏 |
2.2 拦截器生命周期
MyBatis 中的拦截器生命周期较为简单。它的生命周期由 Plugin.wrap()
方法控制,首先会创建代理对象并包装目标对象。拦截器的 intercept
方法在执行目标方法前被调用,拦截器的 plugin
方法用于对目标方法的代理,setProperties
方法用于注入拦截器所需的配置属性。
拦截目标方法调用的顺序:当拦截器装载到 MyBatis 配置中后,每次执行目标方法(如数据库操作的 insert
、update
)时,都会经过拦截器链。如果多个拦截器存在,它们会按照配置顺序依次执行。可以通过实现Ordered接口或使用@Order注解控制执行顺序:
@Intercepts(...)
@Order(Ordered.HIGHEST_PRECEDENCE)
public class FirstInterceptor implements Interceptor {}@Intercepts(...)
@Order(Ordered.LOWEST_PRECEDENCE)
public class LastInterceptor implements Interceptor {}
关键接口解析
-
初始化阶段:通过@Intercepts注解声明拦截目标
-
代理阶段:通过plugin()方法创建代理对象
-
执行阶段:intercept()方法处理拦截逻辑
-
配置阶段:通过XML或Java Config注册拦截器
// 典型拦截器声明
@Intercepts({@Signature(type = Executor.class, method = "update",args = {MappedStatement.class, Object.class})
})
public class CustomInterceptor implements Interceptor {// 实现方法...
}
2.3 代理模式与责任链模式
- 代理模式(Proxy Pattern):MyBatis 拦截器本质上是通过代理模式对目标对象进行包装,通过
Plugin.wrap()
方法将拦截器包装在目标对象上,形成一个代理对象。这个代理对象会拦截对目标方法的调用,执行预定的增强逻辑,再将控制权交给目标方法。 - 责任链模式(Chain of Responsibility Pattern):MyBatis 的拦截器是按顺序进行链式调用的,多个拦截器会组成一个链,按照声明顺序依次执行,直到所有拦截器都被调用。
三、实战:实现自动化字段填充
3.1 需求分析
实现以下字段的自动填充:
字段名 | 插入时自动填充 | 更新时自动填充 | 数据类型 |
---|---|---|---|
created_by | ✔️ | ❌ | String |
created_time | ✔️ | ❌ | Date |
updated_by | ❌ | ✔️ | String |
updated_time | ❌ | ✔️ | Date |
is_deleted | ✔️(默认0) | ❌ | Integer |
3.2 完整实现代码解析
package com.wanren.subject.application.interceptor;import com.wanren.subject.common.util.LoginUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.springframework.stereotype.Component;import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.Executor;/***@ClassName MybatisInterceptor*@Description 填充createBy,createTime等公共字段的拦截器*@Author 彭于晏*Date 2025/2/14 0:31*Version 1.0**/
@Component
@Slf4j
//@Signature指明该拦截器需要拦截哪一个接口的哪一个方法,包括如下
// 接口类型:Executor(拦截执行器方法,负责调用StatementHandler操作数据库)
// StatementHandler(//拦截SQL语法构建处理,直接在数据库执行SQL脚本的对象)、
// ParameterHandler(//拦截参数处理)和ResultSetHandler(//拦截结果集处理,ResultSet结果集对象转换成List类型的集合)
//对应接口中的某一个方法的参数,比如Executor中query方法因为重载原因,有多个,args就是指明参数类型,从而确定是具体哪一个方法。
@Intercepts(@Signature(type = Executor.class,method = "update",args = {MappedStatement.class,Object.class
}))
public class MybatisInterceptor implements Interceptor {//当被拦截的数据库操作(如查询、插入、更新)发生时,intercept 方法会被调用。//invocation的三个方法:Object target = invocation.getTarget();//被代理对象//Method method = invocation.getMethod();//代理方法//Object[] args = invocation.getArgs();//方法参数@Overridepublic Object intercept(Invocation invocation) throws Throwable {MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];//MyBatis 中封装 SQL 语句信息的类,表示一个 SQL 映射。MappedStatement 对象包含 SQL 执行所需的所有信息,比如 SQL 命令类型、SQL 语句、参数类型等SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();Object parameter = invocation.getArgs()[1];//parameter 是一个 Object 类型,通常是实体类对象if(parameter == null){//执行目标方法(invocation.proceed())return invocation.proceed();}//获取当前登录用户的idString loginId = LoginUtil.getLoginId();if(StringUtils.isBlank(loginId)){return invocation.proceed();}if(sqlCommandType.INSERT == sqlCommandType || sqlCommandType.UPDATE == sqlCommandType){replaceEntityProperty(parameter,loginId,sqlCommandType);}return invocation.proceed();}private void replaceEntityProperty(Object parameter, String loginId, SqlCommandType sqlCommandType) {if(parameter instanceof Map){replaceMap((Map) parameter,loginId,sqlCommandType);}else{replace(parameter,loginId,sqlCommandType);}}private void replace(Object parameter, String loginId, SqlCommandType sqlCommandType) {if(SqlCommandType.INSERT == sqlCommandType){dealInsert(parameter,loginId);}else{dealUpdate(parameter,loginId);}}private void dealUpdate(Object parameter, String loginId) {Field[] fields = getAllFields(parameter);for (Field field : fields) {try {field.setAccessible(true);//parameter是字段名,field是字段的值Object o = field.get(parameter);//如果该字段不为null,则跳过if (Objects.nonNull(o)) {field.setAccessible(false);continue;}if ("updateBy".equals(field.getName())) {field.set(parameter, loginId);field.setAccessible(false);} else if ("updateTime".equals(field.getName())) {field.set(parameter, new Date());field.setAccessible(false);} else {field.setAccessible(false);}}catch (Exception e){log.error("dealUpdate.error:{}", e.getMessage(), e);}}}private void dealInsert(Object parameter, String loginId) {Field[] fields = getAllFields(parameter);for(Field field : fields){try{//Java 语言的私有字段是不可访问的。如果你想访问私有字段(即在类外部访问 private 字段),// 你必须调用 setAccessible(true) 来打破 Java 的访问控制,允许访问这些字段。field.setAccessible(true);Object o = field.get(parameter);if (Objects.nonNull(o)) {field.setAccessible(false);continue;}if("isDeleted".equals(field.getName())){field.set(parameter,0);field.setAccessible(false);}else if ("createdBy".equals(field.getName())) {field.set(parameter, loginId);field.setAccessible(false);} else if ("createdTime".equals(field.getName())) {field.set(parameter, new Date());field.setAccessible(false);}else {field.setAccessible(false);}}catch (Exception e){log.error("dealInsert.error:{}", e.getMessage(), e);}}}private Field[] getAllFields(Object parameter) {Class<?> clazz = parameter.getClass();List<Field> fieldList = new ArrayList<>();while (clazz != null){fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));clazz = clazz.getSuperclass();}Field[] fields =new Field[fieldList.size()];fieldList.toArray(fields);return fields;}private void replaceMap(Map parameter, String loginId, SqlCommandType sqlCommandType) {for(Object val : parameter.values()){replace(val,loginId,sqlCommandType);}}//插件用于封装目标对象,通过这个方法可以返回目标对象本身,也可以返回一个他的代理,决定//是否要进行拦截,从而进一步决定要返回一个怎样的对象@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}//如果我们拦截器需要用到一些变量参数,而且这个参数是支持可配置的,类似Spring中的@Value("${}")从application.properties文件获取自定义变量属性,这个时候我们就可以使用这个方法。//在拦截器插件的setProperties方法中进行。这些自定义属性参数会在项目启动的时候被加载。@Overridepublic void setProperties(Properties properties) {}
}
四、扩展应用场景
4.1 敏感数据加密
public Object intercept(Invocation invocation) {Object parameter = invocation.getArgs()[1];if (parameter instanceof User) {User user = (User) parameter;user.setPassword(encrypt(user.getPassword()));}return invocation.proceed();
}
4.2 SQL执行监控
public Object intercept(Invocation invocation) {long start = System.nanoTime();try {return invocation.proceed();} finally {long cost = (System.nanoTime() - start)/1000000;if(cost > 1000){log.warn("慢查询警告: {}ms - {}", cost, getSql(invocation));}Metrics.counter("sql.total.count").increment();Metrics.timer("sql.execute.time").record(cost, MILLISECONDS);}
}
4.3 多租户数据隔离
public void replaceEntityProperty(Object parameter, String tenantId) {if (parameter instanceof TenantAware) {((TenantAware) parameter).setTenantId(tenantId);}
}
五、MyBatis核心组件功能详解
从MyBatis代码实现的角度来看,MyBatis的核心组成部分涉及到多个组件和接口,它们共同协作完成了数据库操作的管理、映射及优化等工作。以下是MyBatis的核心部件及其功能的详细介绍:
1. Configuration(配置对象)
功能概述:
Configuration
是 MyBatis 的全局配置中心,负责管理所有配置信息,包括数据库连接、映射器、类型处理器、插件等。它是 MyBatis 初始化的核心类。
核心职责:
-
加载并解析 MyBatis 配置文件(如
mybatis-config.xml
)。 -
管理 Mapper 文件和接口的配置信息。
-
注册类型处理器(
TypeHandler
)和对象工厂(ObjectFactory
)。 -
维护拦截器链(
InterceptorChain
),用于支持插件扩展。
2. SqlSessionFactory(会话工厂)
功能概述:
SqlSessionFactory
是 MyBatis 的核心工厂类,用于创建 SqlSession
实例。它是线程安全的,通常在应用启动时初始化。
核心职责:
-
根据
Configuration
创建SqlSession
。 -
管理数据库连接池和事务工厂。
-
加载
Mapper
文件和对应的映射语句。
3. SqlSession(会话)
功能概述:
SqlSession
是 MyBatis 与数据库交互的核心接口,提供了执行 SQL、管理事务、获取 Mapper 接口等功能。
核心职责:
-
执行 CRUD 操作(
select
、insert
、update
、delete
)。 -
提供事务管理、批量操作、缓存等功能。
-
获取 Mapper 接口的代理对象。
4. Executor(执行器)
功能概述:
Executor
是 MyBatis 的内部执行器,负责执行 SQL 语句并处理结果。它是 MyBatis 中的核心对象之一,负责查询、插入、更新等数据库操作。
核心职责:
-
调用
StatementHandler
执行 SQL 语句。 -
管理一级缓存和二级缓存。
-
触发拦截器链,支持插件扩展。
5. StatementHandler(语句处理器)
功能概述:
StatementHandler
负责将 SQL 语句发送到数据库执行,主要完成 SQL 的解析、生成和执行。
核心职责:
-
创建
PreparedStatement
并设置参数。 -
执行 SQL 并返回结果。
-
支持一级缓存,避免重复查询。
6. ParameterHandler(参数处理器)
功能概述:
ParameterHandler
负责将 Java 对象转换为 JDBC 参数,并设置到 PreparedStatement
中。
核心职责:
-
处理 SQL 参数的映射。
-
将 Java 对象转换为 JDBC 所需的参数格式。
7. ResultSetHandler(结果集处理器)
功能概述:
ResultSetHandler
负责将 JDBC 返回的 ResultSet
转换为 Java 对象集合。
核心职责:
-
解析
ResultSet
结果集。 -
将数据库查询结果映射为 Java 对象。
8. TypeHandler(类型处理器)
功能概述:
TypeHandler
用于 Java 类型和数据库类型之间的转换。负责将 Java 对象的属性值转换为数据库支持的数据类型,或者将数据库查询结果转化为 Java 对象。
核心职责:
-
处理 Java 类型与 JDBC 类型的映射。
-
支持自定义类型转换。
9. MappedStatement(映射语句)
功能概述:
MappedStatement
负责封装单个 <select>、<insert>、<update>、<delete>
等 SQL 语句的配置信息。包括 SQL 类型、参数映射、结果映射等。
核心职责:
-
存储 SQL 语句、参数映射、返回值映射等信息。
-
提供 SQL 执行的上下文信息。
10. SqlSource(SQL 源)
功能概述:
SqlSource
负责根据传入的 parameterObject
动态生成 SQL 语句。它通过 BoundSql
对象封装 SQL 和参数,并返回给 StatementHandler
执行。
核心职责:
-
动态生成 SQL 语句。
-
根据传入的参数对象构建 SQL 语句,并封装到
BoundSql
中。
11. BoundSql(封装的 SQL)
功能概述:
BoundSql
负责封装动态生成的 SQL 语句和参数信息。它是 SqlSource
生成 SQL 后的载体,存储了 SQL 语句及其相关参数信息。
核心职责:
-
存储动态生成的 SQL 语句。
-
提供 SQL 执行所需的参数信息。
"优秀的框架设计应该像电路板一样,允许开发者在不修改核心逻辑的情况下,通过插件机制扩展功能。" —— MyBatis设计哲学
相关文章:
MyBatis拦截器终极指南:从原理到企业级实战
在本篇文章中,我们将深入了解如何编写一个 MyBatis 拦截器,并通过一个示例来展示如何在执行数据库操作(如插入或更新)时,自动填充某些字段(例如 createdBy 和 updatedBy)信息。本文将详细讲解拦…...
DeepSeek4j 已开源,支持思维链,自定义参数,Spring Boot Starter 轻松集成,快速入门!建议收藏
DeepSeek4j Spring Boot Starter 快速入门 简介 DeepSeek4j 是一个专为 Spring Boot 设计的 AI 能力集成启动器,可快速接入 DeepSeek 大模型服务。通过简洁的配置和易用的 API,开发者可轻松实现对话交互功能。 环境要求 JDK 8Spring Boot 2.7Maven/Gr…...
linux 板子的wifi模块连上路由器后,用udhcpc给板子wifi分配ip,udhcpc获取到ip,但没有写入wlan0网卡上
linux 板子的wifi模块连上路由器后,用udhcpc给板子wifi分配ip,udhcpc获取到ip,但没有写入wlan0网卡上 这里的问题是 /usr/share/udhcpc/default.script脚本有问题 用下面正确脚本,即可写进去 #!/bin/sh# udhcpc script for busybox # Copyr…...
【工业安全】-CVE-2022-35555- Tenda W6路由器 命令注入漏洞
文章目录 1.漏洞描述 2.环境搭建 3.漏洞复现 4.漏洞分析 4.1:代码分析 4.2:流量分析 5.poc代码: 1.漏洞描述 漏洞编号:CVE-2022-35555 漏洞名称:Tenda W6 命令注入 威胁等级:高危 漏洞详情࿱…...
twisted实现MMORPG 游戏数据库操作封装设计与实现
在设计 MMORPG(大规模多人在线角色扮演游戏)时,数据库系统是游戏架构中至关重要的一部分。数据库不仅承担了游戏中各种数据(如玩家数据、物品数据、游戏世界状态等)的存储和管理任务,还必须高效地支持并发访…...
【MySQL】基础篇
1. MySQL中的NULL值是怎么存放的? MySQL的compact行格式中会用【NULL值列表】来标记值为NULL的列,NULL值不会存储在行格式中的真实数据部分。 NULL值列表会占用1字节空间,当表中所有字段都被定义成NOT NULL,行格式中就不会有NULL值…...
【自学笔记】机器学习基础知识点总览-持续更新
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 机器学习重点知识点总览一、机器学习基础概念二、机器学习理论基础三、机器学习算法1. 监督学习2. 无监督学习3. 强化学习 四、机器学习处理流程五、机器学习常见问…...
记录阿里云CDN配置
网站接入CDN全流程,共4步!-阿里云开发者社区 1、开通阿里云CDN服务 2、添加加速域名 3、验证域名归属权 4、域名添加CDN生成的CNAME解析 按照官网描述增加。细节点: 1. 域名和泛域名区别 2.开启https,要用nginx的证书,和项…...
同为科技智能PDU助力Deepseek人工智能和数据交互的快速发展
1 2025开年,人工智能领域迎来了一场前所未有的变革。Deepseek成为代表“东方力量”的开年王炸,不仅在国内掀起了技术热潮,并且在全球范围内引起了高度关注。Deepseek以颠覆性技术突破和现象级应用场景席卷全球,这不仅重塑了产业格…...
聚铭网络入围2025年度江苏省政府采购信息安全设备协议供货名单
近日,2025年度江苏省党政机关、事业单位及团体组织信息安全设备框架协议采购项目入围结果公布。聚铭网络凭借自身专业实力和技术优势脱颖而出,成功入围22个分包。 此次采购项目是江苏省政府采购领域级别最高、覆盖面最广的项目之一。从资格评选到后期材料…...
【Linux】--- 基础开发工具之yum/apt、vim、gcc/g++的使用
Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏: Linux网络编程 本篇博客我们来认识一下Linux中的一些基础开发工具 --- yum,vim,gcc/g。 🏠 yum 🎸 什么是yum 当用户想下载软…...
leetcode 297. 二叉树的序列化与反序列化
题目如下 我们常常说单独先序遍历不能完整的表示一棵树是有前提条件的。 为什么?先序遍历是按 根节点 左子树 右子树的方向遍历树且遇到空子树直接返回,这样会造成我们并不知道某个节点的左右子树存在与否,故我们无法确定树的形状。但是如果…...
OpenAI 放王炸,将发布整合多项技术的 GPT-5,并免费无限使用,该模型有哪些技术亮点
对于 ChatGPT 的免费用户,将可以无限制地访问 GPT-5,但仅限于标准的智能级别。该级别会设定滥用限制,以防止不当使用(意思就是你得付费嘛)。 OpenAI CEO Sam Altman 今天在 X 上透露了 GPT-4.5 和 GPT-5 的最新发展计划。 OpenAI 将发布代…...
Ubuntu22.04 使用useradd 创建用户时,没有创建家目录时,如何手动创建家目录
测试案例: 使用useradd不加参数创建test目录 如下可以看出使用 useradd 创建用户的时候默认不会创建家目录 rootlocal:~# useradd test rootlocal:~# id test uid1001(test) gid1001(test) groups1001(test) rootlocal:~# cat /etc/passwd | grep test test:x:1001:…...
浅聊Docker使用、部署
在Java面试中,当被问到关于Docker中间件的使用、部署及在实际项目中的考虑时,可以按照以下结构和内容来详细回答: 一、Docker中间件的使用 1. Docker是什么? Docker是一个开源平台,允许开发者将应用程序及其依赖项打…...
Java面试第一山!《集合》!
一、引言 在 Java 编程的世界里,数据的存储和处理是非常重要的环节。Java 集合框架就像是一个功能强大的工具箱,为我们提供了各种各样的数据结构来高效地存储和操作数据。今天,跟随小编一起来深入了解 Java 集合框架,这不仅有助于…...
力扣-二叉树-257 二叉树的所有路径
思路 除去根节点,每一层添加->val,然后使用前序遍历的顺序 代码 class Solution { public:vector<string> res;void getTreePaths(string s, TreeNode* root){s "->";s to_string(root->val);if(root->left nullptr &…...
异构计算架构助力智能座舱实现高效低耗体验
摘要: 随着智能汽车的飞速发展,智能座舱作为人车交互的核心区域,对算力、功耗及延迟等性能指标提出了严苛要求。异构计算架构凭借在硬件、软件与系统层面的深度优化,能显著提升智能座舱的算力利用率,降低功耗与延迟,为用户打造高效、低能耗的智能座舱体验。本文深入剖析…...
【vscode】VScode Remote SSH配置
VScode使用remote ssh 到服务器上的Docker容器中 1. 配置远程服务器docker容器的端口映射,例如将服务器的2222端口映射到container的22端口(默认) 1.1 在容器系统的sshd_config文件中配置参数 #配置文件 vim /etc/ssh/sshd_config #打开端口号 Port 221.2 建立容…...
急停信号的含义
前言: 大家好,我是上位机马工,硕士毕业4年年入40万,目前在一家自动化公司担任软件经理,从事C#上位机软件开发8年以上!我们在开发C#的运动控制程序的时候,一个必要的步骤就是确认设备按钮的急停…...
【Azure 架构师学习笔记】- Azure Databricks (11) -- UC搭建
本文属于【Azure 架构师学习笔记】系列。 本文属于【Azure Databricks】系列。 接上文 【Azure 架构师学习笔记】- Azure Databricks (10) – UC 使用 前言 由于ADB 的更新速度很快,在几个月之后重新搭建ADB 时发现UC 已经更新了很多,为了后续做ADB 的功…...
MYSQL批量UPDATE的两种方式
工作中遇到批量更新的场景其实是比较常见的。 但是该如何正确的进行批量UPDATE,很多时候往往有点头大。 这里列2种可用的方式,供选择(请选择方式一,手动狗头。)。 如果使用了MyBatis增强组件MyBatisPlus 如果使用了MyBatisPlus,…...
百度宣布:免费!
2月13日,百度大模型文心一言在官网宣布,随着文心大模型的迭代升级和成本不断下降,文心一言将于4月1日起全面免费,所有PC端和APP端用户均可体验文心系列最新模型。 同时,文心一言将上线深度搜索功能,具备更…...
计算机毕业设计SpringBoot+Vue.js医院住院管理系统(源码+lw文档+PPT+讲解视频)
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
Anaconda +Jupyter Notebook安装(2025最新版)
Anaconda安装(2025最新版) Anaconda简介安装1:下载anaconda安装包2: 安装anaconda3:配置环境变量4:检查是否安装成功5:更改镜像源6:更新包7:检查 Jupyter Notebook一.Jup…...
人工智能任务21-飞蛾火焰优化算法(MFO)在深度学习中的应用
大家好,我是微学AI,今天给大家介绍一下人工智能任务21-飞蛾火焰优化算法(MFO)在深度学习中的应用。飞蛾火焰优化算法(Moth-Flame Optimization, MFO)是一种受自然界中飞蛾向光源趋近行为启发的新型群体智能优化算法。在自然界中&a…...
渗透测试工具:SQLmap安装教程及使用
在渗透测试的世界里,SQL注入攻击无疑是最常见且最具威胁的安全漏洞之一。幸运的是,SQLmap 这个强大的自动化工具,能够帮助我们快速识别和利用这些漏洞。如果你也想了解如何用 SQLmap 进行渗透测试,那么这篇文章就是为你准备的&…...
【ISO 14229-1:2023 UDS诊断全量测试用例清单系列:第十四节】
ISO 14229-1:2023 UDS诊断服务测试用例全解析(CommunicationControl_0x28服务) 作者:车端域控测试工程师 更新日期:2025年02月14日 关键词:UDS协议、0x28服务、通信控制、ISO 14229-1:2023、ECU测试 一、服务功能概述…...
WEB安全--SQL注入--INTO OUTFILE
一、INTO OUTFILE 函数语法: SELECT column1, column2, INTO OUTFILE file_path FROM your_table WHERE your_conditions; 使用此方式在SQL注入的过程中可以: 1、上传shell得到数据库的后端的操作权限 2、爆出数据库的信息 二、使用该函数的条件&#…...
【C语言 】C语言 桌游开发数字竞拍(源码)【独一无二】
👉博__主👈:米码收割机 👉技__能👈:C/Python语言 👉专__注👈:专注主流机器人、人工智能等相关领域的开发、测试技术。 【C语言 】C语言 桌游开发数字竞拍(源码…...
开源赋能,智造未来:Odoo+工业物联网,解锁智能工厂新范式——以真实案例解读制造业数字化转型的降本增效密码
工业物联网的机遇与挑战:为什么企业需要Odoo? 《中国智能制造发展研究报告2023》指出,85%的制造企业已启动数字化转型,但超60%面临“数据孤岛、系统割裂、成本高企”的痛点[1]。传统ERP系统难以实时对接产线设备,而定…...
在分布式场景下可以使用synchronized加锁么?
首先说结论,在分布式系统中,单纯使用 Java 中的 synchronized 关键字是无法满足需求的,下面从 synchronized 的作用原理、在分布式场景下的局限性以及替代方案等方面详细分析。 一、synchronized 的作用原理 在 Java 中,synchron…...
DeepSeek从入门到精通:提示词设计的系统化指南
目录 引言:AIGC时代的核心竞争力 第一部分 基础篇:提示词的本质与核心结构 1.1 什么是提示词? 1.2 提示词的黄金三角结构 第二部分 类型篇:提示词的六大范式 2.1 提示语的本质特征 2.2 提示语的类型 2.2.1 指令型提示词 …...
C# 运算符
总目录 前言 在C#中,运算符是用于执行特定操作的符号。它们可以用于处理变量、常量或其他表达式。C# 提供了丰富的运算符集合,用于执行各种操作,如算术运算、逻辑判断、位操作等。了解这些运算符及其使用方式对于编写高效且功能强大的C#程序…...
duckdb导出Excel和导出CSV速度测试
运行duckdb数据库 D:>duckdb v1.2.0 5f5512b827 Enter “.help” for usage hints. Connected to a transient in-memory database. Use “.open FILENAME” to reopen on a persistent database. 生成模拟数据,10个列,100万行数据; --…...
pt->onnx->rknn(量化) step by step FAQ
文档修订中... 1.pt->onnx 这个转换是在yolov11的docker环境做的转换。非常简单。 #!/usr/bin/env python3 # -*- coding: utf-8 -*- # 获取当前脚本文件所在目录的父目录,并构建相对路径 import os import sys current_dir os.path.dirname(os.path.abspath…...
Apollo 9.0 速度动态规划决策算法 – path time heuristic optimizer
文章目录 1. 动态规划2. 采样3. 代价函数3.1 障碍物代价3.2 距离终点代价3.3 速度代价3.4 加速度代价3.5 jerk代价 4. 回溯 这一章将来讲解速度决策算法,也就是SPEED_HEURISTIC_OPTIMIZER task里面的内容。Apollo 9.0使用动态规划算法进行速度决策,从类名…...
利用AI智能体创建云端文档知识库并集成第三方数据源(上)
许多开发者在管理和集成多种云端的数据源时经常面对各种各样的困难,所以希望能够构建一个聊天机器人来协调这些数据源,针对业务问题并提供全面的答案。本文介绍了一种解决方案,帮助大家开发一个能够从文档和数据库中回答查询的聊天机器人&…...
Cursor AI开发微信小程序教程
1. 准备工作 在开始开发之前,需要完成以下准备工作: 1.1 安装微信开发者工具 前往微信开发者工具官网下载并安装适合操作系统的开发者工具。注册微信小程序账号,登录微信公众平台(https://mp.weixin.qq.com)&#…...
2025常用的SEO工具有哪些?
在互联网时代,如何让自己的网站或内容脱颖而出,成为许多企业和个人站长们最关注的问题。而在这个过程中,SEO(搜索引擎优化)作为一种有效的提升网站曝光度和吸引流量的手段,已经成为了网站运营的核心之一。对…...
支持直接升级到21c的 Oracle 数据库版本
一、支持直接升级的版本 可以从以下版本直接升级到新版本: • 19c (所有版本) • 18c (所有版本) • 12c Release 2 (12.2) 升级到最新Oracle数据库版本必须采用的路径取决于当前数据库的版本号。 如果当前的Oracle数据库版本早于12.2 版,则无法直接将…...
线程池-抢票系统性能优化
文章目录 引言-购票系统线程池购票系统-线程池优化 池化 vs 未池化 引言-购票系统 public class App implements Runnable {private static int tickets 100;private static int users 10000;private final ReentrantLock lock new ReentrantLock(true);public void run() …...
回顾Golang的Channel与Select第一篇
深入解析Golang Channel与Select:并发编程的艺术与哲学 一、通信顺序进程(CSP)的Go实现 Go语言创始人Rob Pike将CSP理论具象化为channel原语,实现了"不要通过共享内存来通信,而要通过通信来共享内存"的哲学…...
乐理笔记(持续更新)
单音与音程 单音:由一个音组成。 音程:由两个音组成,表示两个音之间的音高距离。 如何数音程: 单音程:9 - X,性质相反。例如,9度音程减去某个数,性质会相反。 复音程:…...
FastExcel + Java:打造高效灵活的Excel数据导入导出解决方案
作者:后端小肥肠 🍇 我写过的文章中的相关代码放到了gitee,地址:xfc-fdw-cloud: 公共解决方案 🍊 有疑问可私信或评论区联系我。 🥑 创作不易未经允许严禁转载。 姊妹篇: 基于AOP的数据字典实现…...
【数据结构入门】一、数组
一、数组的概念 数组:一种由相同类型的数据元素组成的基本数据类型,为引用类型 二、数据的顺序 这里很奇怪,讲了一个寻址函数,就是怎样用坐标求该元素的内存地址。说实话我不知道求这个能干什么,但是感觉还挺好玩的…...
编码格式大全解释以及相关编码特性
目录 说明: 1. Base64 Base64编码的字符集通常包括: Base64的工作原理: Base64编码在安全渗透中的应用场景 常见的Base64编码绕过场景 如何防范Base64绕过攻击 2. URL编码(Percent Encoding) URL编码与安全渗透的关系 示…...
正则表达式(Regular expresssion)
正则表达式 匹配单次 . :匹配任意一个字符 [ ] :匹配[ ]里举例的任意一个字符 /d :匹配数字0-9 /D :匹配非数字 /s :匹配空白或tab建 /S :匹配非空白 /w :…...
JavaScript 发起网络请求 axios、fetch、async / await
目录 fetch 发送 GET 请求(fetch) 发送 POST 请求(fetch) 处理后台异常响应 async / await async await 发送 GET 请求(async / await fetch) 发送 POST 请求(async / await fetch&…...
安装OpenJDK21(linux、macos)
文章目录 安装OpenJDK21java21linux下安装配置mac下安装 安装OpenJDK21 java21 封神!Java 21正式发布了,迎来了史诗级新特性,堪称版本最强!!! 视频链接:https://www.bilibili.com/video/BV1E8…...