SSM--MyBatis

139 阅读8分钟

MyBatis

一、MyBatis简介

  1. 框架概念
    框架就是软件的半成品,完成了软件开发过程中的通用操作,程序员只需很少或者不用进行加工就能够实现待定的功能,从而简化开发人员在软件开发中的步骤,提高开发效率。
  2. 常用框架
    • MVC框架:简化了servlet的开发步骤
      • Struts2
      • SpringMVC
    • 持久层框架:完成数据库操作的开
      • apache DBUtils
      • Hibernate
      • Spring JPA
      • MyBatis
    • 胶水框架:Spring
      • SSM Spring SpringMVC MyBatis
      • SSH Spring struts2 Hibernate
  3. MyBatis框架

MyBtis是一个半自动ORM框架
ORM(Object Relational Mapping)对象关系映射,将Java中的一个对象与数据表中的一行记录一一对应。
ORM框架提供了实体类与数据表的映射关系,通过映射文件的配置,实现对象的持久化。

  • MyBatis的前身是iBatis,iBatis是Apache软件基金会提供的一个开源项目
  • 2010年iBatis迁移到Google code,正式更名为MyBatis
  • 2013年迁移到Github托管
  • MyBatis特点:
    • 支持自定义SQL、存储过程
    • 对原有的JDBC进行了封装,几乎消除了所有JDBC代码,让开发者只需关注SOL本身。
    • 支持XML和注解配置方式自定完成ORM操作,实现结果映射

二、MyBatis部署

框架部署,就是将框架引入到我们的项目中

  1. 创建Maven项目
    • Java工程
    • web工程 新建-》项目-》Maven-》下一步-》写项目名字-》完成
  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>8.0.23</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.5</version>
    </dependency>
    
  3. 配置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>
    
    图片.png
    • 在resources中创建名为 mybatis-config.xml 的文件
    • 在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标签中可以定义多个environment,每个environment标签可以定义一套连接配置-->
    <!--    default属性,用来指定使用哪个-->
    <environments default="mysql">
        <environment id="mysql">
    <!--        transactionManager标签用于配置数据库管理方式-->
            <transactionManager type="JDBC"></transactionManager>
    <!--        dataSource标签用于配置数据库连接信息-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/SSM?characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    </configuration>
    

三、MyBatis框架使用

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

  1. 创建数据表(tb_students)
CREATE TABLE tb_students(
sid int PRIMARY key auto_increment,
stu_num CHAR(5) not null UNIQUE, 
stu_name VARCHAR(20) not null,
stu_gender CHAR(2) not null, 
stu_age int not null );
  1. 创建实体类
    • 导入lombok依赖
    <!--        Lombok-->
           <dependency>
               <groupId>org.projectlombok</groupId>
               <artifactId>lombok</artifactId>
               <version>1.18.26</version>
               <scope>provided</scope>
           </dependency>
    
    • 实体类添加注释
    package com.xie.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 stuName;
        private String stuNum;
        private String stuGender;
        private int stuAge;
    }
    
  2. 创建StudentADO接口,定义操作方法
public interface StudentADO {
    public int insertStudent(Student student);
}
  1. 创建StudengMapper(这相当于StudengtDAO的实现类,也是DAO的映射文件。说明了StudengDAO要做什么)
    • 新建mapper模板
    <?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
  2. 在映射文件中对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接口的全限类名-->
    <mapper namespace="com.xie.dao.StudentADO"><!--    放接口的路径-->
    <!--如果在DAO接口定义了一个方法,我们就要在这mappers中实现-->
    
        <!--    id中的名字要和DAO中的方法的名字一致-->
    <!--   parameterType:这个方法需要什么类型的参数 --><!--也可省略不写,因为在DAO中写了类型 -->
    <!--    values:插入(要和实体类中的名字一致)-->
        <insert id="insertStudent" parameterType="com.xie.pojo.Student"> 
        insert into tb_students(stu_num,stu_name,stu_gender,stu_age)
        values (#{stuNum},#{stuName},#{stuGender},#{stuAge})
        </insert>
    </mapper>
    
  3. 将映射文件(StudentMapper.xml)添加到主配置文件(mybatis-config.xml是主配置文件)
    <mappers>
        <mapper resource="mappers/StudentMapper.xml"></mapper>
    </mappers>
    

四、单元测试

  1. 添加单元测试的依赖
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.1</version>
        <scope>test</scope>
    </dependency>
    
  2. 创建单元测试类
    • 在被测试类名后alt+insert----选择Test(右键-》生成-》测试) 图片.png
  3. 测试
    package com.xie.dao;
    import com.xie.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 java.io.IOException;
    import java.io.InputStream;
    
    public class StudentADOTest {
        @Test
        public void insertStudent() {
            try {
    //        加载mybatis配置文件
                InputStream is= Resources.getResourceAsStream("mybatis-config.xml");
                SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
    //        会话工厂
                SqlSessionFactory factory=builder.build(is);
    //        会话(连接)
                SqlSession sqlSession=factory.openSession();
    //            通过会话获取DAO对象
                StudentDAO studentDAO=sqlSession.getMapper(StudentDAO.class);
    //        测试studentDAO中的方法
                int i=studentDAO.insertStudent(new Student(0,"1001","张三","男",21));
    //            需要手动提交
                sqlSession.commit();
                System.out.println(i);
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    }
    

五、MyBatis的CRUD操作

5.1 添加操作(如上)

5.2 删除操作

  • 根据学号删除
    • 在StudentDAO中定义删除方法(StudentDAO.java)
      public int deleteStudent(String stuNum);
      
    • StudentMapper.xml中对接口方法进行实现
      <delete id="deleteStudent">
          delete from tb_students where stu_num=#{stuNum}
      </delete>
      
    • 测试:在StudentDAO的测试类中添加测试方法
       @Test
          public void testDeleteStudent() {
              try {
      //        加载mybatis配置文件
                  InputStream is= Resources.getResourceAsStream("mybatis-config.xml");
                  SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
      //        会话工厂
                  SqlSessionFactory factory=builder.build(is);
      //        会话(连接)
                  SqlSession sqlSession=factory.openSession();
      //            通过会话获取DAO对象
                  StudentDAO studentDAO=sqlSession.getMapper(StudentDAO.class);
      //        测试studentDAO中的方法
                  studentDAO.deleteStudent("1001");
      //            需要手动提交
                  sqlSession.commit();
              } catch (IOException e) {
                  e.printStackTrace();
              }
      

5.3 修改操作

  •  public int updateStudent(Student student);
    
  •   <update id="updateStudent">
          update tb_students set
             stu_name=#{stuName},
             stu_gender=#{stuGender},
              stu_age=#{stuAge}
          where
                stu_num=#{stuNum}
      </update>
    
  •  @Test
     public void updateStudent() {
         try {
             InputStream is= Resources.getResourceAsStream("mybatis-config.xml");
             SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
             SqlSessionFactory factory=builder.build(is);
             SqlSession sqlSession=factory.openSession();
             StudentDAO studentDAO=sqlSession.getMapper(StudentDAO.class);
             int i=studentDAO.updateStudent(new Student(0,"李斯","1001","男",7));
             sqlSession.commit();
            assertEquals (1,i);// 如果i=1则测试通过,不等于1,则测试不通过
    
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
    

5.4 查询操作--查询所有

  1. 方法1(起别名)
    • StudentDAO.java
      public List<Student> listStudents();
      
    • StudentMapper.xml
      <!--   resultType  指定查询结果封装对象的实体类 -->
      <!--  resultSets  指定当前操作返回的集合类型(可省略)  -->
      <select id="listStudents" resultType="com.xie.pojo.Student" resultSets="java.util.List">
          select sid stuId,stu_num stuNum,stu_name stuName,stu_gender stuGender,stu_age stuAge
          from tb_students
      </select>
      
    • StudentDAOTest.java
        @Test
           public void testListStudents() {
               try {
                   InputStream is= Resources.getResourceAsStream("mybatis-config.xml");
                   SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
                   SqlSessionFactory factory=builder.build(is);
                   SqlSession sqlSession=factory.openSession();
                   StudentDAO studentDAO=sqlSession.getMapper(StudentDAO.class);
                   List<Student> list=studentDAO.listStudents();
       //            assertNotNull (list);// 如果i=1则测试通过,不等于1,则测试不通过
                   for(Student stu:list){
                       System.out.println(stu);
                   }
               } catch (IOException e) {
                   e.printStackTrace();
               }
           }
      
  2. 方法2(定义映射关系)
    <!--   resultMap 标签用于定义实体类与数据表的映射关系(ORM) -->
        <resultMap id="studentMap" type="com.xie.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>
    
    复杂查询,联合查询时用这个方法更好

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

  1. 根据学号查询一个学生信息
    • StudentDAO.java
      public Student StudentByNum(String stuNum);
      
    • StudentMapper.xml
      <select id="StudentByNum" resultMap="studentMap">
          select sid ,stu_num ,stu_name ,stu_gender ,stu_age
          from tb_students
          where stu_num=#{stuNum}
      </select>
      
    • StudentDAOTest.java
      @Test
      public void studentByNum() {
          try {
              InputStream is= Resources.getResourceAsStream("mybatis-config.xml");
              SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
              SqlSessionFactory factory=builder.build(is);
              SqlSession sqlSession=factory.openSession();
              StudentDAO studentDAO=sqlSession.getMapper(StudentDAO.class);
              Student student= studentDAO.StudentByNum("1005");
              System.out.println(student);
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
      

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

  • StudentDAO.java
    public int getCount();
    
  • StudentMapper.xml
     <select id="getCount" resultType="int" >
             select count(1) from tb_students
         </select>
     <!--    如果返回的是对象或者是集合,建议是用resultMap,但如果是其他的,建议是resultType-->
    
  • StudentDAOTest.java
    @Test
    public void testGetCount() {
        try {
            InputStream is= Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
            SqlSessionFactory factory=builder.build(is);
            SqlSession sqlSession=factory.openSession();
            StudentDAO studentDAO=sqlSession.getMapper(StudentDAO.class);
           int i=studentDAO.getCount();
            System.out.println(i);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    

5.7 分页查询

分页查询(参数 start,pageSize)

在MyBatis进行条件查询操作

  • 如果操作方法只有一个简单类型或者字符串类型的参数,在Mapper配置中可以直接通过#{key}直接获取
  • 如果操作方法有一个对象类型的参数,在mapper参数中可以直接通过#{attrName}获取对象的指定属性值(attrName必须是参数对象的属性)
  • 如果操作方法有一个Map类型的参数,在Mapper配置中可以直接通过#{key}获取key对应的value
  • 如果操作方法有多个参数,该如何处理?
  1. 方法一
    • StudentDAO.java
    public List<Student> listStudentByPage(HashMap<String,Integer>map);
    
    • StudentMapper.xml
    <select id="listStudentByPage" resultMap="studentMap">
        select sid ,stu_num ,stu_name ,stu_gender ,stu_age
        from tb_students
        limit #{start},#{pageSize}
    </select>
    
    • StudentDaoTest.java
    @Test
    public void listStudentByPage() {
        try {
            InputStream is= Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
            SqlSessionFactory factory=builder.build(is);
            SqlSession sqlSession=factory.openSession();
            StudentDAO studentDAO=sqlSession.getMapper(StudentDAO.class);
            HashMap<String ,Integer> map =new HashMap<String,Integer>();
            map.put("start",0);
            map.put("pageSize",3);
            List<Student> list=studentDAO.listStudentByPage(map);
            for(Student stu:list){
                System.out.println(stu);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
  2. 方法2
    • StudentDAO.java
    public List<Student> listStudentPage(int start,int PageSize);
    
    • StudentMapper.xml
    <select id="listStudentPage" resultMap="studentMap">
        select sid ,stu_num ,stu_name ,stu_gender ,stu_age
        from tb_students
        limit #{arg0},#{arg1}
    </select>
    
    limit #{arg0},#{arg1}也可换成limit #{param1},#{param2}
    • StudentDaoTest.java
    @Test
    public void listStudentPage() {
        try {
            InputStream is= Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
            SqlSessionFactory factory=builder.build(is);
            SqlSession sqlSession=factory.openSession();
            StudentDAO studentDAO=sqlSession.getMapper(StudentDAO.class);
           List<Student> list=studentDAO.listStudentPage(0,3);
            for(Student stu:list){
                System.out.println(stu);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
  3. 方法三:起别名
    • StudentDAO.java
    public List<Student> listStudentPage2(@Param("start") int start,@Param("pageSize") int pageSize );
    
    • StudentMapper.xml
    <select id="listStudentPage2" resultMap="studentMap">
        select sid ,stu_num ,stu_name ,stu_gender ,stu_age
        from tb_students
        limit #{start},#{pageSize}
    </select>
    
    • StudentDaoTest.java
    @Test
    public void listStudentPage2() {
        try {
            InputStream is= Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
            SqlSessionFactory factory=builder.build(is);
            SqlSession sqlSession=factory.openSession();
            StudentDAO studentDAO=sqlSession.getMapper(StudentDAO.class);
            List<Student> list=studentDAO.listStudentPage2(0,3);
            for(Student stu:list){
                System.out.println(stu);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    

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

  • StudentMapper.xml的添加操作标签---insert
    <!--    useGeneratedKeys :设置添加操作是否需要回填生成的主键-->
    <!--   keyProperty:设置回填的主键值复制到参数对象的哪个属性 -->
        <insert id="insertStudent" useGeneratedKeys="true" keyProperty="stuId">
        insert into tb_students(stu_num,stu_name,stu_gender,stu_age)
        values (#{stuNum},#{stuName},#{stuGender},#{stuAge})
        </insert>
    

六、MyBatis工具类的封装

  • MyBatisUtil.java
     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;
     import java.io.InputStream;
     public class MyBatisUtil {
         private static SqlSessionFactory factory;
         private static final ThreadLocal<SqlSession> local=new ThreadLocal<SqlSession>();
         static {
     //        得到factory对象
             try {
                 InputStream  is = Resources.getResourceAsStream("mybatis-config.xml");
                 SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
                 factory=builder.build(is);
             } catch (IOException e) {
                 e.printStackTrace();
             }
    
         }
         public static SqlSession getSqlSession(){
             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){
             SqlSession sqlSession=getSqlSession();
             return sqlSession.getMapper(c);
    
         }
         public static SqlSessionFactory getFactory(){
             return factory;
         }
     }
    
  • StudentDaoTest.java
    public class StudentDAOTest {
        @Test
        public void insertStudent() {
                SqlSession sqlSession=MyBatisUtil.getSqlSession();
    //            通过会话获取DAO对象
                StudentDAO studentDAO=sqlSession.getMapper(StudentDAO.class);
    //        测试studentDAO中的方法
                Student student=new Student(0,"谢榭","1010","男",42);
                int i=studentDAO.insertStudent(student);
    //            int i=studentDAO.insertStudent(new Student(0,"lucy","1008","男",6));
    //            需要手动提交
                sqlSession.commit();
                System.out.println(student);
                System.out.println(i);
        }
        @Test
        public void updateStudent() {
                SqlSession sqlSession=MyBatisUtil.getSqlSession();
                StudentDAO studentDAO=sqlSession.getMapper(StudentDAO.class);
                int i=studentDAO.updateStudent(new Student(0,"李斯","1001","男",7));
                sqlSession.commit();
               assertEquals (1,i);
               }
    
        @Test
        public void testListStudents() {
                StudentDAO studentDAO=MyBatisUtil.getMapper(StudentDAO.class);
                List<Student> list=studentDAO.listStudents();
                for(Student stu:list){
                    System.out.println(stu);
                }
        }
    
        @Test
        public void studentByNum() {
                StudentDAO studentDAO=MyBatisUtil.getMapper(StudentDAO.class);
                Student student= studentDAO.StudentByNum("1010");
                System.out.println(student);
        }
    

增删改都要执行事务管理操作

七、CRUD-事务管理

SqlSession对象

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

7.1手动提交事务

  • sqlSession.commit();提交事务
  • sqlSession.rollback();事务回滚
@Test
    public void insertStudent() {
            SqlSession sqlSession=MyBatisUtil.getSqlSession();
//            1.当在我们获取session对象时就默认开启了事务
        try {
//            通过会话获取DAO对象
            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
//        测试studentDAO中的方法
            Student student = new Student(0, "谢榭", "1011", "男", 42);
            int i = studentDAO.insertStudent(student);
//            int i=studentDAO.insertStudent(new Student(0,"lucy","1008","男",6));
//            需要手动提交
//        2. 操作完成成功后,需要手动提交
            sqlSession.commit();
        }catch (Exception e){
//            当操作异常时,就调用rollback进行回滚
            sqlSession.rollback();
        }
    }

7.2自动提交事务

通过SqlSessionFactory对象调用openSession方法获取SqlSession对象时,可以通过参数设置事务是否自动提交

  • 如果参数设置为true,表示自定义提交事务,factory.openSession(true);

  • 如果参数设置为false或者不设置参数,表示手动提交,factory.openSession();factory.openSession(false);

    MyBatisUtil优化

    //手动事务管理
     public static SqlSession getSqlSession(){
             return getSqlSession(false);
         }
    
         private static SqlSession getSqlSession(boolean isAutoCommit){
             SqlSession sqlSession=local.get();
             if(sqlSession==null){
                 sqlSession=factory.openSession(isAutoCommit);//默认自动提交事务
                 local.set(sqlSession);
             }
             return sqlSession;
         }
         //自动事务管理
         public static <T extends Object>T getMapper(Class<T> c){
             SqlSession sqlSession=getSqlSession(true);
             return sqlSession.getMapper(c);
    
         }
    
    • 手动提交事务(获取SqlSession)
     @Test
        public void insertStudent() {
                SqlSession sqlSession=MyBatisUtil.getSqlSession();
    //            如果设置的是true,则不需要sqlSession.commit();如果设置的是false,则不能少了sqlSession.commit();默认是false(即手动提交)
    //        如果要通过sqlSession多个操作时,要设置为true,
    //        例如:int i=studentDAO.insertStudent(student);
    //        studentDAO.updateStudent(student)
    
    //            1.当在我们获取session对象时就默认开启了事务
            try {
                StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
                Student student = new Student(0, "谢榭", "1013", "男", 42);
                int i = studentDAO.insertStudent(student);
    //            需要手动提交
    //        2. 操作完成成功后,需要手动提交
                sqlSession.commit();
            }catch (Exception e){
    //            当操作异常时,就调用rollback进行回滚
                sqlSession.rollback();
            }
        }
    
    • 自动提交事务(直接获取mapper)
      @Test
         public void testDeleteStudent() {
                 StudentDAO studentDAO=MyBatisUtil.getMapper(StudentDAO.class);
     //        测试studentDAO中的方法
                 studentDAO.deleteStudent("1012");
    
         }
    

八、MyBatis主配置文件

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

8.1 properties标签

  • 可以定义键值对
  • 可以引用属性文件(在resources目录下创建jdbc.properties) 图片.png

图片.png

图片.png

8.2 settings

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

8.3 typeAliases

  • StudentMapper.xml 图片.png
  • mybatis-config.xml
    <!--  typeAliases标签用于给实体类取别名,在映射文件中可以直接使用别名在替代实体类的全限定名,可以typeAliases中有多个typeAlias  -->
        <typeAliases>
            <typeAlias type="com.xie.pojo.Student" alias="Student"></typeAlias>
        </typeAliases>
    
  • StudentMapper.xml 图片.png

8.4 plugins

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

8.5 environments

<environments default="mysql">
    <environment id="mysql">
<!--        transactionManager标签用于配置数据库管理方式-->
        <transactionManager type="JDBC"></transactionManager>
<!--        dataSource标签用于配置数据库连接信息-->
        <dataSource type="POOLED">
            <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
            <property name="url" value="${mysql_url}"/>
            <property name="username" value="root"/>
            <property name="password" value="123456"/>
        </dataSource>
    </environment>
</environments>

图片.png

8.6 mapper

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

九、映射文件

9.1 MyBatis Mapper初始化

图片.png

9.2 mapper根标签

<!--mapper文件相当于DAO的“实现类”,namespace属性要指定实现DAO接口的全限类名-->
<mapper namespace="com.xie.dao.StudentDAO"><!--    放接口的路径-->
<!--如果在DAO接口定义了一个方法,我们就要在这mappers中实现-->

9.3 insert标签

声明添加操作(sql:insert...)
常用属性
id属性:绑定对应的DAO接口中的方法
parameterType:用于指定接口方法中对应的参数类型(可省略)
useGeneratedKeys属性:设置添加操作是否需要回填生成的主键
keyProperty属性:指定回填的id设置到参数对象中的属性
timeout属性:设置此操作的超时时间,如果不设置则一直等待

9.4 主键回填

  1. 方法一
<insert id="insertStudent" useGeneratedKeys="true" keyProperty="stuId" timeout="3000">
insert into tb_students(stu_num,stu_name,stu_gender,stu_age)
values (#{stuNum},#{stuName},#{stuGender},#{stuAge})
</insert>
  1. 方法二
<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.5 delete标签

声明删除标签

9.6 update标签

声明修改操作

9.7 select标签

声明查询操作

  • id属性,指定绑定方法的方法名
  • parameterType属性:设置参数类型
  • resultType属性:指定当前sql返回数据封装的对象类型(实体类)
  • resultMap属性:指定从数据表到实体类的字段和属性的对应关系
  • useCache属性:指定此查询操作是否需要缓存
  • timeout属性:设置超时时间

9.8 resultMap标签

<!--   resultType  指定查询结果封装对象的实体类 -->
<!--  resultSets  指定当前操作返回的集合类型(可省略)  -->
<!--  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.9 cache标签

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

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

9.10 sql标签

SQL片段

<sql id="WLJ">
    sid,stu_num,stu_name,stu_gender,stu_age
</sql>
<select id="listStudents" resultMap="studentMap">
    select <include refid="WLJ"/>
    from tb_students
</select>

相当于

<select id="listStudents" resultMap="studentMap">
    select sid,stu_num,stu_name,stu_gender,stu_age
    from tb_students
</select>

十、分页插件

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

10.1 添加分页插件的依赖

PageHelper

<!--        分页插件-->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.3.0</version>
        </dependency>

10.2 配置插件

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

    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>

10.3 分页实例

对学生信息进行分页查询

直接在测试类中测试使用

//    利用插件分页
    @Test
    public void listStudentPage3(){
        StudentDAO studentDAO=MyBatisUtil.getMapper(StudentDAO.class);
        PageHelper.startPage(2,2);
        List<Student> students=studentDAO.listStudents();
        PageInfo<Student> pageInfo=new PageInfo<Student>(students);
//        pageInfo中就包含了数据分页信息
        
        List<Student> list = pageInfo.getList();
        for (Student stu:list){
            System.out.println(stu);
        }
    }

10.4 带条件分页

  1. StudentDao.Java
    public List<Student> listStudentByGender(String gender);
    
  2. StudentMapper.xml
    <select id="listStudentByGender" resultMap="studentMap">
        select <include refid="WLJ"/>
        from tb_students
        where stu_gender=#{gender}
    </select>
    
  3. StudentDaoTest.java
     @Test
        public void listStudentByGender() {
            StudentDAO studentDAO=MyBatisUtil.getMapper(StudentDAO.class);
            PageHelper.startPage(1,4);
            List<Student> students=studentDAO.listStudentByGender("女");
            PageInfo<Student> pageInfo=new PageInfo<Student>(students);
    //        pageInfo中就包含了数据分页信息
    
            List<Student> nv = pageInfo.getList();
            for (Student stu:nv){
                System.out.println(stu);
            }
        }
    

十一、关联映射

11.1 实体关系

实体--数据实体,实体关系指的是数据与数据之间的关系
例如:用户和角色、房屋和楼订单和商品

实体关系分为以下四种

一对一关联

实例:人和身份证、学生和学生证,用户基本信息和详情。
数据表关系:

  • 主键关联(用户表主键和详情主键相同时,表示是匹配的数据)

图片.png

  • 唯一外键关联

用户信息(30个字段,用户数量也很多)

图片.png

用户登录:根据用户名查询用户信息,当用户表中数据量很大且字段很多时会严重影响查询的速度

一对多关联、多对一关联

实例:

  • 一对多:班级和学生、类别和商品、楼栋和房屋
  • 多对一:学生和班级,商品和类别

数据表关系

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

实例:用户和角色、角色和权限、房屋和业主、学生和社团、订单和商品
数据表关系:建立第三张关系表添加两个外键分别与两张表主键进行关联
用户(user_id)
用户角色表(uid,rid)
角色(role_id)

11.2 创建项目,部署MyBatis框架

  1. 创建Maven项目
  2. 在pom.xml中打包
    <packaging>war</packaging>
    
  3. 创建目录

图片.png

  1. 导入依赖
    • 方法一
      图片.png
      <dependencies>
          <dependency>
              <groupId>javax.servlet</groupId>
              <artifactId>javax.servlet-api</artifactId>
              <version>4.0.1</version>
              <scope>provided</scope>
          </dependency>
      </dependencies>
      
    • 方法二
      <dependencies>
          <dependency>
              <groupId>javax.servlet</groupId>
              <artifactId>javax.servlet-api</artifactId>
              <version>4.0.1</version>
              <scope>provided</scope>
          </dependency>
          <dependency>
              <groupId>javax.servlet</groupId>
              <artifactId>jsp-api</artifactId>
              <version>2.0</version>
              <scope>provided</scope>
          </dependency>
      
      </dependencies>
      
  2. 添加Tomcat插件
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
    
            </plugin>
        </plugins>
    </build>
    
  3. 部署MyBatis框架
    • 添加依赖
    <!--MyBatis-->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.5</version>
    </dependency>
    <!--MySQL-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.23</version>
    </dependency>
    
    • 配置文件(jdbc.properties\mybatis-config.xml)
      driver=com.mysql.cj.jdbc.Driver
      url=jdbc:mysql://localhost:3306/SSM?characterEncoding=utf-8&amp;useSSL=false
      username=root
      password=123456
      
      <properties resource="jdbc.properties"></properties>
      <environments default="mysql">
          <environment id="mysql">
              <transactionManager type="JDBC"></transactionManager>
              <dataSource type="POOLED">
                  <property name="driver" value="${driver}"/>
                  <property name="url" value="${url}"/>
                  <property name="username" value="${username}"/>
                  <property name="password" value="${password}"/>
              </dataSource>
          </environment>
      </environments>
      
    • 帮助类
      package com.xie.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;
      import java.io.InputStream;
      
      public class MyBatisUtil {
          private static SqlSessionFactory factory;
          private static final ThreadLocal<SqlSession> local=new ThreadLocal<SqlSession>();
          static {
              try {
                  InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
                  factory=new SqlSessionFactoryBuilder().build(is);
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
          public static SqlSession getSqlSession(){
              return getSqlSession(false);
          }
      //  两个getSqlSession方法都是默认是false,自动提交
          private static SqlSession getSqlSession(boolean isAutoCommit){
              SqlSession sqlSession=local.get();
              if(sqlSession==null){
                  sqlSession=factory.openSession(isAutoCommit);
                  local.set(sqlSession);
              }
              return sqlSession;
          }
      
          public static <T extends Object>T getMapper(Class<T> c){
              SqlSession sqlSession=getSqlSession(true);
              return sqlSession.getMapper(c);
      
          }
          public static SqlSessionFactory getFactory(){
              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) null,
    uid       int  not null  unique,
);
11.3.2创建实体类
<!--        Lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.26</version>
            <scope>provided</scope>
        </dependency>
  • User
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.ToString;
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @ToString
    public class User {
        private int userId;
        private String userName;
        private String userPwd;
        private String userRealname;
        private String userImg;
    
    }
    
  • Detail
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.ToString;
    
    @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 创建DAO接口定义操作方法
11.3.4 单元测试
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.1</version>
    <scope>test</scope>
</dependency>
11.3.5 添加操作
  • UserDAO.java
public int insertUser(User user);
  • DetailDAO.java
public int insertDetail(Detail detail);
  • mybatis-config.xml
<mappers>
    <mapper resource="mappers/UserMapper.xml"></mapper>
    <mapper resource="mappers/DetailMapper.xml"></mapper>
</mappers>
  • UserMapper.xml
<insert id="insertUser" useGeneratedKeys="true" keyProperty="userId">
    insert into users(user_name,user_pwd,user_realname,user_img)
    values (#{userName},#{userPwd},#{userRealname},#{userImg})
</insert>
  • DetailMapper.xml
<insert id="insertDetail">
    insert into details(user_addr, user_tel, user_desc, uid)
    VALUES (#{userAddr},#{userTel},#{userDesc},#{userId})
</insert>
  • UserDaoTest
@Test
    public void testInsertUser() {
//        用户注册,提交了基本信息和详情到Servlet,Servlet接收注册信息封装到User和Detail对象中
        Detail detail=new Detail(0,"江西省赣州市","13976751288","有个性",0);
        User user = new User(0, "lijin", "425161", "黎锦", "3.jpg");
        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 j = detailDAO.insertDetail(detail);
            System.out.println(j);
            sqlSession.commit();
        }catch (Exception e){
            e.printStackTrace();
            sqlSession.rollback();
        }
    }
11.3.6 一对一关联操作

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

方法一(复杂)
  • UserDAO.java
//    根据用户名查询用户信息
    public User queryUser(String username);
  • DetailDAO.java
public Detail queryDetail(int id);
  • mybatis-config.xml
<typeAliases>
    <typeAlias type="com.xie.pojo.User" alias="User"></typeAlias>
    <typeAlias type="com.xie.pojo.Detail" alias="Detail"></typeAlias>
</typeAliases>
  • UserMapper.xml
<resultMap id="userMap" type="User">
    <id column="user_id" property="userId"/>
    <result column="user_name" property="userName"/>
    <result column="user_pwd" property="userPwd"/>
    <result column="user_realname" property="userRealname"/>
    <result column="user_img" property="userImg"/>
</resultMap>


<sql id="USER">
    user_id,user_name,user_pwd,user_realname,user_img
</sql>


<select id="queryUser" resultMap="userMap">
    select <include refid="USER"/>
    from users where user_name=#{userName}
</select>
  • DetailMapper.xml
<resultMap id="detailMap" type="Detail">
    <id column="user_addr" property="userAddr"/>
    <result column="user_tel" property="userTel"/>
    <result column="user_desc" property="userDesc"/>
    <result column="uid" property="userId"/>
</resultMap>
<sql id="DETAIL">
    detail_id, user_addr, user_tel, user_desc, uid
</sql>

<select id="queryDetail" resultMap="detailMap">
    select <include refid="DETAIL"/>
    from details where uid=#{arg0}
</select>
  • UserDaoTest
@Test
public void queryUser() {
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    try {
    UserDAO userDAO=sqlSession.getMapper(UserDAO.class);
    User user=userDAO.queryUser("lisi");
    int i=user.getUserId();
        System.out.println(user);
    DetailDAO detailDAO = sqlSession.getMapper(DetailDAO.class);
    Detail detail=detailDAO.queryDetail(i);
    System.out.println(detail);
    sqlSession.commit();
    }catch (Exception e){
        e.printStackTrace();
        sqlSession.rollback();
    }
}
方法二(映射文件--连接查询)常用
  • 实体类 图片.png
  • 映射文件
    <resultMap id="userMap" type="User">
        <id column="user_id" property="userId"/>
        <result column="user_name" property="userName"/>
        <result column="user_pwd" property="userPwd"/>
        <result column="user_realname" property="userRealname"/>
        <result column="user_img" property="userImg"/>
        <result column="detail_id" property="detail.detailId"/>
        <result column="user_addr" property="detail.userAddr"/>
        <result column="user_tel" property="detail.userTel"/>
        <result column="user_desc" property="detail.userDesc"/>
        <result column="uid" property="detail.userId"/>
    </resultMap>
    
    <select id="queryUser2" resultMap="userMap">
        select user_id,user_name,user_pwd,user_realname,user_img,detail_id, user_addr, user_tel, user_desc,uid
        from users u
        inner join details d
        on u.user_id=d.uid
        where u.user_name=#{userName}
    </select>
    
  • UserDaoTest.java
    @Test
    public void queryUser2() {
        UserDAO userDAO=MyBatisUtil.getMapper(UserDAO.class);
        User user=userDAO.queryUser2("lihua");
        System.out.println(user);
    }
    
方法三(映射文件--子查询)
  • UserDAO.java
public User queryUser3(String username);
  • DetailDAO.java
public Detail queryDetail(int id);
  • mybatis-config.xml
<typeAliases>
    <typeAlias type="com.xie.pojo.User" alias="User"></typeAlias>
    <typeAlias type="com.xie.pojo.Detail" alias="Detail"></typeAlias>
</typeAliases>
  • UserMapper.xml
<resultMap id="userMap3" type="User">
    <id column="user_id" property="userId"/>
    <result column="user_name" property="userName"/>
    <result column="user_pwd" property="userPwd"/>
    <result column="user_realname" property="userRealname"/>
    <result column="user_img" property="userImg"/>
    <association property="detail" select="com.xie.dao.DetailDAO.queryDetail" column="user_id"/>
</resultMap>
<!--        association:调用子查询,关联查询一个对象-->

<select id="queryUser3" resultMap="userMap3">
    select user_id,user_name,user_pwd,user_realname,user_img
    from users
    where user_name=#{username}
</select>
  • DetailMapper.xml
<resultMap id="detailMap" type="Detail">
    <id column="detail_id" property="detailId"/>
    <result column="user_addr" property="userAddr"/>
    <result column="user_tel" property="userTel"/>
    <result column="user_desc" property="userDesc"/>
</resultMap>
<sql id="DETAIL">
    detail_id, user_addr, user_tel, user_desc, uid
</sql>

<select id="queryDetail" resultMap="detailMap">
    select detail_id, user_addr, user_tel, user_desc
    from details where uid=#{uid}
</select>
  • UserDaoTest
@Test
public void queryUser3() {
    UserDAO userDAO=MyBatisUtil.getMapper(UserDAO.class);
    User user=userDAO.queryUser3("wanglei");
    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) null primary key ,
   sname varchar(20) not null,
   sage int not null,
   scid int not null
);
11.4.2 创建实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Student {
    private int stuId;
    private String stuName;
    private int stuAge;
    private int stuCid;//学生所在班级的id
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Clazz {
    private int classId;
    private String className;
    private String classDesc;
    private List<Student> stus;//存储当前班级下的学生信息
}
11.4.3 关联查询

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

连接查询
  • ClazzDAO.java
public Clazz selectAllStudent(int classId);
  • 映射文件
<resultMap id="clazzMap2" type="Clazz">
        <id column="cid" property="classId"/>
        <result column="cname" property="className"/>
        <result column="cdesc" property="classDesc"/>
<!--        Clazz对象的stus属性是一个List集合,需要使用collection标签-->
<!--        ofType声明集合元素的类型-->
        <collection property="stus" ofType="Student">
            <result column="sid" property="stuId"/>
            <result column="sname" property="stuName"/>
            <result column="sage" property="stuAge"/>
            <result column="scid" property="stuCid"/>
        </collection>

    </resultMap>
    <select id="selectAllStudent" resultMap="clazzMap2">
            select cid, cname, cdesc, sid, sname, sage, scid
            from classes c
                     inner join students s
                                on c.cid=s.scid
            where c.cid=#{classId}
        </select>
  • ClazzDaoTest.java
@Test
public void selectAllStudent() {
        ClazzDAO classDAO= MyBatisUtil.getMapper(ClazzDAO.class);
        Clazz clazz =classDAO.selectAllStudent(2);
    System.out.println(clazz);
}
子查询
  • ClazzDAO.java
public Clazz selectAllStudent2(int classId);
  • StudentDAO.java
public Student SelectById(int classId);
  • mybatis-config.xml
<typeAliases>
    <typeAlias type="com.xie.pojo.Student" alias="Student"></typeAlias>
    <typeAlias type="com.xie.pojo.Clazz" alias="Clazz"></typeAlias>
</typeAliases>
  • ClazzMapper.xml
<mapper namespace="com.xie.dao.ClazzDAO">
    <resultMap id="clazzMap" type="Clazz">
        <id column="cid" property="classId"/>
        <result column="cname" property="className"/>
        <result column="cdesc" property="classDesc"/>
        <association property="stus" select="com.xie.dao.StudentDAO.SelectById" column="cid"/>
    </resultMap>
<select id="selectAllStudent2" resultMap="clazzMap">
    select cid, cname, cdesc
    from classes
    where cid=#{classId}
</select>
  • StudentMapper.xml
<resultMap id="studentMap" type="Student">
    <id column="sid" property="stuId"/>
    <result column="sname" property="stuName"/>
    <result column="sage" property="stuAge"/>
    <result column="scid" property="stuCid"/>
</resultMap>
<select id="SelectById" resultMap="studentMap">
    select sid, sname, sage, scid
    from students
    where scid=#{scid}
</select>
  • ClazzDaoTest
@Test
public void selectAllStudent2() {
    ClazzDAO classDAO= MyBatisUtil.getMapper(ClazzDAO.class);
    Clazz clazz =classDAO.selectAllStudent2(2);
    System.out.println(clazz);
}

11.5 多对一关联

实例:学生(n)——班级(1)
当查询一个学生时,关联查询这个学生所在的班级信息

11.5.1 创建实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Clazz2 {
    private int classId;
    private String className;
    private String classDesc;

}
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Student2 {
    private int stuId;
    private String stuName;
    private int stuAge;
    private Clazz2 clazz;//学生所在班级
}
11.5.2 关联查询
连接查询
  • StudentDAO.java
public Student2 queryStudentBySid(String sid);
  • StudentMapper.xml
<resultMap id="studentMap2" type="Student2">
    <id column="sid" property="stuId"/>
    <result column="sname" property="stuName"/>
    <result column="sage" property="stuAge"/>
    <result column="cid" property="clazz.classId"/>
    <result column="cname" property="clazz.className"/>
    <result column="cdesc" property="clazz.classDesc"/>
</resultMap>
<select id="queryStudentBySid" resultMap="studentMap2">
    select sid, sname, sage, scid, cid, cname, cdesc
    from students s
     inner join classes c
     on s.scid=c.cid
    where s.sid=#{sid}
</select>
  • StudentDaoTest.java
@Test
public void queryStudentBySid() {
    StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);
    Student2 student2 = studentDAO.queryStudentBySid("2001");
    System.out.println(student2);
}
子查询
  • StudentDAO.java
public Student2 queryStudentBySid2(String sid);
  • ClazzDAO.java
public Clazz queryClass(int classId);
  • ClassMapper.xml
<resultMap id="clazzMap3" type="Clazz2">
    <id column="cid" property="classId"/>
    <result column="cname" property="className"/>
    <result column="cdesc" property="classDesc"/>
</resultMap>
<select id="queryClass" resultMap="clazzMap3">
    select cid, cname, cdesc
    from classes
    where cid=#{cId}
</select>
  • StudentMapper.xml
<resultMap id="studentMap3" type="Student2">
    <id column="sid" property="stuId"/>
    <result column="sname" property="stuName"/>
    <result column="sage" property="stuAge"/>
    <association property="clazz" select="com.xie.dao.ClazzDAO.queryClass" column="scid"></association>
</resultMap>
<select id="queryStudentBySid2" resultMap="studentMap3">
    select sid, sname, sage, scid
    from students
    where sid=#{sid}
</select>
  • StudentDaoTest.java
@Test
public void queryStudentBySid2() {
    StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);
    Student2 student2 = studentDAO.queryStudentBySid2("2003");
    System.out.println(student2);
}

11.6 多对多关联

案例:学生(n)——课程(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 关联查询

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

  • Student3.java
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Student3 {
    private int stuId;
    private String stuName;
    private int stuAge;
    private List<Course> courses;
}
  • Course.java
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Course {
    private int couresId;
    private String courseName;
}

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

  • Student4.java
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Student4 {
    private int stuId;
    private String stuName;
    private int stuAge;
}
  • Course2.java
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Course2 {
    private int couresId;
    private String courseName;
    private List<Student> students;
}
连接查询
  • CourseDAO.java
public Course2 queryCourse(int courseId);
  • CourseMapper.xml
<resultMap id="courseMap" type="course2">
    <id column="course_id" property="couresId"/>
    <result column="course_name" property="courseName"/>
    <collection property="students" ofType="Student4">
        <result column="sid" property="stuId"/>
        <result column="sname" property="stuName"/>
        <result column="sage" property="stuAge"/>
    </collection>

</resultMap>
<select id="queryCourse" resultMap="courseMap">
    select course_id, course_name, s.sid,  sname, s.sage
    from courses c inner join grades g inner join students s
    on c.course_id=g.cid and g.sid=s.sid
    where c.course_id=#{courseId}
</select>
  • CourseDAOTest.java
@Test
public void testQueryCourse() {
    CourseDAO courseDAO = MyBatisUtil.getMapper(CourseDAO.class);
    Course2 course = courseDAO.queryCourse(1);
    System.out.println(course);
}
子查询

自行觉悟

图片.png

十二、动态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作为参数
//    优点:无需单独定义传递查询的类
//    缺点:当向Map中存放参数时key必须与动态sql保持一致
    public List<Member> searchMember(HashMap<String ,Object> params);
//      也可以定义专门查询条件的实体类作为封装参数
//    优点:设置参数时无需关注属性名
//    缺点:需要单独定义一个类来封装参数
    public List<Member> searchMember(MemberSearchCondition params);
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class MemberSearchCondition {
    private String gender;
    private int minAge;
    private int maxAge;
    private String city;
}
  • MemberMapper.xml
12.2.4 where
<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>
    <if test="gender !=null">
       and member_gender = #{gender}
    </if>
    <if test="minAge !=null">
      and member_age &gt;=#{minAge}
    </if>
    <if test="maxAge !=null">
      and member_age &lt;=#{maxAge}
    </if>
    <if test="city !=null">
       and member_city = #{city}
    </if>
    </where>
    order by member_age
</select>
  • MemberDAOTest.java
 @Test
    public void searchMember() {
        MemberSearchCondition params2=new MemberSearchCondition();
//        params2.setGender("男");
        params2.setMinAge(20);
        params2.setMaxAge(22);
//        params2.setCity("武汉");

        MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
        List<Member> members=memberDAO.searchMember(params2);
       for(Member m:members){
           System.out.println(m);
       }
}
12.2.5 trim
<select id="searchMember" resultMap="memberMap">
    select member_id, member_nick, member_gender, member_age, member_city
    from members
    <trim prefix="where" prefixOverrides="and | or" suffix="order by member_age">
    <if test="gender !=null">
       and member_gender = #{gender}
    </if>
    <if test="minAge !=null">
      and member_age &gt;=#{minAge}
    </if>
    <if test="maxAge !=null">
      and member_age &lt;=#{maxAge}
    </if>
    <if test="city !=null">
       and member_city = #{city}
    </if>
    </trim>
</select>
12.2.6 foreach
public List<Member> searchMemberByCity(List<String> cities);
<select id="searchMemberByCity" resultMap="memberMap">
    select member_id, member_nick, member_gender, member_age, member_city
    from members where member_city in
    <foreach collection="list" item="cityName" separator="," open="(" close=")">
        #{cityName}
    </foreach>
</select>
@Test
public void searchMemberByCity() {
    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);
    }
}

十三、模糊查询

两种方法


//    根据昵称查询用户信息——模糊查询
    public List<Member> searchMemberByNick(@Param("keyword") String keyword);
    或者 public List<Member> searchMemberByNick(String keyword);
    public List<Member> searchMemberByNick2(HashMap params);

在使用${}时,即使只有一个参数也需要使用@Param注解声明参数的key(对象参数可以不用声明)

 <select id="searchMemberByNick" resultMap="memberMap">
        select member_id, member_nick, member_gender, member_age, member_city
        from members
        where member_nick like '%${keyword}%'
    </select>
    <select id="searchMemberByNick2" 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>
<!--    ${key}表示获取参数,先获取参数的值拼接到SQL语句中,再编译执行SQL语句,可能引起SQL注入问题-->
<!--    #{key}表示获取参数,先完成SQL编译(预编译),预编译之后再将获取的参数设置到SQL语句中,可以避免SQL注入问题-->

如果参数是String类型,需要parameterType声明参数类型

@Test
public void searchMemberByNick() {
    MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
    List<Member> members = memberDAO.searchMemberByNick("李");
    for(Member m:members){
        System.out.println(m);
    }
}
@Test
public void searchMemberByNick2() {
    MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
    HashMap<String,Object>param=new HashMap<String,Object>();
    param.put("keyword","李");
    List<Member> members = memberDAO.searchMemberByNick2(param);
    for(Member m:members){
        System.out.println(m);
    }
}

十四、MyBatis日志配置

MyBatis做为一个封装好的ORM框架,其运行过程我们没办法跟踪,为了让开发者了解MyBatis执行流程及每个执行步骤所完成的工作,MyBatis框架本身继承了log4j日志框架,对运行的过程进行跟踪记录。我们只需对MvBatis进行相关的日志配置,就可以看到MyBatis运行过程中的日志信息。

14.1 添加日志框架依赖

<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 日志信息
log4j.appender.stdout.layout.ConversionPattern=[%t] %5p - %msg :%m%n

14.3 日志信息的级别

在使用日志框架输出日志信息的时候,会根据输出的日志信息的重要程度分为5个级别

级别说明
DEBUG输出调试信息
INFO输出提示信息
WARN输出警告信息
ERROR一般性错误信息
FATAL致命性错误信息

十五、整合Druid连接池

MvBatis做为一个ORM框架,在进行数据库操作时是需要和数据库连接连接的,MyBatis支持基于数据库连接池的连接创建方式
当我们配置MyBatis数据源时,只要配置了datasource标签的type属性值为POOLED时,就可以使用MvBatis内置的连接池管理连接。
如果我们想要使用第三方的数据库连接池,则需进行自定义配置。

15.1 常见的连接池

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

常见数据库连接池性能分析对比

功能dbcpdruidc3p0tomcat-jdbcHikariCP
是否支持PSCache
监控jmxjmx/log/httpjmx,logjmxjmx
扩展性
sql拦截及解析支持
代码简单中等复杂简单简单
更新时间2015.8.62015.10.10 2015.12.09 2015.12.3
特点依赖于common-pool阿里开源,功能全面历史久远,代码逻辑复杂,且不易维护 优化力度大,功能简单,起源于boneCP
连接池管理LinkedBlockingDeque数组 FairBlockingQueuethreadlocal+CopyOnWriteArrayList

15.3 添加Druid依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.16</version>
</dependency>

15.4 创建DruidDataSourceFactory配置给MyBatis


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

}

15.5 将MyBatis的连接池替换为Druid

 <environment id="mysql">
            <transactionManager type="JDBC"></transactionManager>
<!--            POOLED:使用MyBatis内置的连接池实现-->
<!--            mybatis需要一个连接池工厂,这个工厂可以产生数据库连接池Pooled-->
<!--            多态-->
            <dataSource type="com.xie.utils.DruidDataSourceFactory">
                <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
                <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/SSM?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

十六、MyBatis缓存

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

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

16.1 缓存的工作原理

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

图片.png

16.2 MyBatis缓存

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

16.2.1 一级缓存

一级缓存也叫做SglSession级缓存,为每个SalSession单独分配的缓存内存,无需手动开启可直接使用;多个SqlSession的缓存是不共享的。
特性:

  1. 如果多次查询使用的是同一个salSession对象,则第一次查询之后数据会存放到缓存,后续的查询则直接访问缓存中存储的数据:
  2. 如果第一次查询完成之后,对查询出的对象进行修改此修改会影响到缓存),第二次查询会直接访问缓存,造成第二次查询的结果与数据库不一致;
  3. 当我们进行在查询时想要跳过缓存直接查询数据库,则可以通过sglSession.cearCache();来清除当前SqlSession的缓存;
  4. 如果第一次查询之后第二查询之前,使用当前的sglsession执行了修改操作,此修改操作会使第一次查询并缓存的数据失效,因此第二次查询会再次访问数据库。

测试代码:

图片.png

16.2.2 一级缓存的问题

问题:
第一次查询之后,进行了修改操作,数据库数据已经被修改,但是第二查询的时候依然显示的修改前的数据。
分析
修改操作和查询操作不是同一个线程,因此使用的不同的studentDAO对象(使用不同的sqlSession),因此修改操作不会导致查询操作的缓存失效,第二次查询的时候依然访问的是缓存而没有查数据库
解决方案

  1. 让修改操作和查询操作使用相同的SalSession (不太合理)
  2. 每次进行查询操作之后,清空缓存,让再次查询的时候绕过缓存直接访问数据库

图片.png

16.2.3 二级缓存

二级缓存也称为SalSessionFactorv级缓存,通过同一个factory对象获取的Salsession可以共享二级缓存;在应用服务器中SalSessionFactorv是单例的,因此我们二级缓存可以实现全局共享。
特性:

  1. 二级缓存默认没有开启,需要在mybatis-configxml中的settings标签开启
  2. 二级缓存只能缓存实现序列化接口的对象
  • 在MyBatis-config.xml开启使用二级缓存
<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>
  • 在需要使用二级缓存的mapper文件中配置cache标签使用二级缓存
<cache/>

图片.png

  • 按缓存的实体类实现序列化窗口
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Member implements Serializable {
    private int memberId;
    private String memberNick;
    private String memberGender;
    private int memberAge;
    private String memberCity;
}
  • 测试 图片.png

16.3 查询操作的缓存开关

<select id="searchMemberByNick" resultMap="memberMap" useCache="false">
    select member_id, member_nick, member_gender, member_age, member_city
    from members
    where member_nick like '%${keyword}%'
</select>

只有select标签才可以控制是否使用缓存(useCache="false")

十七、延迟加载

延迟加载--如果在MvBatis开启了延迟加载,在执行了子查询(至少查询两次及以上)时,默认只执行第一次查询,当用到子查询的查询结果时,才会触发子查询的执行;如果无需使用子查询结果,则子查询不会执行

延迟查询只有子查询才能用

图片.png