MyBatis 深度解析:高效 Java 持久层框架实践指南(基于 3.5.10)

一、MyBatis 核心架构与设计哲学

MyBatis 作为半自动 ORM 框架,核心设计目标是在灵活性开发效率之间取得平衡。与 Hibernate 等全自动 ORM 框架不同,MyBatis 允许开发者完全控制 SQL 编写,同时通过映射机制减少重复代码,特别适合复杂业务场景和性能敏感的系统。

1.1 核心优势对比

特性传统 JDBC全自动 ORM(如 Hibernate)MyBatis
SQL 控制粒度细粒度(完全手动)粗粒度(自动生成 SQL)中细粒度(手动编写 + 映射)
学习成本高(需处理连接 / 结果集)高(需掌握 ORM 规则)中(聚焦 SQL 与映射)
性能优化空间低(依赖框架优化)高(可针对性优化 SQL)
适用场景底层工具开发简单 CRUD 业务复杂查询 / 性能敏感系统

1.2 架构核心组件

MyBatis 的核心流程围绕以下组件展开:

  1. SqlSessionFactory:通过mybatis-config.xml加载配置,创建SqlSession实例。
    • 包含数据源(DataSource)、事务管理器(TransactionManager)、插件(Plugins)等全局配置。
  2. SqlSession:提供操作数据库的接口(如selectOneinsert),维护一级缓存。
  3. Executor:执行 SQL 的核心引擎,支持三种模式:
    • Simple:默认模式,每次执行 SQL 创建新 Statement。
    • Reuse:重用 Statement 对象(适用于相同 SQL 多次执行)。
    • Batch:批量执行 SQL(适用于批量插入 / 更新)。
  4. MappedStatement:封装单个 SQL 语句的映射信息(如idSQL语句参数类型结果映射)。

二、基础配置与快速入门

2.1 依赖管理最佳实践

Maven 标准配置
<dependencies><!-- MyBatis核心 --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.10</version></dependency><!-- 数据库驱动(建议与数据库版本严格匹配) --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version></dependency><!-- 连接池(推荐HikariCP或Druid) --><dependency><groupId>com.zaxxer</groupId><artifactId>HikariCP</artifactId><version>5.0.1</version></dependency><!-- 与Spring整合时添加 --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>2.0.7</version></dependency>
</dependencies>

2.2 核心配置文件详解

<!-- mybatis-config.xml -->
<configuration><!-- 全局设置 --><settings><setting name="mapUnderscoreToCamelCase" value="true"/> <!-- 驼峰命名自动映射 --><setting name="lazyLoadingEnabled" value="true"/> <!-- 开启延迟加载 --><setting name="multipleResultSetsEnabled" value="false"/> <!-- 禁止多结果集(安全考虑) --></settings><!-- 环境配置(支持多环境切换) --><environments default="dev"><environment id="dev"><transactionManager type="JDBC"/> <!-- 使用JDBC事务 --><dataSource type="POOLED"> <!-- 池化数据源 --><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mydb?useSSL=false&amp;serverTimezone=UTC"/><property name="username" value="root"/><property name="password" value="123456"/><!-- HikariCP特有配置(池化数据源建议显式配置) --><property name="maxPoolSize" value="10"/><property name="minIdle" value="2"/></dataSource></environment></environments><!-- 映射器注册(推荐使用classpath*:mapper/**Mapper.xml通配符) --><mappers><mapper resource="mapper/UserMapper.xml"/><mapper class="com.example.mapper.OrderMapper"/> <!-- 注解映射器注册方式 --></mappers>
</configuration>

2.3 初始化与核心操作

// 初始化SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);// 获取SqlSession(默认自动提交为false)
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {UserMapper mapper = sqlSession.getMapper(UserMapper.class);// 单条查询User user = mapper.selectUserById(1L);// 插入数据(需手动提交事务)User newUser = new User("Alice", 25);mapper.insertUser(newUser);sqlSession.commit(); // 提交事务
}

三、SQL 映射深度实践

3.1 XML 映射与动态 SQL

基础 CRUD 映射
<mapper namespace="com.example.mapper.UserMapper"><!-- 查询(使用resultType自动映射简单对象) --><select id="selectUserById" resultType="User">SELECT id, user_name AS name, user_age AS age FROM t_user WHERE id = #{id, jdbcType=BIGINT}</select><!-- 插入(使用useGeneratedKeys获取自增主键) --><insert id="insertUser" useGeneratedKeys="true" keyProperty="id">INSERT INTO t_user (user_name, user_age)VALUES (#{name, jdbcType=VARCHAR}, #{age, jdbcType=INTEGER})</insert><!-- 更新(动态SET子句) --><update id="updateUser">UPDATE t_user<set><if test="name != null">user_name = #{name},</if><if test="age != null">user_age = #{age},</if>update_time = NOW()</set>WHERE id = #{id}</update>
</mapper>
复杂动态 SQL 场景
<select id="searchUsers" resultType="User">SELECT * FROM t_user<where><!-- 模糊查询(注意%的拼接方式) --><if test="name != null and name != ''">user_name LIKE CONCAT('%', #{name}, '%')</if><!-- 范围查询 --><if test="minAge != null">AND user_age &gt;= #{minAge}</if><!-- 排序(通过OGNL表达式安全拼接字段) --><if test="orderByColumn in {'name', 'age', 'id'}">ORDER BY ${orderByColumn} ${orderByDirection}</if></where><!-- 分页(建议使用PageHelper插件) --><if test="pageable != null">LIMIT #{pageable.offset}, #{pageable.pageSize}</if>
</select>

3.2 注解映射高级用法

public interface UserMapper {// 单参数查询(自动识别参数名)@Select("SELECT * FROM t_user WHERE id = #{id}")User selectById(Long id);// 多参数查询(必须使用@Param注解)@Select("SELECT * FROM t_user WHERE user_name = #{name} AND user_age = #{age}")User selectByNameAndAge(@Param("name") String name, @Param("age") Integer age);// 插入返回主键(通过@Options配置)@Insert("INSERT INTO t_user (user_name, user_age) VALUES (#{name}, #{age})")@Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")int insert(@Param("user") User user);// 动态SQL注解(结合SqlProvider)@SelectProvider(type = UserSqlProvider.class, method = "buildSearchSql")List<User> search(@Param("query") UserQuery query);
}// SQLProvider类(动态拼接SQL)
public class UserSqlProvider {public String buildSearchSql(UserQuery query) {SQL sql = new SQL();sql.SELECT("id, user_name, user_age");sql.FROM("t_user");if (StringUtils.isNotBlank(query.getName())) {sql.WHERE("user_name LIKE CONCAT('%', #{query.name}, '%')");}if (query.getMinAge() != null) {sql.WHERE("user_age >= #{query.minAge}");}if (StringUtils.isNotBlank(query.getOrderBy())) {sql.ORDER_BY(query.getOrderBy());}return sql.toString();}
}

四、结果集映射与关联查询

4.1 基础结果映射

<!-- 使用resultMap显式映射(解决字段名与属性名不匹配) -->
<resultMap id="userResultMap" type="User"><id column="user_id" property="id" jdbcType="BIGINT"/> <!-- 主键映射 --><result column="user_name" property="name" jdbcType="VARCHAR"/><result column="user_age" property="age" jdbcType="INTEGER"/><result column="create_time" property="createTime" jdbcType="TIMESTAMP"/> <!-- 驼峰映射示例 -->
</resultMap><select id="selectUserWithMap" resultMap="userResultMap">SELECT user_id, user_name, user_age, create_time FROM t_user WHERE id = #{id}
</select>

4.2 关联对象映射

一对一关联(使用 association)
<resultMap id="userWithDeptResultMap" type="User"><id column="user_id" property="id"/><result column="user_name" property="name"/><!-- 关联部门对象(使用嵌套查询) --><association property="department" column="dept_id" javaType="Department" select="selectDepartmentById"/>
</resultMap><select id="selectUserWithDept" resultMap="userWithDeptResultMap">SELECT user_id, user_name, dept_id FROM t_user WHERE id = #{id}
</select><select id="selectDepartmentById" resultType="Department">SELECT dept_id, dept_name FROM t_department WHERE dept_id = #{deptId}
</select>
一对多关联(使用 collection)
<resultMap id="userWithRolesResultMap" type="User"><id column="user_id" property="id"/><result column="user_name" property="name"/><!-- 关联角色集合(使用嵌套结果) --><collection property="roles" ofType="Role" columnPrefix="role_"><id column="role_id" property="id"/><result column="role_name" property="name"/></collection>
</resultMap><select id="selectUserWithRoles" resultMap="userWithRolesResultMap">SELECT u.user_id, u.user_name, r.role_id, r.role_name FROM t_user u LEFT JOIN t_user_role ur ON u.user_id = ur.user_id LEFT JOIN t_role r ON ur.role_id = r.role_id WHERE u.id = #{id}
</select>

五、高级特性与性能优化

5.1 缓存机制深度解析

一级缓存(SqlSession 级别)
  • 作用范围:同一SqlSession内,默认开启,无法关闭。
  • 失效场景
    1. 调用sqlSession.clearCache()手动清空缓存。
    2. 执行insert/update/delete操作(自动清空缓存)。
二级缓存(Mapper 级别)
<!-- 在Mapper中开启二级缓存 -->
<cache eviction="LRU"        <!-- 淘汰策略:LRU(最近最少使用) -->flushInterval="60000"  <!-- 刷新间隔:60秒 -->size="512"            <!-- 缓存容量:512个对象 -->readOnly="true"/>      <!-- 是否只读:true(适合只读场景,性能更高) --><!-- 使用二级缓存(需在select语句中声明useCache="true") -->
<select id="selectUserById" resultType="User" useCache="true">SELECT * FROM t_user WHERE id = #{id}
</select>

注意事项

  • 二级缓存跨SqlSession共享,需确保 POJO 可序列化(实现Serializable接口)。
  • 建议仅在读多写少的场景使用二级缓存,频繁更新的表禁用缓存。

5.2 插件开发实战

自定义 SQL 执行监控插件
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class PerformanceInterceptor implements Interceptor {private static final Logger log = LoggerFactory.getLogger(PerformanceInterceptor.class);@Overridepublic Object intercept(Invocation invocation) throws Throwable {long start = System.currentTimeMillis();Object result = invocation.proceed();long cost = System.currentTimeMillis() - start;MappedStatement ms = (MappedStatement) invocation.getArgs()[0];String statementId = ms.getId();String sql = ms.getBoundSql(invocation.getArgs()[1]).getSql();if (cost > 1000) { // 记录耗时超过1秒的SQLlog.warn("Slow SQL executed: {} Cost: {}ms\n{}", statementId, cost, sql);}return result;}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}
}<!-- 在mybatis-config.xml中注册插件 -->
<plugins><plugin interceptor="com.example.performance.PerformanceInterceptor"><!-- 插件参数配置 --><property name="slowSqlThreshold" value="500"/> <!-- 慢SQL阈值(毫秒) --></plugin>
</plugins>

5.3 批量操作优化

批量插入(Batch 模式)
try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH)) {UserMapper mapper = sqlSession.getMapper(UserMapper.class);for (int i = 0; i < 1000; i++) {User user = new User("user_" + i, 20 + i);mapper.insertUser(user);if (i % 200 == 0) { // 每200条提交一次,避免内存溢出sqlSession.flushStatements();}}sqlSession.commit(); // 最终提交事务
}
批量更新(使用<foreach>标签)
<update id="batchUpdateStatus">UPDATE t_userSET status = #{status}WHERE id IN<foreach collection="idList" item="id" open="(" separator="," close=")">#{id}</foreach>
</update>

六、与 Spring 整合最佳实践

6.1 基于注解的整合配置

1. 配置类(替代 XML 配置)
@Configuration
@MapperScan(basePackages = "com.example.mapper") // 扫描Mapper接口
public class MyBatisConfig {@Bean@ConfigurationProperties(prefix = "spring.datasource") // 读取application.yml中的数据源配置public DataSource dataSource() {return new HikariDataSource();}@Beanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();factoryBean.setDataSource(dataSource);factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/**/*.xml")); // 加载Mapper XML文件// 配置MyBatis全局属性Configuration configuration = factoryBean.getObject().getConfiguration();configuration.setMapUnderscoreToCamelCase(true); // 开启驼峰映射configuration.setLogImpl(StdOutImpl.class); // 开发环境打印SQL日志factoryBean.setConfiguration(configuration);return factoryBean.getObject();}@Beanpublic DataSourceTransactionManager transactionManager(DataSource dataSource) {return new DataSourceTransactionManager(dataSource); // 配置事务管理器}
}

6.2 事务管理与 Spring 整合

声明式事务配置
@Service
public class UserService {private final UserMapper userMapper;private final OrderMapper orderMapper;@Autowiredpublic UserService(UserMapper userMapper, OrderMapper orderMapper) {this.userMapper = userMapper;this.orderMapper = orderMapper;}// 使用@Transactional声明事务(默认传播行为:REQUIRED)@Transactionalpublic void createUserAndOrder(User user, Order order) {userMapper.insertUser(user); // 插入用户order.setUserId(user.getId());orderMapper.insertOrder(order); // 插入订单(与用户操作在同一事务中)// 模拟异常回滚if (order.getAmount() <= 0) {throw new BusinessException("订单金额不能为负数");}}
}// 全局异常处理器(回滚事务)
@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(BusinessException.class)public ResponseEntity<String> handleBusinessException(BusinessException ex) {return ResponseEntity.badRequest().body(ex.getMessage());}
}
事务传播行为示例
@Service
public class TransactionService {@Autowiredprivate UserService userService;@Autowiredprivate OrderService orderService;// 外层事务(REQUIRED)@Transactionalpublic void outerTransaction() {userService.updateUser(new User(1L, "Updated")); // 新增事务?取决于内层配置try {orderService.createOrder(new Order(1L, -100)); // 内层事务抛出异常} catch (Exception e) {// 外层可捕获异常并决定是否回滚System.out.println("捕获内层异常,手动回滚");TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}}// 内层事务(REQUIRES_NEW:新建独立事务)@Transactional(propagation = Propagation.REQUIRES_NEW)public void createOrder(Order order) {orderMapper.insertOrder(order);if (order.getAmount() < 0) {throw new IllegalArgumentException("无效金额");}}
}

七、MyBatis 最佳实践

7.1 SQL 编写规范

避免的反模式
-- 反模式:使用SELECT *(性能差、耦合度高)
SELECT * FROM t_user-- 推荐:显式指定字段
SELECT id, user_name, user_age FROM t_user-- 反模式:子查询性能差
SELECT * FROM t_user WHERE id IN (SELECT user_id FROM t_order)-- 推荐:JOIN优化
SELECT u.* FROM t_user u JOIN t_order o ON u.id = o.user_id
动态 SQL 安全原则
<!-- 安全写法:使用预编译防止SQL注入 -->
<select id="findUserByName">SELECT * FROM t_user WHERE user_name = #{name}
</select><!-- 危险写法:直接拼接用户输入(仅用于可信场景) -->
<select id="sortUsers">SELECT * FROM t_user ORDER BY ${sortColumn} ${sortDirection}<!-- 必须确保sortColumn和sortDirection为预定义值 -->
</select>

7.2 性能优化策略

分页查询优化
<!-- 物理分页(推荐使用PageHelper插件) -->
<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>5.3.2</version>
</dependency>// 使用方式
PageHelper.startPage(pageNum, pageSize); // 开启分页
List<User> userList = userMapper.searchUsers(query);
PageInfo<User> pageInfo = new PageInfo<>(userList); // 获取分页信息
延迟加载与懒加载
<!-- 全局开启延迟加载 -->
<settings><setting name="lazyLoadingEnabled" value="true"/><setting name="aggressiveLazyLoading" value="false"/> <!-- 禁止激进加载(默认false) -->
</settings><!-- 关联查询使用延迟加载 -->
<association property="department" column="dept_id" javaType="Department" select="selectDepartmentById" lazy="true"/>
批量操作性能对比
操作方式单次插入 1000 条数据耗时优点缺点
单条插入~2000ms简单直接网络 IO 次数多
批量插入(XML)~200ms性能提升明显需要拼接 SQL
批量插入(Batch)~150ms最优性能需手动管理事务批次

八、常见问题与解决方案

8.1 参数绑定失效问题

现象

org.apache.ibatis.exceptions.PersistenceException: 
Error querying database. Cause: org.apache.ibatis.binding.BindingException: 
Parameter 'name' not found. Available parameters are [0, 1, param1, param2]

原因:多参数未使用@Param注解标识。
解决

// 错误写法(MyBatis无法识别参数名)
User selectUser(String name, Integer age);// 正确写法(显式指定参数名)
User selectUser(@Param("name") String name, @Param("age") Integer age);

8.2 结果映射类型不匹配

现象

org.apache.ibatis.type.TypeException: 
Could not set parameters for mapping: ParameterMapping{property='createTime', ...}
Cause: java.sql.SQLException: Invalid value for getLong() - '2023-10-01 12:00:00'

原因:数据库字段类型(如 TIMESTAMP)与 Java 属性类型(如String)不匹配。
解决

<!-- 显式指定JDBC类型 -->
<result column="create_time" property="createTime" jdbcType="TIMESTAMP" javaType="java.time.LocalDateTime"/>

8.3 二级缓存不生效

排查步骤

  1. 检查 Mapper 是否开启缓存:
    <cache/> <!-- 确保存在缓存声明 -->
    
  2. 确认select语句是否启用缓存:
    <select ... useCache="true"/> <!-- 默认为true,但需显式声明 -->
    
  3. 检查 POJO 是否实现Serializable接口:
    public class User implements Serializable { ... }
    
  4. 确认是否执行了insert/update/delete操作(会清空二级缓存)。

九、MyBatis 3.5 + 新特性

9.1 注解式动态 SQL 增强

// 使用@SqlSource声明动态SQL
public interface UserMapper {@Select({"<script>","SELECT * FROM t_user","<where>","<if test='name != null'>user_name LIKE CONCAT('%', #{name}, '%')</if>","<if test='age != null'>AND user_age &gt;= #{age}</if>","</where>","</script>"})List<User> search(@Param("name") String name, @Param("age") Integer age);
}

9.2 自动映射枚举类型

<!-- 全局配置枚举类型处理器 -->
<typeHandlers><typeHandler handler="org.apache.ibatis.type.EnumTypeHandler"/>
</typeHandlers>// 枚举类
public enum UserStatus {ACTIVE(1, "活跃"),INACTIVE(0, "禁用");private final int code;private final String desc;// 构造方法与getter
}// 映射使用
<result column="status" property="status" typeHandler="org.apache.ibatis.type.EnumTypeHandler"/>

十、总结与学习资源

10.1 核心价值

MyBatis 的核心竞争力在于可控性灵活性

  • 适合复杂业务场景(如多表关联查询、动态报表)。
  • 便于优化 SQL 性能(如索引优化、分页策略)。
  • 轻量级依赖(仅需 MyBatis 核心库,无额外框架侵入)。

10.2 学习路径建议

  1. 基础阶段:掌握 XML 映射、动态 SQL、结果集映射。
  2. 进阶阶段:深入缓存机制、插件开发、批量操作优化。
  3. 实战阶段:结合 Spring Boot/Spring Cloud 构建微服务,处理分布式事务场景。
  4. 源码阶段:阅读 MyBatis 核心类(如ExecutorStatementHandler),理解底层执行逻辑。

10.3 推荐资源

  • 官方文档:MyBatis 3 中文文档
  • 书籍:《MyBatis 从入门到精通》《Java 持久层技术实战》
  • 开源项目:MyBatis-Plus(增强工具包,简化 CRUD)、TkMapper(通用 Mapper 工具)。

结语

MyBatis 通过将 SQL 编写与业务逻辑解耦,在保持开发效率的同时提供了极高的性能优化空间,是企业级应用中持久层的首选方案。开发者需在实际项目中积累 SQL 优化经验,结合业务场景合理使用缓存、批量操作和关联映射,以充分发挥 MyBatis 的优势。未来可进一步关注 MyBatis 与响应式编程(如 Reactive SQL)的整合,以及云原生场景下的持久层解决方案。

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

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

相关文章

二叉树(二)

98.验证二叉树 中序遍历二叉树&#xff0c;每次遍历存下当前节点的值&#xff0c;遍历到下一个节点比较&#xff0c;根据二叉搜索树的特性&#xff0c;左<中<右有&#xff1a; 如果当前值小于或等于上一个的值&#xff0c;说明不是二叉搜索树 如果当前值大于上一个节点…

解决Vue3+uni-app导航栏高亮自动同步方案

路由跳转自动识别导航高亮实现方法 以下代码使用wd-tabbar组件实现路由跳转时自动同步导航栏高亮状态&#xff0c;适用于所有的Vue3uni-app项目。 请根据自身使用框架类型完成&#xff0c;也可根据我使用的UI组件进行完成地址如下&#xff1a; Tabbar 标签栏 | Wot UI &#…

免费论文查重与AI检测工具推荐

文章目录 概要一、PaperPass二、PaperYY注意 概要 毕业季&#xff0c;总少不了查重这一步&#xff0c;甚至查 AI 率。推荐两款免费查重AIGC检测的工具。 论文免费查重查AI&#xff1a; https://paperpass.com/ https://www.paperyy.com/ 一、PaperPass 网址&#xff1a; ht…

4、ubuntu系统 | 文本和目录操作函数

1、目录操作函数 ls(列出目录内容) 用途:列出指定目录中的文件和子目录。语法:ls [选项] [路径]常用选项: -l:以长格式显示文件详细信息(权限、所有者、大小、时间等)。-a:显示隐藏文件(以.开头的文件)。-R:递归列出子目录内容。# 列出当前目录下的所有文件和子目…

C++--范围for循环详解

范围 for 循环是 C11 引入的语法特性&#xff0c;用于简化遍历容器或数组元素的过程。它比传统 for 循环更简洁安全&#xff0c;特别适合初学者。以下是详细讲解&#xff1a; 基本语法 for (元素类型 变量名 : 容器/数组) {// 循环体&#xff08;使用变量名访问当前元素&#…

RDMA简介1之RDMA开发必要性

为了满足大批量数据的采集、存储与传输需求&#xff0c;越来越多的数据密集型应用如机器学习、雷达、金融风控、航空航天等选择使用现场可编程逻辑门阵列作为数据采集前端硬件来实现高性能的数据采集系统。FPGA凭借其高灵活性、高并行能力及可高度定制化的特点&#xff0c;能够…

xmake的简易学习

文章目录 1. xmake是什么2. 一个可执行程序3. 一个库文件4. 遍历文件用法5. 第三方库3.1 系统安装库3.2 独立库 6. 后续 由于前一篇博客的最后说要做一些rknn的优化&#xff0c;其实这个工作很早就完成了&#xff0c;但是我是使用 xmake这个来做我的工程的构建的&#xff0c;不…

【ArcGIS微课1000例】0147:Geographic Imager6.2下载安装教程

文章目录 一、软件功能二、下载地址三、安装教程Geographic Imager地图工具使Adobe Photoshop空间图像可以快速高效地工作。它增加了导入,编辑,操作和导出地理空间图像的工具,例如航空和卫星图像。Geographic Imager Mac功能非常强大,拥有栅格数据输出、投影信息修改、基于…

【 java 集合知识 第一篇 】

1.概念 1.1.集合与数组的区别 集合&#xff1a;长度不固定&#xff0c;动态的根据数据添加删除改变长度&#xff0c;并且只能存入引用类型&#xff0c;读取采用迭代器或其他方法 数组&#xff1a;长度固定&#xff0c;不可改变&#xff0c;既可以存入基本类型也可以存入引用…

嵌入式开发学习日志(linux系统编程--系统编程之 进程间通信IPC)Day32

一、引言 空间独立&#xff0c;需要一些操作&#xff1b; 分为三大类&#xff1a; 1、古老的通信方式 无名管道 有名管道 信号 2、IPC对象通信 system v BSD suse fedora kernel.org 消息队列(用的相对少&#xff0c;这里不讨论) …

metersphere不同域名的参数在链路测试中如何传递?

域名1&#xff1a;https://api.domain1.com 域名2&#xff1a;https://api.domain2.com 域名1的返回参数stteid会作为域名2的入参 步骤&#xff1a; 1&#xff09;先在metersphere—接口测试—接口定义中创建域名1和域名2的接口 2&#xff09;接口创建好后&#xff0c;在接口测…

使用Process Explorer、System Informer(Process Hacker)和Windbg工具排查软件高CPU占用问题

目录 1、问题现象 2、使用Process Explorer和System Informer&#xff08;该工具原先叫Process Hacker&#xff09;查看占用CPU高的线程 3、使用System Informer工具时发现了一个关键细节 4、将Windbg附加到软件进程上&#xff0c;根据System Informer中显示的线程id到Wind…

Linux(线程概念)

目录 一 虚拟地址到物理地址的转换 1. 操作系统如何管理物理内存&#xff1a; 2. 下面来谈谈虚拟地址如何转换到物理地址&#xff1a; 3. 补充字段&#xff1a; 二 Linux中的线程 1. 先来说说进程&#xff1a; 2. 线程&#xff1a; 3. 线程相比较于进程的优缺点&#x…

阿里云为何,一个邮箱绑定了两个账号

阿里云“幽灵账号”之谜&#xff1a;同一个邮箱注销后仍有两个账号&#xff1f;深度揭秘成因与终极解决方案&#xff01; 你是否曾在阿里云上使用同一个邮箱注册过多个账号&#xff0c;明明已经**“彻底”注销了其中一个**&#xff0c;却惊愕地发现系统里依然**“幽灵般”挂着…

动态规划-数位DP

今天开始做关于数位DP的问题&#xff0c;首先对于数位DP来说&#xff0c;这类问题难度较大&#xff0c;比较难理解&#xff0c;所以博主也会尽量讲的更加详细一些&#xff0c;来帮助大家更好地理解这里的相关知识。 前置知识&#xff1a; 1.首先对于数位DP来说&#xff0c;主…

总览四级考试

别被“四级”这个庞然大物吓到&#xff01;我们一起拆解它&#xff1a;​​ &#x1f4cd; ​​核心认知&#xff1a;四级是一场策略性考试&#xff01;​​ 它不考智商&#xff0c;考的是​​基础英语能力 考试技巧 时间管理​​。基础可以通过努力补&#xff0c;技巧可以…

BSRR对比BRR对比ODR

✅ 三种操作方式的本质区别 寄存器功能原子操作特点BSRR同时支持置位(1)和复位(0)✔️ 是单指令完成任意位操作&#xff0c;无竞争风险ODR直接读写输出状态❌ 否需"读-改-写"&#xff0c;多线程/中断中需关中断保护BRR只能复位(0)✔️ 是仅清零功能&#xff0c;无置…

职坐标精选嵌入式AI物联网开源项目

随着嵌入式、AI与物联网技术的深度融合&#xff0c;开源生态已成为开发者构建智能硬件解决方案的核心驱动力。本文将从嵌入式实时操作系统、多模态AI数据集及物联网接入平台三大维度切入&#xff0c;系统性梳理技术选型要点与实践路径。在嵌入式领域&#xff0c;重点解析低功耗…

Ubuntu系统 | 本地部署ollama+deepseek

1、Ollama介绍 Ollama是由Llama开发团队推出的开源项目,旨在为用户提供高效、灵活的本地化大型语言模型(LLM)运行环境。作为Llama系列模型的重要配套工具,Ollama解决了传统云服务对计算资源和网络连接的依赖问题,让用户能够在个人电脑或私有服务器上部署和运行如Llama 3等…

【数据库】关系数据库标准语言-SQL(金仓)下

4、数据查询 语法&#xff1a; SELECT [ALL | DISTINCT] <目标列表达式> [,<目标列表达式>] … FROM <表名或视图名>[, <表名或视图名> ] … [ WHERE <条件表达式> ] [ GROUP BY <列名1> [ HAVING <条件表达式> ] ] [ ORDER BY <…