在前两篇博客中,我们掌握了 MyBatis 的基础搭建、核心架构与 Mapper 代理开发,能应对简单的单表 CRUD 场景。但实际项目中,业务往往更复杂 —— 比如 “多条件动态查询”“员工与部门的关联查询”“高频查询的性能优化” 等。本篇将聚焦 MyBatis 的三大高级特性:动态 SQL(灵活拼接 SQL)关联查询(处理多表关系)查询缓存(提升性能),结合文档中的实战案例,帮你解决复杂业务场景,真正做到 “学以致用”。

目录

一、动态 SQL:MyBatis 的 “灵活拼接神器”

1.1 动态 SQL 的核心价值

1.2 核心动态 SQL 标签实战

(1)标签:基础条件判断

(2)标签:智能处理 AND/OR

(3)--标签:二选一的条件

(4)标签:自定义前缀 / 后缀

(5)标签:动态更新的 “逗号杀手”

(6)标签:处理集合与 IN 条件

(7)片段:SQL 代码复用

(8)标签:跨数据库模糊查询

二、关联查询:处理多表关系(一对一 / 一对多 / 多表)

2.1 关联查询的核心概念

2.2 一对一关联查询:员工→部门

实现步骤:

2.3 一对多关联查询:部门→员工

实现步骤:

2.4 多表关联查询:用户→订单→订单详情→商品

实现步骤:

三、查询缓存:MyBatis 的 “性能优化利器”

3.1 缓存的核心概念

3.2 一级缓存:SqlSession 级别的本地缓存

核心特点:

实战案例:

3.3 二级缓存:Mapper 级别的全局缓存

核心特点:

配置步骤:

实战案例:

关键配置:

3.4 整合第三方缓存:Ehcache(分布式场景)

配置步骤:

四、总结:MyBatis 核心能力回顾与实践建议

实践建议:


一、动态 SQL:MyBatis 的 “灵活拼接神器”

        实际开发中,SQL 语句往往不是固定的 —— 比如 “查询员工” 时,用户可能输入姓名查询,也可能输入部门号查询,还可能两者都输入。如果为每种情况写一个 SQL,会导致代码冗余。MyBatis 的动态 SQL 通过标签判断条件,自动拼接 SQL,彻底解决这一问题。

1.1 动态 SQL 的核心价值

动态 SQL 的本质是 “根据参数是否为空或满足条件,动态生成合法的 SQL 语句”,避免手动拼接 SQL 的痛点:

  • 无需担心 “第一个条件是 AND/OR” 导致的语法错误;
  • 无需处理 “字段末尾多余逗号”(如更新操作中);
  • 支持循环遍历集合(如IN条件),简化批量操作。

MyBatis 提供 8 种常用动态 SQL 标签,我们结合文档中的实战案例,逐一讲解核心用法。

1.2 核心动态 SQL 标签实战

(1)<if>标签:基础条件判断

<if>标签是动态 SQL 的基础,用于 “满足条件则拼接 SQL 片段”,常与test属性(OGNL 表达式)配合使用。
场景:查询员工,姓名不为空则按姓名模糊查询,部门号不为空则按部门号查询。

<select id="selectEmpByCond" parameterType="emp" resultType="emp">select * from emp where 1=1<!-- 若ename不为空且非空字符串,拼接姓名条件 --><if test="ename != null and ename != ''">and ename like concat('%', #{ename}, '%')</if><!-- 若deptno不为空,拼接部门号条件 --><if test="deptno != null">and deptno = #{deptno}</if>
</select>

关键说明

  • test="ename != null and ename != ''":判断参数ename是否有效(非空且非空字符串);
  • concat('%', #{ename}, '%'):MySQL 中拼接模糊查询的%,避免 SQL 注入(文档中强调#{}${}更安全);
  • where 1=1:临时占位,避免 “第一个条件是 AND” 导致的语法错误(后续<where>标签可替代这一写法)。
(2)<where>标签:智能处理 AND/OR

<where>标签是<if>标签的 “好搭档”,能自动处理条件拼接中的语法问题:

  • 若内部有满足条件的<if>,自动添加WHERE关键字;
  • 自动去掉第一个条件前的ANDOR
  • 若内部无满足条件的<if>,不添加WHERE,避免语法错误。

优化上述<if>案例

<select id="selectEmpByCond" parameterType="emp" resultType="emp">select * from emp<where><if test="ename != null and ename != ''">and ename like concat('%', #{ename}, '%') <!-- 无需担心第一个条件是AND --></if><if test="deptno != null">and deptno = #{deptno}</if></where>
</select>

文档要点<where>标签会智能忽略条件开头的AND/OR,且无需手动写where 1=1,代码更简洁。

(3)<choose>-<when>-<otherwise>标签:二选一的条件

类似 Java 的switch-case-default<choose>标签下的<when>按顺序判断,只执行第一个满足条件的<when>,都不满足则执行<otherwise>
场景:查询员工,优先按薪资查(薪资≤指定值),其次按姓名查,都不满足则查部门号 = 10。

<select id="selectEmpByChoose" parameterType="emp" resultType="emp">select * from emp<where><choose><when test="sal != null">sal &lt;= #{sal} <!-- XML中“<”需转义为“&lt;” --></when><when test="ename != null and ename != ''">ename like concat('%', #{ename}, '%')</when><otherwise>deptno = 10 <!-- 所有条件不满足时执行 --></otherwise></choose></where>
</select>

文档说明<choose>适用于 “多个条件中只选一个” 的场景,避免<if>标签的 “多条件同时生效” 问题。

(4)<trim>标签:自定义前缀 / 后缀

<trim>标签比<where>更灵活,支持自定义 “添加前缀”“添加后缀”“覆盖首尾字符”,核心属性如下:

  • prefix:给内部内容添加前缀(如WHERE);
  • suffix:给内部内容添加后缀(如));
  • prefixOverrides:去掉内部内容开头的指定字符(如AND/OR);
  • suffixOverrides:去掉内部内容末尾的指定字符(如,)。

场景 1:替代<where>标签

<trim prefix="where" prefixOverrides="and|or"><if test="ename != null">and ename like '%${ename}%'</if><if test="deptno != null">and deptno = #{deptno}</if>
</trim>

场景 2:动态插入字段(处理末尾逗号)
插入操作中,若部分字段为空,会导致INSERT语句末尾多逗号,<trim>可自动去掉:

<insert id="insertEmp" parameterType="emp">insert into emp<!-- 动态拼接字段名,去掉末尾逗号 --><trim prefix="(" suffix=")" suffixOverrides=","><if test="ename != null">ename,</if><if test="job != null">job,</if><if test="sal != null">sal,</if></trim>values<!-- 动态拼接字段值,去掉末尾逗号 --><trim prefix="(" suffix=")" suffixOverrides=","><if test="ename != null">#{ename},</if><if test="job != null">#{job},</if><if test="sal != null">#{sal},</if></trim>
</insert>

文档要点<trim>标签是动态 SQL 中最灵活的标签,可应对<where><set>无法覆盖的场景。

(5)<set>标签:动态更新的 “逗号杀手”

更新操作中,若用<if>标签,可能出现 “字段末尾多逗号”(如update emp set ename=?,),<set>标签可自动去掉末尾逗号,并添加SET关键字。

场景:动态更新员工信息,字段不为空则更新该字段。

<update id="updateEmp" parameterType="emp">update emp<set><if test="ename != null">ename = #{ename},</if><if test="job != null">job = #{job},</if><if test="sal != null">sal = #{sal},</if></set>where empno = #{empno}
</update>

文档说明<set>标签会自动添加SET关键字,并去掉内部内容末尾的逗号,避免update语句语法错误。

(6)<foreach>标签:处理集合与 IN 条件

        当 SQL 需要IN条件(如where deptno in (10,20,30))或批量操作时,<foreach>标签可遍历集合生成对应 SQL 片段,核心属性如下:

  • collection:集合类型(list=List,array= 数组,map的key=Map 中的集合);
  • open:遍历开始符号(如();
  • close:遍历结束符号(如));
  • item:集合元素的别名(如deptno);
  • separator:元素之间的分隔符(如,)。

场景 1:遍历 List 集合,查询部门号在列表中的员工

<select id="selectEmpByDeptnos" parameterType="java.util.List" resultType="emp">select * from emp<where>deptno in<foreach collection="list" open="(" close=")" item="deptno" separator=",">#{deptno}</foreach></where>
</select>

场景 2:遍历数组,查询员工编号在数组中的员工

<select id="selectEmpByEmpnosArr" parameterType="int[]" resultType="emp">select * from emp<where>empno in<foreach collection="array" open="(" close=")" item="empno" separator=",">#{empno}</foreach></where>
</select>

文档要点collection属性需根据参数类型选择(List 用list,数组用array),若参数是 Map,需写 Map 中集合的key

(7)<sql>片段:SQL 代码复用

        若多个 SQL 有重复片段(如select empno, ename, job from emp),可提取为<sql>片段,避免重复书写,提升维护性。

<!-- 定义SQL片段:id为片段唯一标识 -->
<sql id="empColumns">empno, ename, job, sal, deptno
</sql><!-- 引用SQL片段:用<include refid="片段id"> -->
<select id="selectEmp" resultType="emp">select <include refid="empColumns"/> from emp
</select><select id="selectEmpByDeptno" parameterType="int" resultType="emp">select <include refid="empColumns"/> from emp where deptno = #{deptno}
</select>

文档说明<sql>片段适用于重复的字段列表、查询条件等,减少代码冗余。

(8)<bind>标签:跨数据库模糊查询

        不同数据库的模糊查询语法不同(MySQL 用concat,Oracle 用||),<bind>标签可定义变量统一语法,提升代码可移植性。

<select id="selectEmpByEname" parameterType="emp" resultType="emp"><!-- 定义变量name:值为“%+ename+%” --><bind name="name" value="'%' + ename + '%'"/>select * from emp where ename like #{name}
</select>

文档要点<bind>标签无需关心数据库类型,统一用#{name}引用变量,避免因数据库切换修改 SQL。

二、关联查询:处理多表关系(一对一 / 一对多 / 多表)

        实际业务中,单表查询很少见,更多是 “员工 - 部门”“部门 - 员工”“用户 - 订单 - 商品” 等多表关联场景。MyBatis 通过<resultMap>标签的<association>(一对一)和<collection>(一对多)子标签,实现复杂关联映射。

2.1 关联查询的核心概念

在讲解案例前,需明确两种常见关联关系:

  • 一对一:一个对象对应一个对象(如一个员工对应一个部门);
  • 一对多:一个对象对应多个对象(如一个部门对应多个员工);
  • 多对多:需通过中间表转换为 “一对多 + 多对一”(如用户 - 订单 - 商品,用户与商品是多对多,通过订单表关联)。

MyBatis 通过<resultMap>定义关联规则,无需手动遍历多表结果集,自动映射为实体类对象。

2.2 一对一关联查询:员工→部门

场景:查询员工信息时,同时查询员工所属的部门信息(一个员工只属于一个部门)。

实现步骤:

定义实体类:在Emp类中添加Dept属性,存储关联的部门信息。

public class Emp {private int empno;private String ename;private int deptno;private Dept dept; // 一对一关联:员工所属部门// getter/setter、toString()
}public class Dept {private int deptno;private String dname;private String loc;// getter/setter、toString()
}

编写 Mapper 接口:定义查询方法。

public interface EmpMapper {List<Emp> selectEmpWithDept(); // 查询员工及所属部门
}

配置 Mapper.xml:用<resultMap>+<association>定义关联映射。

<mapper namespace="com.jr.mapper.EmpMapper"><!-- 定义resultMap:映射Emp与Dept的一对一关联 --><resultMap id="empWithDeptMap" type="emp"><!-- 映射Emp的基本字段:id标签对应主键 --><id column="empno" property="empno"/><result column="ename" property="ename"/><result column="deptno" property="deptno"/><!-- 一对一关联Dept:用<association> --><association property="dept" javaType="dept"> <!-- javaType:关联实体类的类型(Dept) --><id column="d_deptno" property="deptno"/> <!-- 用别名避免字段名冲突 --><result column="dname" property="dname"/><result column="loc" property="loc"/></association></resultMap><!-- 关联查询SQL:多表连接,用别名区分字段 --><select id="selectEmpWithDept" resultMap="empWithDeptMap">select e.empno, e.ename, e.deptno, d.deptno as d_deptno, d.dname, d.locfrom emp einner join dept d on e.deptno = d.deptno</select>
</mapper>
  1. 测试代码
@Test
public void testSelectEmpWithDept() {SqlSession session = factory.openSession();EmpMapper empMapper = session.getMapper(EmpMapper.class);List<Emp> emps = empMapper.selectEmpWithDept();for (Emp emp : emps) {System.out.println("员工:" + emp.getEname() + ",部门:" + emp.getDept().getDname());}session.close();
}

文档要点<association>标签用于一对一关联,javaType属性指定关联实体类的类型,需用别名避免多表字段名冲突(如d.deptno as d_deptno)。

2.3 一对多关联查询:部门→员工

场景:查询部门信息时,同时查询部门下的所有员工(一个部门有多个员工)。

实现步骤:

定义实体类:在Dept类中添加List<Emp>属性,存储关联的员工列表。

public class Dept {private int deptno;private String dname;private String loc;private List<Emp> emps; // 一对多关联:部门下的员工列表// getter/setter、toString()
}

编写 Mapper 接口

public interface DeptMapper {Dept selectDeptWithEmp(int deptno); // 查询部门及下属员工
}

配置 Mapper.xml:用<resultMap>+<collection>定义一对多关联。

<mapper namespace="com.jr.mapper.DeptMapper"><!-- 定义resultMap:映射Dept与Emp的一对多关联 --><resultMap id="deptWithEmpMap" type="dept"><!-- 映射Dept的基本字段 --><id column="deptno" property="deptno"/><result column="dname" property="dname"/><result column="loc" property="loc"/><!-- 一对多关联Emp列表:用<collection> --><collection property="emps" ofType="emp"> <!-- ofType:集合中元素的类型(Emp) --><id column="e_empno" property="empno"/> <!-- 别名避免冲突 --><result column="e_ename" property="ename"/><result column="e_sal" property="sal"/></collection></resultMap><!-- 关联查询SQL:左连接查询部门与员工 --><select id="selectDeptWithEmp" parameterType="int" resultMap="deptWithEmpMap">select d.deptno, d.dname, d.loc,e.empno as e_empno, e.ename as e_ename, e.sal as e_salfrom dept dleft join emp e on d.deptno = e.deptnowhere d.deptno = #{deptno}</select>
</mapper>
  1. 测试代码
@Test
public void testSelectDeptWithEmp() {SqlSession session = factory.openSession();DeptMapper deptMapper = session.getMapper(DeptMapper.class);Dept dept = deptMapper.selectDeptWithEmp(10);System.out.println("部门:" + dept.getDname());for (Emp emp : dept.getEmps()) {System.out.println("  员工:" + emp.getEname() + ",薪资:" + emp.getSal());}session.close();
}

文档要点<collection>标签用于一对多关联,ofType属性指定集合元素的类型(区别于javaTypejavaType用于指定属性类型,如List)。

2.4 多表关联查询:用户→订单→订单详情→商品

场景:查询用户信息时,同时查询用户的所有订单、每个订单的详情、每个详情对应的商品(多表关联:用户 1:N 订单 1:N 订单详情 1:1 商品)。

实现步骤:

定义实体类:逐层关联(Users→Orders→OrderDetail→Items)。

// 用户类:1个用户对应多个订单
public class Users {private int uid;private String uname;private List<Orders> orders; // 一对多关联订单// getter/setter
}// 订单类:1个订单对应多个详情
public class Orders {private int oid;private String orderid;private List<OrderDetail> orderdetails; // 一对多关联详情// getter/setter
}// 订单详情类:1个详情对应1个商品
public class OrderDetail {private int odid;private int itemsnum;private Items item; // 一对一关联商品// getter/setter
}// 商品类
public class Items {private int iid;private String name;private double price;// getter/setter
}

配置 Mapper.xml:嵌套<collection><association>实现多表映射。

<mapper namespace="com.jr.mapper.UserMapper"><!-- 多表关联resultMap:用户→订单→详情→商品 --><resultMap id="userOrderDetailItemMap" type="users"><id column="uid" property="uid"/><result column="uname" property="uname"/><!-- 1:N:用户→订单 --><collection property="orders" ofType="orders"><id column="oid" property="oid"/><result column="orderid" property="orderid"/><!-- 1:N:订单→订单详情 --><collection property="orderdetails" ofType="orderdetail"><id column="odid" property="odid"/><result column="itemsnum" property="itemsnum"/><!-- 1:1:订单详情→商品 --><association property="item" javaType="items"><id column="iid" property="iid"/><result column="name" property="name"/><result column="price" property="price"/></association></collection></collection></resultMap><!-- 多表关联SQL:四表连接 --><select id="selectUserWithAll" resultMap="userOrderDetailItemMap">select u.uid, u.uname,o.oid, o.orderid,od.odid, od.itemsnum,i.iid, i.name, i.pricefrom users uinner join orders o on u.uid = o.useridinner join orderdetail od on o.orderid = od.orderidinner join items i on od.itemid = i.iid</select>
</mapper>

文档要点:多表关联需嵌套使用<collection>(一对多)和<association>(一对一),确保每层映射的columnproperty对应。

三、查询缓存:MyBatis 的 “性能优化利器”

缓存是 “以空间换时间” 的优化手段,MyBatis 提供两级缓存,减少数据库访问次数,提升高频查询的性能。

3.1 缓存的核心概念

MyBatis 的缓存分为两级,作用域和生命周期不同:

  • 一级缓存SqlSession级别(本地缓存),默认开启,无需配置;
  • 二级缓存Mapper(namespace)级别(全局缓存),默认关闭,需手动配置;
  • 第三方缓存:如 Ehcache、Redis,用于分布式场景(多服务共享缓存)。

3.2 一级缓存:SqlSession 级别的本地缓存

核心特点:
  • 作用域:同一个SqlSession(从openSession()close());
  • 实现:基于PerpetualCache(HashMap)存储;
  • 失效场景
    1. 调用SqlSession.close()
    2. 调用SqlSession.commit()/rollback()(事务提交 / 回滚会清空缓存);
    3. 执行相同 ID 的insert/update/delete(修改数据会清空缓存,避免脏读);
    4. 调用SqlSession.clearCache()(手动清空缓存)。
实战案例:
@Test
public void testFirstLevelCache() {SqlSession session = factory.openSession();EmpMapper empMapper = session.getMapper(EmpMapper.class);// 第一次查询:执行SQL,结果存入一级缓存Emp emp1 = empMapper.selectEmpByNo(7369);System.out.println(emp1);// 第二次查询:同一SqlSession,相同SQL,从缓存获取(不执行SQL)Emp emp2 = empMapper.selectEmpByNo(7369);System.out.println(emp2);session.close();
}

日志输出:仅第一次查询执行 SQL,第二次从缓存获取。

3.3 二级缓存:Mapper 级别的全局缓存

核心特点:
  • 作用域:同一个Mapper(namespace),跨SqlSession共享;
  • 存储:默认存储序列化后的 Java 对象(需实体类实现Serializable接口);
  • 配置步骤:需开启全局开关 + Mapper 单独配置。
配置步骤:

开启全局二级缓存SqlMapConfig.xml):

<settings><setting name="cacheEnabled" value="true"/> <!-- 全局开关,默认true可省略 -->
</settings>

在 Mapper.xml 中开启二级缓存

<mapper namespace="com.jr.mapper.EmpMapper"><!-- 开启二级缓存:默认使用PerpetualCache --><cache/><!-- 或配置缓存参数(如过期时间、最大容量) --><!--<cacheeviction="LRU"        // 淘汰策略(LRU:最近最少使用)flushInterval="60000" // 60秒刷新一次缓存size="1024"           // 最多缓存1024个对象readOnly="true"/>     // 只读模式(返回对象引用,性能高)--><select id="selectEmpByNo" parameterType="int" resultType="emp">select * from emp where empno = #{empno}</select>
</mapper>

实体类实现 Serializable 接口

public class Emp implements Serializable { // 二级缓存需序列化private static final long serialVersionUID = 1L; // 序列化ID// 字段、getter/setter
}
实战案例:
@Test
public void testSecondLevelCache() {// 第一个SqlSession:查询后关闭,将数据刷入二级缓存SqlSession session1 = factory.openSession();EmpMapper empMapper1 = session1.getMapper(EmpMapper.class);Emp emp1 = empMapper1.selectEmpByNo(7369);System.out.println(emp1);session1.close(); // 关闭SqlSession,一级缓存数据刷入二级缓存// 第二个SqlSession:从二级缓存获取数据(不执行SQL)SqlSession session2 = factory.openSession();EmpMapper empMapper2 = session2.getMapper(EmpMapper.class);Emp emp2 = empMapper2.selectEmpByNo(7369);System.out.println(emp2);session2.close();
}

文档要点:二级缓存需通过session.close()session.commit()将一级缓存数据刷入,否则无法共享。

关键配置:
  • 禁用二级缓存:对实时性要求高的查询(如秒杀商品库存),添加useCache="false"
<select id="selectEmpByNo" parameterType="int" resultType="emp" useCache="false">select * from emp where empno = #{empno}
</select>
  • 刷新二级缓存:执行insert/update/delete后,默认清空二级缓存(避免脏读),可通过flushCache="false"关闭(不推荐):
<update id="updateEmp" parameterType="emp" flushCache="true"> <!-- 默认true,可省略 -->update emp set ename = #{ename} where empno = #{empno}
</update>

3.4 整合第三方缓存:Ehcache(分布式场景)

默认二级缓存是 “本地缓存”,分布式部署时(多台服务器)缓存不共享,需整合分布式缓存框架(如 Ehcache)。

配置步骤:

添加 Maven 依赖

<!-- MyBatis-Ehcache整合包 -->
<dependency><groupId>org.mybatis.caches</groupId><artifactId>mybatis-ehcache</artifactId><version>1.0.2</version>
</dependency><!-- Ehcache核心包 -->
<dependency><groupId>net.sf.ehcache</groupId><artifactId>ehcache</artifactId><version>2.10.1</version>
</dependency>

添加 Ehcache 配置文件(ehcache.xml)

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"><!-- 缓存数据存储路径(磁盘) --><diskStore path="D:/mybatis-ehcache"/><!-- 默认缓存配置 --><defaultCachemaxElementsInMemory="1000"  <!-- 内存最大缓存对象数 -->eternal="false"            <!-- 不永久缓存 -->timeToIdleSeconds="120"     <!-- 120秒未访问则过期 -->timeToLiveSeconds="120"     <!-- 120秒后过期 -->overflowToDisk="true"/>     <!-- 内存满时写入磁盘 -->
</ehcache>

在 Mapper.xml 中指定 Ehcache 缓存

<mapper namespace="com.jr.mapper.EmpMapper"><!-- 启用Ehcache缓存 --><cache type="org.mybatis.caches.ehcache.EhcacheCache"/><!-- SQL语句... -->
</mapper>

文档要点:Ehcache 支持内存 + 磁盘存储,分布式部署时可配置集群,实现缓存共享。

四、总结:MyBatis 核心能力回顾与实践建议

至此,MyBatis 从入门到精通系列三篇博客已全部完成,我们系统覆盖了 MyBatis 的核心能力:

  1. 基础层:框架概念、环境搭建(普通项目 + Maven);
  2. 核心层:三层架构、全局配置、Mapper 代理开发;
  3. 高级层:动态 SQL、关联查询、查询缓存。
实践建议:
  1. 动态 SQL:优先用<where>``<set>标签简化条件拼接,复杂场景用<trim>,避免手动写where 1=1
  2. 关联查询:一对一用<association>,一对多用<collection>,多表关联需注意字段别名冲突;
  3. 缓存优化:一级缓存默认开启,二级缓存按需开启(适合查询多、修改少的场景),分布式项目整合 Ehcache/Redis;
  4. 开发规范:坚持 “Mapper 代理开发”,SQL 集中在 XML 中,通过<sql>片段复用代码,提升维护性。

        MyBatis 的核心优势在于 “轻量、灵活、解耦”—— 既保留了 SQL 的灵活性,又简化了数据映射与连接管理。掌握这些核心能力后,你不仅能应对企业级项目的持久层开发,更能在面试中从容应对 MyBatis 的高频考点(如动态 SQL、缓存机制、关联查询)。后续可进一步学习 MyBatis-Plus(MyBatis 的增强工具,简化 CRUD),但建议先夯实 MyBatis 基础,再逐步拓展。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/96934.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/96934.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/96934.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux内核中IPv4的BEET模式封装机制解析

引言 在Linux网络栈中,IPSec提供了网络层的数据加密和认证服务。传统的IPSec支持两种模式:传输模式(Transport Mode)和隧道模式(Tunnel Mode)。然而,这两种模式各有优缺点:传输模式开销小但无法隐藏原始IP头;隧道模式提供完全封装但增加了开销。 BEET(Bound End-to…

设计模式——创建型模式

什么是设计模式&#xff1f;设计模式是软件工程中解决常见问题的经典方案&#xff0c;它们代表了最佳实践和经验总结。通过使用设计模式&#xff0c;开发者可以创建更加灵活、可维护和可扩展的代码结构。设计模式不是具体的代码实现&#xff0c;而是针对特定问题的通用解决方案…

我爱学算法之—— 位运算(上)

常见位运算 对于位运算&#xff1a; &&#xff1a;按位与&#xff0c;有0则0。 |&#xff1a;按位或&#xff0c;有1则1。 ^&#xff1a;按位异或&#xff0c;相同为0、不同为1。&#xff08;无进位相加&#xff09; ~&#xff1a;二进制位按位取反。 对于位运算的常见使用…

智能语音系统

智能语音系统通过技术手段让机器能够“听懂”、“理解”并“回应”人类的语音&#xff0c;是实现人机交互的关键技术之一。下面我将为你梳理智能语音系统的核心组成部分、工作原理、应用场景以及面临的挑战。&#x1f9e0; 核心技术与工作原理智能语音系统之所以能实现人机交互…

水泵自动化远程监测与控制的御控物联网解决方案

一、行业背景与痛点分析水泵作为工业生产、农业灌溉、城市供水等领域的核心设备&#xff0c;其运行效率直接影响系统稳定性与运营成本。然而&#xff0c;传统管理模式存在三大核心痛点&#xff1a;人工巡检低效&#xff1a;偏远地区水泵分布分散&#xff0c;依赖人工定期巡检&a…

Python实现点云法向量各种方向设定

本次我们分享点云法向量定向的四种方法&#xff0c;分别是XYZ轴、相机位置、最小生成树(MST)和质心设定方法。通常出现在三维点云处理、三维重建、计算机视觉或图形学中&#xff0c;需要估计点云的法向量方向。它们的核心任务是&#xff1a;在已知点坐标和局部几何结构&#xf…

腾讯云智能体开发平台

提供全球领先的云计算服务腾讯云&#xff0c;腾讯集团倾力打造的云计算品牌&#xff0c;面向全世界各个国家和地区的政府机构、企业组织和个人开发者&#xff0c;提供全球领先的云计算、大数据、人工智能等技术产品与服务&#xff0c;以卓越的科技能力打造丰富的行业解决方案&a…

css flex布局,设置flex-wrap:wrap换行后,如何保证子节点被内容撑高后,每一行的子节点高度一致。

flex布局&#xff0c;设置flex-wrap&#xff1a;wrap换行后&#xff0c;如何保证子节点被内容撑高后&#xff0c;每一行的子节点高度一致。核心&#xff1a;需要设置父节点和子节点&#xff1a;align-items: stretch&#xff0c;两个都要。代码&#xff1a;<div class"…

Nginx_Tomcat综合案例

要求 需求&#xff1a;通过 nginx 来代理两个 tomcat 服务器&#xff08;反向代理&#xff09;&#xff0c;然后通过 https://www.nginx.com 来进行访问。主机名IP软件nginx192.168.30.10nginxtomcat1192.168.30.11java&#xff0c;tomcattomcat2192.168.30.12java&#xff0c;…

【Vue2手录12】单文件组件SFC

一、知识回顾-Vue2项目基础操作与环境配置 1.1 项目启动 项目打开方式&#xff1a;直接将项目文件夹&#xff08;如my-app&#xff09;拖拽到 Visual Studio Code&#xff08;推荐编辑器&#xff09;&#xff0c;避免拖拽父级文件夹&#xff0c;防止路径混乱。启动命令&#xf…

VS2022下载+海康SDK环境配置实现实时预览

一.VS2022下载去官网下载就可以了&#xff1a;https://visualstudio.microsoft.com/zh-hans/vs/下载Community版本是免费的。&#xff08;2&#xff09;下载后得安装包VisualStudioSetup.exe打开&#xff1a;点击继续等待下载完成&#xff0c;出现如下界面&#xff0c;这里是选…

YOLO 模型从 PyTorch 转换为 ONNX 并优化

YOLO 模型从 PyTorch 转换为 ONNX 并优化 在深度学习部署中&#xff0c;ONNX&#xff08;Open Neural Network Exchange&#xff09; 已成为跨框架与跨平台的标准格式。我们经常需要将 YOLOv8 在 PyTorch 中训练好的模型转换为 ONNX&#xff0c;并进行优化&#xff0c;以便在 …

推进新型信息基础设施建设发展:蜂窝模组行业迎来结构性机遇

工信部副部长张云明在2025年9月9日国新办新闻发布会上明确表示&#xff0c;将"扎实推进新型信息基础设施建设发展"&#xff0c;并重点强调"打造新型工业网络&#xff0c;推进蜂窝车联网部署" 。这一政策表态对蜂窝模组行业产生深远影响&#xff0c;将推动行…

返利app排行榜的缓存更新策略:基于过期时间与主动更新的混合方案

返利app排行榜的缓存更新策略&#xff1a;基于过期时间与主动更新的混合方案 大家好&#xff0c;我是阿可&#xff0c;微赚淘客系统及省赚客APP创始人&#xff0c;是个冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 在返利APP中&#xff0c;“热门商品排行榜”“用…

科技信息差(9.12)

AI量子计算重塑药物研发&#xff1a;技术融合路径与产业革命一、引言&#xff1a;技术融合的颠覆性机遇2025年9月&#xff0c;AI药物研发公共服务平台正式上线&#xff0c;宣称可将新药上市时间缩短近半1。与此同时&#xff0c;量子计算与AI的跨界合作在KRAS抑制剂开发中取得突…

Java 分布式缓存实现:结合 RMI 与本地文件缓存

目录 一、核心思路 二、项目结构说明 2.1 服务端项目结构&#xff08;IDEA&#xff09; 2.2 客户端项目结构&#xff08;Eclipse&#xff09; 三、服务端实现&#xff08;IDEA&#xff09; 3.1 数据库访问层 3.2 远程接口定义 3.3 远程服务实现 3.4 服务端启动类 四、…

Electron第一个应用

1、安装node nodeJS下载 2、下载完成&#xff0c;需要配置环境。 写道path路径 、 3、安装完成&#xff0c;查看版本 npm -v4、 配置cnpm npm install -g cnpm --registryhttps://registry.npmmirror.com5、参考Electron 写&#xff1a; Electron第一个程序hello 6、安装…

React 原理篇 - React 新架构深度解析

使用过 React v16 之前版本的开发者或许都经历过这样的场景&#xff1a;当页面包含复杂组件或大量列表时&#xff0c;输入框打字会卡顿&#xff0c;滚动会不流畅。这些体验问题的背后&#xff0c;往往与 React 的渲染机制密切相关。2017 年 React v16 推出的 Fiber 架构&#x…

【JavaSE五天速通|第三篇】常用API与日期类篇

适合有其他语言基础想快速入门JavaSE的。用的资料是 Java入门基础视频教程 &#xff0c;从中摘取了笔者认为与其他语言不同或需要重点学习的内容 常用API与日期类只需要有印象即可&#xff0c;用到了再来这查 day04 常用API 一、StringBuilder类 StringBuilder代表可变字符…

K8s学习笔记(二) Pod入门与实战

1 K8s核心资源Pod 1.1 Pod是什么&#xff1f; 官方文档&#xff1a;Pod | Kubernetes Pod 是 Kubernetes&#xff08;k8s&#xff09;中最小的部署与调度单元&#xff0c;并非直接运行容器&#xff0c;而是对一个或多个 “紧密关联” 容器的封装。 核心特点可简单总结为 3 …