MyBatis常用注解全解析:从基础CRUD到高级映射
MyBatis常用注解全解析:从基础CRUD到高级映射
本文全面解析MyBatis核心注解体系,涵盖基础操作、动态SQL、关系映射等高级特性,助你彻底掌握MyBatis注解开发精髓
一、MyBatis注解概述
1.1 注解 vs XML配置
MyBatis同时支持XML配置和注解两种方式实现SQL映射:
特性 | XML配置 | 注解 |
---|---|---|
可读性 | 高(SQL与Java分离) | 中(SQL嵌入代码) |
维护性 | 修改无需重新编译 | 修改需重新编译 |
灵活性 | 支持复杂动态SQL | 动态SQL有限 |
简洁性 | 文件较多 | 零配置 |
适用场景 | 大型复杂项目 | 中小型项目/快速开发 |
1.2 注解核心优势
- 零配置快速启动:无需XML文件
- 接口与SQL紧耦合:SQL直接定义在DAO接口
- 类型安全:编译时检查SQL语法
- 简化开发:减少文件切换
二、基础CRUD注解
2.1 @Select:查询操作
@Select("SELECT * FROM users WHERE id = #{id}")
User getUserById(@Param("id") int id);// 多参数查询
@Select("SELECT * FROM users WHERE name = #{name} AND age > #{age}")
List<User> findUsers(@Param("name") String name, @Param("age") int age);// 返回Map
@Select("SELECT id, name FROM users")
@MapKey("id")
Map<Integer, User> getUserMap();
高级特性:
- 支持结果映射(配合@Result/@Results)
- 支持动态SQL(配合
2.2 @Insert:插入操作
// 返回自增主键
@Insert("INSERT INTO users(name, email) VALUES(#{name}, #{email})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insertUser(User user);// 批量插入
@Insert({"<script>","INSERT INTO users (name, email) VALUES","<foreach collection='list' item='user' separator=','>","(#{user.name}, #{user.email})","</foreach>","</script>"
})
int batchInsertUsers(@Param("list") List<User> users);
2.3 @Update:更新操作
@Update("UPDATE users SET name=#{name}, email=#{email} WHERE id=#{id}")
int updateUser(User user);// 条件更新
@Update({"<script>","UPDATE users","<set>"," <if test='name != null'>name=#{name},</if>"," <if test='email != null'>email=#{email},</if>","</set>","WHERE id=#{id}","</script>"
})
int updateUserSelective(User user);
2.4 @Delete:删除操作
@Delete("DELETE FROM users WHERE id = #{id}")
int deleteUser(int id);// 批量删除
@Delete({"<script>","DELETE FROM users WHERE id IN","<foreach item='id' collection='ids' open='(' separator=',' close=')'>","#{id}","</foreach>","</script>"
})
int deleteUsersByIds(@Param("ids") List<Integer> ids);
三、结果映射注解
3.1 @Results与@Result
@Results(id = "userResultMap", value = {@Result(property = "id", column = "id", id = true),@Result(property = "name", column = "user_name"),@Result(property = "email", column = "user_email"),@Result(property = "createTime", column = "create_time", typeHandler = LocalDateTimeTypeHandler.class)
})
@Select("SELECT * FROM users WHERE id = #{id}")
User getUserWithResultMap(int id);
3.2 @ResultMap复用映射
@ResultMap("userResultMap")
@Select("SELECT * FROM users")
List<User> getAllUsers();
3.3 嵌套结果映射
@Results({@Result(property = "id", column = "id"),@Result(property = "orders", column = "id",many = @Many(select = "com.example.mapper.OrderMapper.findByUserId"))
})
@Select("SELECT * FROM users WHERE id = #{id}")
User getUserWithOrders(int id);
四、关系映射注解
4.1 @One:一对一映射
public class Order {private Integer id;private String orderNo;@Result(property = "user", column = "user_id",one = @One(select = "com.example.mapper.UserMapper.getUserById"))private User user;
}@Select("SELECT * FROM orders WHERE id = #{id}")
Order getOrderWithUser(int id);
4.2 @Many:一对多映射
public class User {private Integer id;private String name;@Result(property = "orders", column = "id",many = @Many(select = "com.example.mapper.OrderMapper.findByUserId"))private List<Order> orders;
}@Select("SELECT * FROM orders WHERE user_id = #{userId}")
List<Order> findByUserId(int userId);
4.3 嵌套查询 vs 嵌套结果
特性 | @One/@Many(嵌套查询) | 嵌套结果(JOIN查询) |
---|---|---|
执行方式 | 执行多次SQL | 执行一次SQL |
性能 | N+1查询问题 | 一次查询获取所有数据 |
适用场景 | 简单关系/数据量小 | 复杂关系/大数据量 |
灵活性 | 高(独立查询) | 低(需设计JOIN) |
代码量 | 少 | 多(需自定义结果映射) |
五、动态SQL注解
5.1 @SelectProvider
public class UserSqlProvider {public String findUsers(Map<String, Object> params) {return new SQL() {{SELECT("*");FROM("users");if (params.get("name") != null) {WHERE("name LIKE #{name}");}if (params.get("minAge") != null) {WHERE("age >= #{minAge}");}if (params.get("maxAge") != null) {WHERE("age <= #{maxAge}");}ORDER_BY("id DESC");}}.toString();}
}@SelectProvider(type = UserSqlProvider.class, method = "findUsers")
List<User> findUsers(Map<String, Object> params);
5.2 @InsertProvider
public class UserSqlProvider {public String insertUser(User user) {return new SQL() {{INSERT_INTO("users");if (user.getName() != null) {VALUES("name", "#{name}");}if (user.getEmail() != null) {VALUES("email", "#{email}");}if (user.getAge() > 0) {VALUES("age", "#{age}");}}}.toString();}
}@InsertProvider(type = UserSqlProvider.class, method = "insertUser")
int insertUserDynamic(User user);
5.3 动态SQL构建技巧
// 条件过滤
public String findActiveUsers(Map<String, Object> params) {SQL sql = new SQL().SELECT("*").FROM("users");if (params.containsKey("active")) {boolean active = (Boolean) params.get("active");if (active) {sql.WHERE("status = 1");} else {sql.WHERE("status = 0");}}if (params.containsKey("role")) {String role = (String) params.get("role");sql.WHERE("role = #{role}");}return sql.toString();
}
六、参数处理注解
6.1 @Param:多参数命名
@Select("SELECT * FROM users WHERE name = #{name} AND age = #{age}")
User findByNameAndAge(@Param("name") String name, @Param("age") int age
);
6.2 集合参数处理
@Select({"<script>","SELECT * FROM users WHERE id IN"," <foreach item='id' collection='ids' open='(' separator=',' close=')'>"," #{id}"," </foreach>","</script>"
})
List<User> findByIds(@Param("ids") List<Integer> ids);
6.3 Map参数处理
@SelectProvider(type = UserSqlProvider.class, method = "findByMap")
List<User> findByMap(Map<String, Object> params);
七、高级特性注解
7.1 @Options:执行选项配置
@Insert("INSERT INTO logs(content) VALUES(#{content})")
@Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
int insertLog(Log log);// 超时设置
@Select("SELECT * FROM large_table")
@Options(timeout = 30) // 30秒超时
List<Record> getLargeData();// 缓存配置
@Select("SELECT * FROM configs")
@Options(useCache = false, flushCache = Options.FlushCachePolicy.TRUE)
List<Config> getConfigsNoCache();
7.2 @SelectKey:非自增主键获取
@Insert("INSERT INTO orders(order_no) VALUES(#{orderNo})")
@SelectKey(statement = "SELECT LAST_INSERT_ID()", keyProperty = "id", resultType = int.class, before = false)
int insertOrder(Order order);
7.3 @Flush:强制刷新语句
@Flush
List<BatchResult> flush();
八、注解与XML混合使用
8.1 XML中引用注解映射
<!-- UserMapper.xml -->
<resultMap id="userMap" type="User"><id property="id" column="id"/><result property="name" column="name"/><!-- 引用注解定义的映射 --><association property="department" select="com.example.mapper.DepartmentMapper.getById"column="dept_id"/>
</resultMap>
8.2 注解中引用XML映射
@ResultMap("com.example.mapper.UserMapper.userMap")
@Select("SELECT * FROM users WHERE id = #{id}")
User getUserById(int id);
8.3 混合使用最佳实践
- 基础CRUD:使用注解
- 复杂动态SQL:使用XML
- 结果映射:优先使用注解
- 关联查询:简单关系用注解,复杂关系用XML
- SQL重用:公共SQL片段使用XML定义
九、Spring Boot集成实践
9.1 配置MyBatis注解扫描
@Configuration
@MapperScan("com.example.mapper")
public class MyBatisConfig {@Beanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();sessionFactory.setDataSource(dataSource);// 配置其他属性return sessionFactory.getObject();}
}
9.2 事务管理配置
@Configuration
@EnableTransactionManagement
public class TransactionConfig {@Beanpublic PlatformTransactionManager transactionManager(DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}
}// 使用事务
@Service
public class UserService {@Transactionalpublic void createUserWithProfile(User user, Profile profile) {userMapper.insert(user);profileMapper.insert(profile);}
}
9.3 分页集成(PageHelper)
@Select("SELECT * FROM users")
Page<User> getUsersByPage();// 控制器中使用
@GetMapping("/users")
public PageInfo<User> getUsers(@RequestParam(defaultValue = "1") int page,@RequestParam(defaultValue = "10") int size) {PageHelper.startPage(page, size);List<User> users = userMapper.getUsersByPage();return new PageInfo<>(users);
}
十、性能优化与最佳实践
10.1 注解开发性能陷阱
-
N+1查询问题:
// 当获取多个用户时,每个用户都会触发订单查询 @Select("SELECT * FROM users") @Results({@Result(property = "orders", column = "id",many = @Many(select = "findOrdersByUserId")) List<User> getAllUsersWithOrders();
解决方案:
- 使用JOIN查询代替嵌套查询
- 开启二级缓存
- 使用@ResultMap结合XML配置
-
大结果集内存溢出:
// 查询百万级数据 @Select("SELECT * FROM huge_table") List<HugeRecord> getAllHugeRecords();
解决方案:
- 使用分页查询
- 使用游标查询
@Select("SELECT * FROM huge_table") @Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = 1000) void streamHugeRecords(ResultHandler<HugeRecord> handler);
10.2 缓存优化策略
// 开启二级缓存
@CacheNamespace(implementation = MybatisRedisCache.class, eviction = LruCache.class,size = 1024)
public interface UserMapper {// ...
}// 缓存配置示例
public class MybatisRedisCache implements Cache {// 实现Redis缓存逻辑
}// 方法级缓存控制
@Select("SELECT * FROM users WHERE id = #{id}")
@Options(useCache = true, flushCache = Options.FlushCachePolicy.FALSE)
User getUserById(int id);
10.3 最佳实践总结
-
项目结构规划:
src/main/java └── com.example├── config├── controller├── service├── mapper│ ├── UserMapper.java│ ├── OrderMapper.java│ └── sqlprovider│ └── UserSqlProvider.java└── model
-
注解使用原则:
- 简单SQL使用@Select/@Insert等直接注解
- 动态SQL使用@*Provider
- 复杂关联查询使用XML
- 公共结果映射使用@ResultMap
-
性能优化点:
- 避免N+1查询
- 批量操作使用foreach
- 大数据集使用流式处理
- 合理配置缓存
-
团队协作规范:
- 统一SQL风格(关键字大写)
- 参数使用@Param明确命名
- 复杂SQL添加注释
- 提供SQL Provider的单元测试
十一、MyBatis注解进阶技巧
11.1 自定义类型处理器
// 枚举类型处理器
@MappedTypes(UserStatus.class)
@MappedJdbcTypes(JdbcType.INTEGER)
public class UserStatusTypeHandler extends BaseTypeHandler<UserStatus> {@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, UserStatus parameter, JdbcType jdbcType) {ps.setInt(i, parameter.getCode());}@Overridepublic UserStatus getNullableResult(ResultSet rs, String columnName) {int code = rs.getInt(columnName);return UserStatus.fromCode(code);}
}// 在Mapper中使用
@Results({@Result(property = "status", column = "status", typeHandler = UserStatusTypeHandler.class)
})
@Select("SELECT * FROM users")
List<User> getAllUsers();
11.2 插件开发(拦截器)
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class QueryMetricsInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {long start = System.currentTimeMillis();Object result = invocation.proceed();long end = System.currentTimeMillis();MappedStatement ms = (MappedStatement) invocation.getArgs()[0];String method = ms.getId();System.out.println(method + " executed in " + (end - start) + "ms");return result;}
}// 注册拦截器
@Configuration
public class MyBatisConfig {@Beanpublic QueryMetricsInterceptor queryMetricsInterceptor() {return new QueryMetricsInterceptor();}
}
11.3 多数据源支持
// 主数据源配置
@Configuration
@MapperScan(basePackages = "com.example.mapper.primary", sqlSessionFactoryRef = "primarySqlSessionFactory")
public class PrimaryDataSourceConfig {@Bean@ConfigurationProperties("spring.datasource.primary")public DataSource primaryDataSource() {return DataSourceBuilder.create().build();}@Beanpublic SqlSessionFactory primarySqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource) throws Exception {SqlSessionFactoryBean bean = new SqlSessionFactoryBean();bean.setDataSource(dataSource);return bean.getObject();}
}// 从数据源配置
@Configuration
@MapperScan(basePackages = "com.example.mapper.secondary", sqlSessionFactoryRef = "secondarySqlSessionFactory")
public class SecondaryDataSourceConfig {@Bean@ConfigurationProperties("spring.datasource.secondary")public DataSource secondaryDataSource() {return DataSourceBuilder.create().build();}@Beanpublic SqlSessionFactory secondarySqlSessionFactory(@Qualifier("secondaryDataSource") DataSource dataSource) throws Exception {SqlSessionFactoryBean bean = new SqlSessionFactoryBean();bean.setDataSource(dataSource);return bean.getObject();}
}
十二、总结
12.1 MyBatis注解核心价值
- 开发效率提升:减少XML配置,加速开发流程
- 代码可读性增强:SQL与Java代码共存
- 维护成本降低:修改点集中,无需多文件切换
- 类型安全保障:编译时检查SQL参数和结果
- 灵活扩展能力:支持自定义TypeHandler、Plugin
12.2 注解适用场景矩阵
项目类型 | 适用度 | 推荐策略 |
---|---|---|
小型项目/微服务 | ★★★★★ | 全注解开发 |
中型后台系统 | ★★★★☆ | 注解为主,复杂SQL用XML |
大型复杂系统 | ★★★☆☆ | XML为主,简单CRUD用注解 |
快速原型开发 | ★★★★★ | 全注解开发 |
遗留系统改造 | ★★☆☆☆ | 逐步引入,混合使用 |
12.3 未来发展趋势
- Kotlin DSL支持:更简洁的SQL构建方式
- 响应式集成:与Spring WebFlux深度整合
- GraalVM原生支持:提升启动速度和内存效率
- AI辅助SQL生成:智能SQL编写建议
- 云原生适配:更好支持Serverless架构
掌握MyBatis注解开发,不仅是学习一种技术手段,更是提升开发效率的关键技能。合理运用注解与XML的组合,根据项目需求选择最佳实践,方能在持久层开发中游刃有余。
相关文章:
MyBatis常用注解全解析:从基础CRUD到高级映射
MyBatis常用注解全解析:从基础CRUD到高级映射 本文全面解析MyBatis核心注解体系,涵盖基础操作、动态SQL、关系映射等高级特性,助你彻底掌握MyBatis注解开发精髓 一、MyBatis注解概述 1.1 注解 vs XML配置 MyBatis同时支持XML配置和注解两种…...

国标GB28181设备管理软件EasyGBS视频平台筑牢文物保护安全防线创新方案
一、方案背景 文物作为人类文明的珍贵载体,具有不可再生性。当前,盗窃破坏、游客不文明行为及自然侵蚀威胁文物安全,传统保护手段存在响应滞后、覆盖不全等局限。随着5G与信息技术发展,基于GB28181协议的EasyGBS视频云平台&…...
十二、【核心功能篇】测试用例列表与搜索:高效展示和查找海量用例
【核心功能篇】测试用例列表与搜索:高效展示和查找海量用例 前言准备工作第一步:更新 API 服务以支持分页和更完善的搜索第二步:创建测试用例列表页面组件 (`src/views/testcase/TestCaseListView.vue`)第三步:测试列表、搜索、筛选和分页总结前言 当测试用例数量逐渐增多…...

Baklib内容中台AI重构智能服务
AI驱动智能服务进化 在智能服务领域,Baklib内容中台通过自然语言处理技术与深度学习框架的深度融合,构建出具备意图理解能力的知识中枢。系统不仅能够快速解析用户输入的显性需求,更通过上下文关联分析算法识别会话场景中的隐性诉求…...

数据库包括哪些?关系型数据库是什么意思?
目录 一、数据库包括哪些 (一)关系型数据库 (二)非关系型数据库 (三)分布式数据库 (四)内存数据库 二、关系型数据库是什么 (一)关系模型的基本概念 …...

Python爬虫监控程序设计思路
最近因为爬虫程序太多,想要为Python爬虫设计一个监控程序,主要功能包括一下几种: 1、监控爬虫的运行状态(是否在运行、运行时间等) 2、监控爬虫的性能(如请求频率、响应时间、错误率等) 3、资…...
Edge浏览器怎样开启兼容模式
允许站点在 IE 模式下重新加载: 打开 Edge 浏览器,点击右上角的三个点图标,选择 “设置”(或者按下 “Alt F” 组合键后再点击 “设置”)。在设置页面中,切换到左侧的 “默认浏览器” 选项卡。在 “Intern…...

【HarmonyOS 5】Laya游戏如何鸿蒙构建发布详解
【HarmonyOS 5】Laya游戏如何鸿蒙构建发布详解 一、前言 LayaAir引擎是国内最强大的全平台引擎之一,当年H5小游戏火的时候,腾讯入股了腊鸭。我还在游戏公司的时候,17年曾经开发使用腊鸭的H5小游戏,很怀念当年和腊鸭同事一起解决…...
C++ TCP传输心跳信息
在C++ TCP程序中实现心跳机制是保持连接活跃、检测连接状态的重要手段。以下是几种常见的心跳实现方式: 1. 应用层心跳(推荐) 基本心跳实现 #include <iostream> #include <thread> #include <chrono>...
Elasticsearch | 如何将修改已有的索引字段类型并迁移数据
CodingTechWork 引言 在 Elasticsearch 中,一旦索引的字段类型被定义,就无法直接修改已有字段的类型。例如,如果你已经将 timestamp 字段的类型设置为 TEXT,并希望将其更改为 DATE 类型,这将需要一些额外的步骤。在这…...
c++之STL容器的学习(上)
一、泛型编程(函数模板和类模板) 这部分围绕泛型编程技术展开,C中的泛型编程主要是通过函数模板和类模板实现的,主要会介绍标准模板库STL的知识点。1.关于模板的理解 模板就是建立一种通用的模式,从而提高复用性。在生…...
Linux 环境下高效视频切帧的实用指南
Linux 环境下高效视频切帧的实用指南 在视频处理领域,切帧是一项基础且常用的操作,它能够将视频按照指定的规则提取出单帧图像,广泛应用于视频分析、视频缩略图生成、视频内容预览等场景。在 Linux 系统中,我们可以借助强大的开源…...

【鱼皮-用户中心】笔记
任务:完整了解做项目的思路,接触一些企业及的开发技术 title 企业做项目流程需求分析技术选型 计划一一、前端初始化1. **下载node.js**2. **安装yarn**3. **初始化 Ant Design Pro 脚⼿架(关于更多可进入官网了解)**4. **开启Umi…...
MUX-VLAN基本概述
目录 1)技术背景: 2)基本概念: 3)配置:进vlan视图下键入 1)技术背景: 在企业网络中,各个部门之间网络需要相互独立,通常使用VLAN技术可以实现这一要求。如果企…...
Cursor使用最佳实践总结
#作者:曹付江 文章目录 1、需求文档怎么写2. 项目文件夹选择3.技术栈的选择4.最重要:Cursor中的Rules(规则)5.对话模式与模型选择6. New Chat(新建对话)7.自动化测试8.前后端细调的方法9、完整Cursor项目模…...

交错推理强化学习方法提升医疗大语言模型推理能力的深度分析
核心概念解析 交错推理:灵活多变的思考方式 交错推理(Interleaved Reasoning)是一种在解决复杂问题时,不严格遵循单一、线性推理路径,而是交替、灵活应用多种推理策略的方法。这种思维方式与人类专家在处理复杂医疗问题时的思考模式更为接近,表现为一种动态、适应性强的…...

SpringBatch+Mysql+hanlp简版智能搜索
资源条件有限,需要支持智搜的数据量也不大,上es搜索有点大材小用了,只好写个简版mysql的智搜,处理全文搜素,支持拼音搜索,中文分词,自定义分词断词,地图范围搜索,周边搜索…...
常见 Web 安全问题
网站在提供便利的同时,也面临着各种安全威胁。一个小小的漏洞可能导致数据泄露、系统瘫痪,甚至带来不可估量的经济损失。本文介绍几种最常见的 Web 安全问题,包括其原理、危害以及防护策略。 一、SQL 注入(SQL Injectionÿ…...
spring切面
概念 两个特点: IOC控制反转AOP主要用来处理公共的代码 例如一个案例就是添加用户,重复的代码包含了记录日志、事务提交和事务回滚等,都是重复的,为了简单,交给AOP来做。 即将复杂的需求分解出不同方面,…...

go语言基础|slice入门
slice slice介绍 slice中文叫切片,是go官方提供的一个可变数组,是一个轻量级的数据结构,功能上和c的vector,Java的ArrayList差不多。 slice和数组是有一些区别的,是为了弥补数组的一些不足而诞生的数据结构。最大的…...

使用 HTML + JavaScript 实现可拖拽的任务看板系统
本文将介绍如何使用 HTML、CSS 和 JavaScript 创建一个交互式任务看板系统。该系统支持拖拽任务、添加新任务以及动态创建列,适用于任务管理和团队协作场景。 效果演示 页面结构 HTML 部分主要包含三个默认的任务列(待办、进行中、已完成)和一个用于添加新列的按钮。 <…...
LangChain核心之Runnable接口底层实现
导读:作为LangChain框架的核心抽象层,Runnable接口正在重新定义AI应用开发的标准模式。这一统一接口设计将模型调用、数据处理和API集成等功能封装为可复用的逻辑单元,通过简洁的管道符语法实现复杂任务的声明式编排。 对于面临AI应用架构选择…...
软件评测师 案例真题笔记
2009 软件测试质量 软件测试质量管理要素包括: •测试过程,例如技术过程、管理过程、支持过程。 •测试人员及组织。 •测试工作文档,例如测试计划、测试说明、测试用例、测试报告、问题报告。 软件测试质量控制的主要方法包括:…...
RAG架构中用到的模型学习思考
前言 RAG(Retrieval-Augmented Generation,检索增强生成)架构结合了检索和生成能力,通过引入外部知识库来提升大语言模型(LLM)的回答准确性和可靠性。以下是RAG架构中常用的模型及其总结: 一、…...

统信 UOS 服务器版离线部署 DeepSeek 攻略
日前,DeepSeek 系列模型因拥有“更低的成本、更强的性能、更好的体验”三大核心优势,在全球范围内备受瞩目。 本次,我们为大家提供了在统信 UOS 服务器版 V20(AMD64 或 ARM64 架构)上本地离线部署 DeepSeek-R1 模型的…...

美尔斯通携手北京康复辅具技术中心开展公益活动,科技赋能助力银龄健康管理
2025 年 5 月 30 日,北京美尔斯通科技发展股份有限公司携手北京市康复辅具技术中心,在朝阳区核桃园社区开展 “全国助残日公益服务” 系列活动。活动通过科普讲座、健康检测与科技体验,将听力保健与心脏健康服务送至居民家门口,助…...
《前端面试题:前端响应式介绍》
前端响应式设计完全指南:从理论到实战 掌握响应式设计是构建现代网站的核心能力,也是前端面试的必考内容 一、响应式设计:移动优先时代的必备技能 在当今多设备时代,用户通过手机、平板、笔记本、桌面显示器等多种设备访问网站。…...

Redis Stack常见拓展
Redis JSON RedisJSON 是 Redis Stack 提供的模块之一,允许你以 原生 JSON 格式 存储、检索和修改数据。相比传统 Redis Hash,它更适合结构化文档型数据,并支持嵌套结构、高效查询和部分更新。 #设置⼀个JSON数据,其中$表示JSON数据的根节点…...

Linux 驱动之设备树
Linux 驱动之设备树 参考视频地址 【北京迅为】嵌入式学习之Linux驱动(第七期_设备树_全新升级)_基于RK3568_哔哩哔哩_bilibili 本章总领 1.设备树基本知识 什么是设备树? Linux之父Linus Torvalds在2011年3月17日的ARM Linux邮件列表…...

12、企业应收账款(AR)全流程解析:从发票开具到回款完成
在商业活动中,现金流如同企业的命脉,而应收管理则是维系这条命脉正常运转的重要保障。许多企业由于对应收账款缺乏有效管理,常常面临资金周转困难的问题。实践证明,建立科学的应收管理体系能够显著提升资金回笼效率,为…...