Mybatis使用和原理

news2024/12/24 9:09:04

Mybatis使用和原理

  • 1、ORM架构
  • 2、Spring整合MyBatis使用
    • 2.1 添加maven依赖
    • 2.2 配置数据源
    • 2.3 创建实体类
    • 2.4 创建 MyBatis Mapper
      • 2.4.1 使用MyBatis注解
      • 2.4.2 使用XML方式
    • 2.5 Service 层
  • 3、Spring整合Hibernate使用
    • 3.1 添加maven依赖
    • 3.2 配置数据源
    • 3.3 创建实体类
    • 3.4 创建 Repository 接口
    • 3.5 创建 Service 层
  • 4、MyBatis字段映射

如有侵权,请联系~
如有错误,也欢迎批评指正~

1、ORM架构

对象关系映射(Object Relational Mapping,简称ORM)架构是为了解决编程语言中对象和数据库中数据不一致的问题,完成对象与数据库的转换。orm除了数据库记录和程序对象的映射以外,还提供操作数据库的封装等。
orm架构位置
优点:
开发效率:通过对象表示数据库,简化了数据库操作。
可读性:代码更接近于业务逻辑,增强了可读性。
数据库独立性:通过 ORM,可以较容易地在不同的数据库间切换。
缺点:
性能开销:在某些情况下,ORM 可能会引入性能问题,特别是在进行复杂查询时。
学习曲线:对于初学者,理解 ORM 的工作原理可能需要时间。

常见的 ORM 框架
Hibernate: Java 中最常用的 ORM 框架,功能强大,支持多种数据库。是一个强大、方便、全自动化【sql都不需要写】的持久化架构。适用于性能要求不高的系统。
MyBatis: MyBatis提供了小巧、高效,半自动化【sql仍然需要写】的持久层架构。半自动化缺点就是需要自己编写sql逻辑,当然这也是优点,可以让用户定制sql。适用于性能要求高、灵活的系统。

2、Spring整合MyBatis使用

2.1 添加maven依赖

<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- MyBatis Spring Boot Starter -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.2.0</version> <!-- 使用最新版本 -->
    </dependency>
    
    <!-- MySQL Driver -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    
    <!-- Spring Boot Starter Test -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

2.2 配置数据源

在 src/main/resources/application.yml 或 application.properties 中配置连接数据库的信息。以application.properties为例子:

// 数据源部分
spring.datasource.url=jdbc:mysql://localhost:3306/your_database_name  
spring.datasource.username=your_username
spring.datasource.password=your_password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

mybatis.mapper-locations=classpath:mapper/*.xml    // 设置 MyBatis XML 映射文件路径
mybatis.type-aliases-package=com.example.model    // 设置实体类所在的包

2.3 创建实体类

创建一个实体类与数据库表映射。例如,我们创建一个 User 类:

package com.example.model;

public class User {
    private Long id;
    private String name;
    private Integer age;

    // Getters and Setters
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

2.4 创建 MyBatis Mapper

创建一个 Mapper 接口,并使用 MyBatis 注解或 XML 文件进行 SQL 操作。

2.4.1 使用MyBatis注解

package com.example.mapper;

import com.example.model.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.List;

@Mapper
public interface UserMapper {
    @Select("SELECT * FROM user")
    List<User> findAll();
}

2.4.2 使用XML方式

在 src/main/resources/mapper 目录下创建 UserMapper.xml 文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
    <select id="findAll" resultType="com.example.model.User">
        SELECT * FROM user
    </select>
</mapper>

创建UserMapper 接口:

package com.example.mapper;

import com.example.model.User;
import java.util.List;

public interface UserMapper {
    List<User> findAll();
}

2.5 Service 层

创建一个 Service 类调用 Mapper 接口:

package com.example.service;

import com.example.mapper.UserMapper;
import com.example.model.User;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService {
	@Autowire
    private final UserMapper userMapper;

    public List<User> getAllUsers() {
        return userMapper.findAll();
    }
}

3、Spring整合Hibernate使用

3.1 添加maven依赖

<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Spring Data JPA -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <!-- Hibernate Validator for JPA -->
    <dependency>
        <groupId>org.hibernate.validator</groupId>
        <artifactId>hibernate-validator</artifactId>
    </dependency>

    <!-- MySQL Driver -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>

    <!-- Spring Boot Starter Test -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

3.2 配置数据源

在 src/main/resources/application.yml 或 application.properties 文件中配置数据库连接信息。以application.properties为例:

// 数据源
spring.datasource.url=jdbc:mysql://localhost:3306/your_database_name
spring.datasource.username=your_username
spring.datasource.password=your_password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

3.3 创建实体类

创建一个实体类,与数据库表映射。例如,我们创建一个 User 类:

package com.example.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private Integer age;

    // Getters and Setters
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

3.4 创建 Repository 接口

创建一个 Repository 接口,以便与数据库进行交互:

package com.example.repository;

import com.example.model.User;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
    // 可以添加自定义查询方法
}

3.5 创建 Service 层

创建一个 Service 类来调用 Repository 进行业务逻辑处理:

package com.example.service;

import com.example.model.User;
import com.example.repository.UserRepository;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService {
	@Autowire
    private UserRepository userRepository;

    public List<User> getAllUsers() {
        return userRepository.findAll();
    }

    public void saveUser(User user) {
        userRepository.save(user);
    }

    public User getUserById(Long id) {
        return userRepository.findById(id).orElse(null);
    }

    public void deleteUser(Long id) {
        userRepository.deleteById(id);
    }
}

4、MyBatis字段映射

ORM框架主要的作用之一就是对象和数据库记录的映射。MyBatis就是从ResultSet结果集中获取到sql记录,然后将记录映射到自定义的java对象。MyBatis映射的方式:

  • 使用列名直接进行映射,这也是默认的方式。这需要java的属性名和mysql字段名保持一致。
  • 使用别名映射。select查询之后使用as给列起别名
  • 使用ResultMap进行映射。ResultMap实现查询结果与java对象映射
  • 自定义TypeHandler映射,java实现BaseTypeHandler。

主要三大步:
1、通过反射创建对象。进行字段映射的核心类DefaultResultSetHandler,创建java对象通过反射创建的:

private  <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    try {
      Constructor<T> constructor;
      if (constructorArgTypes == null || constructorArgs == null) {
        constructor = type.getDeclaredConstructor();
        if (!constructor.isAccessible()) {
          constructor.setAccessible(true);
        }
        return constructor.newInstance();
      }
      constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
      if (!constructor.isAccessible()) {
        constructor.setAccessible(true);
      }
      // 这里创建对象
      return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
    } catch (Exception e) {
      StringBuilder argTypes = new StringBuilder();
      if (constructorArgTypes != null && !constructorArgTypes.isEmpty()) {
        for (Class<?> argType : constructorArgTypes) {
          argTypes.append(argType.getSimpleName());
          argTypes.append(",");
        }
        argTypes.deleteCharAt(argTypes.length() - 1); // remove trailing ,
      }
      StringBuilder argValues = new StringBuilder();
      if (constructorArgs != null && !constructorArgs.isEmpty()) {
        for (Object argValue : constructorArgs) {
          argValues.append(String.valueOf(argValue));
          argValues.append(",");
        }
        argValues.deleteCharAt(argValues.length() - 1); // remove trailing ,
      }
      throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
    }
  }

2、使用resultMap将resultSet中的数据解析出数据。这个类会根据上述说的映射方式resultMap进行映射,其实上述这么多映射方式,最终都是转换为TypeHandler类【子类,如LongTypeHandler、LocalDateTypeHandler】

public class DefaultResultSetHandler implements ResultSetHandler {
	 @Override
	 public List<Object> handleResultSets(Statement stmt) throws SQLException {
	   ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
	
	   final List<Object> multipleResults = new ArrayList<Object>();
	
	   int resultSetCount = 0;
	   // 获取sql查询到的结果集
	   ResultSetWrapper rsw = getFirstResultSet(stmt);
	
	   // 所有配置的映射规则
	   List<ResultMap> resultMaps = mappedStatement.getResultMaps();
	   int resultMapCount = resultMaps.size();
	   validateResultMapsCount(rsw, resultMapCount);
	   while (rsw != null && resultMapCount > resultSetCount) {
	     ResultMap resultMap = resultMaps.get(resultSetCount);
	     // 进行映射处理,存储到multipleResults中
	     handleResultSet(rsw, resultMap, multipleResults, null);
	     rsw = getNextResultSet(stmt);
	     cleanUpAfterHandlingResultSet();
	     resultSetCount++;
	   }
	
	   String[] resultSets = mappedStatement.getResultSets();
	   if (resultSets != null) {
	     while (rsw != null && resultSetCount < resultSets.length) {
	       ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
	       if (parentMapping != null) {
	         String nestedResultMapId = parentMapping.getNestedResultMapId();
	         ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
	         handleResultSet(rsw, resultMap, null, parentMapping);
	       }
	       rsw = getNextResultSet(stmt);
	       cleanUpAfterHandlingResultSet();
	       resultSetCount++;
	     }
	   }
	
	   return collapseSingleResultList(multipleResults);
	 }
	 private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
	    try {
	      if (parentMapping != null) {
	        handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
	      } else {
	        if (resultHandler == null) {
	          DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
	          // 处理映射存储到defaultResultHandler中
	          handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
	          multipleResults.add(defaultResultHandler.getResultList());
	        } else {
	          handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
	        }
	      }
	    } finally {
	      // issue #228 (close resultsets)
	      closeResultSet(rsw.getResultSet());
	    }
	 }

	 public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
	    if (resultMap.hasNestedResultMaps()) {
	      ensureNoRowBounds();
	      checkResultHandler();
	      handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
	    } else {
	      handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
	    }
	 }
	
	// 最终执行这个方法
	private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
      throws SQLException {
	    final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
	    boolean foundValues = false;
	    final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
	    for (ResultMapping propertyMapping : propertyMappings) {
	      String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
	      if (propertyMapping.getNestedResultMapId() != null) {
	        // the user added a column attribute to a nested result map, ignore it
	        column = null;
	      }
	      if (propertyMapping.isCompositeResult()
	          || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
	          || propertyMapping.getResultSet() != null) {
	         
	         // 这里会根据java对象的类型使用不同的xxxTypeHandler获取value
	        Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
	        // issue #541 make property optional
	        final String property = propertyMapping.getProperty();
	        if (property == null) {
	          continue;
	        } else if (value == DEFERED) {
	          foundValues = true;
	          continue;
	        }
	        if (value != null) {
	          foundValues = true;
	        }
	        if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
	          // gcode issue #377, call setter on nulls (value is not 'found')
	          // 将value赋值给属性
	          metaObject.setValue(property, value);
	        }
	      }
	    }
	    return foundValues;
  }
}

3、利用反射将对象给对象相关属性赋值。将获取到的数据利用反射给create好的对象赋值:

private void setBeanProperty(PropertyTokenizer prop, Object object, Object value) {
    try {
      Invoker method = metaClass.getSetInvoker(prop.getName());
      Object[] params = {value};
      try {
        method.invoke(object, params);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    } catch (Throwable t) {
      throw new ReflectionException("Could not set property '" + prop.getName() + "' of '" + object.getClass() + "' with value '" + value + "' Cause: " + t.toString(), t);
    }
  }

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

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

相关文章

C++核心编程和桌面应用开发 第十七天(set和multiset容器 pair map和multimap容器)

目录 1.set和multiset容器 1.1构造和赋值 1.2交换和大小 1.3插入和删除 1.4统计和查找 1.5pair对组 1.6set和multiset的区别 1.7指定内置数据类型排序规则 1.8指定自定义数据类型排序规则 2.map和multimap容器 2.1构造和赋值 2.2交换和大小 2.3插入和删除 2.4统计…

Android中常用adb命令

目录 1.adb连接安卓模拟器 2.adb列出所有已经连接的设备 3.adb显示设备的日志信息 4.adb 电脑文件推送到安卓模拟器中 5.adb 手机传送文件到电脑 6.adb获取安卓应用的包名和Activity名 附录 1--命令 1&#xff09;adb devices 2&#xff09;adb install 路径> 3&#xff09;…

机器学习算法之回归算法

一、回归算法思维导图 二、算法概念、原理、应用场景和实例代码 1、线性回归 1.1、概念 ‌‌线性回归算法是一种统计分析方法&#xff0c;用于确定两种或两种以上变量之间的定量关系。‌ 线性回归算法通过建立线性方程来预测因变量&#xff08;y&#xff09;和一个或多个自变量…

SpringBoot项目如何设置定时任务总开关

目录 方法一&#xff1a;使用Spring Profiles 1.1 步骤1 1.2 步骤2 1.3 步骤3 1.4 步骤4 方法二&#xff1a;使用ConditionalOnProperty 2.1 步骤1 2.2 步骤2 2.3 步骤3 方法三&#xff1a; 主启动类禁用EnableScheduling 定时任务原理剖析&#xff08;选看&#xf…

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-21

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-21 目录 文章目录 计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-21目录1. The Fair Language Model Paradox摘要研究背景问题与挑战如何解决创新点算法模型实验效果重要数据与结论推荐阅读指数&…

Python——实时域名通信状态监控

在互联网时代&#xff0c;网站和在线服务的可用性对于个人和企业都至关重要。域名的通信状态直接关系到服务的可达性&#xff0c;因此&#xff0c;实时监控域名的通信状态成为了一项基本需求。本文将详细介绍一个使用Python编写的简单域名通信状态监控工具&#xff0c;该工具能…

vue vxeui 上传组件 vxe-upload 全局配置上传方法,显示上传进度,最完美的配置方案

Vxe UI 上传组件 vxe-upload 全局配置上传方法&#xff0c;显示上传进度&#xff0c;最完美的配置方案 正常使用上传组件 vxe-upload&#xff0c;都是在用的时候传自定义上传方法&#xff0c;然后进行处理。几个页面是没什么问题&#xff0c;当系统页面非常多的时候&#xff0…

springboot 修复 Spring Framework 特定条件下目录遍历漏洞(CVE-2024-38819)

刚解决Spring Framework 特定条件下目录遍历漏洞&#xff08;CVE-2024-38816&#xff09;没几天&#xff0c;又来一个新的&#xff0c;真是哭笑不得啊。 springboot 修复 Spring Framework 特定条件下目录遍历漏洞&#xff08;CVE-2024-38816&#xff09;https://blog.csdn.ne…

SSH登录介绍

说明&#xff1a;一般登录服务器&#xff0c;我们可以用远程连接工具&#xff0c;如XShell、Windterm等&#xff0c;或者通过公司搭建的JumpServer&#xff08;跳板机、堡垒机&#xff09;来连接。前者是点对点登录&#xff0c;输入主机、端口&#xff0c;通过SSH协议登录&…

Unity计算二维向量夹角余弦值和正弦值的优化方法参考

如果不考虑优化问题&#xff0c;计算两个向量的余弦值或者正弦值可以直接使用类似的方法&#xff1a; [SerializeField] Vector2 v1, v2;void Start() {float valCos Mathf.Acos(Vector2.SignedAngle(v1, v2));float valSin Mathf.Asin(Vector2.SignedAngle(v1, v2)); } 但是…

理解 CSS 中的绝对定位与 Flex 布局混用

理解 CSS 中的绝对定位与 Flex 布局混用 在现代网页设计中&#xff0c;CSS 布局技术如 flex 和绝对定位被广泛使用。然而&#xff0c;这两者结合使用时&#xff0c;可能会导致一些意想不到的布局问题。本文将探讨如何正确使用绝对定位元素&#xff0c;避免它们受到 flex 布局的…

JAVA力扣每日一题:P3216. 交换后字典序最小的字符串

本题来自&#xff1a;力扣-每日一题 力扣 (LeetCode) 全球极客挚爱的技术成长平台https://leetcode.cn/ 题解&#xff1a; class Solution {public String getSmallestString(String s) {int len s.length();int[] arr new int[len];for (int i 0; i < len; i) {arr[i…

Vue3 学习笔记(十二)侦听器详解

在 Vue 3 中&#xff0c;侦听器是一种响应式特性&#xff0c;允许你观察和响应 Vue 实例上的数据变动。Vue 提供了 watch 和 watchEffect 两个函数来创建侦听器。 1、watch 侦听器 watch 是一个用于侦听特定数据源变化的函数。它允许你指定一个或多个数据源&#xff0c;并在这些…

#渗透测试#SRC漏洞挖掘# 信息收集-常见端口及谷歌语法

免责声明 本教程仅为合法的教学目的而准备&#xff0c;严禁用于任何形式的违法犯罪活动及其他商业行为&#xff0c;在使用本教程前&#xff0c;您应确保该行为符合当地的法律法规&#xff0c;继续阅读即表示您需自行承担所有操作的后果&#xff0c;如有异议&#xff0c;请立即停…

SAP ABAP开发学习——第一代增强(包含增强演示)

​​​​​​SAP ABAP开发学习——第二代增强&#xff08;包含增强演示&#xff09;-CSDN博客 SAP ABAP开发学习——第三代增强&#xff08;BADI)-CSDN博客 概念 第一代增强(增强嵌入标准程序中) 第一代出口-User exit 以SD用户出口为例 SD及MM较多的程序都是基于源码控制来…

安宝特分享 | AR技术引领:跨国工业远程协作创新模式

在当今高度互联的工业环境中&#xff0c;跨国合作与沟通变得日益重要。然而&#xff0c;语言障碍常常成为高效协作的绊脚石。安宝特AR眼镜凭借其强大的多语言自动翻译和播报功能&#xff0c;正在改变这一局面&#xff0c;让远程协作变得更加顺畅。 01 多语言翻译优势 安宝特A…

动态规划 01背包(算法)

现有四个物品&#xff0c;小偷的背包容量为8&#xff0c;怎么可以偷得价值较多的物品 如: 物品编号&#xff1a; 1 2 3 4 物品容量&#xff1a; 2 3 4 5 物品价值&#xff1a; 3 4 5 8 记f(k,w) ,当背包容量为w,可以偷k件物品…

AWD挨打记录

前言 昨天参加了星盟的AWD集训&#xff0c;本来寻思能猛猛乱杀&#xff0c;结果加固时间只有20分钟&#xff0c;WAF还没push上去就被三家上了不死马QAQ cms是站帮主&#xff0c;之前没打过&#xff0c;D盾啥也没扫出来&#xff0c;还寻思是个贼安全的系统&#xff0c;结果洞满…

InstructIR: High-Quality Image Restoration Following Human Instructions 论文阅读笔记

这是Radu大佬所在的Wrzburg大学的computer vision lab实验室发表在ECCV2024上的一篇论文&#xff0c;代码开源。文章提出了一种文本引导的All-in-One的restoration模型&#xff0c;如下图所示&#xff1a; 这个工作其实跟"InstructPix2Pix: Learning to Follow Image Edit…

libavdevice.so.58: cannot open shared object file: No such file ordirectory踩坑

博主是将大图切分成小图时遇到 问题一、linux编译后&#xff0c;找不到ffmpeg中的一个文件 产生原因&#xff0c;各种包集成&#xff0c;然后安装以后乱七八糟&#xff0c;甚至官方的教程也不规范导致没有添加路径到系统文件导致系统执行的时候找不到 1.下载 博主进行的离线…