1 数据库环境切换

1.1 表设计

01.表设计
    BEGIN
        EXECUTE IMMEDIATE 'DROP TABLE student';
    EXCEPTION
        WHEN OTHERS THEN
            IF SQLCODE != -942 THEN
                RAISE;
            END IF;
    END;
    /

    create table student(stuno number, stuname varchar2(20), stuage number, graname varchar2(20));
    insert into student values(1, 'zs', 23, 'g1');
    insert into student values(2, '1s', 24, 'g1');
    commit;

1.2 指定数据库:3处

01.第1处
    <?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>

        <properties resource="db.properties"/>

        <!-- 设置单个/多个别名 -->
        <typeAliases>
            <!-- 单个别名 -->
            <typeAlias type="org.myslayers.entity.Student" alias="student"/>
            <!--  批量定义别名,以下会自动将该包中的所有类批量定义别名: 别名就是类名(不带包名的类名)   -->
            <package name="org.myslayers.entity"/>
        </typeAliases>


        <!--default指定环境 -->
        <environments default="development">

            <environment id="development">
                <transactionManager type="JDBC" />
                <dataSource type="POOLED">
                    <property name="driver" value="${oracle.driver}" />
                    <property name="url" value="${oracle.url}" />
                    <property name="username" value="${oracle.username}" />
                    <property name="password" value="${oracle.password}" />
                </dataSource>
            </environment>

            <environment id="xx">
                <transactionManager type="JDBC" />
                <dataSource type="POOLED">
                    <property name="driver"  value="${mysql.driver}" />
                    <property name="url"
                              value="${mysql.url}" />
                    <property name="username" value="${mysql.username}" />
                    <property name="password" value="${mysql.password}" />
                </dataSource>
            </environment>

        </environments>

        <databaseIdProvider type="DB_VENDOR">
            <property name="MySQL" value="mysql" />
            <property name="Oracle" value="oracle" />
        </databaseIdProvider>

        <mappers>
            <!-- 加载映射文件 -->
            <mapper resource="org/myslayers/mapper/StudentMapper.xml" />
        </mappers>
    </configuration>

02.第2处
    <mapper namespace="org.myslayers.mapper.StudentMapper">
        <!-- 增:无resultType -->
        <insert id="addStudent" parameterType="student" databaseId="oracle">
            insert into student(stuno, stuname, stuage, graname) values(#{stuNo}, #{stuName}, #{stuAge}, #{graName})
        </insert>

        <!-- 删:无resultType -->
        <delete id="deleteStudentBySno" parameterType="int" databaseId="mysql">
            delete from student where stuno = #{stuNo}
        </delete>
    </mapper>

03.第3处
    public static void queryStudentBySno() throws IOException {
        Reader reader = Resources.getResourceAsReader("conf.xml");
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader, "development");
        SqlSession session = sessionFactory.openSession();

        String statement = "org.myslayers.entity.studentMapper." + "queryStudentBySno";
        // selectOne
        Student student = session.selectOne(statement, 1);// 输入:int 返回:Student
        System.out.println(student);

        session.close();
    }

2 MyBatis注解、接口方法的返回值、事务提交

2.1 接口方法的返回值

01.接口方法的返回值
    public interface StudentMapper {
        //增加
        void addStudent(Student student);

        //删除
        void deleteStudentBySno(int stuNo);

        //更新
        void updateStudentBySno(Student student);

        //查看单个
        Student queryStudentBySno(int stuNo);

        //查询全部
        List<Student> queryAllStudents();
    }

02.返回值可以为"int""long""boolean"/"Integer""Long""Boolean"
    查:Mapper.xml中一般有reslutType
    增删改:Mapper.xml中一般没有reslutType,因此,接口中方法返回值一般void,但是也可以设置"int""long""boolean"/"Integer""Long""Boolean"
    ---------------------------------------------------------------------------------------------------------
    操作:只需要在接口中"修改返回值",无需在Mapper.xml中作任何修改
    ---------------------------------------------------------------------------------------------------------
    public interface StudentMapper {
        //增加
        int addStudent(Student student);

        //删除
        boolean deleteStudentBySno(int stuNo);

        //更新
        int updateStudentBySno(Student student);

        //查看单个
        Student queryStudentBySno(int stuNo);

        //查询全部
        List<Student> queryAllStudents();
    }

03.汇总
    insert、update、delete语句的返回值类型:
        在MyBatis(使用版本3.4.6,早期版本不支持)中insert、update、delete语句的返回值可以是Integer、Long和Boolean。
        在定义Mapper接口时直接指定需要的类型即可,无需在对应的<insert><update><delete>标签中显示声明。
    select语句的返回值类型:
        返回Integer或Long类型
        select语句不能返回Boolean类型
        返回自定义类型的Bean
        返回List、Set、Array类型

2.2 事务提交:Commit方法

01.手动提交(增删改):commit
    public static void addStudent() throws IOException {
        Reader reader = Resources.getResourceAsReader("conf.xml");
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader, "development");
        SqlSession session = sessionFactory.openSession();

        StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        Student stu = new Student(3,"ll",23,"c2");
        studentMapper.addStudent(stu);
        session.commit();                                                          //手动提交(增删改):commit
        System.out.println(stu);

        session.close();
    }

02.自动提交(增删改):true
    public static void addStudent() throws IOException {
        Reader reader = Resources.getResourceAsReader("conf.xml");
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader, "development");
        SqlSession session = sessionFactory.openSession(true);                     //自动提交(增删改):true

        StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        Student stu = new Student(3,"ll",23,"c2");
        studentMapper.addStudent(stu);
        System.out.println(stu);

        session.close();
    }

3 不同数据库的自增方式

01.对于支持主键自增的数据库(MySQL)
    <insert id="insertUser" useGeneratedKeys="true" keyProperty="userId" >
        insert into user(
        user_name, user_password, create_time)
        values(#{userName}, #{userPassword} , #{createTime, jdbcType= TIMESTAMP})
    </insert>
    parameterType 可以不写,Mybatis可以推断出传入的数据类型。如果想要访问主键,那么应当parameterType 应当是java实体或者Map。这样数据在插入之后 可以通过ava实体或者Map 来获取主键值。通过 getUserId获取主键
    ---------------------------------------------------------------------------------------------------------
    useGeneratedKeys参数只针对 insert 语句生效,默认为 false;
    useGeneratedKeys设置为 true 时,表示如果插入的表id以自增列为主键,允许 JDBC 支持自动生成主键;
    keyProperty="id"代表可将自动生成的主键id返回,keyProperty的值是对象的属性值而不是数据库表中的字段名;

02.不支持主键自增的数据库(Oracle)
    对于像Oracle这样的数据,没有提供主键自增的功能,而是使用序列的方式获取自增主键。
    可以使用<selectKey>标签来获取主键的值,这种方式不仅适用于不提供主键自增功能的数据库,也适用于提供主键自增功能的数据库
    <selectKey keyColumn="id" resultType="long" keyProperty="id" order="BEFORE">
    </selectKey>

3.1 MySQL:默认自增

01.准备数据
    -- 表结构
    create table student(
        stuno INT primary key auto_increment,
        stuname VARCHAR(20),
        stuage INT,
        graname VARCHAR(20)
    );
    insert into student (stuname, stuage, graname) values ('zs',23,'al');
    insert into student (stuname, stuage, graname) values ('ls',24,'a2');

02.实体类
    public class Student {
        private Integer stuNo;
        private String stuName;
        private int stuAge;
        private String graName;
        ...
    }
    ---------------------------------------------------------------------------------------------------------
    必须Integer对应stuNo,若int result = studentMapper.addStudent(stu)
    int <- Integer,自动拆箱,转换无任何问题
    int <- null,报错null

02.配置StudentMapper.xml
    <mapper namespace="org.myslayers.mapper.StudentMapper">
        <!-- 自增问题: mysql -->
        <insert id="addStudent" parameterType="student" databaseId="mysql" useGeneratedKeys="true" keyProperty="stuNo">
            insert into student(stuname, stuage, graname) values(#{stuName}, #{stuAge}, #{graName})
        </insert>
    </mapper>
    ---------------------------------------------------------------------------------------------------------
    useGeneratedKeys="true"      //是否使用自增策略
    keyProperty="stuNo"          //回写的值:stuNo

03.测试回写stuNo是否成功
    public static void addStudent() throws IOException {
        Reader reader = Resources.getResourceAsReader("conf.xml");
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = sessionFactory.openSession(true);

        StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        Student stu = new Student(null,"ww",23,"c2");

        //增加数据前,打印stu
        System.out.println(stu);

        Integer result = studentMapper.addStudent(stu);
        session.commit();
        System.out.println(result);

        //增加数据后,打印stu
        System.out.println(stu);

        Integer stuno = stu.getStuNo();
        System.out.println(stuno);

        session.close();
    }
    ---------------------------------------------------------------------------------------------------------
    结果:
    nu11-ww-23-c2
    1
    9-WW-23-c2
    9

3.2 Oracle:序列模拟实现自增

01.准备数据
    -- 表结构
    CREATE TABLE student (
        stuno INT PRIMARY KEY,
        stuname VARCHAR(20),
        stuage INT,
        graname VARCHAR(20)
    );

    -- 创建序列(步数1,起始值1)
    create sequence myseq
        increment by 1
        start with 1;

    -- 删除序列
    drop sequence myseq;

    -- 清空数据
    truncate table student;

    -- 通过序列添加数据
    insert into student values(myseq.nextval,'zs1',23,'a1');
    insert into student values(myseq.nextval,'zs2',24,'a2');
    insert into student values(myseq.nextval,'zs3',25,'a3');
    insert into student values(myseq.nextval,'zs4',26,'a4');
    insert into student values(myseq.nextval,'zs5',27,'a5');

    SCOTT@ORCL>select * from student;
         STUNO STUNAME                  STUAGE GRANAME
    ---------- -------------------- ---------- --------------------
             1 zs1                          23 a1
             2 zs2                          24 a2
             3 zs3                          25 a3
             4 zs4                          26 a4
             5 zs5                          27 a5

02.方式1:回写数据:before 对应 myseq.nextval(序列中的下一个值)
    <!-- 自增问题: oracle模拟自增order="BEFORE"-->
    <insert id="addStudent" parameterType="student" databaseId="oracle">
        <selectKey keyProperty="stuNo" resultType="Integer" order="BEFORE">
            select myseq.nextval from dual
        </selectKey>
        insert into student(stuno, stuname, stuage, graname) values(#{stuNo}, #{stuName}, #{stuAge}, #{graName})
    </insert>

03.方式2:回写数据:AFTER 对应 myseq.currval(序列中的当前值)
    <!-- 自增问题: oracle模拟自增order="AFTER"-->
    <insert id="addStudent" parameterType="student" databaseId="oracle">
        <selectKey keyProperty="stuNo" resultType="Integer" order="AFTER" >
            select myseq.currval from dual
        </selectKey>
        insert into student(stuno, stuname, stuage, graname) values(myseq.nextval, #{stuName}, #{stuAge}, #{graName})
    </insert>

4 插入字段为null

01.Mybatis中插入字段为null
    oracle:如果插入的字段为Null,提示发生错误:Other而不是Null
    mysql:如果插入的字段为Null,可以正常执行,不报错,没有约束
    oracle与mysql二者区别:
        各个数据库在mybatis中对各种数据类型的默认值不一致。
        mybatis中,jdbcTypeForNull(如果是null),则默认值OTHER。对于Other来说,MySQL能够处理(NULL),但是Oracle不行,
    ---------------------------------------------------------------------------------------------------------
    jdbcTypeForNull
    当没有为参数提供特定的JDBC类型时,为空值指定JDBC类型。某些驱动需要指定列的JDBC类型,多数情况直接用一般类型即可,比如NU儿L、VARCHAR或OTHER
    JdbcType常量,常用值:NULL,VARCHAR或OTHER。
    OTHER

02.解决Oracle无法识别OTHER
    a.修改具体的sql标签
        <!--null : oracle-->
        <insert id="addStudent" parameterType="student" databaseId="oracle">
            insert into student(stuno, stuname, stuage, graname)
            values (#{stuNo}, #{stuName, jdbcType=NULL}, #{stuAge}, #{graName})
        </insert>
        -----------------------------------------------------------------------------------------------------
        Student stu = new Student(5, null, 23, "c2");
        studentMapper.addStudent(stu);
    b.配置mybatis全局配置文件conf.xml
        <settings>
            <setting name="jdbcTypeForNull" value="NULL"/>
        </settings>
        -----------------------------------------------------------------------------------------------------
        Student stu = new Student(5, null, 23, "c2");
        studentMapper.addStudent(stu);

5 [重]mapper传递多个参数

5.1 传递参数:4种

01.方法1:顺序传参法
    public User selectUser(String name, int deptId);

    <select id="selectUser" resultMap="UserResultMap">
        select * from user
        where user_name = #{0} and dept_id = #{1}
    </select>
    #{}里面的数字代表传入参数的顺序。
    这种方法不建议使用,sql层表达不直观,且一旦顺序调整容易出错。

02.方法2:@Param注解传参法
    public User selectUser(@Param("userName") String name, int @Param("deptId") deptId);

    <select id="selectUser" resultMap="UserResultMap">
        select * from user
        where user_name = #{userName} and dept_id = #{deptId}
    </select>
    #{}里面的名称对应的是注解@Param括号里面修饰的名称。
    这种方法在参数不多的情况还是比较直观的,推荐使用。

03.方法3:Map传参法
    public User selectUser(Map<String, Object> params);

    <select id="selectUser" parameterType="java.util.Map" resultMap="UserResultMap">
        select * from user
        where user_name = #{userName} and dept_id = #{deptId}
    </select>
    #{}里面的名称对应的是Map里面的key名称。
    这种方法适合传递多个参数,且参数易变能灵活传递的情况。

04.方法4:Java Bean传参法
    public User selectUser(User user);

    <select id="selectUser" parameterType="com.jourwon.pojo.User" resultMap="UserResultMap">
        select * from user
        where user_name = #{userName} and dept_id = #{deptId}
    </select>
    #{}里面的名称对应的是User类里面的成员属性。
    这种方法直观,需要建一个实体类,扩展不容易,需要加属性,但代码可读性强,业务逻辑处理方便,推荐使用。

5.2 输入类型案例:3种

00.parameterType 配置参数
    a.参数的使用说明
        SQL语句传参,使用标签的parameterType属性来设定。
        该属性的取值可以是基本类型,引用类型(例如:String 类型),还可以是实体类类型(POJO 类)。
        同时也可以使用实体类的包装类
    b.参数配置的注意事项
        parameterType配置参数:
            在一个参数时可以忽略(可写可不写),
            多个相同参数要写
            多个不同参数不写
        -----------------------------------------------------------------------------------------------------
        基本类型和String可以直接写类型名称也可以使用包名.类名的方式,例如:java.lang.String。
        实体类类型,目前我们只能使用全限定类名。
        究其原因,是mybaits在加载时已经把常用的数据类型注册了别名,从而我们在使用时可以不写包名,而我们的是实体类并没有注册别名,所以必须写全限定类名。

00.输入类型案例:3种
    a.一个参数
        只有一个参数一个值【8个基本类型+引用类型String】时,使用#{value}
        使用对象封装的一个参数多个值 / 使用(bean、pojo或entity来指代实体类)封装的一个参数多个值
        对象数组
        -----------------------------------------------------------------------------------------------------
        Array数组                                                      1.数组,Array                                            {'1','2','3','4'}
        Collection集合 = List列表封装in + Set列表封装in                 2.集合,Collection = List,ArrayList + Set,HashSet      ['1','2','3','4']
        Map封装的一个参数多个值、Map封装的一个参数多个值(复杂)          3.Map,HashMap                                          [id="11234",code="ABCD"]
        -----------------------------------------------------------------------------------------------------
        collection: 表示对哪一个集合或数组做迭代;如果参数是数组类型,此时Map的key为array;如果参数是List类型,此时Map的key为list
        item: 变量名。即从迭代的对象中取出的每一个值
        index: 索引的属性名。当迭代的对象为Map时,该值为Map中的Key
        open: 循环开头的字符串
        close: 循环结束的字符串
        separator: 每次循环的分隔符
    b.使用@param指定一个参数/多个参数
        一个参数
        多个参数(重点)
    c.多个参数,基于参数顺序
        参数顺序,{0}、{1}
        混合参数类型

01.一个参数
    a.只有一个参数一个值【8个基本类型+引用类型String】时,使用#{value}
        数据对象:
            8个基本类型+引用类型String
        -----------------------------------------------------------------------------------------------------
        Mapper接口:
            public interface UserMapper{
                public List<SysUser> getUserList(String name);
            }
        -----------------------------------------------------------------------------------------------------
        mapper.xml:
            <select id="getUserList" parameterType="java.lang.String" resultType="SysUser">
              select t.* from sys_user  t where t.name= #{value}
            </select>
            -------------------------------------------------------------------------------------------------
            <select id="getUserList" parameterType="java.lang.String" resultType="SysUser">
              select t.* from sys_user  t where t.name= #{aaa}
            </select>
            -------------------------------------------------------------------------------------------------
            <select id="getUserList" parameterType="java.lang.String" resultType="SysUser">
              select t.* from sys_user  t where t.name= #{任意名}
            </select>
    b.使用对象封装的一个参数多个值 / 使用(bean、pojo或entity来指代实体类)封装的一个参数多个值
        数据对象:
            public class UserQueryVO {
                private Integer id;
                private String code;

                // getter/setter....
            }
        -----------------------------------------------------------------------------------------------------
        Mapper接口:
            public interface UserMapper{
                public List<SysUser> getUserList(UserQueryVO userQueryVO);
            }
        -----------------------------------------------------------------------------------------------------
        mapper.xml:
            <select id="getUserList" parameterType="com.xxx.entity.vo.UserQueryVO" resultType="SysUser">
               select t.* from sys_user t where id=#{id} code = #{code}
            </select>
    c.对象数组
        <!-- 动态SQL: foreach,多个元素放入对象数组中,Student[] students = {student0,student1,student2}  -->
        <select id="queryStudentsWithObjectArray" parameterType="Object[]" resultType="student">
            select * from student
            <where>
                <if test="array!=null and array.length>0">
                    <foreach collection="array"  open=" and stuno in ("   close=")"
                        item="student" separator=",">
                        #{student.stuNo}
                    </foreach>
                </if>
            </where>
        </select>

        //动态SQL: foreach,多个元素放入对象数组中,Student[] students = {student0,student1,student2}
        List<Student> queryStudentsWithObjectArray(Student[] students);

        //动态SQL: foreach,多个元素放入对象数组中,Student[] students = {student0,student1,student2}
        public static void queryStudentsWithObjectArray() throws IOException {
            Reader reader = Resources.getResourceAsReader("conf.xml");
            SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader, "development");
            SqlSession session = sessionFactory.openSession();

            StudentMapper studentMapper = session.getMapper(StudentMapper.class);
            //对象数组
            Student stu1 = new Student();
            Student stu2 = new Student();
            Student stu3 = new Student();
            stu1.setStuNo(1);
            stu2.setStuNo(2);
            stu3.setStuNo(3);
            Student[] stus = new Student[] {stu1, stu2, stu3} ;

            List<Student> students = studentMapper.queryStudentsWithObjectArray(stus);
            System.out.println(students);

            session.close();
        }
    c.Map封装的一个参数多个值
        数据对象:
            HashMap <String, Object> params = new HashMap<String, Object>();
            params.put("id", "1234");
            params.put("code ", "ABCD");
        -----------------------------------------------------------------------------------------------------
        Mapper接口:
            public interface UserMapper{
                public List<SysUser>  getUserList(Map params);
            }
        -----------------------------------------------------------------------------------------------------
        mapper.xml:
            <select id="getUserList" parameterType="map" resultType="SysUser">
              select t.* from sys_user t where  id=#{id}  and  code = #{code}
            </select>
    d.Map封装的一个参数多个值(复杂)
        数据对象:如果参数既要包含String类型,又包含List类型,使用Map来封装参数,将参数放入Map,再取出Map中的List遍历。
            // 定义一个map
            Map<String, Object> params = new HashMap<String, Object>();
            params.put("status", "0");

            // List类型
            List<String> ids= new ArrayList<String>();
            ids.add("1");
            ids.add("2");

            params.put("ids", ids);
        -----------------------------------------------------------------------------------------------------
        Mapper接口:
            public interface UserMapper{
                public List<SysUser> getUserList(Map  params);
            }
        -----------------------------------------------------------------------------------------------------
        mapper.xml:
            <select id="getUserList" parameterType="map" resultType="SysUser">
              select t.*
              from sys_user t
              WHERE t.status = #{status}
              <if test=' ids != null and ids.size() > 0 '>
                  and t.id not in
                  <foreach collection="ids" item="item" index="index" open="(" close=")" separator=",">
                      #{item}
                  </foreach>
               </if>
            </select>
    e.List列表封装in / Collection集合
        数据对象:
            idsIn = ['1','2','3','4']
        Mapper接口:
            public interface UserMapper{
                public List<SysUser> getUserList(List<String> idsIns);
            }
        mapper.xml:
            <select id="getUserList" resultType="SysUser">
              select t.* from sys_user t
              where id in
              <foreach collection="idsIns" item="item" open="(" separator="," close=")">
                #{item}
              </foreach>
            </select>
        foreach最后效果:
            select 字段... from XXX where id in ('1','2','3','4')

        -----------------------------------------------------------------------------------------------------
        <!-- 动态SQL: foreach,多个元素放入集合中,List<Tnteger> stuNos 值 {1,2,3} -->
        <select id="queryStudentsWithList" parameterType="list" resultType="student">
            select * from student
            <where>
                <if test="stuNos!=null and stuNos.size>0">
                    <foreach collection="stuNos"  open=" and stuno in (" close=")" item="stuNo" separator=",">
                        #{stuNo}
                    </foreach>
                </if>
            </where>
        </select>

        //动态SQL: foreach,多个元素放入集合中
        List<Student> queryStudentsWithList(List<Integer> stuNos);

        //动态SQL: foreach,多个元素放入集合中
        public static void queryStudentsWithList() throws IOException {
            Reader reader = Resources.getResourceAsReader("conf.xml");
            SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader, "development");
            SqlSession session = sessionFactory.openSession();

            StudentMapper studentMapper = session.getMapper(StudentMapper.class);
            //集合
            List<Integer> stuNos = new ArrayList<Integer>();
            stuNos.add(1);
            stuNos.add(2);
            stuNos.add(5);

            List<Student> students = studentMapper.queryStudentsWithList(stuNos);
            System.out.println(students);

            session.close();
        }

        -----------------------------------------------------------------------------------------------------

        public MyUser selectMyUserByList(List<Integer> ids);

        <!-- #{collection[0]}:取出参数值,若为 List 还可使用 #{list[0]} -->
        <select id="selectMyUserByList" resultType="myUser">
            select * from myuser where id = #{list[0]}
        </select>
    f.Array数组
        <!-- 动态SQL: foreach,多个元素放入数组中int[] stuNos = {1,2,3} -->
        <select id="queryStudentsWithArray" parameterType="int[]" resultType="student">
            select * from student
            <where>
                <if test="array!=null and array.length>0">
                    <foreach collection="array"  open=" and stuno in ("   close=")"
                        item="stuNo" separator=",">
                        #{stuNo}
                    </foreach>
                </if>
            </where>
        </select>

        //动态SQL: foreach,多个元素放入数组中
        List<Student> queryStudentsWithArray(int[] stuNos);

        //动态SQL: foreach,多个元素放入数组中
        public static void queryStudentsWithArray() throws IOException {
            Reader reader = Resources.getResourceAsReader("conf.xml");
            SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader, "development");
            SqlSession session = sessionFactory.openSession();

            StudentMapper studentMapper = session.getMapper(StudentMapper.class);
            //数组
            int[] stuNos = {1,2,5};

            List<Student> students = studentMapper.queryStudentsWithArray(stuNos);
            System.out.println(students);

            session.close();
        }

        -----------------------------------------------------------------------------------------------------

        public MyUser selectMyUserByArray(Integer[] integers);

        <!-- #{array[0]}:取出参数值 -->
        <select id="selectMyUserByArray" resultType="myUser">
          select * from myuser where id = #{array[0]}
        </select>

02.使用@param指定一个参数/多个参数
    a.一个参数
        数据对象:
            8个基本类型+String
        -----------------------------------------------------------------------------------------------------
        Mapper接口:
            public interface UserMapper{
                public List<SysUser> getUserList(@param("id")String id);
            }
        -----------------------------------------------------------------------------------------------------
        mapper.xml:
            <select id="getUserList" parameterType="java.lang.String" resultType="SysUser">
              select t.* from sys_user t where t.id= #{id}
            </select>
        -----------------------------------------------------------------------------------------------------
        mapper.xml 中 #{id} 的 id ,对应的是 @param("id") 中指定的名称 id ,而不是String id 的 id
    b.多个参数(重点)
        数据对象:
            // List类型
            List<String> ids= new ArrayList<String>();
            ids.add("1");
            ids.add("2");
            params.put("ids", ids);

            String code = "1";
        -----------------------------------------------------------------------------------------------------
        Mapper接口:
            public interface UserMapper{
                public List<SysUser>  getUserList(@Param("idsIn")String ids, @Param("codeStr")String code);
            }
        -----------------------------------------------------------------------------------------------------
        mapper.xml:
            <select id="getUserList" resultType="SysUser">
              select t.*
              from sys_user t
              where id in
              <foreach collection="idsIn" item="item" open="(" separator="," close=")">
                #{item}
              </foreach>
                and code= #{ codeStr }
            </select>
        -----------------------------------------------------------------------------------------------------
        多参数时,@Param() 中指定参数的名称,在 mapper.xml 中引用

03.多个参数,基于参数顺序
    a.参数顺序,{0}、{1}
        数据对象:
            相对于【01.一个参数(对象):只有一个参数一个值【8个基本类型+引用类型String】时,使用#{value}】
        -----------------------------------------------------------------------------------------------------
        Mapper接口:
            public interface UserMapper{
                public List<SysUser> getUserList(String id , String code);
            }
        -----------------------------------------------------------------------------------------------------
        mapper.xml:不需要写parameterType参数
            <select id="getUserList" resultType="SysUser">
              select t.* from sys_user t
              where id = #{0} and name = #{1}
            </select>
        -----------------------------------------------------------------------------------------------------
        这里不再使用参数名,使用索引,即 #{ index }。index 索引从 0 开始递增
    b.混合参数类型
        数据对象:
            Integer id
            MyUser user
        -----------------------------------------------------------------------------------------------------
        Mapper接口:
            public MyUser selectMyUserIdAndAge(Integer id, @Param("user") MyUser user);
        -----------------------------------------------------------------------------------------------------
        mapper.xml:
            <select id="selectMyUserIdAndAge" resultType="myUser">
              select * from myuser where id = #{arg0} and age = #{user.age}
            </select>

6 [重]resultType:HashMap

6.1 表设计

00.表设计
    BEGIN
        EXECUTE IMMEDIATE 'DROP TABLE student';
    EXCEPTION
        WHEN OTHERS THEN
            IF SQLCODE != -942 THEN
                RAISE;
            END IF;
    END;
    /

    create table student(stuno number, stuname varchar2(20), stuage number);
    insert into student values(1, 'zs', 23);
    insert into student values(2, '1s', 24);
    insert into student values(3, 'ww', 23);
    insert into student values(4, 'zl', 24);
    commit;

    --给student增加主键
    alter table student add constraint pk_student_stuno primary key(stuno);

6.2 三种情况

01.key=字段名的别名,value=表值
    <!--resultType="HashMap" -->
        <select id="queryStudentOutByHashMap"  parameterType="int"  resultType="HashMap" >
        select stuNo "no", stuName "name", stuAge "age"
        from student where stuNo = #{stuNo}
    </select>

    //resultType="HashMap"
    HashMap<String,Object> queryStudentOutByHashMap(int stuNo);

    // 查询单个学生
    public static void queryStudentOutByHashMap() throws IOException {
        Reader reader = Resources.getResourceAsReader("conf.xml");
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = sessionFactory.openSession();

        StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        HashMap<String, Object> studentMap = studentMapper.queryStudentOutByHashMap(1);

        System.out.println(studentMap.get("no") + "," + studentMap.get("name") + "," + studentMap.get("age"));
        System.out.println(studentMap);
        session.close();
    }

    ---------------------------------------------------------------------------------------------------------
    结果:
    1,zs,23
    {no=1, name=zs, age=23}

02.key=字段名,value=表值
    <!--resultType="HashMap",无别名 -->
    <select id="queryStudentOutByHashMap"  parameterType="int"  resultType="HashMap" >
        select stuNo, stuName , stuAge
        from student where stuNo = #{stuNo}
    </select>

    //resultType="HashMap"
    HashMap<String,Object> queryStudentOutByHashMap(int stuNo);

    // 查询单个学生
    public static void queryStudentOutByHashMap() throws IOException {
        Reader reader = Resources.getResourceAsReader("conf.xml");
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = sessionFactory.openSession();

        StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        HashMap<String, Object> studentMap = studentMapper.queryStudentOutByHashMap(1);

        System.out.println(studentMap.get("STUNO") + "," + studentMap.get("STUNAME") + "," + studentMap.get("STUAGE"));
        System.out.println(studentMap);
        session.close();
    }
    ---------------------------------------------------------------------------------------------------------
    结果:
    1,zs,23
    {STUNAME=zs, STUAGE=23, STUNO=1}

03.key=@MapKey("STUNO"),value=该行记录
    <!--多个key、value:不指定stuno值 -->
    <select id="queryStudentsOutByHashMap" resultType="HashMap" >
        select stuNo, stuName, stuAge from student
    </select>

    //多个key、value:不指定stuno值
    @MapKey("STUNO")
    HashMap<Integer, Student> queryStudentsOutByHashMap();

    //多个key、value:不指定stuno值
    public static void queryStudentsOutByHashMap() throws IOException {
        Reader reader = Resources.getResourceAsReader("conf.xml");
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = sessionFactory.openSession();

        StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        HashMap<Integer, Student> studentMaps = studentMapper.queryStudentsOutByHashMap();

    //  System.out.println(studentMap.get("no") + "," + studentMap.get("name") + "," + studentMap.get("age"));
        System.out.println(studentMaps);
        session.close();
    }
    ---------------------------------------------------------------------------------------------------------
    结果:
    {4={STUNAME=zl, STUAGE=24, STUNO=4}, 3={STUNAME=ww, STUAGE=23, STUNO=3}, 2={STUNAME=1s, STUAGE=24, STUNO=2}, 1={STUNAME=zs, STUAGE=23, STUNO=1}}

7 [重]resultMap:鉴别器

7.1 表设计

00.表设计
    BEGIN
        EXECUTE IMMEDIATE 'DROP TABLE student';
    EXCEPTION
        WHEN OTHERS THEN
            IF SQLCODE != -942 THEN
                RAISE;
            END IF;
    END;
    /

    create table student(stuno number, stuname varchar2(20), stuage number, graname varchar2(20));

    -- 更改字段名
    alter table student rename column stuno to sno;
    alter table student rename column stuname to sname;
    alter table student rename column stuage to sage;
    alter table student rename column graname to gname;

    -- 添加主键
    alter table student add constraint pk_student3 primary key(sno);

    -- 增加字段
    alter table student add nickname varchar2(10);

    -- 增加数据
    insert into student(sno, sname, nickname, gname) values(1, 'zs', 'zxs', 'a');
    insert into student(sno, sname, nickname, gname) values(2, 'ls', 'zss', 'a');
    insert into student(sno, sname, nickname, gname) values(3, 'ww', 'www', 'b');
    insert into student(sno, sname, nickname, gname) values(4, 'nn', 'nns', 'b');
    commit;

    SCOTT@ORCL>select * from student;

           SNO SNAME                      SAGE GNAME                NICKNAME
    ---------- -------------------- ---------- -------------------- ----------
             1 zs                              a                    zxs
             2 ls                              a                    zss
             3 ww                              b                    www
             4 nn                              b                    nns

7.2 类别名alias+鉴别器discriminator

01.类别名
    <!-- 设置单个/多个别名 -->
    <typeAliases>
        <!-- 单个别名 -->
        <typeAlias type="org.myslayers.entity.Student" alias="student"/>
        <!--  批量定义别名,以下会自动将该包中的所有类批量定义别名: 别名就是类名(不带包名的类名)   -->
        <package name="org.myslayers.entity"/>
    </typeAliases>

    org/myslayers/entity/Student.java                        // 别名student
    public class Student {
        private Integer stuNo;
        private String stuName;
        private int stuAge;
        private String graName;
        ...
    }

    org/myslayers/entity/a/Student.java                      // 别名mystudent
    @Alias("mystudent")
    public class Student {

    }

02.鉴别器
    a.目的
        对查询结果进行分支处理,如果是a年级,使用真名sname;如果是b年级,使用昵称nickname
    b.代码
        <mapper namespace="org.myslayers.mapper.StudentMapper">
            <!--字段和属性名的对应关系 -->
            <select id="queryStudentsByNoWithResultMap" resultMap="studentResultMap" >
                select sno, sname, nickname, sage, gname from student
            </select>

            <!--如果字段名、属性名不一致,需要resultMap指定对应关系-->
            <resultMap type="student" id="studentResultMap">
                <!--主键-->
                <id column="sno" property="stuNo"/>
                <!--普通字段-->
                <!--
                <result column="sname" property="stuName"/>
                -->
                <result column="sage" property="stuAge"/>
                <result column="gname" property="graName"/>

                <!-- 鉴别器:对查询结果进行分支处理,如果是a年级,使用真名sname; 如果是b年级,使用昵称nickname-->
                <discriminator javaType="string" column="gname">
                    <case value="a" resultType="student">
                        <result column="sname" property="stuName"/>
                    </case>
                    <case value="b" resultType="student">
                        <result column="nickname" property="stuName"/>
                    </case>
                </discriminator>
            </resultMap>
        </mapper>

        //Student:属性名stuNo, stuName, stuAge, graName
        //字段名:sno, sname, sage, gname
        List<Student> queryStudentsByNoWithResultMap();

        public static void queryStudentsByNoWithResultMap() throws IOException {
            Reader reader = Resources.getResourceAsReader("conf.xml");
            SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
            SqlSession session = sessionFactory.openSession();

            StudentMapper studentMapper = session.getMapper(StudentMapper.class);
            List<Student> students = studentMapper.queryStudentsByNoWithResultMap();

            System.out.println(students);
            session.close();
        }
    c.结果
        [1-zs-0-a, 2-ls-0-a, 3-www-0-b, 4-nns-0-b]

8 [重]trim、内置参数、bind

8.1 表设计

01.表设计
    BEGIN
        EXECUTE IMMEDIATE 'DROP TABLE student';
    EXCEPTION
        WHEN OTHERS THEN
            IF SQLCODE != -942 THEN
                RAISE;
            END IF;
    END;
    /

    create table student(
        stuno number ,
        stuname VARCHAR2(20),
        stuage number,
        graname VARCHAR(20)
    );

    insert into student values(1, 'zs', 23, 'abc');
    insert into student values(2, 'ls', 24, 'abc');
    insert into student values(3, 'ww', 25, 'abc');

8.2 trim标签

01.trim(set、where)
    a.wher:不使用 where 1=1
            查询条件:根据输入的学生信息进行条件检索
                      当只输入用户名时, 使用用户名进行模糊检索;
                      当只输入性别时, 使用性别进行完全匹配
                      当用户名和性别都存在时, 用这两个条件进行查询匹配查询
        动态 SQL
            接口方法:List<Student> selectByStudentSelectiveWhereTag(Student student);
            对应SQL:
              <select id="selectByStudentSelectiveWhereTag" resultMap="BaseResultMap" parameterType="com.homejim.mybatis.entity.Student">
                select
                <include refid="Base_Column_List" />
                from student
               <where>
                <if test="name != null and name !=''">
                  and name like concat('%', #{name}, '%')
                </if>
                <if test="sex != null">
                  and sex=#{sex}
                </if>
               </where>
              </select>
    b.set
        set和where其实都是trim标签的一种类型,该两种功能都可以使用trim标签进行实现。
    c.trim
        trim来表示where:
            <trim prefix="where" prefixOverrides="AND |OR"></trim>
            当 trim 中含有内容时, 添加 where, 且第一个为 and 或 or 时, 会将其去掉。 而如果没有内容, 则不添加 where。
        trim来表示 set:
            <trim prefix="SET" suffixOverrides=","></trim>
            表示当 trim 中含有内容时,添加set,且最后的内容为,时,会将其去掉。而没有内容,不添加set
        trim的几个属性
            prefix: 当 trim 元素包含有内容时, 增加 prefix 所指定的前缀
            prefixOverrides: 当 trim 元素包含有内容时, 去除 prefixOverrides 指定的 前缀
            suffix: 当 trim 元素包含有内容时, 增加 suffix 所指定的后缀
            suffixOverrides: 当 trim 元素包含有内容时, 去除 suffixOverrides 指定的后缀
            -------------------------------------------------------------------------------------------------
            prefix:在trim标签中的内容的 前面添加某些内容
            prefixOverrides:在trim标签中的内容的 前面去掉某些内容
            suffix:在trim标签中的内容的 后面添加某些内容
            suffixOverrides:在trim标签中的内容的 后面去掉某些内容
        trim示例
            <trim prefix="where"prefixOverrides="and">
                给拼接的SQL加prefix="where"
                prefixOverrides="and",处理拼接SQL中【开头】第一个and
                suffixOverrides:="and",处理拼接SQL中【结尾】最后一个and
            -------------------------------------------------------------------------------------------------
            prefix:拼接
            prefixOverrides:删除
            -------------------------------------------------------------------------------------------------
            <where>可以处理拼接sql中【开头】第一个and
            <trim>可以处理拼接sql中【开头或结尾】第一个and

8.3 bind标签

01.xml
    <!--模糊查询:bind-->
    <select id="queryStudentBind" parameterType="student" resultType="student">
        select * from student
        <trim prefix="where" suffixOverrides="and">
            <bind name="_queryName" value="'%'+stuName+'%'"/>
                <if test="stuName!=null and stuName!='' ">
                    StuName like #{_queryName} and
                </if>
                <if test="graName!=null and graName!='' ">
                    graName like '%${graName}%' and
                </if>
                <if test="stuAge!=null and stuAge!='' ">
                    stuAge like #{stuAge}
                </if>
        </trim>
    </select>

02.mapper
    //模糊查询:bind
    List<Student> queryStudentBind(Student student);

03.service
    //模糊查询:bind
    public static void queryStudentBind() throws IOException {
        Reader reader = Resources.getResourceAsReader("conf.xml");
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = sessionFactory.openSession();

        StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        Student student = new Student("s", 23, "b");
        List<Student> students = studentMapper.queryStudentBind(student);

        System.out.println(students);
        session.close();
    }

8.4 内置参数

01._parameter:  代表mybatis的输入参数
    <!--内置参数-->
    <select id="queryStudentByNoWithONGL2" parameterType="student" resultType="student">
        select * from student
        <trim prefix="where" prefixOverrides="and">
            <if test="_parameter.stuName!=null and _parameter.stuName!='' ">
                and StuName like '%${_parameter.stuName}%'
            </if>
            <if test="_parameter.graName!=null and _parameter.graName!='' ">
                and graName like '%${_parameter.graName}%'
            </if>
            <if test="_parameter.stuAge!=null and _parameter.stuAge!='' ">
                and stuAge like #{_parameter.stuAge}
            </if>
        </trim>
    </select>

    //内置参数:_parameter对象类型
    public static void queryStudentByNoWithONGL2() throws IOException {
        Reader reader = Resources.getResourceAsReader("conf.xml");
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = sessionFactory.openSession();

        StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        // select * from student where stuname like '%s%' and stuage = 23 and graname like '%b%' ;
        Student student = new Student();
        student.setStuName("s");
        List<Student> students = studentMapper.queryStudentByNoWithONGL2(student);

        System.out.println(students);
        session.close();
    }

02._databaseId:代表当前数据库的名字
    <!--内置参数:_databaseId-->
    <select id="queryStudentByNo" resultType="student" parameterType="int" >
        <if test="_databaseId == 'oracle'">
            select * from student where stuNo = #{_parameter}
        </if>
        <if test="_databaseId == 'mysql'">
            select * from student where stuNo != #{_parameter}
        </if>
    </select>

9 逆向工程、log4j日志、MyBatisX快速开发

10 MyBatis架构分析

10.1 四大组件、四大处理器

01.四大核心组件
    SqlSessionFactory(SQL会话工厂):SqlSessionFactory是MyBatis的主要入口点之一。它是在应用程序启动时创建的,负责创建SqlSession对象。SqlSession提供了执行SQL语句和管理事务的方法。
    SqlSession(SQL会话):SqlSession是与数据库交互的核心接口。它包含了执行SQL语句、获取映射器(Mapper)以及管理事务的方法。SqlSession通过SqlSessionFactory创建,用完后需要关闭。
    Mapper接口(映射器):Mapper接口定义了执行SQL语句的方法。通过使用注解或XML配置,Mapper接口将SQL语句映射到具体的数据库操作。开发人员可以通过调用Mapper接口的方法来执行数据库操作,MyBatis会自动将方法与相应的SQL语句进行映射。
    映射配置文件(Mapper XML):映射配置文件包含了SQL语句与Mapper接口方法的映射规则。它使用XML格式进行配置,将SQL语句、参数映射和结果映射定义在配置文件中。通过这个文件,MyBatis可以根据Mapper接口的方法名找到对应的SQL语句。

00.四个处理器
    Executor(执行器):Executor负责管理和执行SQL语句。它接收Mapper接口的方法调用请求,将方法与对应的SQL语句进行映射,然后执行SQL并返回结果。
    StatementHandler(语句处理器):StatementHandler负责处理SQL语句的执行。它使用JDBC提供的Statement对象执行SQL,并处理参数的设置和结果的获取。
    ParameterHandler(参数处理器):ParameterHandler负责处理SQL语句的参数。它将Java对象转换为JDBC可以接受的参数类型,并将参数设置到PreparedStatement中。
    ResultSetHandler(结果集处理器):ResultSetHandler负责处理SQL语句的结果集。它将JDBC返回的ResultSet对象转换为Java对象,并提供给调用者使用。

00.几个重要对象
    SqlSessionFactory:SqlSessionFactory是MyBatis的主要入口点之一。它是在应用程序启动时创建的,负责创建SqlSession对象。
    SqlSession:SqlSession是与数据库交互的核心接口。它提供了执行SQL语句、获取Mapper接口实例以及管理事务的方法。
    Mapper接口:Mapper接口定义了执行SQL语句的方法。通过使用注解或XML配置,Mapper接口将SQL语句映射到具体的数据库操作。开发人员可以通过调用Mapper接口的方法来执行数据库操作,MyBatis会自动将方法与相应的SQL语句进行映射。
    Configuration:Configuration对象是MyBatis的核心配置对象,它包含了MyBatis的配置信息,如数据库连接信息、映射配置等。SqlSessionFactory和Mapper接口都是通过Configuration对象进行配置和创建的。

01.MyBatis中使用步骤
    a.获取SqlSessionFactory对象
    b.获取SqlSession对象
    c.获取XxxMapper对象(代理接口中的方法、mapper..xml中的<select:>等标签)
    d.执行<select>等标签中定义的SQL语句

02.parser解析器
    通过parseConfiguration(在configuration标签,设置了properties、.settings、environments等属性标签
    1.将所有的配置信息放在了Configuration.对象中
    2.解析所有的XxxMapper.xml文件(分析其中的“增删改查”标签)
    3.<select id="resultType=">等属性是通过parseStatementNode0解析
    4.将XxxMapper.xml中的<select>等标签解析成MappedStatement对象
    结论1:MappedStatement对象就是<select>等标签

    1.MappedStatement->存在于Configuration中
    2.environment->存在于Configuration中
    结论2:所有的配置信息、增删改标签全部存在于Configuration中
    结论3:结论Configuration又存在于DefaultSqlSessionFactory对象中(SqlSessionFactory)

    逻辑:SqlSessionFactory对象->DefaultSqlSessionFactory->Configuration->包含了一切配置信息

03.获取SqlSession对象
    SqlSession -> openSession() -> openSessionFromDataSource() -> DefaultSqlSession -> SQL
                                             ↓
                                configuration.newExecutor(tx,execType)
                                ->选择SimpleExecutor执行器(装饰模式增强功能)
                                ->返回DefaultSqlSession(configuration,executor,事务)
                                             ↓
                                根据不同的类型execType,产生不同的Executor,并且会对执行器进行拦截操作
                                executor =(Executor)interceptorChain.pluginAll(executor);
                                通过装饰模式,将刚才产生的executor包装成一个更加强大的executor

04.获取XxxMapper对象、执行
    studentMapper代理对象 -> MapperProxy对象
    执行增删改查 -> MapperProxy/invoke() -> InvocationHandler:JDK动态代理接口
    动态代理模式:①增删改查 -> ②代理对象(MapperProxy对象) -> ③代理对象帮我们“"代理执行”增删改查

    一、mapperMethod.execute(sqlSession,args):
    实际调用增删改查的方法依靠了sqlSession中的configuration和executor..

    二、method.convertArgsToSqlCommandParam(args);
    处理增删改查方法的参数:如果参数是0个,reutrun null:
                          如果参数是1,返回第一个:
                          如果有多个参数放入map中

    三、查询方法
    ①selectOne() -> selectList():configuration.getMappedStatement(),即获取到用于增删改查的对象
    ②boundSql:将我们写的SQL和参数值进行了拼接后的对象,即最终能被真正执行的SQL,叫做"boundSql"
    ③执行SQL是通过Executor,如果缓存中没有要查询的内容,则进入数据库真实查询:queryFromDatabase0
    ④mybatist使用的jdbc对象是PreparedStatement,底层执行增删改查:PreparedStatement的execute()

    四、MyBatis底层在执行CRUD时可能会涉及到四个处理器:
    StatementHandler:处理对象PreparedStatement的控制器
    ParameterHandler:处理参数的控制器
    TypeHandler:类型转换的控制器
    ResultSetHandler:处理结果集的控制器

05.核心组件和API
    SqlSession:mybatis最核心的组件,可以发送sql去执行并返回结果,也可以获取Mapper接口。类似于jdbc的Connection对象
    SqlSessionFactory:创建SqlSession的工厂类,包含所有创建SqlSession实例的方法
    SqlSessionFactoryBuilder: 根据配置信息Configuration或代码构建SqlSessionFactory对象
    SQL Mapper:由Java接口和xml文件构成,并给出对应的sql和映射规则,负责发送sql执行并返回

06.为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里?
    Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。
    而Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以,称之为半自动ORM映射工具。

07.Mybatis优缺点
    优点
        与传统的数据库访问技术相比,ORM有以下优点:
        基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,
        解除sql与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用
        与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接
        很好的与各种数据库兼容(因为MyBatis使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持)
        提供映射标签,支持对象与数据库的ORM字段关系映射;提供对象关系映射标签,支持对象关系组件维护
        能够与Spring很好的集成
    缺点
        SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求
        SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库

08.JDBC编程有哪些不足之处,MyBatis是如何解决这些问题的?
    1、数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库连接池可解决此问题。
    解决:在mybatis-config.xm<x>l中配置数据链接池,使用连接池管理数据库连接。
    2、Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。
    解决:将Sql语句配置在XXXXmapper.xm<x>l文件中与java代码分离。
    3、向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应。
    解决: Mybatis自动将java对象映射至sql语句。
    4、对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。
    解决:Mybatis自动将sql执行结果映射至java对象。

09.Hibernate 和 MyBatis 的区别
    相同点
        都是对jdbc的封装,都是持久层的框架,都用于dao层的开发。
    不同点
        映射关系
            MyBatis 是一个半自动映射的框架,配置Java对象与sql语句执行结果的对应关系,多表关联关系配置简单
            Hibernate 是一个全表映射的框架,配置Java对象与数据库表的对应关系,多表关联关系配置复杂
        SQL优化和移植性
            Hibernate 对SQL语句封装,提供了日志、缓存、级联(级联比 MyBatis 强大)等特性,此外还提供 HQL(Hibernate Query
            Language)操作数据库,数据库无关性支持好,但会多消耗性能。如果项目需要支持多种数据库,代码开发量少,但SQL语句优化困难。
            MyBatis 需要手动编写 SQL,支持动态 SQL、处理列表、动态生成表名、支持存储过程。开发工作量相对大些。
            直接使用SQL语句操作数据库,不支持数据库无关性,但sql语句优化容易。
        开发难易程度和学习成本
            Hibernate 是重量级框架,学习使用门槛高,适合于需求相对稳定,中小型的项目,比如:办公自动化系统
            MyBatis 是轻量级框架,学习使用门槛低,适合于需求变化频繁,大型的项目,比如:互联网电子商务系统

10.2 解析和运行原理

01.执行原理
    MyBatis编程步骤是什么样的?
        创建SqlSessionFactory
        通过SqlSessionFactory创建SqlSession
        通过sqlsession执行数据库操作
        调用session.commit()提交事务
        调用session.close()关闭会话

    请说说MyBatis的工作原理
        1)读取 MyBatis 配置文件:mybatis-config.xml 为 MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等信息,例如数据库连接信息。
        2)加载映射文件。映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句,需要在 MyBatis 配置文件 mybatis-config.xml 中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。
        3)构造会话工厂:通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。
        4)创建会话对象:由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法。
        5)Executor 执行器:MyBatis 底层定义了一个 Executor 接口来操作数据库,它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护。
        6)MappedStatement 对象:在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等信息。
        7)输入参数映射:输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型。输入参数映射过程类似于 JDBC 对 preparedStatement 对象设置参数的过程。
        8)输出结果映射:输出结果类型可以是 Map、 List 等集合类型,也可以是基本数据类型和 POJO 类型。输出结果映射过程类似于 JDBC 对结果集的解析过程。

    MyBatis的功能架构
        API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。
                    接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。
        数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。
        基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,
                    这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。

    MyBatis框架架构设计
        加载配置:配置来源于两个地方,一处是配置文件,一处是Java代码的注解,
                    将SQL的配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),
                    存储在内存中。
        SQL解析:当API接口层接收到调用请求时,会接收到传入SQL的ID和传入对象(可以是Map、JavaBean或者基本数据类型),
                    Mybatis会根据SQL的ID找到对应的MappedStatement,然后根据传入参数对象对MappedStatement进行解析,
                    解析后可以得到最终要执行的SQL语句和参数。
        SQL执行:将最终得到的SQL和参数拿到数据库进行执行,得到操作数据库的结果。
        结果映射:将操作数据库的结果按照映射的配置进行转换,可以转换成HashMap、JavaBean或者基本数据类型,并将最终结果返回。

02.预编译
    为什么需要预编译
    SQL 预编译指的是数据库驱动在发送 SQL 语句和参数给 DBMS 之前对 SQL 语句进行编译,这样 DBMS 执行 SQL 时,就不需要重新编译。
    JDBC 中使用对象 PreparedStatement 来抽象预编译语句,使用预编译。预编译阶段可以优化 SQL 的执行。预编译之后的 SQL 多数情况下可以直接执行,DBMS 不需要再次编译,越复杂的SQL,编译的复杂度将越大,预编译阶段可以合并多次操作为一个操作。同时预编译语句对象可以重复利用。把一个 SQL 预编译后产生的 PreparedStatement 对象缓存下来,下次对于同一个SQL,可以直接使用这个缓存的 PreparedState 对象。Mybatis默认情况下,将对所有的 SQL 进行预编译。

03.执行器
    Mybatis都有哪些Executor执行器?它们之间的区别是什么?
        Mybatis有三种基本的Executor执行器,SimpleExecutor、ReuseExecutor、BatchExecutor。
        SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
        ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map<String, Statement>内,供下一次使用。简言之,就是重复使用Statement对象。
        BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。

    Mybatis中如何指定使用哪一种Executor执行器?
        在Mybatis配置文件中,在设置(settings)可以指定默认的ExecutorType执行器类型,也可以手动给DefaultSqlSessionFactory的创建SqlSession的方法传递ExecutorType类型参数,如SqlSession openSession(ExecutorType execType)。
        配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新。

    Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?
        Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。

11 自定义插件:拦截器

11.1 实现原理

00.汇总
    四个处理:StatementHandler、ParameterHandler、ResultSetHandler、TypeHandler
    四大核心对象:StatementHandler、ParameterHandler、ResultSetHandler、Executor
    四大核心对象:1都涉及到了拦截器用于增强 2.四大核心对象都包含了该增强操作

01.自定义插件的编写逻辑
    根据MyBatis规则编写一个拦截器,在拦截器内部加入“自定义增强功能”
    步骤:a.编写拦截器 b.编写签名注解 c.配置

02.简述Mybatis的插件运行原理,以及如何编写一个插件
    Mybatis仅可以编写针对ParameterHandler、ResultSetHandler、StatementHandler、Executor这4种接口的插件,
    Mybatis使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的方法时,
    就会进入拦截方法,具体就是InvocationHandler的invoke()方法,当然,只会拦截那些你指定需要拦截的方法。
    实现Mybatis的Interceptor接口并复写intercept()方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,
    记住,别忘了在配置文件中配置你编写的插件。

11.2 代码实现

01.代码实现
    package org.myslayers.my.interceptors;

    import org.apache.ibatis.executor.statement.StatementHandler;
    import org.apache.ibatis.plugin.*;
    import org.apache.ibatis.reflection.MetaObject;
    import org.apache.ibatis.reflection.SystemMetaObject;

    import java.sql.Statement;
    import java.util.Properties;

    @Intercepts({
            //拦截query(Statement statement, ResultHandler resultHandler)
    //        @Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class})

            //拦截mybatis设置参数的方法
            @Signature(type = StatementHandler.class, method = "parameterize", args = {Statement.class})
    })
    public class MyInterceptor1 implements Interceptor {

        //拦截方法
        public Object intercept(Invocation invocation) throws Throwable {
            System.out.println("------拦截方法intercept1------");
    //        //放行
    //        Object proceed = invocation.proceed();

            //真正执行,目标方法:select * from student where stuno=#{stuNo}
            Object target = invocation.getTarget();

            //SystemMetaObject:mybatis提供查看
            System.out.println("目标对象" + target);//目标对象org.apache.ibatis.executor.statement.RoutingStatementHandler@545997b1

            //包装后的产物:metaObject
            MetaObject metaObject = SystemMetaObject.forObject(target);
            //传入的参数值 = metaObject.getValue("")
            Object value = metaObject.getValue("parameterHandler.parameterObject");
            //value:2
            System.out.println(value+"---value1---");
            //修改value:1
            metaObject.setValue("parameterHandler.parameterObject", 1);
            //修改后,value
            Object value2 = metaObject.getValue("parameterHandler.parameterObject");
            System.out.println(value2+"---value2---");

            //放行
            Object proceed = invocation.proceed();

            return proceed;
        }

        //将拦截器中定义的增加功能,与原来的核心对象合并起来
        public Object plugin(Object target) {
            Object wrap = Plugin.wrap(target, this);//返回值=核心对象+拦截器this
            System.out.println("------plugin1-----" + wrap);
            return wrap;
        }

        //属性
        public void setProperties(Properties properties) {
            System.out.println("设置属性properties1" + properties);
        }
    }

02.代码示例
    <!--根据学号查询学生-->
    <select id="queryStudentByNo" parameterType="int" resultType="student">
        select * from student where stuno=#{stuNo}
    </select>

    //根据学号查询学生
    Student queryStudentByNo(int stuNo);


    //根据学号查询学生
    public static void queryStudentByNo() throws IOException {
        //加载conf.xml
        Reader reader = Resources.getResourceAsReader("conf.xml");

        //1.获取SqlSessionFactory对象
        SqlSessionFactory sessionFactory
                = new SqlSessionFactoryBuilder().build(reader);
        //2.获取SqlSession对象
        SqlSession session = sessionFactory.openSession();
        //3.获取XxxMapper对象(代理接口中的方法,本质为:mapper.xml中的<select>等标签)
        StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        //4.执行<select>等标签中定义的SQL语句
        Student student = studentMapper.queryStudentByNo(2);

        System.out.println(student);
        session.close();
    }

12 批量操作DML

12.1 MyBatis处理批量BATCH(推荐)

01.设置ExecutorType.BATCH
    sessionFactory.openSession(ExecutorType.BATCH );  //推荐的写法

02.预编译
    insert into student(stuNo,stuName,stuAge,graName)
    values(#(stuNo),#(stuName),#(stuAge),#(graName))
    推荐      BATCH:预编译SQL一次,其余DML只需要设置参数值即可
          没有BATCH:预编译N次,每次DML都需要执行完整的SQL

03.使用ExecutorType.BATCH
    Mybatis内置的ExecutorType有3种,默认为simple,该模式下它为每个语句的执行创建一个新的预处理语句,单条提交sql;
    而batch模式重复使用已经预处理的语句,并且批量执行所有更新语句,显然batch性能将更优;
    但batch模式也有自己的问题,比如在Insert操作时,在事务没有提交之前,是没有办法获取到自增的id,这在某型情形下是不符合业务要求的

    //批量保存方法测试
    @Test
    public void testBatch() throws IOException{
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        //可以执行批量操作的sqlSession
        SqlSession openSession = sqlSessionFactory.openSession(ExecutorType.BATCH);

        //批量保存执行前时间
        long start = System.currentTimeMillis();
        try {
            EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
            for (int i = 0; i < 1000; i++) {
                mapper.addEmp(new Employee(UUID.randomUUID().toString().substring(0, 5), "b", "1"));
            }

            openSession.commit();
            long end = System.currentTimeMillis();
            //批量保存执行后的时间
            System.out.println("执行时长" + (end - start));
            //批量 预编译sql一次==》设置参数==》10000次==》执行1次   677
            //非批量  (预编译=设置参数=执行 )==》10000次   1121

        } finally {
            openSession.close();
        }
    }

    public interface EmployeeMapper {
        //批量保存员工
        Long addEmp(Employee employee);
    }

    <mapper namespace="com.jourwon.mapper.EmployeeMapper"
         <!--批量保存员工 -->
        <insert id="addEmp">
            insert into employee(lastName,email,gender)
            values(#{lastName},#{email},#{gender})
        </insert>
    </mapper>

12.2 传统批量操作DML(不推荐,拼接SQL)

12.2.1 Oracle:存储过程,拼接SQL

01.xml
    <!--批量操作DML: 储存过程oracle -->
    <select id="addBatchStudentOracle" parameterType="student" >
        <foreach collection="list" open="begin" item="student" close="end;">
            insert into student(stuno,stuname) values(#{student.stuNo},#{student.stuName});
        </foreach>
    </select>

02.mapper
    //批量操作DML: 储存过程oracle
    void addBatchStudentOracle(List<Student> students);

03.service
    //批量操作DML: 储存过程oracle
    public static void addBatchStudentOracle() throws IOException {
        Reader reader = Resources.getResourceAsReader("conf.xml");
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = sessionFactory.openSession(ExecutorType.BATCH);//修改执行器类型

        StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        List<Student> students = new ArrayList<>();
        students.add(new Student(1, "zs"));
        students.add(new Student(2, "ls"));
        students.add(new Student(3, "ww"));
        studentMapper.addBatchStudentOracle(students);

        session.commit();
        session.close();
    }

12.2.2 MySQL:values(100,‘zs’),(200,‘ls’),拼接SQL

01.使用foreach标签
    foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合。foreach标签的属性主要有item,index,collection,open,separator,close。
    item  表示集合中每一个元素进行迭代时的别名,随便起的变量名;
    index  指定一个名字,用于表示在迭代过程中,每次迭代到的位置,不常用;
    open  表示该语句以什么开始,常用“(”;
    separator表示在每次进行迭代之间以什么符号作为分隔符,常用“,”;
    close  表示以什么结束,常用“)”。

    在使用foreach的时候最关键的也是最容易出错的就是collection属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的,主要有一下3种情况:
    如果传入的是单参数且参数类型是一个List的时候,collection属性值为list
    如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array
    如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map,实际上如果你在传入参数的时候,在MyBatis里面也是会把它封装成一个Map的,
    map的key就是参数名,所以这个时候collection属性值就是传入的List或array对象在自己封装的map里面的key

02.使用foreach标签
    <insert id="batchInsert" parameterType="list">
        insert ignore into user (id, name, gender, addr, status) values
        <foreach collection="list" item="item" separator=",">
            (
                #{item.id,jdbcType=INT},
                #{item.name,jdbcType=VARCHAR},
                #{item.gender,jdbcType=BIT},
                #{item.addr,jdbcType=VARCHAR},
                #{item.status,jdbcType=BIT}
            )
        </foreach>
    </insert>

03.代码实现
    <!--批量操作DML: 储存过程mysql -->
    <insert id="addBatchStudentMysql" parameterType="student" databaseId="mysql">
        insert into student(stuno, stuname) values
        <foreach collection="list" item="student" separator="," close=";">
            (#{student.stuNo},#{student.stuName})
        </foreach>
    </insert>

    //批量操作DML: 储存过程mysql
    void addBatchStudentMysql(List<Student> students);

    //批量操作DML: 储存过程mysql
    public static void addBatchStudentMysql() throws IOException {
        Reader reader = Resources.getResourceAsReader("conf.xml");
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = sessionFactory.openSession(ExecutorType.BATCH);//修改执行器类型

        StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        List<Student> students = new ArrayList<>();
        students.add(new Student(20, "zs"));
        students.add(new Student(21, "ls"));
        students.add(new Student(22, "ww"));
        studentMapper.addBatchStudentMysql(students);

        session.commit();
        session.close();
    }

12.2.3 MySQL:重复sql(数据库通用,不推荐)

01.xml
    <!--批量操作DML: 重复sql -->
    <insert id="addBatchStudentMoreSql" parameterType="student" databaseId="mysql">
        <foreach collection="list" item="student" >
            insert into student(stuno, stuname) values(#{student.stuNo}, #{student.stuName});
        </foreach>
    </insert>

02.mapper
    //批量操作DML: 重复sql
    void addBatchStudentMoreSql(List<Student> students);

03.service
    //批量操作DML: 重复sql
    public static void addBatchStudentMoreSql() throws IOException {
        Reader reader = Resources.getResourceAsReader("conf.xml");
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = sessionFactory.openSession(ExecutorType.BATCH);//修改执行器类型

        StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        List<Student> students = new ArrayList<>();
        students.add(new Student(30, "zs"));
        students.add(new Student(31, "ls"));
        students.add(new Student(32, "ww"));
        studentMapper.addBatchStudentMoreSql(students);

        session.commit();
        session.close();
    }

13 PageHelper插件分页

13.1 原理

01.Mybatis是如何进行分页的?分页插件的原理是什么?
    分页:【RowBounds】对象进行分页,使用 RowBounds(m,n),其中m表示开始位置,n表示间隔。

02.分页limit (currentPage-1)*pageSize,pageSize
    //表示查询第一页的10条数据,也就是第1 -10条数据
    select * from table limit 0,10;

    //表示查询第二页的10条数据,也就是第11-20条数据
    select * from table limit 10,10;

     //表示查询第三页的10条数据,也就是第21-30条数据。
    select * from table limit 20,10;

03.pagehelper的使用
    使用的时候,只需在查询list前,调用 startPage 设置分页信息,即可使用分页功能。
    public Object getUsers(int pageNum, int pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        // 不带分页的查询
        List<UserEntity> list = userMapper.selectAllWithPage(null);
        // 可以将结果转换为 Page , 然后获取 count 和其他结果值
        com.github.pagehelper.Page listWithPage = (com.github.pagehelper.Page) list;
        System.out.println("listCnt:" + listWithPage.getTotal());
        return list;
    }
    即使用时, 只需提前声明要分页的信息, 得到的结果就是有分页信息的了. 如果不想进行count,只要查分页数据,
    则调用: PageHelper.startPage(pageNum, pageSize, false); 即可, 避免了不必要的count消耗

13.2 表设计

00.表设计
    BEGIN
        EXECUTE IMMEDIATE 'DROP TABLE student';
    EXCEPTION
        WHEN OTHERS THEN
            IF SQLCODE != -942 THEN
                RAISE;
            END IF;
    END;
    /

    create table student(stuno number, stuname varchar2(20), stuage number, graname varchar2(20));

    insert into student(stuno, stuname) values(4,'ww');
    insert into student(stuno, stuname) values(5,'ww');
    insert into student(stuno, stuname) values(6,'ww');
    insert into student(stuno, stuname) values(7,'ww');
    insert into student(stuno, stuname) values(8,'ww');
    insert into student(stuno, stuname) values(9,'ww');
    insert into student(stuno, stuname) values(10,'ww');
    insert into student(stuno, stuname) values(11,'ww');
    insert into student(stuno, stuname) values(12,'ww');
    insert into student(stuno, stuname) values(13,'ww');
    insert into student(stuno, stuname) values(14,'ww');
    insert into student(stuno, stuname) values(15,'ww');
    commit;

    SCOTT@ORCL>select * from student;

           SNO SNAME                      SAGE GNAME                NICKNAME
    ---------- -------------------- ---------- -------------------- ----------
             1 zs                              a                    zxs
             2 ls                              a                    zss
             3 ww                              b                    www
             4 nn                              b                    nns

13.3 PageHelper:拦截器

01.第一种,RowBounds方式的调用
    List<Country> list = sqlSession.selectList("x.y.selectIf", null, new RowBounds(0, 10));

02.第二种,Mapper接口方式的调用,推荐这种使用方式。
    PageHelper.startPage(1, 10);
    List<Country> list = countryMapper.selectIf(1);

03.第三种,Mapper接口方式的调用,推荐这种使用方式。
    PageHelper.offsetPage(1, 10);
    List<Country> list = countryMapper.selectIf(1);

04.第四种,参数方法调用
    //存在以下 Mapper 接口方法,你不需要在 xml 处理后两个参数
    public interface CountryMapper {
        List<Country> selectByPageNumSize(
                @Param("user") User user,
                @Param("pageNum") int pageNum,
                @Param("pageSize") int pageSize);
    }
    //配置supportMethodsArguments=true
    //在代码中直接调用:
    List<Country> list = countryMapper.selectByPageNumSize(user, 1, 10);

05.第五种,参数对象
    //如果 pageNum 和 pageSize 存在于 User 对象中,只要参数有值,也会被分页
    //有如下 User 对象
    public class User {
        //其他fields
        //下面两个参数名和 params 配置的名字一致
        private Integer pageNum;
        private Integer pageSize;
    }
    //存在以下 Mapper 接口方法,你不需要在 xml 处理后两个参数
    public interface CountryMapper {
        List<Country> selectByPageNumSize(User user);
    }
    //当 user 中的 pageNum!= null && pageSize!= null 时,会自动分页
    List<Country> list = countryMapper.selectByPageNumSize(user);

06.第六种,ISelect 接口方式
    //jdk6,7用法,创建接口
    Page<Country> page = PageHelper.startPage(1, 10).doSelectPage(new ISelect() {
        @Override
        public void doSelect() {
            countryMapper.selectGroupBy();
        }
    });
    //jdk8 lambda用法
    Page<Country> page = PageHelper.startPage(1, 10).doSelectPage(()-> countryMapper.selectGroupBy());

    //也可以直接返回PageInfo,注意doSelectPageInfo方法和doSelectPage
    pageInfo = PageHelper.startPage(1, 10).doSelectPageInfo(new ISelect() {
        @Override
        public void doSelect() {
            countryMapper.selectGroupBy();
        }
    });
    //对应的lambda用法
    pageInfo = PageHelper.startPage(1, 10).doSelectPageInfo(() -> countryMapper.selectGroupBy());

    //count查询,返回一个查询语句的count数
    long total = PageHelper.count(new ISelect() {
        @Override
        public void doSelect() {
            countryMapper.selectLike(country);
        }
    });
    //lambda
    total = PageHelper.count(()->countryMapper.selectLike(country));

13.4 项目使用

01.依赖
    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper-spring-boot-starter</artifactId>
        <version>1.3.0</version>
    </dependency>

02.实体类
    @Data
    public class DishPageQueryDTO implements Serializable {

        private int page;

        private int pageSize;

        private String name;

        //分类id
        private Integer categoryId;

        //状态 0表示禁用 1表示启用
        private Integer status;

    }
    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public class DishVO implements Serializable {

        private Long id;
        //菜品名称
        private String name;
        //菜品分类id
        private Long categoryId;
        //菜品价格
        private BigDecimal price;
        //图片
        private String image;
        //描述信息
        private String description;
        //0 停售 1 起售
        private Integer status;
        //更新时间
        private LocalDateTime updateTime;
        //分类名称
        private String categoryName;
        //菜品关联的口味
        private List<DishFlavor> flavors = new ArrayList<>();

        //private Integer copies;
    }
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class PageResult implements Serializable {

        private long total; //总记录数

        private List records; //当前页数据集合

    }
    @Data
    public class Result<T> implements Serializable {

        private Integer code; //编码:1成功,0和其它数字为失败
        private String msg; //错误信息
        private T data; //数据

        public static <T> Result<T> success() {
            Result<T> result = new Result<T>();
            result.code = 1;
            return result;
        }

        public static <T> Result<T> success(T object) {
            Result<T> result = new Result<T>();
            result.data = object;
            result.code = 1;
            return result;
        }

        public static <T> Result<T> error(String msg) {
            Result result = new Result();
            result.msg = msg;
            result.code = 0;
            return result;
        }

    }

03.使用
    @GetMapping("/page")
    @ApiOperation("菜品分页查询")
    public Result<PageResult> page(DishPageQueryDTO dishPageQueryDTO) {
        log.info("菜品分页查询:{}", dishPageQueryDTO);
        PageResult pageResult = dishService.pageQuery(dishPageQueryDTO);
        return Result.success(pageResult);
    }

    public PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO) {
        PageHelper.startPage(dishPageQueryDTO.getPage(), dishPageQueryDTO.getPageSize());
        Page<DishVO> page = dishMapper.pageQuery(dishPageQueryDTO);
        return new PageResult(page.getTotal(), page.getResult());
    }

    Page<DishVO> pageQuery(DishPageQueryDTO dishPageQueryDTO);

    <select id="pageQuery" resultType="com.sky.vo.DishVO">
      select d.*, c.name as categoryName
      from dish d left outer join category c on d.category_id = c.id
      <where>
        <if test="name != null">
          and d.name like concat('%', #{name}, '%')
        </if>
        <if test="categoryId != null">
          and d.category_id = #{categoryId}
        </if>
        <if test="status != null">
          and d.status = #{status}
        </if>
      </where>
      order by d.create_time desc
    </select>

13.5 使用1:PageHelper.startPage(2, 5)

01.代码
    //Page<Object> page = PageHelper.startPage(2, 5);
    public static void queryStudents() throws IOException {
        Reader reader = Resources.getResourceAsReader("conf.xml");
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = sessionFactory.openSession();
        StudentMapper studentMapper = session.getMapper(StudentMapper.class);

        //加入分页功能
        Page<Object> page = PageHelper.startPage(2, 5);//第二页,每页三条数据

        List<Student> list = studentMapper.queryStudents();
        for (Student student: list){
            System.out.println(student);
        }
        System.out.println("当前页:" + page.getPageNum());
        System.out.println("总数据:" + page.getTotal());
        System.out.println("总页码:" + page.getPages());
        System.out.println("页面大小:" + page.getPageSize());
        session.close();
    }

13.6 使用2:lambda

01.代码
    //lambda
    public static void queryStudents() throws IOException {
        Reader reader = Resources.getResourceAsReader("conf.xml");
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = sessionFactory.openSession();
        StudentMapper studentMapper = session.getMapper(StudentMapper.class);

        //加入分页功能
        Page<Student> page = PageHelper.startPage(2,3).doSelectPage(()-> studentMapper.queryStudents());
        List<Student> result = page.getResult();
        for (Student student: result){
            System.out.println(student);
        }
        System.out.println("当前页:" + page.getPageNum());
        System.out.println("总数据:" + page.getTotal());
        System.out.println("总页码:" + page.getPages());
        System.out.println("页面大小:" + page.getPageSize());
        session.close();
    }

13.7 使用3:PageInfo

01.代码
    //PageInfo
    public static void queryStudents() throws IOException {
        Reader reader = Resources.getResourceAsReader("conf.xml");
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = sessionFactory.openSession();
        StudentMapper studentMapper = session.getMapper(StudentMapper.class);

        PageHelper.startPage(2, 5);

        List<Student> list = studentMapper.queryStudents();
        for (Student student: list){
            System.out.println(student);
        }
        //加入分页功能
        PageInfo<Student> pageInfo = new PageInfo<>(list);

        System.out.println("当前页:" + pageInfo.getPageNum());
        System.out.println("总数据:" + pageInfo.getTotal());
        System.out.println("总页码:" + pageInfo.getPages());
        System.out.println("页面大小:" + pageInfo.getPageSize());

        System.out.println("最开头的那一页" + pageInfo.getNavigateFirstPage());
        System.out.println("每一页的页号");
        //pageInfo.getNavigatepageNums()
        for(int  pageNum: pageInfo.getNavigatepageNums()){
            System.out.println(pageNum);
        }

        session.close();
    }

14 tkmybatis

14.1 介绍

01.介绍
    MaBtis:SqlSessionFactoryBean
    MyBatisPlus:MyBatisSqlSessionFactoryBean
    Mapper:SqlSessionFactoryBean(MaBtis扩展)

02.特性
    1.属性:驼峰命名
    2.主键:包装类Integer/Long,不要使用基本类型
    3.Mapper单表操作,并不是多表操作

14.2 Spring整合MyMapper

01.pom.xml(spring集成)
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>

        <groupId>org.example</groupId>
        <artifactId>Mapper</artifactId>
        <version>1.0-SNAPSHOT</version>

        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
            </plugins>
        </build>

        <dependencies>
            <!--mybatis-->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.4.6</version>
            </dependency>

            <!--mybatis-spring整合包-->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis-spring</artifactId>
                <version>2.0.1</version>
            </dependency>

            <!--spring-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>4.3.24.RELEASE</version>
            </dependency>

            <!--spring-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-tx</artifactId>
                <version>4.3.24.RELEASE</version>
            </dependency>

            <!--spring-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>4.3.24.RELEASE</version>
            </dependency>

            <!--引入mapper插件-->
            <dependency>
                <groupId>tk.mybatis</groupId>
                <artifactId>mapper</artifactId>
                <version>4.1.5</version>
            </dependency>

            <!--连接池c3p0-->
            <dependency>
                <groupId>com.mchange</groupId>
                <artifactId>c3p0</artifactId>
                <version>0.9.5.2</version>
            </dependency>

            <!--mysql-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.40</version>
            </dependency>

            <!--oracle-->
            <dependency>
                <groupId>ojdbc</groupId>
                <artifactId>ojdbc7</artifactId>
                <version>7.0.0.1</version>
            </dependency>

            <!--日志-->
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.17</version>
            </dependency>
        </dependencies>
    </project>

02.扫描XxMapper接口所在的包:tk
    <!--扫描接口下所在包:tk-->
    <bean class="tk.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="org.myslayers.mapper"/>
    </bean>
    -----------------------------------------------------------------------------------------------------
    <!--扫描接口下所在包:mybatis、mybatisplus-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="org.myslayers.mapper"/>
    </bean>

03.接口
    MyBatis
    public interface StudentMapper                                     +自己写SQL
    ---------------------------------------------------------------------------------------------------------
    mybatisplus
    public interface StudentMapper extends BaseMapper<Student>         +内置SQL
    ---------------------------------------------------------------------------------------------------------
    Mapper
    public interface StudentMapper extends Mapper<Student>             +内置SQL
    ---------------------------------------------------------------------------------------------------------
    核心:Mapper父接口中有很多细化的父接口(每个细化的父接口负责一件事情:x)

04.项目配置
    a.applicationContext.xml
        <?xml version="1.0" encoding="UTF-8"?>
        <beans xmlns="http://www.springframework.org/schema/beans"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:context="http://www.springframework.org/schema/context"
               xmlns:tx="http://www.springframework.org/schema/tx"
               xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">

            <!--数据源c3p0 -->
            <context:property-placeholder  location="classpath:db.properties" />
            <!--数据源存在位置:db.properties-->
            <bean  id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
                <property name="driverClass" value="${jdbc.driver}"/>
                <property name="jdbcUrl" value="${jdbc.url}"/>
                <property name="user" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </bean>

            <!--事务管理器-->
            <bean id="dataSourceTransactionManger"  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
                <!--数据源-->
                <property name="dataSource" ref="dataSource" />
            </bean>
            <tx:annotation-driven  transaction-manager="dataSourceTransactionManger" />

            <!--操作MyBatisplus-->
            <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
                <!--数据源-->
                <property name="dataSource" ref="dataSource"/>
            </bean>

            <!--扫描接口下所在包-->
            <bean class="tk.mybatis.spring.mapper.MapperScannerConfigurer">
                <property name="basePackage" value="org.myslayers.mapper"/>
            </bean>
        </beans>

    b.db.properties
        jdbc.driver=com.mysql.jdbc.Driver
        jdbc.url=jdbc:mysql://localhost:3306/mp
        jdbc.username=root
        jdbc.password=4023615

        ##oracle
        #jdbc.driver=oracle.jdbc.OracleDriver
        #jdbc.url=jdbc:oracle:thin:@localhost:1521:ORCL
        #jdbc.username=scott
        #jdbc.password=tiger

    c.log4j.properties
        log4j.rootLogger=DEBUG, stdout
        log4j.appender.stdout=org.apache.log4j.ConsoleAppender
        log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
        log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

    d.mybatis.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>
            <!--配置日志 LOG4J日志默认的配置文件是 log4j.properties-->
            <settings>
                <setting name="mapUnderscoreToCamelCase" value="true"/>
                <setting name="logImpl" value="LOG4J"/>
            </settings>
        </configuration>

14.3 注解:@id、@Transient

14.4 Mapper默认不会将主键值回写到对象

14.5 CRUD操作

14.6 自由组合、自定义Mapper接口

14.7 Mapper项目

14.8 逆向工程