MyBatis 进阶:连接池、动态 SQL 与多表关联查询

MyBatis 作为一款灵活的持久层框架,除了基础的 CRUD 操作,还提供了连接池管理、动态 SQL 以及多表关联查询等高级特性。本文将从连接池原理出发,深入讲解动态 SQL 的常用标签,并通过实例演示一对多、多对多等复杂关联查询的实现,帮助你掌握 MyBatis 的进阶用法。

一、MyBatis 连接池:提升数据库交互性能

连接池是存储数据库连接的容器,它的核心作用是避免频繁创建和关闭连接,从而减少资源消耗、提高程序响应速度。在 MyBatis 中,连接池的配置通过dataSource标签的type属性实现,支持三种类型的连接池:

1. 连接池类型详解

  • POOLED:使用 MyBatis 内置的连接池
    MyBatis 会维护一个连接池,当需要连接时从池中获取,使用完毕后归还给池,避免频繁创建连接。适用于高并发场景,是开发中最常用的类型。

    配置示例:

    <dataSource type="POOLED"><property name="driver" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/>
    </dataSource>
    
  • UNPOOLED:不使用连接池
    每次执行 SQL 时都会创建新的连接,使用后直接关闭。适用于低并发场景,性能较差,一般仅用于简单测试。

    配置示例:

    <dataSource type="UNPOOLED"><!-- 同POOLED的属性配置 -->
    </dataSource>
    
  • JNDI:依赖容器的连接池
    由 Web 容器(如 Tomcat)提供连接池管理,MyBatis 仅负责从容器中获取连接。适用于Java EE 环境,需在容器中提前配置连接池。

    配置示例:

    <dataSource type="JNDI"><property name="data_source" value="java:comp/env/jdbc/mybatis_db"/>
    </dataSource>
    

2. 连接池的优势

  • 资源复用:连接池中的连接可重复使用,减少创建连接的开销;
  • 响应速度:提前创建连接,避免 SQL 执行时的连接创建延迟;
  • 并发控制:通过最大连接数限制,防止数据库因连接过多而崩溃。

二、动态 SQL:灵活拼接 SQL 语句

在实际开发中,查询条件往往是动态变化的(如多条件筛选、批量操作等)。MyBatis 的动态 SQL 标签可以优雅地解决 SQL 语句拼接问题,避免手动拼接导致的语法错误和 SQL 注入风险。

1. <if>标签:条件判断

<if>标签用于根据参数值动态生成 SQL 片段,常用来处理多条件查询。

示例场景:根据用户名和性别查询用户(参数非空时才添加条件)。

  • UserMapper 接口

    public interface UserMapper {// 条件查询用户List<User> findByWhere(User user);
    }
    
  • UserMapper.xml 配置

    <select id="findByWhere" parameterType="user" resultType="user">select * from user<where><!-- 当username非空且非空字符串时,添加条件 --><if test="username != null and username != ''">and username like #{username}</if><!-- 当sex非空且非空字符串时,添加条件 --><if test="sex != null and sex != ''">and sex = #{sex}</if></where>
    </select>
    
  • 测试代码

    @Test
    public void testFindByWhere() {User user = new User();user.setUsername("%zz%"); // 模糊查询包含"zz"的用户名user.setSex("m");List<User> list = userMapper.findByWhere(user);// 遍历结果...
    }
    

说明test属性中的表达式用于判断参数是否有效,where标签会自动处理多余的andor,避免 SQL 语法错误。

 

2. <foreach>标签:遍历集合

<foreach>标签用于遍历集合或数组,常用来处理in查询或批量操作。

场景 1:查询 ID 在指定集合中的用户(in查询)
  • User 实体类:添加存储 ID 集合的属性

    public class User {private List<Integer> ids; // 存储多个ID// 省略getter、setter
    }
    
  • UserMapper 接口

    List<User> findByIds(User user);
    
  • UserMapper.xml 配置

    <select id="findByIds" parameterType="user" resultType="user">select * from user<where><!-- collection:集合属性名(此处为ids)open:SQL片段开头close:SQL片段结尾separator:元素分隔符item:遍历的元素别名--><foreach collection="ids" open="id in (" separator="," close=")" item="id">#{id}</foreach></where>
    </select>
    
  • 测试代码

    @Test
    public void testFindByIds() {User user = new User();List<Integer> ids = new ArrayList<>();ids.add(1);ids.add(2);ids.add(3);user.setIds(ids);List<User> list = userMapper.findByIds(user); // 查询ID为1、2、3的用户
    }
    

 

场景 2:批量查询(or条件)

如需生成id = 1 or id = 2 or id = 3形式的 SQL,只需调整<foreach>openseparator

<foreach collection="ids" open="id = " separator="or id = " item="id">#{id}
</foreach>

3. <sql><include>标签:SQL 片段复用

对于频繁使用的 SQL 片段(如查询字段、表名等),可以用<sql>标签定义,再通过<include>标签引用,减少代码冗余。

示例:复用查询用户的 SQL 片段。

  • UserMapper.xml 配置
    <!-- 定义SQL片段 -->
    <sql id="userColumns">id, username, birthday, sex, address
    </sql><!-- 引用SQL片段 -->
    <select id="findAll" resultType="user">select <include refid="userColumns"/> from user
    </select>
    

说明id为片段唯一标识,refid指定要引用的片段 ID,适用于多表查询中重复的字段列表。

三、一对多查询:用户与账户的关联

在实际业务中,表之间往往存在关联关系(如用户与账户:一个用户可以有多个账户)。MyBatis 通过<collection>标签处理一对多关联查询。

1. 表结构与实体类设计

  • 用户表(user):存储用户基本信息(id、username 等);
  • 账户表(account):存储账户信息,通过uid关联用户表(多对一关系)。

实体类设计

  • Account 类(多对一:一个账户属于一个用户):

    public class Account implements Serializable {private Integer id;private Integer uid; // 关联用户IDprivate Double money;// 关联的用户对象private User user; // 省略getter、setter
    }
    
  • User 类(一对多:一个用户有多个账户):

    public class User implements Serializable {private Integer id;private String username;// 关联的账户列表private List<Account> accounts; // 省略getter、setter
    }
    

2. 多对一查询(账户关联用户)

查询所有账户,并关联查询所属用户的信息。

  • AccountMapper 接口

    public interface AccountMapper {List<Account> findAll();
    }
    
  • AccountMapper.xml 配置

    <select id="findAll" resultMap="accountMap"><!-- 关联查询账户和用户 -->select a.*, u.username, u.address from account aleft join user u on a.uid = u.id
    </select><!-- 定义结果映射 -->
    <resultMap id="accountMap" type="account"><id property="id" column="id"/><result property="uid" column="uid"/><result property="money" column="money"/><!-- 关联用户对象(多对一) --><association property="user" javaType="user"><result property="username" column="username"/><result property="address" column="address"/></association>
    </resultMap>
    

说明<association>标签用于映射关联的单个对象,javaType指定对象类型。

 

3. 一对多查询(用户关联账户)

查询所有用户,并关联查询其名下的所有账户。

  • UserMapper 接口

    public interface UserMapper {// 查询用户及关联的账户List<User> findOneToMany();
    }
    
  • UserMapper.xml 配置

    <select id="findOneToMany" resultMap="userAccountMap">select u.*, a.id as aid, a.money from user uleft join account a on u.id = a.uid
    </select><resultMap id="userAccountMap" type="user"><id property="id" column="id"/><result property="username" column="username"/><result property="birthday" column="birthday"/><result property="sex" column="sex"/><result property="address" column="address"/><!-- 关联账户列表(一对多) --><collection property="accounts" ofType="account"><id property="id" column="aid"/> <!-- 注意别名避免与用户ID冲突 --><result property="money" column="money"/></collection>
    </resultMap>
    

说明<collection>标签用于映射关联的集合对象,ofType指定集合中元素的类型。

 

四、多对多查询:用户与角色的关联

多对多关系需要通过中间表实现(如用户与角色:一个用户可拥有多个角色,一个角色可分配给多个用户,通过user_role表关联)。

1. 表结构与实体类设计

  • 角色表(role):存储角色信息(id、role_name 等);
  • 中间表(user_role):通过uidrid关联用户表和角色表。

实体类设计

  • Role 类(多对多:一个角色包含多个用户):
    public class Role implements Serializable {private Integer id;private String role_name;private String role_desc;// 关联的用户列表private List<User> users;// 省略getter、setter
    }
    

2. 多对多查询实现

查询所有角色,并关联查询拥有该角色的用户信息。

  • RoleDao 接口

    public interface RoleDao {List<Role> findAll();
    }
    
  • RoleDao.xml 配置

    <select id="findAll" resultMap="roleMap">SELECT r.*, u.id as user_id, u.username FROM role rJOIN user_role ur ON r.id = ur.RIDJOIN user u ON u.id = ur.UID
    </select><resultMap type="role" id="roleMap"><id property="id" column="id"/><result property="role_name" column="role_name"/><result property="role_desc" column="role_desc"/><!-- 关联用户列表(多对多) --><collection property="users" ofType="user"><id property="id" column="user_id"/> <!-- 别名避免与角色ID冲突 --><result property="username" column="username"/></collection>
    </resultMap>
    
  • 测试代码

    @Test
    public void testFindAllRoles() {List<Role> roles = roleDao.findAll();for (Role role : roles) {System.out.println("角色:" + role.getRole_name());System.out.println("关联用户:" + role.getUsers());}
    }
    

说明:多对多查询本质是双向的一对多查询,通过中间表建立关联,同样使用<collection>标签映射集合对象。

 

 

五、MyBatis 延迟加载策略

1. 延迟加载的概念

延迟加载(Lazy Loading)是一种数据库查询优化策略,其核心思想是:仅在需要使用关联数据时才进行实际查询。与立即加载(Eager Loading)相比,延迟加载避免了不必要的数据库访问,提高了系统性能。

对比示例(一对多关系)

  • 立即加载:查询用户时,同时加载该用户的所有账户信息(即使后续可能不使用账户数据)。
  • 延迟加载:查询用户时,仅加载用户基本信息;当程序调用user.getAccounts()时,才会触发账户数据的查询。

2. 应用场景选择

场景加载策略示例
多对一关系立即加载查询账户时,同时加载所属用户
一对多 / 多对多延迟加载查询用户时,暂不加载账户信息

3. 多对一延迟加载实现

(1)配置文件示例
<!-- AccountMapper.xml -->
<resultMap type="Account" id="accountMap"><id column="id" property="id"/><result column="uid" property="uid"/><result column="money" property="money"/><!-- 配置延迟加载:通过select属性指定关联查询方法 --><association property="user" javaType="User" select="com.qcbyjy.mapper.UserMapper.findById" column="uid"><id column="id" property="id"/><result column="username" property="username"/></association>
</resultMap>
(2)核心配置参数
<!-- SqlMapConfig.xml -->
<settings><!-- 开启延迟加载功能 --><setting name="lazyLoadingEnabled" value="true"/><!-- 禁用积极加载(默认false,按需加载) --><setting name="aggressiveLazyLoading" value="false"/>
</settings>

 测试方法

@Testpublic void testFindAll1() throws IOException {// 先加载主配置文件,加载到输入流中InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");// 创建SqlSessionFactory对象,创建SqlSession对象SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);// 创建SqlSession对象SqlSession session = factory.openSession();// 获取代理对象AccountMapper mapper = session.getMapper(AccountMapper.class);// 1. 查询主对象(账户)List<Account> accounts = mapper.findAll();System.out.println("===== 主查询已执行 =====");// 2. 遍历账户,但不访问关联的用户for (Account account : accounts) {System.out.println("账户ID:" + account.getId() + ",金额:" + account.getMoney());}System.out.println("===== 未访问关联对象 =====");// 3. 首次访问关联的用户for (Account account : accounts) {System.out.println("===== 开始访问用户 =====");System.out.println("用户名:" + account.getUser().getUsername()); // 触发懒加载System.out.println("===== 访问用户结束 =====");}// 关闭资源session.close();inputStream.close();}

执行findAll()时,日志仅输出账户表的查询 SQL 。遍历账户但不访问用户时,无新的 SQL 输出

 

首次访问account.getUser()时,日志输出用户表的查询 SQL(按需加载)

 

(3)工作原理

当执行account.getUser()时,MyBatis 会:

  1. 检查lazyLoadingEnabled是否为true
  2. 通过select属性调用UserMapper.findById(uid)方法;
  3. 将结果封装到Account.user属性中。

4. 一对多延迟加载实现

(1)配置文件示例
<!-- UserMapper.xml -->
<resultMap type="User" id="userMap"><id column="id" property="id"/><result column="username" property="username"/><!-- 配置延迟加载:集合属性 --><collection property="accounts" ofType="Account" select="com.qcbyjy.mapper.AccountMapper.findByUid" column="id"><id column="id" property="id"/><result column="money" property="money"/></collection>
</resultMap>
(2)延迟加载触发时机
List<User> users = userMapper.findAll();
for (User user : users) {// 调用getAccounts()时触发延迟查询System.out.println(user.getAccounts()); 
}

测试代码:

    @Testpublic void testFindAllq() throws Exception {// 调用方法List<User> list = mapper.findAll();for (User user : list) {System.out.println(user.getUsername());System.out.println(user.getAccounts());System.out.println("==============");}}

 

5. 延迟加载注意事项

  1. N+1 查询问题:延迟加载可能导致 N+1 查询(主查询 1 次,关联查询 N 次),需结合二级缓存优化。
  2. Session 生命周期:延迟加载需确保关联查询时SqlSession未关闭(可通过openSession(true)保持会话)。
  3. 序列化问题:延迟加载的对象在序列化时可能丢失代理状态,需通过<setting name="serializationFactory" value="..."/>配置。

七、MyBatis 缓存机制

1. 缓存的基本概念

缓存是一种内存临时存储技术,用于减少数据库访问次数,提高系统响应速度。适合缓存的数据特点:

  • 频繁查询但很少修改;
  • 数据一致性要求不高;
  • 数据量适中且访问频率高。

2. 一级缓存(SqlSession 级缓存)

(1)缓存原理
  • 作用域:每个SqlSession独享一个缓存实例;
  • 存储结构:底层使用PerpetualCache(基于HashMap实现);
  • 生命周期:与SqlSession一致,session.close()后缓存销毁。
(2)缓存验证示例
@Test
public void testFirstLevelCache() {try (SqlSession session = sqlSessionFactory.openSession()) {UserMapper mapper = session.getMapper(UserMapper.class);// 第一次查询:触发SQLUser user1 = mapper.findById(1);// 第二次查询:命中缓存User user2 = mapper.findById(1);System.out.println(user1 == user2); // 输出true(同一对象)}
}

 

(3)缓存失效场景

以下操作会导致一级缓存清空:

  • session.clearCache():手动清空缓存;
  • session.commit()/session.rollback():事务提交或回滚;
  • 执行insert/update/delete操作(任何数据变更)。

3. 一级缓存源码分析

核心源码位于BaseExecutor类:

// BaseExecutor.java
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {BoundSql boundSql = ms.getBoundSql(parameter);// 1. 创建缓存KeyCacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);// 2. 查询一级缓存return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {// 从本地缓存中获取List<E> list = localCache.getObject(key);if (list != null) {// 缓存命中handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);return list;} else {// 缓存未命中,查询数据库return queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);}
}

4. 一级缓存的应用建议

  • 优势:无需额外配置,自动生效,适用于单次会话内的重复查询;
  • 局限:无法跨SqlSession共享,对长事务可能导致数据不一致;
  • 最佳实践
    • 避免在同一SqlSession内进行重复查询;
    • 及时提交事务或关闭SqlSession以释放缓存资源。

八、延迟加载与一级缓存的协同工作

当延迟加载与一级缓存结合时,需注意:

  1. 关联查询缓存:延迟加载的关联对象(如user.getAccounts())会被存入一级缓存;
  2. 会话隔离:不同SqlSession的延迟加载结果相互独立;
  3. 数据一致性:若主对象已缓存,关联对象的变更可能无法实时反映。

mysql缓存存的是语句,稍有修改就会更新缓存。一级缓存,输出的对象是同一个,二级缓存输出不同是因为通过序列化组装了两

一、一级缓存(SqlSession 级缓存)—— 同一个对象实例

1. 缓存本质与作用范围

一级缓存是 SqlSession 私有 的本地缓存,MyBatis 默认开启。在同一个 SqlSession 内,只要查询条件、SQL 语句相同,MyBatis 会直接从缓存取结果,不会重复访问数据库。

2. “输出对象是同一个” 的原因
  • 当执行查询时,MyBatis 会先检查一级缓存:若命中缓存,直接返回 缓存中存储的对象引用 。
  • 也就是说,多次查询拿到的是同一个 Java 对象实例(JVM 中同一个内存地址的对象 )。例如:
try (SqlSession session = sqlSessionFactory.openSession()) {UserMapper mapper = session.getMapper(UserMapper.class);// 第一次查询,从数据库加载,存入一级缓存User user1 = mapper.getUserById(1);  // 第二次查询,命中一级缓存,直接返回 user1 的引用User user2 = mapper.getUserById(1);  System.out.println(user1 == user2); // 输出 true,是同一个对象实例
}

 

3. 缓存失效场景

当执行 updateinsertdeletecommitclose 等操作时,一级缓存会被清空 。后续查询会重新从数据库加载数据,存入新的对象实例到缓存。

二、二级缓存(Mapper 级缓存)—— 不同对象实例(因序列化 / 反序列化)

1. 缓存本质与作用范围

二级缓存是 Mapper 作用域 的缓存,需手动开启(在 Mapper XML 或注解中配置 )。它可以在多个 SqlSession 间共享,底层通常依赖序列化 / 反序列化机制存储数据 。

2. “输出不同对象” 的原因
  • 二级缓存存储的是 对象的序列化数据 (如 Java 对象先序列化为字节流,再存入缓存 )。
  • 当不同 SqlSession 查询命中二级缓存时,MyBatis 会 反序列化 缓存中的字节流,重新生成一个新的 Java 对象实例 。例如:
// 开启二级缓存后,不同 SqlSession 测试
try (SqlSession session1 = sqlSessionFactory.openSession()) {UserMapper mapper1 = session1.getMapper(UserMapper.class);User user1 = mapper1.getUserById(1); session1.commit(); // 提交后,数据可能同步到二级缓存(取决于配置)
}try (SqlSession session2 = sqlSessionFactory.openSession()) {UserMapper mapper2 = session2.getMapper(UserMapper.class);User user2 = mapper2.getUserById(1); // 命中二级缓存,反序列化生成新对象System.out.println(user1 == user2); // 输出 false,是不同对象实例
}

 

3. 二级缓存的核心特点
  • 跨 SqlSession 共享:多个 SqlSession 可共用 Mapper 级的缓存数据;
  • 序列化存储:缓存数据需实现 Serializable 接口,存储和读取时会经历序列化 / 反序列化,因此每次命中缓存会生成新对象;
  • 缓存策略灵活:可配置 eviction(回收策略,如 LRU、FIFO )、flushInterval(刷新间隔 )、readOnly(是否只读 )等。

三、一、二级缓存的核心差异对比

对比项一级缓存二级缓存
作用范围SqlSession 私有Mapper 作用域(跨 SqlSession 共享)
对象实例同一对象引用反序列化生成新对象
开启方式默认开启需手动配置(<cache> 标签或注解)
存储机制直接存对象引用存序列化后的字节流
数据一致性依赖 SqlSession 内操作,易维护需注意多表关联、更新同步问题

四、实际开发中的注意事项

  1. 一级缓存的 “隐式风险”
    若在同一个 SqlSession 内,先查询再更新数据,由于一级缓存未及时清理(需手动 commit/close 触发 ),可能拿到旧数据。建议在增删改后,及时 commit 或 close SqlSession,保证缓存与数据库一致。

  2. 二级缓存的 “使用前提”
    启用二级缓存时,实体类必须实现 Serializable 接口(否则序列化报错 );同时,若涉及多表关联查询,需注意缓存的更新策略(比如某张表数据变化后,关联的 Mapper 缓存需及时刷新 )。

  3. 缓存的合理选择

    • 一级缓存适合短生命周期的 SqlSession(如单次请求内的多次查询 );
    • 二级缓存适合查询频率高、数据变化少的场景(如系统字典表 ),但需谨慎处理数据更新后的缓存同步。

简单来说,一级缓存是 “同一个对象复用”,二级缓存是 “序列化后重新组装对象”,这种差异由它们的作用范围和存储机制决定。开发中根据业务场景合理利用缓存,既能提升性能,又能避免数据一致性问题~ 若你在实际配置或调试缓存时遇到具体问题(比如二级缓存不生效、序列化报错 ),可以接着展开说场景帮你分析 。

 

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

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

相关文章

反射型跨站点脚本(XSS)漏洞中网络安全防火墙(WAF)被绕过进行内容植入与远程劫持机制分析

在一次安全测试中&#xff0c;我发现目标站点在错误处理页面对用户输入的查询参数名未做任何转义&#xff0c;当参数名中包含 <script> 标签时&#xff0c;页面会原样渲染并执行其中的 JavaScript。本文将从实战角度&#xff0c;详细讲解如何定位该反射型 XSS 漏洞、通过…

RAG实战指南 Day 15:多语言与领域特定嵌入技术

【RAG实战指南 Day 15】多语言与领域特定嵌入技术 引言 欢迎来到"RAG实战指南"系列的第15天&#xff01;今天我们将深入探讨多语言与领域特定嵌入技术——这是构建全球化、专业化RAG系统的关键技术。在现实业务场景中&#xff0c;我们经常需要处理多种语言的文档&a…

无铅PCB和无卤pcb有什么区别?

在电子制造领域&#xff0c;环保法规的升级催生了多种特殊工艺的PCB产品。其中&#xff0c;无铅PCB与无卤PCB作为两大主流方向&#xff0c;虽同属绿色制造范畴&#xff0c;却在技术路径与应用场景上存在本质差异。环保指向的根本区别无铅PCB的核心在于焊接材料的革新。传统PCB采…

基于51单片机的贪吃蛇游戏Protues仿真设计

目录 1 系统设计目的 2 系统实现功能 3 系统硬件设计 3.1系统设计框图 3.2 液晶显示模块LCD12864 3.3 按键输入模块 3.4 时钟电路和复位电路 4 系统软件设计 4.1系统软件流程 4.2 游戏引擎模块程序设计 4.3 显示模块程序设计 4.4 输入处理模块程序设计 5 系统仿真…

HTML+CSS

一、HTML相关内容- <img> 标签&#xff1a;- 用于在网页中嵌入图像&#xff0c; src 属性指定图像的路径&#xff0c;可以是绝对路径&#xff08;如 D:\Git\java115_java116\课堂代码\前端代码\pic\cat.jpg &#xff09;、相对路径&#xff08;如 ./pic/cat.jpg &#x…

基于 Gitlab、Jenkins与Jenkins分布式、SonarQube 、Nexus 的 CiCd 全流程打造

前言 在当今数字化飞速发展的时代&#xff0c;软件开发与交付的效率和质量成为了企业竞争的关键要素。为了满足市场对软件快速迭代和高质量交付的需求&#xff0c;越来越多的企业开始探索和实践持续集成与持续交付&#xff08;CI/CD&#xff09;的开发模式。而 GitLab、Jenkin…

[密码学实战]密评相关题库解析

[密码学实战]密评相关题库解析 一、背景 依据《密码法》第二十二条&#xff0c;关键信息基础设施&#xff08;关基&#xff09;运营者必须开展商用密码应用安全性评估&#xff0c;且需定期进行&#xff08;不少于每年一次&#xff09;。 二、核心解析 2.1 测评标准框架&#x…

谷歌开源库gflags详细说明

目录 一.gflags 介绍 二.gflags安装 三.gflags使用 1.包含头文件 2.定义参数 3.访问参数 4.不同文件访问参数 5.初始化所有参数 6.运行参数设置 7.配置文件的使用 8.特殊参数标识 四.总结 一.gflags 介绍 gflags 是 Google 开发的一个开源库&#xff0c;用于 C 应用…

Python爬虫实战:研究XlsxWriter 库相关技术

1. 研究背景与意义 1.1 网络爬虫技术价值 网络爬虫作为数据采集的核心工具,在金融、医疗、教育等领域发挥关键作用。据 Statista 数据显示,2025 年全球大数据市场规模预计达 3250 亿美元,高效的数据获取能力成为企业核心竞争力。Python 以其 80% 的市场占有率成为爬虫开发首…

ThreadLocal内部结构深度解析(Ⅰ)

目录 使用ThreadLocal 例子 内部结构分析 源码解析 图示详解 ThreadLocal是Java中一个非常重要且常用的线程局部变量工具类&#xff0c;它使得每个线程可以独立地持有自己的变量副本&#xff0c;而不是共享变量&#xff0c;解决了多线程环境下变量共享的线程安全问题。下面我…

Python 数据挖掘之数据探索

在数据挖掘的流程中&#xff0c;数据探索是非常关键的第一步&#xff0c;它能帮助我们深入了解数据的特点&#xff0c;为后续的预处理和模型构建打下坚实的基础。我们主要围绕四个方面展开&#xff1a;数据对象与特征、数据统计描述、数据可视化以及相关性和相似性度量。一、数…

高并发点赞场景Synchronized、AtomicLong、LongAdder 和 LongAccumulator性能分析

在高并发点赞场景中&#xff0c;我们需要一个高效、线程安全的计数器来记录点赞数。synchronized、AtomicLong、LongAdder 和 LongAccumulator 都是 Java 中用于实现原子操作的类&#xff0c;但它们的性能在高并发下差异显著。性能主要取决于线程竞争程度&#xff1a;竞争越高&…

postgreSQL的sql语句

目录 一&#xff1a;前提准备1.postgreSQL的安装可以参考我下面一片文章&#xff1a; 二&#xff1a;SQL语句 1.相同点&#xff1a;支持标准sql类型 2.参考详细学习地址&#xff1a; 3.postgresql与mysql的不同点 一&#xff1a;前提准备 1.postgreSQL的安装可以参考我下面…

vue3 JavaScript 数据累加 reduce

在Vue 3中&#xff0c;你可以使用JavaScript的reduce方法来处理数据累加。reduce方法通常用在数组上&#xff0c;它将数组中的每个元素通过一个累加器函数&#xff08;accumulator&#xff09;从左到右累积&#xff0c;最终生成一个单一的值。这在计算总和、累加值等场景中非常…

史上最清楚!读者,写者问题(操作系统os)

读者-写者问题是另一个里程碑式的同步互斥问题。它比生产者-消费者更复杂&#xff0c;因为它引入了不对称的访问权限&#xff1a;读者和读者之间是共享的&#xff0c;但写者和任何人&#xff08;包括读者和其他写者&#xff09;之间都是互斥的。我们用一个生动的比喻来解析这个…

使用Starrocks替换Clickhouse的理由

背景 Starrocks和clickhouse都是非常优秀的OLAP数据库&#xff0c;那么什么情况下使用clickhouse&#xff0c;什么场景下使用starrocks呢&#xff0c;本文就简单列举一下他们的优缺点 理由 首先两者都是列存储&#xff0c;并且都实现了列压缩&#xff0c;所以从存储中两者的压缩…

Mybatis 两级缓存可能导致的问题

Mybatis 两级缓存可能导致的问题两级缓存简介一级缓存 localCache效果开关二级缓存两级缓存可能导致的问题分布式环境下查询到过期数据事务隔离级别失效读已提交失效读未提交失效总结两级缓存简介 一级缓存 localCache 效果 一级缓存是 session 或者说事务级别的&#xff0c…

vue3+uniapp 使用vue-plugin-hiprint中实现打印效果

前言&#xff1a; vue3uniapp 使用vue-plugin-hiprint中实现打印效果 官网地址&#xff1a;gitee https://gitee.com/ccsimple/vue-plugin-hiprinthttps://gitee.com/ccsimple/vue-plugin-hiprint 实现效果&#xff1a; 预览打印内容&#xff1a; 实现步骤&#xff1a; 1、安…

【elementUI踩坑记录】解决 el-table 固定列 el-table__fixed 导致部分滚动条无法拖动的问题

目录一、问题背景二、 问题现象三、核心原因四、解决办法增强方案&#x1f680;写在最后一、问题背景 在使用 Element UI 的 el-table 组件时&#xff0c;固定列功能虽然实用&#xff0c;但会引发滚动条交互问题&#xff1a; 固定列区域悬浮显示滚动条但无法正常拖动滚动条 …

【机器人编程基础】python文件的打开和关闭

文件的打开和关闭 在Python中,文件操作是一项基本而重要的任务,涉及到打开、读取、写入、关闭文件等操作。正确地管理文件对于数据持久化、输入输出处理等至关重要。下面将详细解释如何在Python中打开和关闭文件,并提供相应的代码示例。 文件打开 在Python中,可以使用内…