MyBatis框架基础到进阶
1、为什么要学习MyBatis
如果没有MyBatis框架,我们依靠JDBC和连接池已经能够很好的和数据库进行交互了,而学习MyBatis框架最核心的原因是为了减少SQL语句对代码的侵入性。
因为在过往不管是使用连接池还是JDBC Templete,所有的SQL语句都写在代码中,这导致了
一、SQL语句与代码的耦合性太高并且写死的SQL语句无法在其他代码中进行复用
二、我们需要记忆很多操作数据库的方法(例如queryForList、queryForMap……)
2、MyBatis介绍
一、原是Apache的一个开源项目叫做iBatis,后因为商标冲突与2010年被更改为MyBatis
二、MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架,它避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的操作。
三、MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的实体类映射成数据库中的记录
3、SpringBoot整合MyBatis
①引入依赖
这里不需要引入Spring对JDBC的依赖,因为MyBatis会自动引入
<!--mysql数据库驱动依赖-->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope>
</dependency>
<!--引入相关mybatis依赖-->
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.0</version>
</dependency>
②在yml文件中配置数据源
spring:datasource:url: jdbc:mysql://localhost:3306/empdb?serverTimezone=Asia/Shanghai&characterEncoding=utf8&serverTimeZone=Asia/Shanghaiusername: rootpassword: Qwertuiop123.
一、基于注解使用MyBatis(掌握即可,工作中一般使用XML形式进行数据库编程)
基于注解方法使用是通过SQL语句和接口方法相绑定,从而达到在调用方法时就可以执行SQL语句
①准备实体类,用于将数据库查询信息封装到实体类中
@Data
public class Teacher {private long id;private String name;private long age;private String title;private long manager;private long salary;private long comm;private String gender;private long subjectId;}
②准备一个持久层接口方法
一般来讲一个接口操作一张表,比如说下面这个接口里定义的方法就是专门用于操作Teacher表
@Mapper
public interface TeacherMapper {@Select("SELECT * FROM teacher")//如果SQL操作后返回的是一条结果,则直接将返回值定义为返回的实体类型//如果SQL操作后返回的是多条结果,则直接将返回值定义为返回的实体类型的集合并定义泛型public List<Teacher> getTeacherAll();//如果SQL绑定的是增删改语句,如果要求返回执行的记录数,则将返回值类型改为int;如果不要求返回则可以改为void@Insert("INSERT INTO teacher VALUES (6666,'光头师傅',22,'宗师',null,100000,50000,'男',0);")public int addTeacher();
}
③准备测试代码
@SpringBootTest
public class TestMyBatisAnno {@Autowiredprivate TeacherMapper teacherMapper;@Testpublic void testSelectAll() {List<Teacher> teacherAll = teacherMapper.getTeacherAll();for (Teacher teacher : teacherAll) {System.out.println(teacher);}}
}
注意:当数据库表字段由多个单词组成或表字段形式为xxx_xxx时,我们要在yml配置中配置驼峰规则映射,否则该字段的值无法查询到
#开启驼峰规则
mybatis:configuration:map-underscore-to-camel-case: true
二、@MapperScan注解的使用
随着后续的开发,可能这样的Mapper接口会变得很多,那么可能每个接口都要加@Mapper注解,就会变得很麻烦,所以可以在SpringBoot的主启动类上添加@MapperScan注解,并指定要扫描的mapper包,这样的话,就会自动去获取指定包下的接口了。
但这样做也会有一个问题,因为我们的持久层的接口一定是要被注入的,当项目没有启动并且在持久层上又没有加mapper注解时,在注入的接口上会有红色波浪线报错提示,大概报错内容是"Spring容器中没有这个bean",所以尽可能在开发过程中还是勤写@mapper注解
三、基于XML文件使用MyBatis
①定义xxxMapper.xml文件,这个文件的命名一般都为表名+mapper,也就是说和持久层的接口名相同
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">//namespace中指定要绑定接口的全路径
<mapper namespace="cn.tedu.mapper.StudentMapper">//id值要和接口中的对应方法名相同
//在查询SQL中,必须指定resultType,它的值和对应接口方法的返回值类型相同<select id="getStudentAll" resultType="cn.tedu.pojo.Student">SELECT id, name, age, gender, job, birth, location_id, team_leader, class_id FROM student</select></mapper>
②指定MyBatis中mapper文件扫描路径(即找到SQL文件的所在位置)
mybatis:mapper-locations: classpath:mapper/*.xml
③定义测试文件(在这里不做赘述,和使用注解的时候一致)
四、在mapper.xml中占位符的介绍
当我们在SQL查询语句中需要根据参数查询时,我们需要将该参数用 #{参数} 表示,并且在对应的接口方法中传入形参
①当参数为一个时:
<?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="tedu.mapper.StudentMapper"><select id="getStudentById" resultType="tedu.pojo.Student">SELECT *FROM student WHERE id = #{id}</select></mapper>
public interface StudentMapper {//这里的形参值比一定要和SQL中占位符内的参数一样,但是为了代码的可读性,一般都定义为一样的public Student getStudentById(Long id);}
②当参数为多个时:SQL中占位符内的参数名必须要和接口方法中的参数名一致
<select id="getStudentByBirth" resultType="cn.tedu.pojo.Student">SELECT id,name,age,gender,job,birth,location_id,team_leader,class_idFROM studentWHERE birth > #{minbirth}//在配置文件中 < 为特殊符号,所以直接写会报错,因此需要使用替换符AND birth < #{maxbirth}</select>
public interface StudentMapper {public List<Student> getStudentByBirth(String minBirth, String maxBirth);}
③当SQL中占位符中传入多个参数正好是属于一个实体类中的属性,则该参数直接命名为对应实体类中的属性即可,例如:
<insert id="addStudent">INSERT INTO studentvalues (#{id}, #{name}, #{age}, #{gender}, #{job}, #{birth}, #{locationId}, #{teamLeader}, #{classId})</insert>
public interface StudentMapper {public int addStudent(Student student);}
五、ResultMap——手动映射和ResultType——自动映射
ResultType 在之前我们已经使用实际案例说过了,就是通过SQL语句中的字段名和实体类中的属性名相关联,将访问数据库之后的结果封装到对应Type的实体类中,它是属于自动映射。
当然,这样的映射方式也有一个弊端,当我们在xml文件的SQL语句中给某些字段取别名时,通过自动映射结果为null,即SQL字段和实体类属性不能完成匹配
而resultMap可以手动的将属性和表字段匹配
<?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="cn.tedu.mapper.TeacherMapper"><select id="getTeacherById" resultMap="teacher">SELECT id idddd,name,age,title,manage,salary sal,comm,gender gen,subject_id sub,FROM TeacherWHERE id = #{id}</select><!--resultMap自定义映射规则type: 自定义映射的实体类,相当于ResultType后面的值id:唯一id,resultMap中的id字段要和上方resultMap中的值相一致建议只要定义resultMap,就将整表的全部列的映射全部写上--><resultMap id="teacher" type="cn.tedu.pojo.Teacher"><!--指定主键列的封装规则id映射主键,底层会有优化column:指定数据库表的哪一列,如果在SQL中定义了别名则需要写别名property:指定实体类的属性--><id column="idddd" property="id"/><!--result:映射普通字段--><result column="name" property="name"/><result column="age" property="age"/><result column="title" property="title"/><result column="manager" property="manager"/><result column="sal" property="salary"/><result column="comm" property="comm"/><result column="gen" property="gender"/><result column="sub" property="subjectId"/></resultMap>
</mapper>
当然,ResultMap也没有咱们想的这么不智能,它还是可以和自动映射兼容的(即ResultMap也可以实现自动映射),当数据库表字段列和实体类属性列一致时,我们同样可以将它删除,完成自动映射。
也就是说上方的代码改成如下也没有问题,原因是ResultMap规则中会有一个默认属性autoMapping=true属性,如果改为autoMapping=false则会映射失败。
<?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="cn.tedu.mapper.TeacherMapper"><select id="getTeacherById" resultMap="teacher">SELECT id idddd,name,age,title,manage,salary sal,comm,gender gen,subject_id sub,FROM TeacherWHERE id = #{id}</select><resultMap id="teacher" type="cn.tedu.pojo.Teacher"><id column="idddd" property="id"/><result column="sal" property="salary"/><result column="gen" property="gender"/><result column="sub" property="subjectId"/></resultMap>
</mapper>
六:ResultMap的高级用法:级联操作
一对一映射
当我们进行多表联查时,很明显,一张表内会有另一张表的关联信息(例如老师表和科目表,老师表内部一定会有一个字段和科目表产生联系),此时,只在手动映射规则中添加一个实体类显然不能满足要求 ,此时我们可以采取如下操作:
①在其中一张表(以教师表为例)中添加另一张表(以科目表为例)的类属性
注意:这里因为是一个老师教一个科目,相当于是一个一对一的关系,所以在添加的subject属性直接使用类就可以,但是如果一个老师教多个科目,则相当于是一个一对多的关系,因此添加的subject属性应该是一个集合类 ,例如 "private List<Subject> subject "
②在mapper.xml文件的SQL语句中做映射关系
<?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="cn.tedu.mapper.TeacherMapper"><select id="getTeacherById" resultMap="teacher">SELECT *FROM TeacherWHERE id = #{id}</select><select id="getTeacherSubjectById" resultMap="teacher2">SELECT t.id,t.name,t.age,t.title,t.manager,t.salary,t.comm,t.gender,t.subject_id,<!--以下两个字段属于科目表,而字段名和教师表相同,必须取别名-->s.id sid,s.name snameFROM teacher t,subject sWHERE t.subject_id = s.idAND t.id = #{id};</select><!--联合查询: 使用级联属性封装结果集--><resultMap id="teacher2" type="Teacher" ><id column="id" property="id"/><result column="name" property="name"/><result column="age" property="age"/><result column="title" property="title"/><result column="manager" property="manager"/><result column="salary" property="salary"/><result column="comm" property="comm"/><result column="gender" property="gender"/><result column="subject_id" property="subjectId"/><!--通过教师表的subject属性映射到科目表的id和name属性--><result column="sid" property="subject.id"/><result column="sname" property="subject.name"/></resultMap>
</mapper>
一对多映射
①在其中一张表(以科目表为例)中添加另一张表(以教师表为例)的类属性,因为一个科目有多个老师教,所以定义的老师类应该是一个集合
②在mapper.xml文件的SQL语句中做映射关系,这里需要使用collection标签
<?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="cn.tedu.mapper.SubjectMapper"><select id="getSubjectTeacherByName" resultMap="subject">SELECT s.id,s.name,t.id tid,t.name tname,t.age,t.title,t.manager,t.salary,t.comm,t.gender,t.subject_idFROM subject sLEFT JOIN teacher t ON s.id = t.subject_idWHERE s.name = #{name};</select><resultMap id="subject" type="Subject"><id column="id" property="id"/><result column="name" property="name"/><!--collection:表示要开始一对多映射property:表示要进行一对多映射的属性名,即下面这些字段要映射到Subject类的那个属性里呢ofType 表示一对多映射的属性的类型,相当于一对一映射关系中的type,表示要映射的实体类的属性--><collection property="teachers" ofType="Teacher"><!--定义集合中的元素的封装规则--><id column="tid" property="id"/><result column="tname" property="name"/><result column="age" property="age"/><result column="title" property="title"/><result column="manager" property="manager"/><result column="salary" property="salary"/><result column="comm" property="comm"/><result column="gender" property="gender"/><result column="subject_id" property="subjectId"/></collection></resultMap>
</mapper>
4、日志配置
日志的作用是用来追踪和记录我们的程序运行中的信息,我们可以利用日志很快定位问题,追踪分析,
在以上测试中,虽然最终测试都成功了,但是我们没办法清晰的指导知道哪条查询的结果是对应哪条SQL语句的,使用日志就能够结解决这个问题。
日志级别:
SpringBoot的日志框架提供了如下几种日志级别(从高到低,日志级别越高,反映在控制台所打印的内容就越少,比如ERROR级别他就只打印程序的错误信息;WARN级别可能同时会打印警告和错误信息):
ERROR:错误级别,指出发生错误事件,需要立即处理;
WARN:警告级别,表明可能发生错误的情况,需要注意和防止;
INFO:信息级别,描述应用程序的正常运行信息,但不会打印太多的信息;(SpringBoot默认级别)
DEBUG:调试级别,用于调试应用程序,打印详细的调试信息;
TRACE:跟踪级别,记录应用程序中每个步骤的详细信息。
在配置文件中设置日志级别:
注意:一定要设置扫描主启动类所在的包的内容,这里的cn.tedu就是主启动类所在的包
#日志设置
logging:level:cn:tedu: debug
—————————————————————
路漫漫其修远兮,吾将上下而求索~
到此关于MyBatis基础到进阶的讲解就暂时结束啦后续随着博主的功力增加会不断更新(#^.^#),
写作不易,如果你认为博主写的不错!
请点赞、关注、评论给博主一个鼓励吧,您的鼓励就是博主前进的动力。
相关文章:

MyBatis框架基础到进阶
1、为什么要学习MyBatis 如果没有MyBatis框架,我们依靠JDBC和连接池已经能够很好的和数据库进行交互了,而学习MyBatis框架最核心的原因是为了减少SQL语句对代码的侵入性。 因为在过往不管是使用连接池还是JDBC Templete,所有的SQL语句都写在代…...

【答案】2023年国赛信息安全管理与评估正式赛答案-模块1任务一
1.根据网络拓扑图所示,按照IP 地址规划表,对防火墙的名称、各接口IP 地址进行配置。共8 分,每错1 处(行)扣1 分,扣完为止。地址、安全域、接口(状态为UP)、名称都正确。 2.根据网络…...

【REMB 】翻译:草案remb-03
REMB REMB消息 以及 绝对时间戳选项 在带宽估计中的使用 :an absolute-value timestamp option for use in bandwidth estimatoin. 接收方带宽估计的RTCP消息 REMB 这位大神翻译的更好。 RTCP message for Receiver Estimated Maximum Bitrate draft-alvestrand-rmcat-remb-03…...
力扣(leetcode)第830题较大分组的位置(Python)
830.较大分组的位置 题目链接:830.较大分组的位置 在一个由小写字母构成的字符串 s 中,包含由一些连续的相同字符所构成的分组。 例如,在字符串 s “abbxxxxzyy” 中,就含有 “a”, “bb”, “xxxx”, “z” 和 “yy” 这样的…...
【导航】繁星学习随想录
导航:繁星学习随想录 一、编程启示录 01 数据结构漫谈 序号博文名称/链接01扁扁笨算法-AVL树的插入与删除02扁扁笨算法-B树的插入与删除 02 概念小扫盲 序号博文名称/链接01简单理解决策树_如何理解决策树的生长过程-CSDN博客02白盒测试方法与黑盒测试方法简析…...

Oracle 隐式数据类型转换
目录 Oracle类型转换规则: 看如下实验: 1、创建一张表,字段id的类型为number,id字段创建索引,插入一条测试数据 2、我们做如下查询,id的值设置为字符型的1 3、查看执行计划: Oracle类型转换…...

压缩编码之不同缩放参数对重建图像质量的影响的python实现——JPEG变换编码不同压缩率的模拟
原理 JPEG(Joint Photographic Experts Group)是一种常用的图像压缩标准,它通过采用离散余弦变换(DCT)和量化来实现图像的压缩。 离散余弦变换(DCT): JPEG首先将图像分割成8x8的块…...

大数据导论(2)---大数据与云计算、物联网、人工智能
文章目录 1. 云计算1.1 云计算概念1.2 云计算的服务模式和类型1.3 云计算的数据中心与应用 2. 物联网2.1 物联网的概念和关键技术2.2 物联网的应用和产业2.3 大数据与云计算、物联网的关系 1. 云计算 1.1 云计算概念 1. 首先从商业角度给云计算下一个定义:通过网络…...

有序矩阵中第 K 小的元素
题目链接 有序矩阵中第 K 小的元素 题目描述 注意点 每行和每列元素均按升序排序找到一个内存复杂度优于 O(n) 的解决方案 解答思路 使用二分查找,思路为: (1)因为左上角的元素值更小,右下角的元素值更大…...

Nginx详细介绍(并从技术层面深度剖析)
nginx介绍 1.nginx 介绍2.nginx的优势3.Nginx VS Apache3.1.内核、语言、诞生时间比较3.2.功能比较3.3.Nginx 相对 apache 的优点 4.Nginx为什么有这么多的优势?4.1.IO多路复用(I/O multiplexing【多并发】)4.2.nginx的驱动模型介绍4.3.nginx…...
单元测试基本概念
单元测试一般是开发来做的,但是因为业务需要也曾涉及过单元测试。目前就单元测试的基础概念做下总结。 一、 单元测试定义: 单元测试是软件开发中的一种测试方法,用于验证程序中的最小可测单元——即代码中的单个函数、方法或模块。单元测试…...

ECTouch 电商微信小程序 SQL注入漏洞复现(CVE-2023-39560)
0x01 产品简介 ECTouch是一款开源的电商系统,为中小企业提供最佳的新零售解决方案 0x02 漏洞概述 ECTouch 电商系统 /ectouch-main/include/apps/default/helpers/insert.php 文件中第285行的 insert_bought_notes 函数中,传入的 $arr[id] 参数未进行验证和过滤,导致未经…...

MCM备赛笔记——熵权法
Key Concept 熵权法是一种基于信息熵概念的权重确定方法,用于多指标决策分析中。信息熵是度量信息量的不确定性或混乱程度的指标,在熵权法中,它用来反映某个指标在评价过程中的分散程度,进而确定该指标的权重。指标的分散程度越高…...

vscode设置terminal的最大行数
今天跑代码出现一个问题,就是整个程序跑完,整个程序的输出信息过多,最开始输出的信息已经被vscode的缓存冲掉了,只能看到最后的一部分,具体的原因是vscode的terminal默认只能保存1000行的信息,所以如果想保…...
kafka hang 问题记录
参考文档 https://cloud.tencent.com/developer/article/1821477 9092端口 端口9092通常与Apache Kafka关联。 Kafka是一个开源的分布式事件流平台,用于构建实时的数据管道和流应用。 它能够处理任意大小的数据,以容错的方式处理数据流。 在默认配置…...
Jmeter-BeanShell脚本中for循环里面使用random随机数函数,每次生成的都一样
预想的是每次循环生成的随机数不一样,但实际使用Random函数生成的是重复的。 以下是部分原代码: List updateList new ArrayList(); for(Object o: fieldList){Map map new HashMap();map.put("id", o.get("id"));map.put("…...

高级编程。JavaScript中有哪些类型转换机制?
一、概述 前面我们讲到,JS中有六种简单数据类型:undefined、null、boolean、string、number、symbol,以及引用类型:object 但是我们在声明的时候只有一种数据类型,只有到运行期间才会确定当前类型 let x y ? 1 : …...

Linux系统下常用软件安装汇总,包括mysql,java,git,redis等
01.环境搭建 1.安装列表 MySQL 5.7.11 Java 1.8 Apache Maven 3.6 tomcat8.5 git Redis Nginx python docker 2.安装mysql 1.拷贝mysql安装文件到Linux的某个目录下 2.解压Linux安装包:tar -xvzf mysql-5.7.32-linux-glibc2.12-x86_64.tar.gz 3.进入解压后…...
【Linux】——期末复习题(一)
🎃个人专栏: 🐬 算法设计与分析:算法设计与分析_IT闫的博客-CSDN博客 🐳Java基础:Java基础_IT闫的博客-CSDN博客 🐋c语言:c语言_IT闫的博客-CSDN博客 🐟MySQL:…...

【论文阅读】Speech Driven Video Editing via an Audio-Conditioned Diffusion Model
DiffusionVideoEditing:基于音频条件扩散模型的语音驱动视频编辑 code:GitHub - DanBigioi/DiffusionVideoEditing: Official project repo for paper "Speech Driven Video Editing via an Audio-Conditioned Diffusion Model" paper&#…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...

HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...
基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解
JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用,结合SQLite数据库实现联系人管理功能,并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能,同时可以最小化到系统…...

【JVM】Java虚拟机(二)——垃圾回收
目录 一、如何判断对象可以回收 (一)引用计数法 (二)可达性分析算法 二、垃圾回收算法 (一)标记清除 (二)标记整理 (三)复制 (四ÿ…...
Web中间件--tomcat学习
Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机,它可以执行Java字节码。Java虚拟机是Java平台的一部分,Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...

Golang——7、包与接口详解
包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...
前端中slice和splic的区别
1. slice slice 用于从数组中提取一部分元素,返回一个新的数组。 特点: 不修改原数组:slice 不会改变原数组,而是返回一个新的数组。提取数组的部分:slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...
Spring Boot + MyBatis 集成支付宝支付流程
Spring Boot MyBatis 集成支付宝支付流程 核心流程 商户系统生成订单调用支付宝创建预支付订单用户跳转支付宝完成支付支付宝异步通知支付结果商户处理支付结果更新订单状态支付宝同步跳转回商户页面 代码实现示例(电脑网站支付) 1. 添加依赖 <!…...
在golang中如何将已安装的依赖降级处理,比如:将 go-ansible/v2@v2.2.0 更换为 go-ansible/@v1.1.7
在 Go 项目中降级 go-ansible 从 v2.2.0 到 v1.1.7 具体步骤: 第一步: 修改 go.mod 文件 // 原 v2 版本声明 require github.com/apenella/go-ansible/v2 v2.2.0 替换为: // 改为 v…...