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

MyBatis 从入门到精通:一篇就够的实战指南(Java)

MyBatis 从入门到精通:一篇就够的实战指南(Java)

目标读者:Java 初/中/高级开发、准备面试的同学、正在从 JPA 切换到 MyBatis 的团队。

文章结构:概念 → 快速上手 → 核心原理 → 高级特性 → 性能调优 → 常见问题 → 最佳实践 → 面试题。


目录

  • 1. 什么是 MyBatis?为什么选择它
  • 2. 快速开始(Spring Boot 版)
  • 3. 核心概念与运行机制
  • 4. 映射语法与动态 SQL
  • 5. 类型处理器 TypeHandler(含自定义 JSON/枚举示例)
  • 6. Spring 事务与多数据源整合
  • 7. 分页:LIMIT、RowBounds 与 PageHelper
  • 8. 缓存机制:一级/二级缓存
  • 9. 性能优化与批处理
  • 10. 插件(拦截器)机制
  • 11. 代码生成:MyBatis Generator 与替代方案
  • 12. 常见问题排查(Troubleshooting)
  • 13. 工程落地与包结构建议
  • 14. 面试高频题与答题要点
  • 15. 纯 MyBatis(非 Spring)最小可运行示例
  • 16. 参考资料与学习路径

1. 什么是 MyBatis?为什么选择它

MyBatis 是一个轻量级持久层框架,核心价值是:

  • SQL 自由:你自己写 SQL,掌控查询、索引、复杂联表、性能。
  • ORM 适度:相比 JPA/Hibernate,MyBatis 不做全自动映射,避免“黑盒”,排查更直观。
  • 可扩展:插件拦截器、TypeHandler、动态 SQL、二级缓存,足够灵活。

什么时候优先选 MyBatis?

  • 报表/复杂查询/存储过程多、对 SQL 可控性要求强。
  • 性能极致场景:需要自己精细化调优 SQL。
  • 需要多租户/审计/数据权限等“跨切面”能力(拦截器很好用)。

什么时候考虑 JPA?

  • 以 CRUD 为主,领域模型聚合清晰,希望更少 SQL(但也要接受性能可预期性降低)。

2. 快速开始(Spring Boot 版)

目标:5 分钟跑通一个 User 的增删改查。

2.1 数据表

CREATE TABLE `user` (`id`          BIGINT PRIMARY KEY AUTO_INCREMENT,`username`    VARCHAR(50) NOT NULL,`email`       VARCHAR(100) NOT NULL,`status`      TINYINT NOT NULL DEFAULT 1,   -- 1:启用 0:停用`created_at`  TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,`updated_at`  TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,UNIQUE KEY uk_email (email)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

2.2 依赖(Maven)

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>3.0.3</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency><dependency><groupId>com.zaxxer</groupId><artifactId>HikariCP</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>
</dependencies>

2.3 application.yml

spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8&serverTimezone=UTCusername: rootpassword: rootmvc:pathmatch:matching-strategy: ant_path_matcher
mybatis:mapper-locations: classpath:mapper/**/*.xmltype-aliases-package: com.example.demo.domainconfiguration:map-underscore-to-camel-case: truedefault-fetch-size: 100default-statement-timeout: 10

2.4 实体类

package com.example.demo.domain;import lombok.Data;
import java.time.LocalDateTime;@Data
public class User {private Long id;private String username;private String email;private Integer status;private LocalDateTime createdAt;private LocalDateTime updatedAt;
}

2.5 Mapper 接口

package com.example.demo.mapper;import com.example.demo.domain.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;@Mapper
public interface UserMapper {int insert(User user);int updateById(User user);int deleteById(@Param("id") Long id);User selectById(@Param("id") Long id);List<User> selectByUsernameLike(@Param("keyword") String keyword);
}

2.6 Mapper XML(resources/mapper/UserMapper.xml)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.example.demo.mapper.UserMapper"><resultMap id="BaseResultMap" type="com.example.demo.domain.User"><id column="id" property="id"/><result column="username" property="username"/><result column="email" property="email"/><result column="status" property="status"/><result column="created_at" property="createdAt"/><result column="updated_at" property="updatedAt"/></resultMap><sql id="Base_Column_List">id, username, email, status, created_at, updated_at</sql><insert id="insert" parameterType="com.example.demo.domain.User" useGeneratedKeys="true" keyProperty="id">INSERT INTO user (username, email, status)VALUES (#{username}, #{email}, #{status})</insert><update id="updateById" parameterType="com.example.demo.domain.User">UPDATE user<set><if test="username != null">username = #{username},</if><if test="email != null">email = #{email},</if><if test="status != null">status = #{status},</if></set>WHERE id = #{id}</update><delete id="deleteById" parameterType="long">DELETE FROM user WHERE id = #{id}</delete><select id="selectById" parameterType="long" resultMap="BaseResultMap">SELECT <include refid="Base_Column_List"/>FROM user WHERE id = #{id}</select><select id="selectByUsernameLike" parameterType="string" resultMap="BaseResultMap">SELECT <include refid="Base_Column_List"/>FROM user<where><if test="keyword != null and keyword != ''">AND username LIKE CONCAT('%', #{keyword}, '%')</if></where>ORDER BY id DESCLIMIT 100</select></mapper>

2.7 Service & Controller(示例)

package com.example.demo.service;import com.example.demo.domain.User;
import com.example.demo.mapper.UserMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.List;@Service
@RequiredArgsConstructor
public class UserService {private final UserMapper userMapper;@Transactionalpublic Long create(User user) {userMapper.insert(user);return user.getId();}@Transactionalpublic void update(User user) {userMapper.updateById(user);}public User get(Long id) {return userMapper.selectById(id);}public List<User> search(String keyword) {return userMapper.selectByUsernameLike(keyword);}
}

3. 核心概念与运行机制

MyBatis 运行时关键对象

  • SqlSessionFactory:会话工厂(线程安全),由配置构建,创建 SqlSession

  • SqlSession:一次数据库会话(非线程安全),管理 Statement/事务。

  • Mapper 接口与代理:MyBatis 使用 MapperProxy 动态代理接口方法 → MappedStatement → 执行器(Executor)。

  • 执行器 Executor:负责缓存、SQL 执行,类型有 SIMPLEREUSEBATCH

  • 处理器

    • ParameterHandler(参数预处理),
    • ResultSetHandler(结果映射),
    • TypeHandler(Java ↔ JDBC 类型转换)。

执行流程(简化)

  1. Mapper 方法被调用 → 动态代理找到 MappedStatement(由 XML/注解解析)。
  2. ParameterHandler 绑定 #{} 参数,生成 PreparedStatement
  3. Executor 执行,命中缓存则返回,否则访问数据库。
  4. ResultSetHandlerResultSet 映射为对象(resultMap/自动映射)。

4. 映射语法与动态 SQL

4.1 参数绑定:#{} vs ${}

  • #{}:预编译占位符,安全(防注入),自动做类型转换。
  • ${}:字符串拼接,谨慎使用(表名/列名动态时),注意 SQL 注入风险。

4.2 resultType vs resultMap

  • resultType:直接映射到类/基本类型,适合简单查询。
  • resultMap:可定义 <id/><result/><association/>(一对一)、<collection/>(一对多)、<discriminator/>(鉴别器),适合复杂对象图。

4.3 关联映射:一对一 / 一对多

<!-- 一对一:User 包含 Profile -->
<resultMap id="UserWithProfile" type="User"><id column="u_id" property="id"/><result column="username" property="username"/><association property="profile" javaType="Profile" columnPrefix="p_"><id column="p_id" property="id"/><result column="p_phone" property="phone"/></association>
</resultMap><select id="selectUserWithProfile" resultMap="UserWithProfile">SELECT u.id AS u_id, u.username,p.id AS p_id, p.phone AS p_phoneFROM user u LEFT JOIN profile p ON u.id = p.user_idWHERE u.id = #{id}
</select>
<!-- 一对多:User 包含 roles 列表(通过嵌套查询或嵌套结果) -->
<resultMap id="UserWithRoles" type="User"><id column="u_id" property="id"/><result column="username" property="username"/><collection property="roles" ofType="Role"><id column="r_id" property="id"/><result column="r_name" property="name"/></collection>
</resultMap><select id="selectUserWithRoles" resultMap="UserWithRoles">SELECT u.id AS u_id, u.username,r.id AS r_id, r.name AS r_nameFROM user u LEFT JOIN user_role ur ON u.id=ur.user_idLEFT JOIN role r ON ur.role_id=r.idWHERE u.id=#{id}
</select>

建议:能一次性 JOIN 出来就不要 N+1(嵌套查询),必要时才懒加载。

4.4 动态 SQL 常用标签

  • <if> / <choose-when-otherwise> 条件拼接。
  • <where> 自动处理多余 AND/OR。
  • <set> 动态更新字段(自动处理逗号)。
  • <trim prefix="(" suffix=")" suffixOverrides=","/> 高级裁剪。
  • <foreach> 循环(IN 查询、批量插入)。
  • <bind> 绑定变量(如 LIKE 模糊匹配)。

综合示例:复杂查询

<select id="queryUsers" resultMap="BaseResultMap">SELECT <include refid="Base_Column_List"/>FROM user<where><if test="username != null and username != ''">AND username LIKE CONCAT('%', #{username}, '%')</if><if test="emails != null and emails.size > 0">AND email IN<foreach collection="emails" item="e" open="(" close=")" separator=",">#{e}</foreach></if><if test="statusList != null and statusList.size>0">AND status IN<foreach collection="statusList" item="s" open="(" close=")" separator=",">#{s}</foreach></if></where>ORDER BY id DESC<if test="limit != null">LIMIT #{limit}</if><if test="offset != null"> OFFSET #{offset}</if>
</select>

5. 类型处理器 TypeHandler(含自定义 JSON/枚举示例)

5.1 内置与枚举处理

  • 内置 Date, LocalDateTime, 基本类型 等常见映射开箱即用。
  • 枚举:使用 EnumTypeHandler(存字符串)或 EnumOrdinalTypeHandler(存序号)。不建议用序号,改名/顺序调整有风险。

枚举示例

public enum UserStatus {DISABLED(0), ENABLED(1);private final int code;UserStatus(int code){this.code=code;}public int getCode(){return code;}
}
// 自定义枚举 TypeHandler:用 code 入库
@MappedJdbcTypes(JdbcType.TINYINT)
@MappedTypes(UserStatus.class)
public class UserStatusTypeHandler extends BaseTypeHandler<UserStatus> {@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, UserStatus parameter, JdbcType jdbcType) throws SQLException {ps.setInt(i, parameter.getCode());}@Overridepublic UserStatus getNullableResult(ResultSet rs, String columnName) throws SQLException {int code = rs.getInt(columnName);return code==1?UserStatus.ENABLED:UserStatus.DISABLED;}@Overridepublic UserStatus getNullableResult(ResultSet rs, int columnIndex) throws SQLException {int code = rs.getInt(columnIndex);return code==1?UserStatus.ENABLED:UserStatus.DISABLED;}@Overridepublic UserStatus getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {int code = cs.getInt(columnIndex);return code==1?UserStatus.ENABLED:UserStatus.DISABLED;}
}

在 Mapper XML 中声明或通过全局配置扫描

<result column="status" property="status" typeHandler="com.example.demo.type.UserStatusTypeHandler"/>

5.2 JSON 字段映射(以 Jackson 为例)

public class Profile {private String phone;private List<String> tags;
}
@MappedJdbcTypes(JdbcType.VARCHAR)
@MappedTypes(Profile.class)
public class JsonProfileTypeHandler extends BaseTypeHandler<Profile> {private static final ObjectMapper MAPPER = new ObjectMapper();@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, Profile parameter, JdbcType jdbcType) throws SQLException {try { ps.setString(i, MAPPER.writeValueAsString(parameter)); }catch (JsonProcessingException e) { throw new SQLException(e); }}@Overridepublic Profile getNullableResult(ResultSet rs, String columnName) throws SQLException {String json = rs.getString(columnName);if (json == null) return null;try { return MAPPER.readValue(json, Profile.class); }catch (IOException e) { throw new SQLException(e); }}@Overridepublic Profile getNullableResult(ResultSet rs, int columnIndex) throws SQLException {String json = rs.getString(columnIndex);if (json == null) return null;try { return MAPPER.readValue(json, Profile.class); }catch (IOException e) { throw new SQLException(e); }}@Overridepublic Profile getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {String json = cs.getString(columnIndex);if (json == null) return null;try { return MAPPER.readValue(json, Profile.class); }catch (IOException e) { throw new SQLException(e); }}
}

6. Spring 事务与多数据源整合

6.1 事务

  • 使用 @Transactional(默认运行时异常回滚)。
  • MyBatis 与 Spring 事务整合由 SqlSessionTemplate 完成,无需手工 commit/rollback

注意:同类内部方法调用不会触发 AOP 事务,可用 @Transactional 放到 public 对外方法,或通过 self-injection 解决。

6.2 多数据源(读写分离示例)

思路:定义两个 DataSource,两个 SqlSessionFactory,分别 @MapperScan 指向不同包。

@Configuration
@MapperScan(basePackages = "com.example.demo.mapper.read", sqlSessionFactoryRef = "readSqlSessionFactory")
public class ReadDataSourceConfig {@Beanpublic DataSource readDataSource() { /* 配置 HikariDataSource */ }@Beanpublic SqlSessionFactory readSqlSessionFactory(@Qualifier("readDataSource") DataSource ds) throws Exception {SqlSessionFactoryBean bean = new SqlSessionFactoryBean();bean.setDataSource(ds);return bean.getObject();}
}

也可使用路由数据源(AbstractRoutingDataSource)+ 拦截器/注解在运行时切换。


7. 分页:LIMIT、RowBounds 与 PageHelper

  • 直接 LIMIT/OFFSET:最可控,适合大多数场景。
  • RowBounds:会把所有结果查出后在内存截断,大表慎用
  • PageHelper 插件:拦截 SQL 自动改写为分页语句,并统计总数,开发效率高。

PageHelper 示例

<!-- Maven -->
<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>1.4.7</version>
</dependency>
Page<User> page = PageHelper.startPage(1, 20).doSelectPage(() -> userMapper.selectByUsernameLike("tom"));
return new PageResult<>(page.getResult(), page.getTotal());

8. 缓存机制:一级/二级缓存

  • 一级缓存(SqlSession 级别):同一会话内相同查询直接命中;会在 commit/close 后失效。
  • 二级缓存(Mapper 命名空间级别):多个会话共享;需开启 <cache/>@CacheNamespace;更新/插入/删除默认会清空本命名空间缓存。

二级缓存开启示例

<mapper namespace="com.example.demo.mapper.UserMapper"><cache eviction="LRU" flushInterval="600000" size="1024" readOnly="false"/><!-- 其余 SQL ... -->
</mapper>

注意:跨表更新导致数据不一致时,使用 cache-ref 引用同一缓存区域,或干脆关闭二级缓存,改用业务层缓存(如 Redis)。


9. 性能优化与批处理

9.1 批处理

  • ExecutorType.BATCH:减少网络往返,成批提交。
try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {UserMapper mapper = session.getMapper(UserMapper.class);for (User u: users) { mapper.insert(u); }session.commit();
}
  • 批量插入 SQL(更快):
<insert id="batchInsert">INSERT INTO user (username, email, status) VALUES<foreach collection="list" item="u" separator=",">(#{u.username}, #{u.email}, #{u.status})</foreach>
</insert>

9.2 其他优化要点

  • 尽量命中 覆盖索引,避免回表。
  • 合理选择 JOIN vs. 子查询,规避 N+1
  • <sql> 片段重用列清单,保持一致性。
  • 大列表分页:使用 游标/seek 方法where id > ? limit ?)替代传统 offset,提高性能。

10. 插件(拦截器)机制

MyBatis 允许对 4 大接口进行拦截:ExecutorStatementHandlerParameterHandlerResultSetHandler

示例:SQL 耗时打印 + 租户注入

@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class SqlCostInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {StatementHandler handler = (StatementHandler) Plugin.getTarget(invocation.getTarget());BoundSql boundSql = handler.getBoundSql();String sql = boundSql.getSql();long t1 = System.nanoTime();try { return invocation.proceed(); }finally {long t2 = System.nanoTime();System.out.println("SQL耗时(ms): " + (t2 - t1)/1_000_000 + " => " + sql);}}@Overridepublic Object plugin(Object target) { return Plugin.wrap(target, this); }@Overridepublic void setProperties(Properties properties) {}
}

注意:拦截器容易引入隐形开销与副作用,审慎上线,可灰度测试。


11. 代码生成:MyBatis Generator 与替代方案

11.1 MyBatis Generator(MBG)

优点:稳健、官方、能根据表结构生成 model/mapper/xml
缺点:生成代码风格偏老,需要二次改造;复杂 SQL 仍需手写。

MBG 配置(片段)

<generatorConfiguration><context id="MySqlContext" targetRuntime="MyBatis3Simple"><jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"connectionURL="jdbc:mysql://localhost:3306/demo"userId="root" password="root"/><javaModelGenerator targetPackage="com.example.demo.domain" targetProject="src/main/java"/><sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources"/><javaClientGenerator targetPackage="com.example.demo.mapper" targetProject="src/main/java" type="XMLMAPPER"/><table tableName="user" domainObjectName="User"/></context>
</generatorConfiguration>

11.2 替代方案

  • MyBatis-Plus:增强 CRUD、代码生成、分页、Wrapper 条件构造器(学习/上手快)。
  • 自建代码生成器:基于模板(Freemarker/Beetl),按团队规范定制输出。

12. 常见问题排查(Troubleshooting)

  1. Parameter 'xxx' not found:方法参数未加 @Param,或 XML 使用的名字与实际不一致。
  2. Invalid bound statement (not found)namespace + id 未匹配;mapper-locations 扫描路径不对;包路径大小写错误。
  3. TooManyResultsExceptionselectOne 返回多行;请改 limit 1 或使用 selectList
  4. 驼峰映射不生效:未开启 map-underscore-to-camel-case 或列别名未对齐。
  5. javaType/jdbcType 冲突jdbcTypeNULL 可避免某些数据库驱动因 null 无法推断类型而报错。
  6. 二级缓存脏读:跨命名空间更新;使用 cache-ref 或关闭二级缓存。
  7. SQL 注入:使用 ${} 拼接外部输入(尤其是 order by、表名)造成风险,优先 #{} 或白名单校验。
  8. 事务不生效:同类内部调用;数据源未配置到 DataSourceTransactionManager;异常被吞。
  9. RowBounds 内存炸裂:大数据量分页必须改 SQL → LIMIT 或使用插件。
  10. 批处理返回主键useGeneratedKeys + keyProperty;批量插入需注意驱动/数据库是否支持批量返回主键。

13. 工程落地与包结构建议

com.example.demo
├── common        // 公共组件(拦截器、枚举、异常、工具)
├── config        // 数据源、MyBatis、事务、分页插件配置
├── domain        // 领域模型(DO)
├── dto|vo        // 入参/出参对象
├── mapper        // Mapper 接口 + XML(resources/mapper)
├── repository    // 组合多个 Mapper 的仓储实现(可选)
├── service       // 业务服务层(含 @Transactional)
├── web           // 控制器
└── starter       // 启动类

建议

  • 基础列片段统一 <sql id="Base_Column_List"/>,减少遗漏。
  • Mapper 拆分:BaseMapper(通用) + XxxMapper(个性化)。
  • 复杂 SQL 封装成视图/存储过程时,务必评估可维护性与迁移成本。

14. 面试高频题与答题要点

  1. MyBatis 与 Hibernate 区别? —— SQL 自由 vs. 自动 ORM;可控性/性能 vs. 生产力。
  2. #{} 与 ${} 区别? —— 预编译占位符(安全) vs. 字符串拼接(有注入风险)。
  3. 一级/二级缓存工作原理? —— 会话级/命名空间级;更新语句清缓存;cache-ref
  4. 动态 SQL 标签 —— if/choose/where/set/trim/foreach/bind 场景与常见坑。
  5. 插件能拦截哪些点? —— Executor/StatementHandler/ParameterHandler/ResultSetHandler;谨慎使用。
  6. 分页实现方案? —— LIMIT、RowBounds(慎用)、PageHelper(优点/缺点)。
  7. 批处理如何做? —— ExecutorType.BATCH vs. 多值 INSERT;差异与回滚策略。
  8. 如何避免 N+1? —— JOIN 一次查全;必要时懒加载但注意一致性与事务边界。
  9. 事务为什么没生效? —— AOP 代理、同类调用、异常类型、事务管理器配置。
  10. TypeHandler 的作用与自定义示例 —— 枚举/JSON 映射。

15. 纯 MyBatis(非 Spring)最小可运行示例

15.1 mybatis-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><environments default="dev"><environment id="dev"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/demo"/><property name="username" value="root"/><property name="password" value="root"/></dataSource></environment></environments><mappers><mapper resource="mapper/UserMapper.xml"/></mappers>
</configuration>

15.2 启动代码

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
try (SqlSession session = sqlSessionFactory.openSession()) {UserMapper mapper = session.getMapper(UserMapper.class);User u = mapper.selectById(1L);System.out.println(u);
}

16. 参考资料与学习路径

  • 官方文档:熟悉 XML 标签、配置项、缓存与插件机制。
  • 深入源码:MapperProxyExecutorConfigurationMappedStatementBoundSql
  • 实战演练:把线上一个复杂查询用 MyBatis 重写,做 Explain 分析 + 索引优化 + 压测。
  • 扩展阅读:MyBatis-Plus、ShardingSphere(分库分表)、多租户/数据权限设计。

最后

MyBatis 的真正价值在于“可控性 + 可观测性 + 可维护性”。
掌握本文的路线与示例,配合你所在业务的真实 SQL 场景,基本可以实现从入门 → 熟练 → 精通。祝使用愉快!🚀

http://www.xdnf.cn/news/18674.html

相关文章:

  • 3-3〔OSCP ◈ 研记〕❘ WEB应用攻击▸WEB应用安全评估工具
  • 火山引擎配置CDN
  • 【Linux | 网络】多路转接IO之poll
  • 计算机网络课堂笔记
  • AutoCAD Electrical缺少驱动程序“AceRedist“解决方法
  • C++ Core Guidelines 核心理念
  • 关于单片机串口通讯的多机操作说明---单片机串口通讯如何实现多机操作?
  • 16-day13强化学习和训练大模型
  • 怎么把iphone文件传输到windows电脑?分场景选方法
  • jasperreports 使用
  • 解锁处暑健康生活
  • 企业级监控可视化系统 Prometheus + Grafana
  • LoRA(低秩适应,Low-Rank Adaptation)的 alpha 参数解析(54)
  • 雷卯针对香橙派Orange 4G-IOT开发板防雷防静电方案
  • kafka 原理详解
  • 【OpenAI】ChatGPT-4o-latest 真正的多模态、长文本模型的详细介绍+API的使用教程!
  • 深入理解 Python Scapy 库:网络安全与协议分析的瑞士军刀
  • ES6/ES2015 - ES16/ES2025
  • 在压力测试中如何确定合适的并发用户数?
  • 挖币与区块链技术有怎样的联系?
  • 基于 Prometheus+Alertmanager+Grafana 打造监控报警后台(一)-Prometheus介绍及安装
  • DMP-Net:面向脑组织术中成像的深度语义先验压缩光谱重建方法|文献速递-深度学习人工智能医疗图像
  • PyTorch实战(1)——深度学习概述
  • 阿里:基于设计逻辑的LLM数据合成
  • crc16是什么算法
  • C++ 指针与引用面试深度解析
  • STM32项目分享:基于STM32的智能洗衣机
  • 开源大模型天花板?DeepSeek-V3 6710亿参数MoE架构深度拆解
  • 微软恶意软件删除工具:官方免费的系统安全防护利器
  • 网络编程1-基本概念、函数接口