MyBatis

MyBatis

一、MyBatis:是一款持久层的框架 ;简化crud 的
二、MyBatis 的快速入门 演示
2.1、准备MyBatis 需要的依赖

 <!-- mybatis依赖 -->
  <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.11</version>
  </dependency>

  <!-- MySQL驱动 mybatis底层依赖jdbc驱动实现,本次不需要导入连接池,mybatis自带! -->
  <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.25</version>
  </dependency>

  <!--junit5测试-->
  <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <version>5.3.1</version>
  </dependency>

2.2、定义一个实体类 来存放查询返回的结果

public class Employee {

    private Integer empId;

    private String empName;

    private Double empSalary;
    
    //getter | setter
}

2.3、生成一个Mapper接口

package com.zh.Mapper;

import com.zh.Pojo.Employee;

/**
 * @author ZhouHao
 * @version 1.0
 * 拼不了爹,只能拼命
 * 只要干不死,就往死里干
 * 成为想成为的人,越努力越幸运
 * 2023/10/6 17:43
 */
public interface emp_Mapper {
//    根据id 查询员工的信息
    Employee select_id(int id);
    Employee delete_id(int id);
}

2.4、生成一个 Mapper.xml文件(专门来写sql语句的) 跟上面接口形成映射关系

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace等于mapper接口类的全限定名,这样实现对应 -->
<mapper namespace="com.zh.Mapper.emp_Mapper">
    <!--
            一、一个标签对应着接口的一个方法 这就是方法的实现
                id 对应接口方法名    result 对应接口方法的返回值(返回值是全限定名)
            二、Mapper 接口不能重载(重名不同参数) ;因为xml文件根据方法名称识别的
    -->
    <select id="select_id" resultType="com.zh.Pojo.Employee">
        select emp_id empId,emp_name empName, emp_salary empSalary from
        t_emp where emp_id = #{id}
    </select>

    <delete id="delete_id" >
        delete from t_emp where emp_id= #{id}
    </delete>

</mapper>

2.5、准备MyBatis的配置文件(配置数据库的连接信息和性能配置);习惯上命名为 mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <!-- environments表示配置Mybatis的开发环境,可以配置多个环境,在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。 -->
    <environments default="development">
        <!-- environment表示配置Mybatis的一个具体的环境 -->
        <environment id="development">
            <!-- Mybatis的内置的事务管理器 -->
            <transactionManager type="JDBC"/>
            <!-- 配置数据源 -->
            <dataSource type="POOLED">
                <!-- 建立数据库连接的具体信息 -->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///mybatis-example"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <!-- Mapper注册:指定Mybatis映射文件的具体位置 -->
        <!-- mapper标签:配置一个具体的Mapper映射文件 -->
        <!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 -->
        <!--    对Maven工程的目录结构来说,resources目录下的内容会直接放入类路径,所以这里我们可以以resources目录为基准 -->
        <mapper resource="Mappers/emp_Mapper.xml"/>
    </mappers>

</configuration>

2.6、运行测试

    @Test
    public void test_01() throws IOException {
//       1、读取外部配置文件(Mybatis_Config.xml)(连接数据库的)
        InputStream ips = Resources.getResourceAsStream("Mybatis_Config.xml");
//       2、创建sqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);
//       3、获取sqlSessionFactory创建sqlSession(每次业务创建一个,用完就释放)
        SqlSession sqlSession = sqlSessionFactory.openSession();
//       4、获取接口的代理对象 (代理技术)调用代理对象的方法,就会查找mapper接口的方法
        emp_Mapper mapper = sqlSession.getMapper(emp_Mapper.class);
        Employee employee = mapper.select_id(1);
        System.out.println("employee = " + employee);
//       5、提交事务(非DQL(查询))和释放资源
        sqlSession.commit();
        sqlSession.close();
    }

MyBatis 的基本使用

一 、取值符合 #{} 和 ${} 号的区别

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace等于mapper接口类的全限定名,这样实现对应 -->
<mapper namespace="com.zh.Mapper.emp_Mapper">

    <!--  #{key} :占位符 + 赋值 emp_id =?  ?=赋值  推荐使用 防止【注入攻击】的问题
          ${key} :字符串拼接  emp_id = +id

          #{} 只能代替值的位置 不能代替 列名 容器名 sql关键字 等      只能 emp_id =?
          ${} 可以代替上面这些  列名是动态的 ${列名} =动态值#{值}  可以 ? = ?

    -->
    <select id="select_id_01" resultType="com.zh.Pojo.Employee">
        select emp_id empId,emp_name empName, emp_salary empSalary from
        t_emp where emp_id = #{id}
    </select>
    <select id="select_id_02" resultType="com.zh.Pojo.Employee">
        select emp_id empId,emp_name empName, emp_salary empSalary from
        t_emp where ${arg0} = #{arg1}
    </select>

</mapper>

数据的输入

接口:

package com.zh.Mapper;

import com.zh.Pojo.Employee;
import org.apache.ibatis.annotations.Param;

import java.util.List;
import java.util.Map;

/**
 * @author ZhouHao
 * @version 1.0
 * 拼不了爹,只能拼命
 * 只要干不死,就往死里干
 * 成为想成为的人,越努力越幸运
 * 2023/10/7 19:03
 */
public interface emp_Mapper {
    Employee select_id_01(int id);



//    插入员工数据[传入实体对象]
    int insert_03(Employee employee);
//    根据 名字 和 工资 去查询员工信息
    List<Employee> query_04(@Param("a") String name ,@Param("b") Double salary);
    Employee select_id_02(String emp_id ,int id);
//    map
    int insert_05(Map map);
}

Mapper.xml :

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace等于mapper接口类的全限定名,这样实现对应 -->
<mapper namespace="com.zh.Mapper.emp_Mapper">

    <!--  #{key} :占位符 + 赋值 emp_id =?  ?=赋值  推荐使用 防止【注入攻击】的问题
          ${key} :字符串拼接  emp_id = +id

          #{} 只能代替值的位置 不能代替 列名 容器名 sql关键字 等      只能 emp_id =?
          ${} 可以代替上面这些  列名是动态的 ${列名} =动态值#{值}  可以 ? = ?

    -->
    <select id="select_id_01" resultType="com.zh.Pojo.Employee">
        select emp_id empId,emp_name empName, emp_salary empSalary from
        t_emp where emp_id = #{id}
    </select>

<!--    二、传入一个实体类对象 key应该怎么写   key=这个类的属性名 -->
    <insert id="insert_03">
        insert into t_emp (emp_name,emp_salary) values (#{empName},#{empSalary})
    </insert>

<!--    三、传入多个参数的写法-->
    <!--方式一、 使用注解的方式指定-->
    <select id="query_04" resultType="com.zh.Pojo.Employee">
        select emp_id empId,emp_name empName, emp_salary empSalary from
        t_emp where emp_name = #{a} and emp_salary =#{b}
    </select>
<!--    方式二、使用 age0 age1 的方式指定   等价于 param1 param2-->
    <select id="select_id_02" resultType="com.zh.Pojo.Employee">
        select emp_id empId,emp_name empName, emp_salary empSalary from
        t_emp where ${arg0} = #{arg1}
    </select>

<!--    Map 应该怎么写  key=等于map 的key   -->
    <insert id="insert_05">
        insert into t_emp (emp_name,emp_salary) values (#{name},#{salary})
    </insert>
</mapper>

Test:

  @Test
    public void test_01() throws IOException {
        InputStream ips = Resources.getResourceAsStream("Mybatis_Config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        emp_Mapper mapper = sqlSession.getMapper(emp_Mapper.class);
        Employee employee = mapper.select_id_01(1);

//        传入一个实体对象应该怎么写
        Employee employee1 = new Employee();
        employee1.setEmpName("周浩");
        employee1.setEmpSalary(100.0);
        mapper.insert_03(employee1);
//      多个参数传入的实现
//        方式一、 使用注解的方式实现
        List<Employee> query=mapper.query_04("周浩",100.0);
//        方式二、 使用age0 age1 的方式实现
        Employee employee2 = mapper.select_id_02("emp_id",1);

//        演示 may的写法
        HashMap<Object, Object> map = new HashMap<>();
        map.put("name","zhou");
        map.put("salary",102.0);
        mapper.insert_05(map);


        System.out.println("employee = " + employee);
        System.out.println("employee2 = " + employee2);
        System.out.println(query);
        sqlSession.commit();
        sqlSession.close();

    }

数据的输出

1、resultType 的值应该怎么写 别名应该怎么取
2、开启驼峰命名自动映射
3、使用map接值当value是多个的时候会报错
4、List接值
5、主键回显 (自增长的主键)
6、自己定义映射关系 resultMap

接口:

package com.zh.Mapper;

import com.zh.Pojo.Employee;

import java.util.List;
import java.util.Map;

/**
 * @author ZhouHao
 * @version 1.0
 * 只要干不死,就往死里干
 * 成为想成为的人,越努力越幸运
 * 我自光芒万丈,何须他人半点光
 * 有要做的事,请从知晓的那一刻百分之百去执行
 * 2023/10/8 15:04
 */
public interface emp_Mapper {
//    根据id 来查询 员工的姓名
    String select_id_01(int  id);
//    根据id 来查询 员工的薪水
    double select_id_02(int id);
//    根据 id 返回员工的所有信息
    Employee select_id_03(Integer id);
//    根据 name 返回员工信息
    List<Employee> select_name_04(String  name );
//    查询平均工资,使用map接值
    Map<String,Object> selectEmpNameAndMaxSalary();
//      查询员工工资高于200 的员工姓名
    List<String> select_salary_05(int salary);
//    查询所有员工的信息
    List<Employee> select_06();
//    插入一条数据
    int  insert_07(Employee employee);
    //    根据id 查询信息
    Employee select_08( int id );
}

mapper.xml 的配置文件应该怎么写

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace等于mapper接口类的全限定名,这样实现对应 -->
<mapper namespace="com.zh.Mapper.emp_Mapper">
    <!-- 一、resultType 的值应该怎么写 两种写法
                        1、全部限定类名 java.lang.String
                        2、别名简写 double  mabatis提供了 72种别名的书写方法
               扩展:自己定义的类 怎么定义别名 在 MyBatis_Config.xml 的<typeAliases>标签下配置


    -->
    <select id="select_id_01" resultType="java.lang.String">
        select emp_name empName from t_emp where emp_id = #{id}
    </select>

    <select id="select_id_02" resultType="double">
        select emp_salary from t_emp where emp_id = #{id}
    </select>

<!--    别名的定义演示-->
    <select id="select_id_03" resultType="zhou">
        select emp_id empId,emp_name empName, emp_salary empSalary from t_emp where emp_id=#{id}
    </select>

<!--    二、因为要求 列名和 属性名 要保持一致 这样才可以进行实体类的映射-->
<!--     可以在MyBatis_Config.xml 文件中 开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。-->
<!--     这样就可以不用给列名去取别名了-->
    <select id="select_name_04" resultType="zhou">
        select * from t_emp where emp_name=#{name};
    </select>

<!--    三、使用map接值 key=查询列名 value=查询的值   当返回的值是多个的话 会报错哦!-->
    <select id="selectEmpNameAndMaxSalary" resultType="map">
        SELECT
        emp_name 员工姓名,
        emp_salary 员工工资,
        (SELECT AVG(emp_salary) FROM t_emp) 部门平均工资
        FROM t_emp WHERE emp_salary=(
        SELECT MAX(emp_salary) FROM t_emp
        )
    </select>
<!--    四、返回List 集合类型 resultType=集合的泛型就好了  底层还是调用了selectList的-->
    <select id="select_salary_05" resultType="String">
        select emp_name from t_emp where emp_salary > #{salary}
    </select>
    <select id="select_06" resultType="zhou">
        select * from t_emp
    </select>

<!--    五、主键回显 获取插入主键的数据
           useGeneratedKeys="true" 我们想要数据库自动增长的主键值
           keyColumn="emp_id"  告诉mybatis 主键的列
           keyProperty="empId" 接收主键值的属性列
-->
    <insert id="insert_07" useGeneratedKeys="true" keyColumn="emp_id" keyProperty="empId">
        insert into t_emp (emp_name,emp_salary) values (#{empName},#{empSalary})
    </insert>
    <!--    六 、自定义映射关系
        6.1、当 列名和属性名不一致的时候的解决方法 1、自定义别名 2、开启驼峰命名自动映射 3、自定义映射关系ResuleMap

        6.2 resultType 和 resultMap的区别
               resultType:只能映射一层结构,深层次的对象结构无法映射 就是多表查询的结构无法映射
               resultMap : 自己定义映射关系
                                id :自己定义的标识
                                type:具体的返回类型 全限定类名 |别名 |集合list写泛型
                                <id/> 主健的映射关系
                                <result> 普通的列的映射关系
-->
<!--    自己定义 别名 主要是可以开启多成映射-->
    <resultMap id="aa" type="zhou">
        <id column="emp_id" property="empId"/>
        <result column="emp_name" property="empName"></result>
        <result column="emp_salary" property="empSalary"></result>
    </resultMap>
    <select id="select_08" resultMap="aa">
        select * from t_emp where emp_id=#{id};
    </select>

</mapper>

MyBatis_Config.xml 配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
<!--        开启 mybatis 的日志输出 选择使用system控制台输出-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
<!--        开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>


    <typeAliases>
        <!--    定义别名的方式 一个一个类定义别名-->
        <typeAlias type="com.zh.Pojo.Employee" alias="zhou"/>
<!--        整个包下定义别名   它的别名就是类的首字母小写  不想使用它这个别名的话 可以在类上加个注解自定义一个别名 @Alias(zhouhao)-->
        <package name="com.zh"/>
    </typeAliases>

    <!-- environments表示配置Mybatis的开发环境,可以配置多个环境,在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。 -->
    <environments default="development">
        <!-- environment表示配置Mybatis的一个具体的环境 有开发、测试等环境 在environment标签内配置就好了 -->
        <environment id="development">
            <!-- Mybatis的内置的事务管理器 JDBC | MANAGED  JDBC: 自动开启事务(需要提交事务) MANAGED:啥也不干(不会开启事务)-->
            <transactionManager type="JDBC"/>
            <!-- 配置数据源 type=POOLED 表示帮我维护一个连接池 |UNPOOLED 每次都新建或则释放支援 没有连接池-->
            <dataSource type="POOLED">
                <!-- 建立数据库连接的具体信息 -->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///mybatis-example"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <!-- Mapper注册:指定Mybatis映射文件的具体位置 -->
        <!-- mapper标签:配置一个具体的Mapper映射文件 -->
        <!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 -->
        <!--    对Maven工程的目录结构来说,resources目录下的内容会直接放入类路径,所以这里我们可以以resources目录为基准 -->
        <mapper resource="Mappers/emp_Mapper.xml"/>
    </mappers>

</configuration>

Test:

    @Test
    public void test_01() throws IOException {
        //       1、读取外部配置文件(Mybatis_Config.xml)(连接数据库的)
        InputStream ips = Resources.getResourceAsStream("Mybatis_Config.xml");
//       2、创建sqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);
//       3、获取sqlSessionFactory创建sqlSession(每次业务创建一个,用完就释放)
        SqlSession sqlSession = sqlSessionFactory.openSession();
//       4、获取接口的代理对象 (代理技术)调用代理对象的方法,就会查找mapper接口的方法
//            kdl动态代理对象生成mapper代理对象
        emp_Mapper mapper = sqlSession.getMapper(emp_Mapper.class);
//        内部拼接全限定符 + 方法名 去查找sql语句标签
//         mybatis 底层依然调用 ibatis 只不过 有固定的模式供我们使用


        String  aa=mapper.select_id_01(1);
        System.out.println(aa);

        Employee employee=mapper.select_id_03(1);
        System.out.println("employee~~~ = " + employee);

        List<Employee> bb=mapper.select_name_04("周浩");
        System.out.println("bb = " + bb);

//      当返回的值是多个的话 会报错哦!
//        Map<String ,Object> map_01= mapper.selectEmpNameAndMaxSalary();
//        System.out.println("map_01 = " + map_01);

        List<String> list_01=mapper.select_salary_05(200);
        List<Employee> list_02=mapper.select_06();
        System.out.println("list_02 = " + list_02);
        System.out.println("list_01 = " + list_01);

        Employee employee1 = new Employee();
        System.out.println("没插入前的主键"+ employee1.getEmpId());
        employee1.setEmpName("周浩啊");
        employee1.setEmpSalary(8888.0);
        int rows =mapper.insert_07(employee1);
        System.out.println("rows = " + rows);
        System.out.println("插入数据后的主键" + employee1.getEmpId());

        Employee employee2=mapper.select_08(1);
        System.out.println("employee2 = " + employee2);


//       5、提交事务(非DQL(查询))和释放资源
        sqlSession.commit();
        sqlSession.close();
    }

6、非自动增长的主键怎么交给MyBatis维护 p82
在这里插入图片描述

crud练习

@beforeEach:在Test执行先 ;先执行beforeEach这个方法
@AfterEach:junit会在每一个@Test方法最后执行@BeforeEach方法
接口:

package com.zh.UserMapper;

import com.zh.Pojo.User;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author ZhouHao
 * @version 1.0
 * 只要干不死,就往死里干
 * 成为想成为的人,越努力越幸运
 * 我自光芒万丈,何须他人半点光
 * 有要做的事,请从知晓的那一刻百分之百去执行
 * 2023/10/9 21:54
 */
public interface User_Mapper {
    int insert(User user);

    int update(User user, int id);

    int delete(Integer id);

    User selectById(Integer id);

    List<User> selectAll();
}

实现类:

package com.zh.Pojo;

import lombok.Data;

@Data //自动生成get set方法  不过要下一个插件 lombok
public class User {
  private Integer id;
  private String username;
  private String password;
}

mapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace等于mapper接口类的全限定名,这样实现对应 -->
<mapper namespace="com.zh.UserMapper.User_Mapper">
<!--    主键回显-->
        <insert id="insert" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
            insert into user (username,password) values (#{username},#{password})
        </insert>

        <update id="update" >
            update user set username=#{username},password=#{password} where id=#{id}
        </update>

        <delete id="delete" >
            delete from user where id=#{id}
        </delete>

        <select id="selectById" resultType="user">
            select * from user where id=#{id}
        </select>

        <select id="selectAll" resultType="user">
            select * from user
        </select>


</mapper>

MyBatis_Config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <settings>
<!--        开启 mybatis 的日志输出 选择使用system控制台输出-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
<!--        开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>


    <typeAliases>
        <!--    定义别名的方式 一个一个类定义别名-->
<!--        <typeAlias type="com.zh.Pojo.Employee" alias="zho"/>-->
<!--        整个包下定义别名   它的别名就是类的首字母小写  不想使用它这个别名的话 可以在类上加个注解自定义一个别名 @Alias(zhouhao)-->
        <package name="com.zh"/>
    </typeAliases>

    <!-- environments表示配置Mybatis的开发环境,可以配置多个环境,在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。 -->
    <environments default="development">
        <!-- environment表示配置Mybatis的一个具体的环境 有开发、测试等环境 在environment标签内配置就好了 -->
        <environment id="development">
            <!-- Mybatis的内置的事务管理器 JDBC | MANAGED  JDBC: 自动开启事务(需要提交事务) MANAGED:啥也不干(不会开启事务)-->
            <transactionManager type="JDBC"/>
            <!-- 配置数据源 type=POOLED 表示帮我维护一个连接池 |UNPOOLED 每次都新建或则释放支援 没有连接池-->
            <dataSource type="POOLED">
                <!-- 建立数据库连接的具体信息 -->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///mybatis-example"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <!-- Mapper注册:指定Mybatis映射文件的具体位置 -->
        <!-- mapper标签:配置一个具体的Mapper映射文件 -->
        <!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 -->
        <!--    对Maven工程的目录结构来说,resources目录下的内容会直接放入类路径,所以这里我们可以以resources目录为基准 -->
        <mapper resource="Mappers/Mapper.xml"/>
    </mappers>

</configuration>

Test:

package com.zh;

import com.zh.Pojo.User;
import com.zh.UserMapper.User_Mapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;

/**
 * @author ZhouHao
 * @version 1.0
 * 只要干不死,就往死里干
 * 成为想成为的人,越努力越幸运
 * 我自光芒万丈,何须他人半点光
 * 有要做的事,请从知晓的那一刻百分之百去执行
 * 2023/10/9 22:15
 */
public class Test_user {
    public SqlSession sqlSession;
    public User_Mapper mapper;
    public  User  user;

    @BeforeEach //junit会在每一个@Test方法前执行@BeforeEach方法
    public void before() throws IOException {
        InputStream ips = Resources.getResourceAsStream("Mybatis_Config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);
        sqlSession = sqlSessionFactory.openSession();
        mapper = sqlSession.getMapper(User_Mapper.class);
        user = new User();
        user.setId(8);
        user.setUsername("zzzz");
        user.setPassword("123");
    }

    @AfterEach //junit会在每一个@Test方法最后执行@BeforeEach方法
    public void after(){
        sqlSession.commit(); //执行dml语句的时候 必须要提交事务 ,否则的话就不会插入数据
        sqlSession.close();
    }

    @Test
    public void insert_01(){
        mapper.insert(user);
        System.out.println(user.getId()); //主键回显
    }
    @Test
    public void update_02(){
        mapper.insert(user);
    }
    @Test
    public void delete_03(){
        mapper.delete(10);
    }
    @Test
    public void select_id_04(){
        User user1 = mapper.selectById(11);
        System.out.println("user1 = " + user1);
    }
    @Test
    public void select_all_04(){
        List<User> users = mapper.selectAll();
        System.out.println("users = " + users);
    }
}

MyBatis的多表映射

多表查询应该怎么做?
1、多表查询的sql语句还是得我们自己写(连接查询)
2、自己设计存储数据的实体类(承受多个表的结果)
小技巧 : 1对1 的话:实体类属性中包含对方的对象
1对多的话:实体类属性中包含对方的对象集合
3、自己定义结果集映射(resultMap)
1对1的话:association标签写第二层属性结构
1对多的话在collection标签中写list结构

快速入门:
接口:
1对1

package com.zh.Mapper;

import com.zh.Pojo.Order;

/**
 * @author ZhouHao
 * @version 1.0
 * 只要干不死,就往死里干
 * 成为想成为的人,越努力越幸运
 * 我自光芒万丈,何须他人半点光
 * 有要做的事,请从知晓的那一刻百分之百去执行
 * 2023/10/11 11:33
 */
//根据id 的订单号来查询 客户表的信息
public interface Id_Select_Customer {
    Order query_OrderId(int id);
}

1对多:

package com.zh.Mapper;

import com.zh.Pojo.Order;

import java.util.List;

/**
 * @author ZhouHao
 * @version 1.0
 * 只要干不死,就往死里干
 * 成为想成为的人,越努力越幸运
 * 我自光芒万丈,何须他人半点光
 * 有要做的事,请从知晓的那一刻百分之百去执行
 * 2023/10/11 15:24
 */
public interface name_select_Order {
//    根据客户的名字来查询 有多少个订单
    List<Order>  select_00(String name);
}

实现类:
customer:

package com.zh.Pojo;

import lombok.Data;

import java.util.List;

/**
 * @author ZhouHao
 * @version 1.0
 * 只要干不死,就往死里干
 * 成为想成为的人,越努力越幸运
 * 我自光芒万丈,何须他人半点光
 * 有要做的事,请从知晓的那一刻百分之百去执行
 * 2023/10/11 11:27
 */
@Data
public class Customer {
    private int customerId;
    private String customerName;

//    根据客户的名字来查询 客户有多少个订单
//    是一对多的关系 所有要写一个订单的集合
    private List<Order> orderList;
}

order:

package com.zh.Pojo;

import lombok.Data;

/**
 * @author ZhouHao
 * @version 1.0
 * 只要干不死,就往死里干
 * 成为想成为的人,越努力越幸运
 * 我自光芒万丈,何须他人半点光
 * 有要做的事,请从知晓的那一刻百分之百去执行
 * 2023/10/11 11:27
 */
@Data
public class Order {
    private int orderId;
    private String orderName;
    private int customerId;

//    一个订单对应着一个客户 所以还需要一个客户对象
    private  Customer customer;

}

mapper.xml
1对1:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace等于mapper接口类的全限定名,这样实现对应 -->
<mapper namespace="com.zh.Mapper.Id_Select_Customer">

    <resultMap id="aaa" type="order">
        <id column="order_id" property="orderId" />
        <result column="order_name" property="orderName"/>
        <result column="customer_id" property="customerId"/>

        <!--association 对象属性赋值 就是另一个表 第二层结构
        javaType 另一个表的全限定类(这里取了别名)
        -->
        <association property="customer" javaType="customer">
            <id column="customer_id" property="customerId"/>
            <result column="customer_name" property="customerName"/>
        </association>

    </resultMap>

    <select id="query_OrderId" resultMap="aaa">
        select * from t_order tor
        join t_customer tc on tor.customer_id = tc.customer_id
        where order_id=#{id};
    </select>

</mapper>

1对多:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace等于mapper接口类的全限定名,这样实现对应 -->
<mapper namespace="com.zh.Mapper.name_select_Order">
    <resultMap id="aa" type="customer">
        <id column="customer_id" property="customerId"/>
        <result column="customer_name" property="customerName"/>
        <!-- collection 用来存放集合的  ofType 是用来存放集合的泛型的 -->
        <collection property="orderList" ofType="order">
            <id column="order_id" property="orderId"/>
            <result column="order_name" property="orderName"/>
            <result column="customer_id" property="customerId"/>
        </collection>
    </resultMap>
    <select id="select_00" resultMap="aa">
        select * from
        t_customer tc
        join
        t_order t
        on tc.customer_id = t.customer_id
        where tc.customer_name=#{name}
    </select> 

</mapper>

MyBatis_Config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <settings>
<!--        开启 mybatis 的日志输出 选择使用system控制台输出-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
<!--        开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>


    <typeAliases>
        <!--    定义别名的方式 一个一个类定义别名-->
<!--        <typeAlias type="com.zh.Pojo.Employee" alias="zho"/>-->
<!--        整个包下定义别名   它的别名就是类的首字母小写  不想使用它这个别名的话 可以在类上加个注解自定义一个别名 @Alias(zhouhao)-->
        <package name="com.zh"/>
    </typeAliases>

    <!-- environments表示配置Mybatis的开发环境,可以配置多个环境,在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。 -->
    <environments default="development">
        <!-- environment表示配置Mybatis的一个具体的环境 有开发、测试等环境 在environment标签内配置就好了 -->
        <environment id="development">
            <!-- Mybatis的内置的事务管理器 JDBC | MANAGED  JDBC: 自动开启事务(需要提交事务) MANAGED:啥也不干(不会开启事务)-->
            <transactionManager type="JDBC"/>
            <!-- 配置数据源 type=POOLED 表示帮我维护一个连接池 |UNPOOLED 每次都新建或则释放支援 没有连接池-->
            <dataSource type="POOLED">
                <!-- 建立数据库连接的具体信息 -->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///mybatis-example"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <!-- Mapper注册:指定Mybatis映射文件的具体位置 -->
        <!-- mapper标签:配置一个具体的Mapper映射文件 -->
        <!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 -->
        <!--Maven工程的目录结构来说,resources目录下的内容会直接放入类路径,所以这里我们可以以resources目录为基准 -->
        <mapper resource="Mappers/name_select_mapper.xml"/>
    </mappers>

</configuration>

映射优化:

在MyBatis_Config.xml文件中开启
在这里插入图片描述
在mapper.xml文件中 result标签中的属性就会帮我们自动的映射了

Mybatis的动态语句

一、if where标签的使用
二、set标签的使用
三、trim 标签的使用
四、choose、when、otherwise 标签的使用
五、forech 标签的使用
六、sql片段的调用

下面是上面知识点的总结
接口:

package com.zh.Mapper_interface;

import com.zh.Pojo.Employee;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author ZhouHao
 * @version 1.0
 * 只要干不死,就往死里干
 * 成为想成为的人,越努力越幸运
 * 我自光芒万丈,何须他人半点光
 * 有要做的事,请从知晓的那一刻百分之百去执行
 * 2023/10/11 19:41
 */
public interface emp_mapper {
//    根据员工的 姓名 和 工资 查询 员工的信息
    List<Employee> query_name_salary(@Param("aa") String name,@Param("bb") double salary);

//    演示标签 choose when otherwise 标签的使用
    List<Employee>query_name_salary_02(@Param("zz") String name,@Param("hh") double salary);


//    根据员工id更新员工的数据,我们要求传入的name 和 salary 不为null 才更新
    int update_name_salary(@Param("employee")Employee emp,@Param("zhou") int _id);

    //    演示 Trim标签的使用
    List<Employee> queryTrim(@Param("aa") String name,@Param("bb") double salary);

//    演示 foreach 怎么使用
//    根据id 批量查询
    List<Employee> queryBatch(@Param("ids") List<Integer> ids);
//    根据id 的批量删除
    int deleteBatch(List<Integer> id);
    int insertBatch(List<Employee> employeeList);
    int updateBatch(List<Employee> employeeList);

}

实现类:

package com.zh.Pojo;

import lombok.Data;

/**
 * @author ZhouHao
 * @version 1.0
 * 只要干不死,就往死里干
 * 成为想成为的人,越努力越幸运
 * 我自光芒万丈,何须他人半点光
 * 有要做的事,请从知晓的那一刻百分之百去执行
 * 2023/10/11 19:36
 */
@Data //会生成get set方法
public class Employee {
    private  int  empId;
    private String  empName;
    private double empSalary;
}

mapper.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace等于mapper接口类的全限定名,这样实现对应 -->
<mapper namespace="com.zh.Mapper_interface.emp_mapper">

    <!--
    <if/> :判断传入的参数是否加入语句
            test属性:内部作比较 true则拼接sql语句 false不拼接
                    大于小于 推荐使用 实体符号 &gt;== >   &lt;== <

    <where> : 当where中有一个if满足的时候 自动的会添加where关键字
              当if标签都不满足的话 会去掉 后面的关键字
    -->

    <select id="query_name_salary" resultType="employee">
<!--        调用代码最下面的 sql片段  include表示调用下面的-->
        <include refid="zhouhao"></include>
        <where>
            <if test="aa != null">
                emp_name=#{aa}
            </if>
            <if test="bb &gt; 50 and bb &lt; 200">
                and emp_salary=#{bb}
            </if>
        </where>
    </select>

    <!--set  1、自动去掉多余的 ,逗号
             2、自动添加set关键字

        注意: 下面二个if标签必须要满足一个 否则的话语法会报错 因为两个都不满足的话 语法本身就是错的
    -->
    <update id="update_name_salary">
        update t_emp
            <set>
                <if test="employee.empName!=null">
                    emp_name=#{employee.empName},
                </if>
                <if test="employee.empSalary!=null">
                    emp_salary=#{employee.empSalary}
                </if>
            </set>
        where emp_id=#{zhou}
    </update>

    <!-- trim 标签可以在 控制条件部分 的两端多余的字符  有4个属性
                prefix: 动态的添加前缀
                suffix: 动态的添加后缀
       prefixOverrides: 指定要动态的去除前缀那些关键字 有多个值的话使用 | 分隔
       suffixOverrides: 指定要动态的去除后缀那些关键字 有多个值的话使用 | 分隔

        不止可以用在 select 也可以用在的sql语句上
    -->
    <select id="queryTrim" resultType="employee">
        select * from t_emp
<!--       prefix 下面语句有一个满足 就添加 where关键字 -->
<!--        prefixOverrides 如果第一个条件false 就去除前缀的关键字and 或 or 的-->
        <trim prefix="where" prefixOverrides="and | or">
            <if test="bb &gt; 50 and bb &lt; 200">
                 emp_salary=#{bb}
            </if>
            <if test="aa != null">
                and emp_name=#{aa}
            </if>
        </trim>
    </select>

    <!-- 演示 choose when otherwise 标签
    -->

    <select id="query_name_salary_02" resultType="employee">
        select * from t_emp where
        <!-- choose 里面只会有一个条件生效  第一个when生效了就不会执行下面的了
            最少要满足一个生效
        -->
        <choose>
            <when test="zz!=null">
                emp_name=#{zz}
            </when>
            <when test="hh!=null">
                emp_salary=#{hh}
            </when>
            <otherwise>1=1</otherwise>
        </choose>
    </select>
        
    
    <!-- 演示:foreach 的标签的使用:常用的4个属性
                    collection:要遍历的值 ids | arg0 | list
                    open:遍历之前要追加的字符串
                    close:遍历结束要添加的字符串
                    separator: 每次遍历的分隔符 ,如果是最后一次的话 不会追加
                    item: 获取每个遍历项


    -->
    <select id="queryBatch" resultType="employee" >
        select * from t_emp where emp_id in 
            <foreach collection="ids" open="(" close=")" separator="," item="id">
                #{id}
            </foreach>
    </select>

    <delete id="deleteBatch">
        delete from t_emp where emp_id in
            <foreach collection="list" open="(" close=")" separator="," item="bb">
                #{bb}
            </foreach>
    </delete>

    <insert id="insertBatch">
        insert into t_emp (emp_name,emp_salary) values
            <foreach collection="list" separator="," item="emp">
                (#{emp.empName},#{emp.empSalary})
            </foreach>
    </insert>
<!--    因为 update 批量更新不了 所以就给他整体遍历-->
    <update id="updateBatch">
<!--        zh 相当于是 list里面的一个一个对象   下面这条语句就相当于是 几条语句在执行-->
        <foreach collection="list" item="zh">
            update t_emp set emp_name=#{zh.empName},emp_salary=#{zh.empSalary} where emp_id=#{zh.empId};
        </foreach>
    </update>

    <!--
            演示sql片段提取 提取上面 query_name_salary 的select * from t_emp语句
            在 这个上面 那个标签中演示了怎么使用
    -->
    <sql id="zhouhao">
        select * from t_emp
    </sql>

</mapper>

MyBatis_Config.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <settings>
<!--        开启 mybatis 的日志输出 选择使用system控制台输出-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
<!--        开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
<!--        开启了map的自动映射结果集了 除了主键还是要自己写的 result就不用我们自己写了-->
        <setting name="autoMappingBehavior" value="FULL"/>
    </settings>


    <typeAliases>
        <!--    定义别名的方式 一个一个类定义别名-->
<!--        <typeAlias type="com.zh.Pojo.Employee" alias="zho"/>-->
<!--        整个包下定义别名   它的别名就是类的首字母小写  不想使用它这个别名的话 可以在类上加个注解自定义一个别名 @Alias(zhouhao)-->
        <package name="com.zh"/>
    </typeAliases>

    <!-- environments表示配置Mybatis的开发环境,可以配置多个环境,在众多具体环境中,使用default属性指定实际运行时使用的环境。default属性的取值是environment标签的id属性的值。 -->
    <environments default="development">
        <!-- environment表示配置Mybatis的一个具体的环境 有开发、测试等环境 在environment标签内配置就好了 -->
        <environment id="development">
            <!-- Mybatis的内置的事务管理器 JDBC | MANAGED  JDBC: 自动开启事务(需要提交事务) MANAGED:啥也不干(不会开启事务)-->
            <transactionManager type="JDBC"/>
            <!-- 配置数据源 type=POOLED 表示帮我维护一个连接池 |UNPOOLED 每次都新建或则释放支援 没有连接池-->
            <dataSource type="POOLED">
                <!-- 建立数据库连接的具体信息 -->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<!--                ?allowMultiQueries=true 表示一次性开启多条sql语句到数据库中执行-->
                <property name="url" value="jdbc:mysql:///mybatis-example?allowMultiQueries=true"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <!-- Mapper注册:指定Mybatis映射文件的具体位置 -->
        <!-- mapper标签:配置一个具体的Mapper映射文件 -->
        <!-- resource属性:指定Mapper映射文件的实际存储位置,这里需要使用一个以类路径根目录为基准的相对路径 -->
        <!--    对Maven工程的目录结构来说,resources目录下的内容会直接放入类路径,所以这里我们可以以resources目录为基准 -->
        <mapper resource="Mappers/emp_mapper.xml"/>
    </mappers>

</configuration>

Test:

package com.zh.Test;

import com.zh.Mapper_interface.emp_mapper;
import com.zh.Pojo.Employee;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * @author ZhouHao
 * @version 1.0
 * 只要干不死,就往死里干
 * 成为想成为的人,越努力越幸运
 * 我自光芒万丈,何须他人半点光
 * 有要做的事,请从知晓的那一刻百分之百去执行
 * 2023/10/11 21:38
 */
public class Dome {
    public SqlSession sqlSession;
    public emp_mapper mapper;


    @BeforeEach //junit会在每一个@Test方法前执行@BeforeEach方法
    public void before() throws IOException {
        InputStream ips = Resources.getResourceAsStream("Mybatis_Config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);
        sqlSession = sqlSessionFactory.openSession();
        mapper = sqlSession.getMapper(emp_mapper.class);
    }
    @AfterEach //junit会在每一个@Test方法最后执行@BeforeEach方法
    public void after(){
        sqlSession.commit(); //执行dml语句的时候 必须要提交事务 ,否则的话就不会插入数据
        sqlSession.close();
    }

//    演示  where if 标签的使用
    @Test
    public void test_01(){
        List<Employee> list = mapper.query_name_salary("周浩", 150);
        System.out.println("list = " + list);

        Employee employee = new Employee();
//        employee.setEmpId(9);
        employee.setEmpName("周浩");
//        employee.setEmpSalary(100.0);  //让 salary为空(就是不设置他的值)
        int i = mapper.update_name_salary(employee,9);

    }
//    演示 trim 标签的使用
    @Test
    public void test_02(){
        List<Employee> list = mapper.queryTrim("周浩", 10);
        System.out.println("list = " + list);
    }

//    foreach 查询 标签的使用
    @Test
    public void test_03(){
        ArrayList<Integer> id = new ArrayList<>();
        id.add(1);
        id.add(2);
        id.add(3);

        List<Employee> list = mapper.queryBatch(id);
        System.out.println("list = " + list);

    }
//    foreach 批量删除
    @Test
    public void test_04(){
        ArrayList<Integer> id = new ArrayList<>();
        id.add(1);
        id.add(2);
        id.add(3);

        int i = mapper.deleteBatch(id);
        System.out.println("i = " + i);
    }
//    foreach 批量插入
    @Test
    public void test_05() {
        List<Employee> employees = new ArrayList<>();
        Employee employee = new Employee();
        employee.setEmpName("4545");
        employee.setEmpSalary(150);
        employees.add(employee);

        Employee employee1 = new Employee();
        employee1.setEmpName("zhouzh");
        employee1.setEmpSalary(125);
        employees.add(employee1);

        int i = mapper.insertBatch(employees);
        System.out.println("i = " + i);
    }
//    foreach 批量 更新的操作
    @Test
    public void text_07(){
        Employee employee = new Employee();
        employee.setEmpId(1);
        employee.setEmpName("张健健");
        employee.setEmpSalary(180);
        List<Employee> list = new ArrayList<>();
        list.add(employee);
        int i = mapper.updateBatch(list);
        System.out.println("i = " + i);
    }
//    演示 choose when otherwise
    @Test
    public void test_06(){
        List<Employee> list = mapper.query_name_salary_02(null, 150);
        System.out.println("list = " + list);
    }


}

Mapper高级扩展

一、批量打包操作 p96
在这里插入图片描述
二、PageHelper 分页插件的使用
注意:sql语句后面不要加 ; 号
会拦截sql语句帮我们追加 limit 语句
2.1、导入依赖

<!--        分页插件需要的依赖-->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.1.11</version>
        </dependency>

2.2、在MyBatis_Config.xml 中配置分页
在这里插入图片描述
3.3、分页插件的使用

    @Test
    public void test_01(){
//        调用之前要设置分页数据  当前第几页  每页显示多少个
        PageHelper.startPage(1,2);
        List<Employee> employees = mapper.query_all();
//        将查询的数据放到一个PageInfo 的实体类中
        PageInfo<Employee> aa =new PageInfo<>(employees);
//        当前页的数据  获取的条数 获取的总页数
        int pages = aa.getPages();
        System.out.println("pages = " + pages); //数据
        long total = aa.getTotal();
        System.out.println("total = " + total); //条数
        int pageNum = aa.getPageNum();
        System.out.println("pageNum = " + pageNum);//页数
    }
}

三、orm介绍和逆向工程
1、orm是什么:应用在持久层上,使用面对对象思维进行数据库(面向过程)操作
hibernate:全自动的orm框架;提供了crud 且自动生成sql语句
MyBatis:半自动的orm框架 ;提供了crud 要自己写sql语句

2、半自动的orm框架,希望也能实现单表的crud自动生成 这个过程叫逆向工程
逆向工程:半自动orm 向 全自动orm 迈进

3、myBatis使用插件实现单表的sql语句自动生成
3.1、插件:
在这里插入图片描述
没啥用,不学了 知道自个插件就好了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

没有心肝,只有干

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值