• SSM学习笔记之MyBatis


    0x00 概述

    本文涉及SSM框架中MyBatis知识点,详细见目录。

    0x01 MyBatis介绍

    MyBatis是一个半自动的ORM框架,你给sql,我来执行。

    ORM(Object Relation Mapping)对象关系映射,将Java中的一个对象与数据表中的一行记录一一对应。

    ORM框架提供了实体类与数据表的映射关系,通过映射文件的配置,将文件保存到了数据表中,实现了对象的持久化。

     MyBatis特点:

    • 支持自定义sql,存储过程
    • 对原有的JDBC进行了封装,几乎消除了所有JDBC代码(打开连接,执行sql等),让开发者专注sql
    • 支持XML和注解配置(@xxxx)的方式自动完成ORM操作,实现结果映射,对象的属性和数据表的对应关系就叫映射

    0x02 MyBatis框架部署

    2.1 创建Maven项目

    • java工程
    • Web工程

    2.2 在项目中添加MyBatis依赖

    • 在pom.xml中添加依赖--mybatis和mysql driver
    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
    
    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.4.6</version>
    </dependency>

    说明,在pom.xml中添加包的说明以后,需要到Maven中刷新一下,将包安装进来

    2.3 创建MyBatis配置文件,配置数据库连接

    需要先定义MyBatis配置模版,其他的模版也可以在这里进行配置

    选择resources--右键New--Edit File Templates

     

    <?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>

    </configuration>

     

     

     在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配置数据库连接信息  -->
    <!--    environments中可以定义多个environments标签(正式测试预发布),每个environments可以定义一套数据库连接配置-->
    <!--    default属性,用来执行使用哪个environment 标签,-->
        <environments default="mysql">
            <environment id="mysql">
    <!--      transactionManager用于配置数据库管理方法      -->
                <transactionManager type="JDBC"></transactionManager>
    <!--            dataSource标签用来配置数据库连接信息-->
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/jdbcpractice?characterEncoding = utf-8"/>
                    <property name="username" value="root"/>
                    <property name="password" value="123456"/>
                </dataSource>
            </environment>
        </environments>
    </configuration>

    0x03 MyBatis框架使用

    案例:学生信息的数据库操作

    3.1 创建数据表

    CREATE TABLE tb_students(
            sid int PRIMARY key auto_increment,
            stu_num CHAR(5) not null UNIQUE,
            stu_name VARCHAR(20) not null,
            sut_gender CHAR(2) not null,
            stu_age int not null
    );

    3.2 创建实体类

    导入lombok,然后添加注释

    package com.MyBatisDemo.pojo;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.ToString;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @ToString
    public class Student {
        private int stuId;
        private String stuNum;
        private String stuName;
        private String stuGender;
        private  int stuAge;
    
    }

    3.3 创建DAO接口,并创建操作方法

    package com.MyBatisDemo.dao;
    
    import com.MyBatisDemo.pojo.Student;
    
    public interface StudentDAO {
        public int insertStudent(Student student);
        public int deleteStudent(Student stuNum);
    }

    3.4 创建DAO接口的映射文件

    模版:

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

    在resources目录下,新建mappers文件夹

    在mappers中新建StudentMapper.xml文件,这个可以根据上面的模版进行创建,注意这里的StudentMapper名称建议和DAO联系起来

    在映射文件中,对DAO中的方法进行实现

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!--mapper文件相当于DAO接口的实现类,namespace属性要指定实现DAO接口的全限定名(全限定名:包名加类名)-->
    <!-- copy reference-->
    <mapper namespace="com.MyBatisDemo.dao.StudentDAO">
    <!--parameterType 这个参数在DAO中已经指定类型了,在这里可以省略-->
        <insert id="insertStudent" parameterType="com.MyBatisDemo.pojo.Student">
             insert into tb_student(stu_num,stu_name,stu_gender,stu_age)
             values(#{stuNum},#{stuName},#{stuGender},#{stuAge})
        </insert>
        <delete id="deleteStudent">
            delete  from  tb_student where stu_num = #{stuNum}
        </delete>
    </mapper>

    3.5 将映射文件添加到主配置文件

    <?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配置数据库连接信息  -->
    <!--    environments中可以定义多个environments标签(正式测试预发布),每个environments可以定义一套数据库连接配置-->
    <!--    default属性,用来执行使用哪个environment 标签,-->
        <environments default="mysql">
            <environment id="mysql">
    <!--      transactionManager用于配置数据库管理方法      -->
                <transactionManager type="JDBC"></transactionManager>
    <!--            dataSource标签用来配置数据库连接信息-->
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/jdbcpractice?characterEncoding = utf-8"/>
                    <property name="username" value="root"/>
                    <property name="password" value="123456"/>
                </dataSource>
            </environment>
        </environments>
    
        <mappers>
            <mapper resource="mappers/StudentMapper.xml"></mapper>
        </mappers>
    
    </configuration>

    0x04 单元测试

    测试类往往是在测试类名后加Test,测试方法往往是在测试方法名前加Test

    4.1 添加单元测试依赖

    junit

    4.2 创建测试类

    在被测试接口类名后面, alt+insert --> test

    package com.MyBatisDemo.dao;
    
    import com.MyBatisDemo.pojo.Student;
    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.Test;
    
    import javax.annotation.Resource;
    
    import java.io.IOException;
    import java.io.InputStream;
    
    import static org.junit.Assert.*;
    
    public class StudentDAOTest {
    
        @Test
        public void insertStudent() {
    
            try {
                //加载mybatis配置文件
                InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
                //创建builder
                SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
                //会话工厂,连接工厂
                SqlSessionFactory factory = builder.build(is);
                //sqlsession 代表数据库的连接,也代表数据库的连接对象
                //会话(连接)
                SqlSession sqlSession = factory.openSession();
                StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
    //            System.out.println(studentDAO);
                int i = studentDAO.insertStudent(new Student(0,"10002","java少年","男",24));
                //需要手动提交
                sqlSession.commit();
                System.out.println(i);
            } catch (IOException e) {
                e.printStackTrace();
            }
    
    
        }
    
        @org.junit.Test
        public void deleteStudent() {
        }
    }

    0x05 MyBatisCRUD操作

    案例:学生信息的增删改查

    5.1 添加操作

    5.2 删除操作

    根据学号删除一条学生信息

    步骤:

    • 在StudentDAO中定义删除方法
    package com.MyBatisDemo.dao;
    
    import com.MyBatisDemo.pojo.Student;
    
    public interface StudentDAO {
        public int insertStudent(Student student);
        public int deleteStudent(String stuNum);
    }
    • 在StudentMapper.xml中对接口方法进行“实现”
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!--mapper文件相当于DAO接口的实现类,namespace属性要指定实现DAO接口的全限定名(全限定名:包名加类名)-->
    <!-- copy reference-->
    <mapper namespace="com.MyBatisDemo.dao.StudentDAO">
    <!--parameterType 这个参数在DAO中已经指定类型了,在这里可以省略-->
        <insert id="insertStudent" parameterType="com.MyBatisDemo.pojo.Student">
             insert into tb_students(stu_num,stu_name,stu_gender,stu_age)
             values(#{stuNum},#{stuName},#{stuGender},#{stuAge})
        </insert>
        <delete id="deleteStudent">
            delete  from  tb_students where stu_num = #{stuNum}
        </delete>
    </mapper>
    • 测试:在StudentDAO的测试类中添加测试方法

    package com.MyBatisDemo.dao;
    
    import com.MyBatisDemo.pojo.Student;
    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.Test;
    
    import javax.annotation.Resource;
    
    import java.io.IOException;
    import java.io.InputStream;
    
    import static org.junit.Assert.*;
    
    public class StudentDAOTest {
    
        @Test
        public void insertStudent() {
    
            try {
                //加载mybatis配置文件
                InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
                //创建builder
                SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
                //会话工厂,连接工厂
                SqlSessionFactory factory = builder.build(is);
                //sqlsession 代表数据库的连接,也代表数据库的连接对象
                //会话(连接)
                SqlSession sqlSession = factory.openSession();
                StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
    //            System.out.println(studentDAO);
                int i = studentDAO.insertStudent(new Student(0,"10002","java少年","男",24));
                //需要手动提交
                sqlSession.commit();
                System.out.println(i);
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    
        @Test
        public void testDeleteStudent() {
            try {
    
                InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
                SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
                //SqlSessionFactory表示mybatis的会话工厂,工厂模式
                SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
                //SqlSession 对象是mybatis和数据库之间的连接,也就是会话,创建了连接,可以得到所有的mapper对象(映射关系)
                SqlSession sqlSession = sqlSessionFactory.openSession();
                //通过SqlSession 对象调用getMapper方法获取DAO接口对象
                StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
                //调用被测试方法
                int i = studentDAO.deleteStudent("10001");
                //提交事务
                sqlSession.commit();
                System.out.println(i);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    5.3 修改操作

    根据学号,修改其他字段信息

    @Test
    public void  testUpdateStudent(){
        try {
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory sqlSessionFactory = builder.build(is);
            SqlSession sqlSession = sqlSessionFactory.openSession();
            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
            int i = studentDAO.updateStudent(new Student(3,"10002",".net小小少年","男",25));
            sqlSession.commit();
            assertEquals(1,i);//期望,单元测试的期望返回结果
            System.out.println(i);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    5.4 查询操作--查询所有

    package com.MyBatisDemo.dao;
    
    import com.MyBatisDemo.pojo.Student;
    
    import java.util.List;
    
    public interface StudentDAO {
        public int insertStudent(Student student);
        public int deleteStudent(String stuNum);
        public int updateStudent(Student student);
        public List<Student> listStudents();
    
    }
    <!--    resultType执行查询结果,封装的对象的实体类-->
    <!--    resultSets指定当前操作返回的集合类型(可省略)-->
    <!--    resultType返回的类型-->
    <!--    <select id="listStudents" resultType="com.liguoqing.pojo.Student" resultSets="java.util.List">-->
    <!--        select-->
    <!--            sid as stuId,-->
    <!--            stu_num as stuNum,-->
    <!--            stu_name as stuName,-->
    <!--            stu_gender as stuGender,-->
    <!--            stu_age as stuAge-->
    <!--        from tb_students-->
    <!--    </select>-->
    
    <!--resultMap标签用于定义实体类和数据表的映射关系(ORM)-->
    <resultMap id="studentMap" type="com.MyBatisDemo.pojo.Student">
        <id column="sid" property="stuId"/>
        <result column="stu_num" property="stuNum"/>
        <result column="stu_name" property="stuName"/>
        <result column="stu_gender" property="stuGender"/>
        <result column="stu_age" property="stuAge"/>
    </resultMap>
    
    <!--resultMap 用于引用一个实体的映射关系,当配置了resultMap以后,resultType就可以省略-->
    <select id="listStudents" resultMap="studentMap">
        select sid,
               stu_num,
               stu_name,
               stu_gender,
               stu_age
        from tb_students
    </select>
    @Test
    public void testStudentList() {
        try {
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory sqlSessionFactory = builder.build(is);
            SqlSession sqlSession = sqlSessionFactory.openSession();
            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
            List<Student> studentList = studentDAO.listStudents();
            assertNotNull(studentList);
            sqlSession.commit();
            for (Student student : studentList
            ) {
                System.out.println(student);
            }
    
        } catch (IOException e) {
    
        }
    }

    5.5 查询操作--查询一条记录

    根据学号查询一个学生信息

    在StudentDAO中定义接口方法

    package com.MyBatisDemo.dao;
    
    import com.MyBatisDemo.pojo.Student;
    
    import java.util.List;
    
    public interface StudentDAO {
        public int insertStudent(Student student);
        public int deleteStudent(String stuNum);
        public int updateStudent(Student student);
        public List<Student> listStudents();
        public Student queryStudent(String stuNum);
    
    }

    在StudentDAOMapper.xml中配置StudentDAO接口的方法实现--SQL

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!--mapper文件相当于DAO接口的实现类,namespace属性要指定实现DAO接口的全限定名(全限定名:包名加类名)-->
    <!-- copy reference-->
    <mapper namespace="com.MyBatisDemo.dao.StudentDAO">
        <!--parameterType 这个参数在DAO中已经指定类型了,在这里可以省略-->
        <insert id="insertStudent" parameterType="com.MyBatisDemo.pojo.Student">
            insert into tb_students(stu_num, stu_name, stu_gender, stu_age)
            values (#{stuNum}, #{stuName}, #{stuGender}, #{stuAge})
        </insert>
    
        <delete id="deleteStudent">
            delete
            from tb_students
            where stu_num = #{stuNum}
        </delete>
    
        <update id="updateStudent">
            update tb_students
            set stu_name   = #{stuName},
                stu_gender = #{stuGender},
                stu_age    = #{stuAge}
            where stu_num = #{stuNum}
    
        </update>
    
        <!--    resultType执行查询结果,封装的对象的实体类-->
        <!--    resultSets指定当前操作返回的集合类型(可省略)-->
        <!--    resultType返回的类型-->
        <!--    <select id="listStudents" resultType="com.MyBatisDemo.pojo.Student" resultSets="java.util.List">-->
        <!--        select-->
        <!--            sid as stuId,-->
        <!--            stu_num as stuNum,-->
        <!--            stu_name as stuName,-->
        <!--            stu_gender as stuGender,-->
        <!--            stu_age as stuAge-->
        <!--        from tb_students-->
        <!--    </select>-->
    
    <!--resultMap标签用于定义实体类和数据表的映射关系(ORM)-->
        <resultMap id="studentMap" type="com.MyBatisDemo.pojo.Student">
            <id column="sid" property="stuId"/>
            <result column="stu_num" property="stuNum"/>
            <result column="stu_name" property="stuName"/>
            <result column="stu_gender" property="stuGender"/>
            <result column="stu_age" property="stuAge"/>
        </resultMap>
    <!--    resultMap 用于引用一个实体的映射关系,当配置了resultMap以后,resultType就可以省略-->
        <select id="listStudents" resultMap="studentMap">
            select sid,
                   stu_num,
                   stu_name,
                   stu_gender,
                   stu_age
            from tb_students
        </select>
    
    
        <select id="queryStudent" resultMap="studentMap">
            select sid,
                   stu_num,
                   stu_name,
                   stu_gender,
                   stu_age
            from tb_students
            where stu_num = #{stuNum}
        </select>
    
    </mapper>

    单元测试--查询不需要提交事物

    @Test
    public void testStudent() {
        try {
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory sqlSessionFactory = builder.build(is);
            SqlSession sqlSession = sqlSessionFactory.openSession();
            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
            Student student = studentDAO.queryStudent("10002");
             assertNotNull(student);
            //查询不用提交事务
            System.out.println(student);
        } catch (IOException e) {
    
        }
    }

     5.6 查询操作--多参数查询

    分页查询

    • 参数start, pagesize

    在StudentDAO中定义操作方法,如果方法有多个参数,使用@Param注解声明参数的别名

    package com.MyBatisDemo.dao;
    
    import com.MyBatisDemo.pojo.Student;
    import org.apache.ibatis.annotations.Param;
    import java.util.HashMap;
    import java.util.List;
    
    public interface StudentDAO {
        /*
        * 在MyBatis进行操作:
        * 1:如果操作方法只有一个简单类型或者字符串类型的参数,
        * 在Mapper配置中可以直接通过#{key}获取,这个key的占位符号可以随便写
        * 2:如果操作方法有一个对象类型的参数,
        * 在Mapper配置中可以直接通过#{attrName}获取对象的指定属性值(attrName必须是参数对象的属性)
        * 3:如果操作方法有一个Map类型的参数,
        * 在Mapper配置中可以直接通过#{key}获取key的指定value值
        * 4:如果操作方法有多个参数,该如何处理呢?
        * 通过MyBatis自带的参数 arg0 arg1 .. 来获取相应的参数
        * 也可以通过@Param("key") 给参数添加注解的方式
         */
        public int insertStudent(Student student);
        public int deleteStudent(String stuNum);
        public int updateStudent(Student student);
        public List<Student> listStudents();
        public Student queryStudent(String stuNum);
    //    public List<Student> listStudentsByPage(HashMap<String,Integer> map);
    //    public List<Student> listStudentsByPage(int start,int pageSize);
        public List<Student> listStudentsByPage(@Param("start") int start
                                            ,@Param("pageSize") int pageSize);
    }

     在StudentMapper.xml配置sql时,使用#{}获取到指定的参数

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!--mapper文件相当于DAO接口的实现类,namespace属性要指定实现DAO接口的全限定名(全限定名:包名加类名)-->
    <!-- copy reference-->
    <mapper namespace="com.MyBatisDemo.dao.StudentDAO">
        <!--parameterType 这个参数在DAO中已经指定类型了,在这里可以省略-->
        <insert id="insertStudent" parameterType="com.MyBatisDemo.pojo.Student">
            insert into tb_students(stu_num, stu_name, stu_gender, stu_age)
            values (#{stuNum}, #{stuName}, #{stuGender}, #{stuAge})
        </insert>
    
        <delete id="deleteStudent">
            delete
            from tb_students
            where stu_num = #{stuNum}
        </delete>
    
        <update id="updateStudent">
            update tb_students
            set stu_name   = #{stuName},
                stu_gender = #{stuGender},
                stu_age    = #{stuAge}
            where stu_num = #{stuNum}
    
        </update>
    
        <!--    resultType执行查询结果,封装的对象的实体类-->
        <!--    resultSets指定当前操作返回的集合类型(可省略)-->
        <!--    resultType返回的类型-->
        <!--    <select id="listStudents" resultType="com.liguoqing.pojo.Student" resultSets="java.util.List">-->
        <!--        select-->
        <!--            sid as stuId,-->
        <!--            stu_num as stuNum,-->
        <!--            stu_name as stuName,-->
        <!--            stu_gender as stuGender,-->
        <!--            stu_age as stuAge-->
        <!--        from tb_students-->
        <!--    </select>-->
    
        <!--resultMap标签用于定义实体类和数据表的映射关系(ORM)-->
        <resultMap id="studentMap" type="com.MyBatisDemo.pojo.Student">
            <id column="sid" property="stuId"/>
            <result column="stu_num" property="stuNum"/>
            <result column="stu_name" property="stuName"/>
            <result column="stu_gender" property="stuGender"/>
            <result column="stu_age" property="stuAge"/>
        </resultMap>
        <!--    resultMap 用于引用一个实体的映射关系,当配置了resultMap以后,resultType就可以省略-->
        <select id="listStudents" resultMap="studentMap">
            select sid,
                   stu_num,
                   stu_name,
                   stu_gender,
                   stu_age
            from tb_students
        </select>
    
    
        <select id="queryStudent" resultMap="studentMap">
            select sid,
                   stu_num,
                   stu_name,
                   stu_gender,
                   stu_age
            from tb_students
            where stu_num = #{stuNum}
        </select>
    
    <!--    <select id="listStudentsByPage" resultMap="studentMap">-->
    <!--        select sid,-->
    <!--               stu_num,-->
    <!--               stu_name,-->
    <!--               stu_gender,-->
    <!--               stu_age-->
    <!--        from tb_students limit #{start}, #{pageSize}-->
    <!--    </select>-->
    
            <select id="listStudentsByPage" resultMap="studentMap">
                select sid,
                       stu_num,
                       stu_name,
                       stu_gender,
                       stu_age
                from tb_students
                        limit #{start}, #{pageSize}
                <!-- limit #{arg0}, #{arg1}-->
                <!-- limit #{param1}, #{param2}-->
    
            </select>
    
    
    </mapper>

    注意:如果DAO操作方法,没有通过@Param指定参数别名,在SQL中也可以通过MyBatis自带的arg0,arg1...或者param1,param2...获取参数

    单元测试:

    @Test
        public void testlistStudentsByPage() {
            try {
                InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
                SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
                SqlSessionFactory sqlSessionFactory = builder.build(is);
                SqlSession sqlSession = sqlSessionFactory.openSession();
                StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
    
    //            HashMap<String,Integer> map = new HashMap<String,Integer>();
    //            map.put("start",0);
    //            map.put("pageSize",2);
    //            List<Student> studentList = studentDAO.listStudentsByPage(map);
    
                List<Student> studentList = studentDAO.listStudentsByPage(0,2);
    
                assertNotNull(studentList);
                sqlSession.commit();
                for (Student student : studentList
                ) {
                    System.out.println(student);
                }
            } catch (IOException e) {
    
            }
        }

    5.7 查询操作--查询总记录数

    StudentDAO

    package com.MyBatisDemo.dao;
    
    import com.MyBatisDemo.pojo.Student;
    import org.apache.ibatis.annotations.Param;
    
    import java.util.HashMap;
    import java.util.List;
    
    public interface StudentDAO {
        /*
        * 在MyBatis进行操作:
        * 1:如果操作方法只有一个简单类型或者字符串类型的参数,
        * 在Mapper配置中可以直接通过#{key}获取,这个key的占位符号可以随便写
        * 2:如果操作方法有一个对象类型的参数,
        * 在Mapper配置中可以直接通过#{attrName}获取对象的指定属性值(attrName必须是参数对象的属性)
        * 3:如果操作方法有一个Map类型的参数,
        * 在Mapper配置中可以直接通过#{key}获取key的指定value值
        * 4:如果操作方法有多个参数,该如何处理呢?
        * 通过MyBatis自带的参数 arg0 arg1 .. 来获取相应的参数
        * 也可以通过@Param("key") 给参数添加注解的方式
         */
        public int insertStudent(Student student);
        public int deleteStudent(String stuNum);
        public int updateStudent(Student student);
        public List<Student> listStudents();
        public Student queryStudent(String stuNum);
    //    public List<Student> listStudentsByPage(HashMap<String,Integer> map);
    //    public List<Student> listStudentsByPage(int start,int pageSize);
        public List<Student> listStudentsByPage(@Param("start") int start
                                            ,@Param("pageSize") int pageSize);
    
        public int getCount();
    
    }

    在StudentMapper.xml配置sql时,使用resultType指定当前操作的返回类型为int

    <select id="getCount" resultType="int">
        select count(1) from tb_students
    </select>

    单元测试:

    @Test
    public void testGetStudentCount(){
        try{
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory sqlSessionFactory = builder.build(is);
            SqlSession sqlSession = sqlSessionFactory.openSession();
            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
            int i = studentDAO.getCount();
            System.out.println(i);
        }catch (IOException e){
    
        }
    
    }

    5.8 添加操作回填生成的主键

    在StudentMapper.xml的insert标签中,useGeneratedKeys设置添加操作是否需要回填生成的主键keyProperty设置回填的位置

    <!--parameterType 这个参数在DAO中已经指定类型了,在这里可以省略-->
    <!--useGeneratedKeys 设置添加操作是否需要回填生成的主键-->
    <!--keyProperty 设置回填的位置-->
        <insert id="insertStudent" parameterType="com.MyBatisDemo.pojo.Student" useGeneratedKeys="true" keyProperty="stuId">
            insert into tb_students(stu_num, stu_name, stu_gender, stu_age)
            values (#{stuNum}, #{stuName}, #{stuGender}, #{stuAge})
        </insert>

    0x06 MyBatis工具类封装

    MyBatisUtil

    package com.MyBatisDemo.utils;
    
    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 java.io.IOException;
    
    public class MyBatisUtil {
    
        private static SqlSessionFactory factory;//单例的
        private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>(); //线程锁,各个线程之间不影响,例如开启关闭数据库连接之类的
    
        static {
            try {
                //加载myBatis配置文件,创建会话工厂
                factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        //得到sqlSession对象
        public static SqlSession getSqlSession() throws IOException {
            SqlSession sqlSession = local.get();
            if (sqlSession == null){
                sqlSession = factory.openSession();
                local.set(sqlSession);
            }
            return sqlSession;
        }
    
        public static <T extends Object>T getMapper(Class<T> C) throws IOException {
            SqlSession sqlSession = getSqlSession();
            T dao = sqlSession.getMapper(C);
            return dao;
        }
    
        public static SqlSessionFactory getFactory() throws IOException {
            if (factory == null){
                factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
            }
            return factory;
        }
    
    }

    增删改,需要commit操作

    0x07 事务管理

    sqlSession对象:

    • getMapper(DAO.class)获取Mapper(DAO接口的实例)
    • 事物管理

    7.1 手动提交事物

    sqlSession.commit()  提交事务

    sqlSession.rollback()  事务回滚

    @Test
        public void insertStudent() {
    
            try {
    //            //加载mybatis配置文件
    //            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    //            //创建builder
    //            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    //            //会话工厂,连接工厂
    //            SqlSessionFactory factory = builder.build(is);
    //            //sqlsession 代表数据库的连接,也代表数据库的连接对象
    //            //会话(连接)
    //            SqlSession sqlSession = factory.openSession();
    //            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
    //            System.out.println(studentDAO);
    
                 SqlSession sqlSession = MyBatisUtil.getSqlSession();
                 //1:当我们获取sqlSession对象时,就默认开启了事务
                try{
                    StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
                    Student student = new Student(0, "10005", "java少年", "男", 24);
                    int i = studentDAO.insertStudent(student);
                    //2:操作完成并成功以后,需要手动提交
                    sqlSession.commit();
                    System.out.println(student);
                    System.out.println(i);
                }catch (Exception e){
                    //3:当操作出现异常,调用rollback进行回滚
                    sqlSession.rollback();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }

     7.2 自动提交事务

    sqlSession = factory.openSession(true);//这里的参数,如果不填,默认是false,也就是需要手动进行提交,如果填了true会自动提交事务

    MyBatisUtil优化

    package com.MyBatis.utils;
    
    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 java.io.IOException;
    
    public class MyBatisUtil {
    
        private static SqlSessionFactory factory;//单例的
        private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>(); //线程锁,各个线程之间不影响,例如开启关闭数据库连接之类的
    
        static {
            try {
                //加载myBatis配置文件,创建会话工厂
                factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        //得到sqlSession对象
                                          
            SqlSession sqlSession = local.get();
            if (sqlSession == null){
                sqlSession = factory.openSession(isAutoCommit);//这里的参数,如果不填,默认是false,也就是需要手动进行提交,如果填了true会自动提交事务
                local.set(sqlSession);
            }
            return sqlSession;
        }
    
        public static <T extends Object>T getMapper(Class<T> C) throws IOException {
            SqlSession sqlSession = getSqlSession(true);
            T dao = sqlSession.getMapper(C);
            return dao;
        }
    
        public static SqlSessionFactory getFactory() throws IOException {
            if (factory == null){
                factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
            }
            return factory;
        }
    
    
    }

    测试操作:

        @Test
        public void testDeleteStudent() {
            try {
    
    //            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    //            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
    //            //SqlSessionFactory表示mybatis的会话工厂,工厂模式
    //            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
    //            //SqlSession 对象是mybatis和数据库之间的连接,也就是会话,创建了连接,可以得到所有的mapper对象(映射关系)
    //            SqlSession sqlSession = sqlSessionFactory.openSession();
    //            //通过SqlSession 对象调用getMapper方法获取DAO接口对象
    //            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
    //            //调用被测试方法
    //            int i = studentDAO.deleteStudent("10001");
    //            //提交事务
    //            sqlSession.commit();
    //            System.out.println(i);
                StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);
                int i = studentDAO.deleteStudent("10001");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    当有多个操作一般都使用手动提交

    MyBatisUtil事务管理优化:

    package com.MyBatisDemo.utils;
    
    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 java.io.IOException;
    
    public class MyBatisUtil {
    
        private static SqlSessionFactory factory;//单例的
        private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>(); //线程锁,各个线程之间不影响,例如开启关闭数据库连接之类的
    
        static {
            try {
                //加载myBatis配置文件,创建会话工厂
                factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
    
        //得到sqlSession对象
        private static SqlSession getSqlSession(boolean isAutoCommit) throws IOException {
            SqlSession sqlSession = local.get();
            if (sqlSession == null){
                sqlSession = factory.openSession(isAutoCommit);//这里的参数,如果不填,默认是false,也就是需要手动进行提交,如果填了true会自动提交事务
                local.set(sqlSession);
            }
            return sqlSession;
        }
    
        //手动事务管理
        public static SqlSession getSqlSession() throws IOException {
            return getSqlSession(false);
        }
    
        //自动事务管理
        public static <T extends Object>T getMapper(Class<T> C) throws IOException {
            SqlSession sqlSession = getSqlSession(true);
            T dao = sqlSession.getMapper(C);
            return dao;
        }
    
        public static SqlSessionFactory getFactory() throws IOException {
            if (factory == null){
                factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
            }
            return factory;
        }
    
    
    }

    0x08 MyBatis主配置文件

    mybatis-config.xml是MyBatis框架的主配置文件,主要用于配置MyBatis数据源以及工作属性信息

    注意:

    • 标签使用时需要注意顺序
    • 空标签会导致报错

    8.1 properties标签

    • 可以定义键值对
    • 可以引用属性文件

     在resource文件夹下创建jdbc.properties文件,配置键值对如下:

    mysql_driver=com.mysql.jdbc.Driver
    mysql_url=jdbc:mysql://localhost:3306/jdbcpractice?characterEncoding = utf-8
    mysql_username=root
    mysql_password=123456

    在mybatis-config.xml中通过properties标签引用jdbc.properties文件,引入后在配置environment时,可以直接使用jdbc.properties的key获取value

    8.2 settings标签

    <!--设置mybatis的属性-->
        <settings>
    <!--启动二级缓存-->
            <setting name="cacheEnable" value="true"/>
    <!--启用延迟加载-->
            <setting name="lazyLoadingEnabled" value="true"/>
        </settings>

    8.3 typeAliases标签

        <!--typeAliases用于给实体类取别名,在映射文件中可以直接使用别名来替代实体类的全限定名-->
        <typeAliases>
            <typeAlias type="com.liguoqing.pojo.Student" alias="Student"></typeAlias>
        </typeAliases>

    8.4 plugins标签

        <!--plugins主要用于配置MyBatis插件,例如分页插件-->
        <plugins>
            <plugin interceptor=""></plugin>
        </plugins>

    8.5 environment

    <!--  environments配置数据库连接信息  -->
    <!--    environments中可以定义多个environments标签(正式测试预发布),每个environments可以定义一套数据库连接配置-->
    <!--    default属性,用来执行使用哪个environment 标签,-->
        <environments default="mysql">
            <environment id="mysql">
    <!--      transactionManager用于配置数据库管理方法     type="JDBC" 可以进行事务的提交和回滚操作,type="MANAGED" 事务的提交和回滚由容器进行控制 -->
                <transactionManager type="JDBC"></transactionManager>
    <!--            dataSource标签用来配置数据库连接信息 POOLED|UNPOOLED 是否使用连接池-->
                <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>

     8.6 mappers标签

    <!--    mappers用于载入映射文件,载入DAO类-->
        <mappers>
            <mapper resource="mappers/StudentMapper.xml"></mapper>
        </mappers>

    0x09 映射文件

    9.1 MyBatis初始化

    可以参考源码流程

    9.2 Mappers根标签

    mapper文件相当与DAO接口的实现类,namespace属性要执行实现DAO的全限定名(Copy Reference)

    9.3 insert标签

    声明添加操作(sql:insert...)

    常用属性:

    • id属性,绑定对应DAO中的方法
    • parameterType属性,用以指定接口中对应方法的参数类型(可省略)
    • useGeneratedKeys属性,用以设置添加操作是否需要回填生成的主键
    • keyProperty属性,指定回填的id设置到参数对应中的哪个字段中
    • timeout属性,设置超时时间,如果不设置会一直等待,如果设置了值并到时候还没执行,就会报错

    主键回填:

    方式一:

    <insert id="insertStudent" parameterType="com.liguoqing.pojo.Student" useGeneratedKeys="true" keyProperty="stuId">
        insert into tb_students(stu_num, stu_name, stu_gender, stu_age)
        values (#{stuNum}, #{stuName}, #{stuGender}, #{stuAge})
    </insert>

     方式二:

    <insert id="insertStudent">
        <selectKey keyProperty="stuId" resultType="java.lang.Integer">
            select  last_insert_id()
        </selectKey>
        insert into tb_students(stu_num, stu_name, stu_gender, stu_age)
        values (#{stuNum}, #{stuName}, #{stuGender}, #{stuAge})
    </insert>

     9.4 deleted标签

    声明删除操作

    9.5 update标签

    声明修改操作

    9.6 select标签

    声明查询操作

    • id属性,指定绑定方法的方法名
    • resultType属性,指望从这条语句中返回结果的类全限定名或者别名;注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。resultType和resultMap之间只能同时使用一个。
    • resultMap属性,对外部resultMap的命名引用,结果映射是MyBatis最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。resultType和resultMap之间只能同时使用一个。
    • parameterType属性,将会传入这条语句的参数的类全限定名或者别名。这个属性是可选的,因为MyBatis可以通过类型处理器(Typehandler)推断出具体传入语句的参数,默认值为未设置(unset)。
    • useCache属性,指定查询操作是否使用缓存
    • timeout属性,设置超时时间
    • ...其他的属性可以查看MyBatis文档

    9.7 resultMap标签

    <!--resultMap标签用于定义实体类和数据表的映射关系(ORM)-->
    <resultMap id="studentMap"  type="Student">
        <id column="sid" property="stuId"/>
        <result column="stu_num" property="stuNum"/>
        <result column="stu_name" property="stuName"/>
        <result column="stu_gender" property="stuGender"/>
        <result column="stu_age" property="stuAge"/>
    </resultMap>

    9.8 cache标签

    设置当前dao进行数据库操作时的缓存属性设置

    <cache type="" size="" readOnly="false"></cache>

    9.9 sql和include

    sql片段

    <sql id="sqlpianduan">sid,
               stu_num,
               stu_name,
               stu_gender,
               stu_age</sql>
    
    <!--    resultMap 用于引用一个实体的映射关系,当配置了resultMap以后,resultType就可以省略-->
    <select id="listStudents" resultMap="studentMap">
        select <include refid="sqlpianduan"/>
        from tb_students
    </select>

    0x0A 分页插件

    分页插件是一个独立于MyBatis之外的第三方插件;

    10.1 添加分页插件的以来--PageHelper

    <!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper</artifactId>
        <version>5.2.0</version>
    </dependency>

    10.2 配置插件

    在MyBatis的主配置文件中mybatis-config.xml 中通过 plugins标签进行配置,注意标签的位置

    <!--    plugins主要用于配置MyBatis插件,例如分页插件-->
        <plugins>
            <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
        </plugins>

     10.3 分页实例

    对学生信息进行分页查询

    @Test
    public void testGetStudentByPage() throws IOException {
        StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);//sqlSession
        PageHelper.startPage(1,4);//分页,从第1页开始,往后的4条数据
        List<Student> studentList = studentDAO.listStudents();//查全量就可以,分页组件会自动将数据进行分页
        PageInfo<Student> pageInfo = new PageInfo<Student>(studentList);
        //返回的时候只需要将pageInfo 返回就可以,因为pageInfo中就包含了分页以及数据信信息
    }

    带条件的分页

    package com.MyBatisDemo.dao;
    
    import com.MyBatisDemo.pojo.Student;
    import org.apache.ibatis.annotations.Param;
    
    import java.util.HashMap;
    import java.util.List;
    
    public interface StudentDAO {
    
        public List<Student> listStudentsByGender(String gender);
    
    }
    ————————————————————————————————————————————————————————————————————————————————
    <select id="listStudentsByGender" resultMap="studentMap">
        select <include refid="sqlpianduan"/>
            from tb_students where stu_gender = #{stuGender}
    </select>
    —————————————————————————————————————————————————————————————————————————————————
       @Test
        public void testGetStudentByPageGender() throws IOException {
            StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);
            PageHelper.startPage(1,4);
            List<Student> girls = studentDAO.listStudentsByGender("女");
            PageInfo<Student> pageInfo = new PageInfo<Student>(girls);
            //pageInfo中就包含了分页以及数据信息
            for(Student s : pageInfo.getList()){
                System.out.println(s);
            }
        }

    0x0B 关联映射

    11.1 实体关系

    实体--数据实体,实体关系指的就是数据和数据之间的关系

    例如:用户和角色,房屋和漏洞,订单和商品

    实体关系分为以下四种:

    11.1.1 一对一关联

    实例:人和身份证,学生和学生证,用户基本信息和详情

    数据表关系:

    • 主键关连:用户表主键和详情表主键相同时,表示是匹配的数据
    •  唯一外键关联

     11.1.2 一对多,多对多关联

    实例:

    • 一对多:班级和学生,类别和商品
    • 多对一:学生和班级,商品和类别

    数据表关系:

    • 在多的一端添加外键和一对一端进行关联

    11.1.3 多对多关联

    实例:用户和角色,角色和权限,房屋和业主,学生和社团,订单和商品

    数据表关系:建立第三张关系表添加两个外键,分别与两张表的主键进行关联

    用户--用户角色表--角色

     11.2创建项目,部署MyBatis框架

     11.2.1 创建web项目(Maven)

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                          http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
    
    </web-app>

     添加依赖

    <dependencies>
    
        <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
    
        <!-- https://mvnrepository.com/artifact/javax.servlet/jsp-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.0</version>
            <scope>provided</scope>
        </dependency>
    
    
    </dependencies>

     11.2.2 部署MyBatis框架

    添加依赖

    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.4.6</version>
    </dependency>
    
    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>

     11.2.3 配置文件

    帮助类

    package com.MyBatisDemo.utils;
    
    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 java.io.IOException;
    
    public class MyBatisUtil {
    
        private static SqlSessionFactory factory;//单例的
        private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>(); //线程锁,各个线程之间不影响,例如开启关闭数据库连接之类的
    
        static {
            try {
                //加载myBatis配置文件,创建会话工厂
                factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
    
        //得到sqlSession对象
        private static SqlSession getSqlSession(boolean isAutoCommit) throws IOException {
            SqlSession sqlSession = local.get();
            if (sqlSession == null){
                sqlSession = factory.openSession(isAutoCommit);//这里的参数,如果不填,默认是false,也就是需要手动进行提交,如果填了true会自动提交事务
                local.set(sqlSession);
            }
            return sqlSession;
        }
    
        //手动事务管理
        public static SqlSession getSqlSession() throws IOException {
            return getSqlSession(false);
        }
    
        //自动事务管理
        public static <T extends Object>T getMapper(Class<T> C) throws IOException {
            SqlSession sqlSession = getSqlSession(true);
            T dao = sqlSession.getMapper(C);
            return dao;
        }
    
        public static SqlSessionFactory getFactory() throws IOException {
            if (factory == null){
                //加载主配置文件
                factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
            }
            return factory;
        }
    
    
    }

     11.3 一对一关联

    实例:用户和详情

    11.3.1 创建数据表

    #用户信息表
    create table users(
        user_id int primary key auto_increment,
        user_name varchar(20) not null unique,
        user_pwd varchar(20) not null,
        user_realname varchar(20) not null,
        user_img varchar(100) not null
    );
    
    
    #用户详情表
    create table details(
        detail_id int primary key auto_increment,
        user_addr varchar(50) not null,
        user_tel char(11) not null,
        user_desc varchar(200),
        uid int not null unique
    );

    11.3.2 创建实体类

    • User
    <!--这里的@注解是通过lombook包帮我们实现的-->
    <!--这里重写ToString 是为了打印-->
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @ToString
    public class User {
        private int userId;
        private String userName;
        private String userPwd;
        private String userRealName;
        private String userImg;
    }
    •  Detail
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @ToString
    public class Detail {
        private int detailId;
        private String userAddr;
        private String userTel;
        private String userDesc;
        private int userId;
    }

    11.3.3 添加操作

    package com.MyBatisDemo.dao;
    
    import com.MyBatisDemo.pojo.User;
    
    public interface UserDao {
        public int insertUser(User user);
    }

     测试代码:

    @Test
    public void testInsertUser() throws IOException {
        //用户注册提交了基本信息和详情到Servlet,Servlet接收到注册信息,分装到User和Detail对象中
        User user = new User(0,"zhangsan6","123123","张三","01.jpg",null);
        Detail detail = new Detail(0,"安徽合肥市","15866666666","heihei",0);
    
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        try{
    
            UserDao userDao = sqlSession.getMapper(UserDao.class);
            int i = userDao.insertUser(user);//添加用户
            System.out.println(i);
    
            detail.setUserId(user.getUserId());
            DetailDao detailDao = sqlSession.getMapper(DetailDao.class);
            int n = detailDao.insertDetail(detail);//添加详情
            System.out.println(n);
    
            sqlSession.commit();//提交事务
    
        }catch (Exception e){
            e.printStackTrace();
            sqlSession.rollback();//回滚事务
        }
    
    }

      11.3.4 一对一关联查询

    在查询用户的同时关联查询出与之对应的详情

    实体

      映射文件

      子查询

      测试代码:

    @Test
    public void testQueryUser() throws IOException {
        UserDao userDao = MyBatisUtil.getMapper(UserDao.class);
        User user = userDao.queryUser("zhangsan3");
        System.out.println(user);
    }

      11.4 一对多关联

    案例:班级(一)--学生(多)

     11.4.1 创建数据表

    #创建班级信息表
    CREATE TABLE classes(
    cid int PRIMARY key auto_increment,
    cname VARCHAR(30) not null unique,
    cdesc VARCHAR(100)
    );
    
    #创建学生信息表
    CREATE TABLE students(
    sid CHAR(5) PRIMARY key,
    sname VARCHAR(20) not null,
    sage int not null,
    scid int not null 
    );

    11.4.2 创建实体类

     

      11.4.3 关联查询

    当查询一个班级的时候,要关联查询出这个班级下的所有学生

    连接查询的映射配置

     子查询

    注意mapper与主配置文件的关系以及变脸重命名之间的配置

    子查询的映射配置

      11.5 多对一关联

    实例:学生(n)-- 班级(1)

    当查询一个学生的时候,关联查询这个学生所在的班级信息

    11.5.1 创建实体类

      11.5.2 关联查询

    连接查询的映射配置

      子查询的映射配置

     

      11.6 多对多查询

    案例:学生(m)--课程(n)

    11.6.1 创建数据表

    #学生信息表如上
    #课程信息表
    CREATE TABLE courses(
     course_id int PRIMARY key auto_increment,
     course_name VARCHAR(50) not null
    );
    
    #选课信息表/成绩表(学号,课程号,成绩)
    CREATE TABLE grades(
        sid CHAR(5) not null,
        cid int not null,
        score int not null
    );

    11.6.2 关联查询

    查询学生时,同时查询出学生选择的课程

      根据课程编号查询课程时,同时查询选择了这门课程的学生

      连接查询的映射配置

      子查询的映射配置

    0x0C 动态SQL

    交友网,电商等网站都有筛选功能;

    不同的需求筛选条件不同,需要动态的拼接sql;

    用户的筛选条件不同,我们完成筛选执行的sql也不一样,我们可以通过穷举来完成不同条件的筛选,但是 这种实现思想过于繁琐和复杂,MyBatis就提供了动态SQL的配置方式来实现多条件的查询。

    12.1 什么是动态SQL

    根据搜索条件,动态完成SQL的拼接。

    12.2 动态SQL使用案例

    案例:心仪对象搜索

     12.2.1 创建数据表

    #数据表
    CREATE TABLE members(
        member_id int PRIMARY key auto_increment,
        member_nick VARCHAR(20) not null UNIQUE,
        member_gender char(2) not null,
        member_age int not null,
        member_city varchar(30) not null
    );

     12.2.2 创建实体类

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @ToString
    public class Member {
        private int memberId;
        private String memberNick;
        private String memberGender;
        private int memberAge;
        private String memberCity;
    }

    12.2.3 创建DAO接口

    在DAO接口中定义一个多条件查询的方法

    public interface MemberDAO {
        //在多条件查询中,如果查询条件不确定,可以直接使用HashMap作为参数
        public List<Member> searchMember(HashMap<String,Object> params);
    }

     MemberMapper.xml

    <resultMap id="memberMap" type="Member">
        <id column="member_id" property="memberId"/>
        <result column="member_nick" property="memberNick"/>
        <result column="member_gender" property="memberGender"/>
        <result column="member_age" property="memberAge"/>
        <result column="member_city" property="memberCity"/>
    </resultMap>
    
    <select id="searchMember" resultMap="memberMap">
        select member_id,member_nick,member_gender,member_age,member_city
        from members
        where 1 = 1
        <if test="gender != null"><!--gender 就是参数对象的属性、参数Map的key-->
            and member_gender = #{gender}
        </if>
        <if test="minAge != null"><!--&gt; 大于号  -->
            and member_age &gt;= #{minAge}
        </if>
        <if test="maxAge != null"><!--&lt; 小于号  -->
            and member_age &lt;= #{maxAge}
        </if>
        <if test="city != null">
            and member_city != #{city}
        </if>
    </select>

      MemberDAOTest

    @Test
        public void TestSearchMember() throws IOException {
            HashMap<String,Object> params = new HashMap<String,Object>();
            params.put("gender","女");
    //        params.put("minAge",19);
    //        params.put("maxAge",23);
    //        params.put("city","武汉");
            //当向Map中存放参数时,key必须与动态Sql中的参数保持一致
            MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
            List<Member> members = memberDAO.searchMember(params);
    
            System.out.println(members);
    
        }

     12.2.4 where标签

     12.2.5 trim标签

     12.2.6 foreach标签

     测试类:

    @Test
    public void testSearchMemberByCity() throws IOException {
        List<String> cities = new ArrayList<String>();
        cities.add("武汉");
        cities.add("厦门");
        MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
        List<Member> members = memberDAO.searchMemberByCity(cities);
        for (Member m : members){
            System.out.println(m);
        }
    }

    0x0D 模糊查询

    案例:根据昵称查询会员信息(模糊匹配like)

    13.1 模糊查询实现

    13.1.1 DAO

    public interface MemberDAO {
    
    //根据昵称查询用户信息-- 模糊查询
    //模糊查询需要使用 ${} 取值,与SQL进行拼接
    //在使用${}时,即使只有一个参数也需要使用@Param注释声明参数的key (非String类型不需要)
        public List<Member> searchMemberByNick(@Param("keyWord") String keyWord);
    
    }

    13.1.2 映射文件

    <!--如果参数是String 类型,需要使用parameterType声明参数类型-->
    <select id="searchMemberByNick" parameterType="java.lang.String" resultMap="memberMap">
        select member_id,member_nick,member_gender,member_age,member_city
        from members
        where member_nick like '%${keyWord}%'
    </select>

    13.1.3 测试

        @Test
        public void testSearchMemberByNick() throws IOException {
            MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
            List<Member> members = memberDAO.searchMemberByNick("丽");
            for (Member m : members){
                System.out.println(m);
            }
        }

    13.2 #{}和${}的区别

    <!--${keyWord} 表示获取参数,先获取参数的值,拼接到SQL语句中,再编译执行SQL语句  可能引起SQL注入问题-->
    <!--#{keyWord} 表示获取参数,先完成SQL的编译(预编译),预编译之后再将获取的参数设置到SQL中 可以避免SQL注入问题-->

    0x0E MyBatis日志配置

    MyBatis作为一个封装好的ORM框架,其运行过程中我们没办法跟踪,为了让开发者了解MyBatis执行流程以及每个执行步骤所完成的工作,

    MyBatis框架本身集成了log4j日志框架,对运行的过程进行跟踪记录,我们只需要对MyBatis进行相关的日志配置,就可以看到MyBatis运行过程中的日志信息。

    14.1 添加日志框架依赖

    <!-- https://mvnrepository.com/artifact/log4j/log4j -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>

    14.2 添加日志配置文件

    • 在resources目录下创建名为log4j.properties文件,名称必须如此
    • 在log4j.properties文件中配置日志输出方式
    # 声明日志的输出级别及输出格式
    log4j.rootLogger=DEBUG,stdout
    
    # MyBatis logging configuration...
    log4j.logger.org.mybatis.example.BlogMapper=TRACE
    
    # Console output ...
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    
    # 定义日志的打印格式    %t表示 线程名称    %5p 日志级别 %msg 日志信息  \:%m%n 换行
    log4j.appender.stdout.layout.ConversionPattern=[%t] %5p -%msg \:%m%n

    14.3 日志信息的等级

    0x0F 配置数据库连接池--整合Druid

    MyBatis作为一个ORM框架,在进行数据库操作的时候是需要和数据库连接的,MyBatis支持基于数据库连接池的连接创建方式。

    当我们配置MyBatis数据源时,只要配置了dataSource标签的type属性值为pooled时,就可以使用MyBatis内置的连接池管理连接。

    如果我们想要使用第三方的数据库连接池,则需要进行自定义配置。

    15.1 常见的连接池

    • DBCP
    • C3P0
    • Druid   性能比较好,提供了比较便捷的监控系统
    • Hikari  性能最好

     15.2 添加Druid依赖

    <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.10</version>
    </dependency>

    15.3 创建Druid连接池工厂

    public class DruidDataSourceFactory extends PooledDataSourceFactory {
    
        public DruidDataSourceFactory() {
            this.dataSource = new DruidDataSource();
        }
    
    }

    15.4 将DruidDataSourceFactory配置给MyBatis数据源

    <environments default="mysql">
            <environment id="mysql">
                <transactionManager type="jdbc"></transactionManager>
                <!--POOLED 使用MyBatis内置的连接池实现   -->
                <!--mybatis 需要一个连接池工厂  这个工厂可以产生数据库连接池 PooledDataSourceFactory -->
                <!--使用了多态-->
                <dataSource type="com.MyBatisDemo.utils.DruidDataSourceFactory">
                    <property name="driverClass" value="${driver}"/>
                    <property name="jdbcUrl" value="${url}"/>
                    <property name="username" value="${username}"/>
                    <property name="password" value="${password}"/>
                </dataSource>
            </environment>
        </environments>

    0x10 MyBatis缓存

    MyBatis是基于JDBC的封装,使数据库操作更加便捷,MyBatis除了对JDBC操作步骤进行封装外也对其性能进行了优化

    • 在MyBatis引入了缓存机制,用于提升MyBatis的检索效率
    • 在MyBatis引入了延迟加载机制,用于对数据库不必要的访问

    16.1 缓存的工作原理

    缓存,就是存储数据的内存

     16.2 MyBatis缓存

    MyBatis缓存分为一级缓存和二级缓存。

    16.2.1 一级缓存

    一级缓存也叫做SqlSession缓存,为每个sqlsession单独分配的缓存内存,无需手动开启,可直接使用。

    多个sqlsession的缓存是不共享的。

    特性:

    1:如果多次查询使用的是同一个Sqlsession对象,则第一次查询以后,数据会存放到缓存,后续的查询则直接访问缓存中存储的数据。

    2:如果第一次查询完成后,对查询出的对象进行修改(此修改会影响到缓存),第二次查询会直接访问缓存,造成第二次查询的结果与数据库不一致。

    3:当我们进行再次查询时,想要跳过缓存直接查询数据库,则可以通过sqlsession.clearCache();来清除当前SqlSession的缓存。

    4:如果第一次查询以后,第二次查询以前,使用当前的sqlsession执行了修改操作,此修改操作会使第一次查询并缓存的数据失效,因此第二次查询会再次访问数据库。

     测试代码:

        @Test
        public void testQueryMemberById() throws IOException {
            SqlSession sqlSession = MyBatisUtil.getFactory().openSession();
            SqlSession sqlSession2 = MyBatisUtil.getFactory().openSession();
    
    //        SqlSession sqlSession = MyBatisUtil.getSqlSession();
    //        SqlSession sqlSession2 = MyBatisUtil.getSqlSession();
    
            MemberDAO memberDAO = sqlSession.getMapper(MemberDAO.class);
            Member member1 = memberDAO.queryMemberById(1);
            System.out.println(member1);
            member1.setMemberAge(200);
            sqlSession.clearCache();
            
            System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
    
            MemberDAO memberDAO2 = sqlSession.getMapper(MemberDAO.class);
            Member member2 =memberDAO2.queryMemberById(1);
            System.out.println(member2);
        }

     16.2.2 两次查询与数据库不一致问题

     16.2.3 二级缓存

    二级缓存也成为SqlSessionFactory级缓存。通过同一个factory对象获取的SqlSession可以共享二级缓存;

    在应用服务器中SqlSessionFactory是单例的,因此我们二级缓存可以实现全局共享。

    特性:

    1:二级缓存默认没有开启,需要在mybatis-config.xml中的setting标签中开启

    2:二级缓存只能缓存实现了序列化接口的对象

    •  在mybatis-config.xml 开启使用二级缓存
    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>
    • 在需要使用二级缓存的Mapper文件中配置cache标签使用二级缓存
    <cache/>  
    
    //该标签中有一些属性可以自行进行配置
    //例如
    淘汰策略属性: eviction = "FIFO"  先进先出
    更新频率属性: flushInterval = "6000" 间隔多长时间刷新一次缓存
    缓存区的大小属性:size = "223" 多少个对象的引用
    只读属性: readOnly = "true" 只能读不能改
    •  被缓存的实体类实现序列化接口
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @ToString
    public class Member implements Serializable {
        private int memberId;
        private String memberNick;
        private String memberGender;
        private Integer memberAge;
        private String memberCity;
    }
    • 测试
        @Test
        public void testQueryMemberById() throws IOException {
            SqlSessionFactory factory = MyBatisUtil.getFactory();
            //1:多个sqlsession对象来自于同一个sqlsessionFactory
            SqlSession sqlSession = factory.openSession(true);
            SqlSession sqlSession2 = factory.openSession(true);
    
            MemberDAO memberDAO = sqlSession.getMapper(MemberDAO.class);
            Member member1 = memberDAO.queryMemberById(1);
            System.out.println(member1);
    //        sqlSession.clearCache();
            sqlSession.commit(); //2:第一次查询之后,执行sqlSession.commit() 会将当前sqlsession的查询结果缓存到二级缓存
    
            System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
    
            MemberDAO memberDAO2 = sqlSession2.getMapper(MemberDAO.class);
            Member member2 =memberDAO2.queryMemberById(1);
            System.out.println(member2);
        }

    16.3 查询操作的缓存开关

    <select id="queryMemberById" resultMap="memberMap" useCache="false">
        select member_id,member_nick,member_gender,member_age,member_city
        from members
        where member_id=#{mid}
    </select>

    0x11 延迟加载

    延迟加载——如果在MyBatis中开启了延迟加载执行了子查询(至少查询两次以上),默认只实行第一次查询,当用到子查询的查询结果时,才会出发子查询的执行,如果无需使用子查询结果,则子查询不会执行。
    •  开启延迟加载
    fetchType="lazy"
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.guoqing.dao.ClassDAO">
    
        <resultMap id="classMap" type="Clazz">
            <id column="cid" property="classId"/>
            <result column="cname" property="className"/>
            <result column="cdesc" property="classDesc"/>
            <collection property="stus" select="com.guoqing.dao.StudentDAO.queryStudentsByCid" column="cid" fetchType="lazy"/>
        </resultMap>
    
        <select id="queryClassByCid" resultMap="classMap">
            select cid,cname,cdesc from classes where cid = #{cid}
        </select>
    
    </mapper>
    •  测试类
    @Test
    public void queryClassByCid() throws IOException {
        ClassDAO classDAO = MyBatisUtil.getMapper(ClassDAO.class);
        Clazz clazz = classDAO.queryClassByCid(1);
        System.out.println(clazz.getClassName());
        System.out.println(clazz.getStus());
    }
  • 相关阅读:
    Python、Lua和Ruby比较——脚本语言大P.K.
    vim 设置默认显示行号
    C语言
    How To Add Swap on Ubuntu 14.04
    How To Install Apache Kafka on Ubuntu 14.04
    php的错误和异常处理
    md5sum
    大牛的博客
    【转】4G手机打电话为什么会断网 4G上网和通话不能并存原因分析
    【转】女人最想要的是什么
  • 原文地址:https://www.cnblogs.com/JetpropelledSnake/p/15997894.html
Copyright © 2020-2023  润新知