MyBatis

一、MyBatis简介

1.1框架概念

框架:就是软件的半成品,完成了软件开发过程中的通用操作,只需要很少或者不用加工就能实现特定的功能,从而提高开发效率

1.2常用框架

  • MVC框架:简化了Servlet的开发步骤
    • Struts
    • Struts2
    • SpringMVC
  • 持久层框架:完成数据库操作的框架
    • apache DBUtils
    • Hibernate
    • Spring JPA
    • MyBatis
  • 胶水框架Spring

SSM Spring SpringMVC MyBatis

SSH Spring Struts2 Hibernate

1.3MyBatis介绍

MyBatis是一个半自动ORM框架

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

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

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

二、MyBatis框架部署

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

2.1 创建Maven项目

  • Jva工程
  • Web工程

2.2 在项目中添加MyBatis依赖

  • 在pom.xml中添加依赖

    • mybatis
    • mysql driver
    <dependencies>
        <!-- mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.10</version>
        </dependency>
        <!-- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.38</version>
        </dependency>
    </dependencies>
    

2.3创建MyBatis配置文件

  • 创建自定义模板

  • mybatis-config.xml文件配置数据库连接信息

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <!-- 在environments配置数据库连接信息 -->
        <environments default="mysql">
            <environment id="mysql">
                <!-- transactionManager标签用于配置数据库管理方式 -->
                <transactionManager type="JDBC"></transactionManager>
                <dataSource type="PEELED">
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/le_tian_study?characterEncoding=UTF-8"/>
                    <property name="username" value="root"/>
                    <property name="password" value="root"/>
                </dataSource>
            </environment>
        </environments>
    </configuration>
    

三、MyBatis框架使用

案例:信息的数据库操作

3.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
);

3.2创建实体类

Student.java

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Student {
    private int sid;
    private String stuNum;
    private String stuName;
    private String stuGander;
    private int stuAge;
}

3.3创建DAO接口,定义操作方法

StudentDAO.java

import com.le_tian.study.pojo.Student;

public interface StudentDAO {
 //添加数据库信息
 public int insertStudent(Student student);
 //删除数据库信息
 public int deleteStudent(int stuNum);
}

3.4创建DAO接口的映射文件

resources文件定义为包

创建mappers文件夹并创建以实体类+Mapper命名的xml文件

在映射文件中对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.le_tian.study.dao.StudentDAO">
    <!-- id要与接口的名称一致,parameterType定义参数类型,接口中实现可不写 -->
    <insert id="insertStudent" parameterType="com.le_tian.study.pojo.Student">
        insert into tb_student(stu_num,sut_name,stu_gander,stu_age)
        values(#{stuNum},#{stuName},#{stuGander},#{stuAge})
    </insert>
    
    <delete id="deleteStudent">
        delete from tb_student where sut_name=#{stuNum}
    </delete>
</mapper>   

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

<mappers>
    <mapper resource="mappers/StudentMapper.xml"></mapper>
</mappers>

四、单元测试

在被测试类名后ALT+INSERT选择Test

五、MyBatis的CRUD操作

5.1添加操作

package com.le_tian.study.dao;

import com.le_tian.study.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 StudentDAOTest {

    @Test
    public void insertStudent() {
        try {
            //加载mybatis配置文件
            InputStream resources = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            //会话工厂
            SqlSessionFactory factory = builder.build(resources);
            //会话(连接)
            SqlSession sqlSession = factory.openSession();
            //通过会话获取DAO对象
            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
            //测试方法
            int i = studentDAO.insertStudent(new Student(0,"1002", "张三", "男", 21));
            sqlSession.commit();
            System.out.println(i);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
<insert id="insertStudent" parameterType="com.le_tian.study.pojo.Student">
    insert into tb_students(stu_num,stu_name,stu_gender,stu_age)
    values(#{stuNum},#{stuName},#{stuGender},#{stuAge})
</insert>

5.2删除操作

@Test
public void deleteStudent() {
    try {
        //读取mybatis配置文件
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //SqlSessionFactory表示MyBatis的会话工厂
        SqlSessionFactory factory = builder.build(is);
        //SqlSession表示MyBatis与数据库之间的会话,通过工厂方法设计模式
        SqlSession sqlSession = factory.openSession();
        //通过SqlSession对象调用getMapper方法获取DAO接口对象
        StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
        int i = studentDAO.deleteStudent("1002");
        sqlSession.commit();
        System.out.println(i);
    } catch (IOException e) {
        e.printStackTrace();
    }
}
<delete id="deleteStudent">
    delete from tb_students where stu_num=#{stuNum}
</delete>

5.3修改操作

@Test
public void updateStudent() {
    try {
        //读取mybatis配置文件
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //会话工厂
        SqlSessionFactory build = builder.build(is);
        //创建会话(连接)
        SqlSession sqlSession = build.openSession();
        //虚拟StudentDAO对象
        StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
        //测试
        int i = studentDAO.updateStudent(new Student(0, "1002", "李四", "女", 21));
        //断言
        asserEquals(1,i);
        sqlSession.commit();
        System.out.println(i);
    } catch (IOException e) {
        e.printStackTrace();
    }
}
<update id="updateStudent">
    update tb_students set stu_name=#{stuName},stu_gender=#{stuGender},stu_age=#{stuAge} where stu_num=#{stuNum}
</update>

5.4查询操作

@Test
public void selectStudent() {
    try {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory build = builder.build(is);
        SqlSession sqlSession = build.openSession();
        StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
        Student student = studentDAO.selectStudent("1002");
        assertNotNull(student);
    } catch (IOException e) {
        e.printStackTrace();
    }
}
<!-- resultType指定结果封装的对象类型 -->
<!-- resultSets指定当前操作返回的集合类型(可省略) -->
<!-- resultMap用于定义实体类与数据表的映射关系(ORM) -->
<!-- resultMap用于一个实体的映射关系,当配置了resultMap之后 resultType就可以省略 -->
<resultMap id="StudentMap" type="com.le_tian.study.pojo.Student">
    <id column="stu_id" 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>
<select id="selectStudent" resultType="com.le_tian.study.pojo.Student" resultSets= resultMap="StudentMap">
    select sid,stu_num stuNum,stu_name stuName,stu_gender stuGender,stu_age stuAge from tb_students
</select>

5.5统计

@Test
public void countStudent(){
    try {
        InputStream resources = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(resources);
        SqlSession sqlSession = factory.openSession();
        StudentDAO mapper = sqlSession.getMapper(StudentDAO.class);
        System.out.println(mapper.countStudent());
    } catch (IOException e) {
        e.printStackTrace();
    }
}
<select id="countStudent" resultType="int">
    select count(1) from tb_students
</select>

5.6查询操作-多参数查询

在MyBatis进行操作

单个参数

  1. 操作方法只有一个简单类型或者字符串类型的参数,在Mapper配置中可以直接通过#{str}直接获取
  2. 操作方法只有一个对象类型的参数,在Mapper配置中可以直接通过#attrName获取对象的指定属性值(attrName)必须是参数对象的属性
  3. 操作方法有一个Map类型的参数,在Mapper配置中可以直接通过#{key}获取key对应的value

多个参数

  • 在StudentDAO中定义操作方法,使用@Param注解声明参数的别名
//分页查询
public List<Student> selectStudentByPage(@Param("start") int start,@Param("page") int page);
  • 在StudentMapper.xml配置sql时,使用#{别名}获取指定的参数

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

 <select id="selectStudentByPage" resultMap="StudentMap">
<!-- limit #{arg0},#{arg1} limit #{param1},#{param2} -->
 select sid,stu_num,stu_name,stu_gender,stu_age from tb_students limit #{start},#{page}
</select>

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

StduentMapper.xml的添加操作标签--insertt

<!-- 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>
@Test
public void selectStudent() {
    try {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory build = builder.build(is);
        SqlSession sqlSession = build.openSession();
        StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
        List<Student> student = studentDAO.selectStudent();
        student.forEach(student1 -> System.out.println(student1.toString()));
        assertNotNull(student);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

六、工具类的封装

package com.le_tian.study.util;

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 {
    public static SqlSessionFactory factory;
    public static final ThreadLocal<SqlSession> sqlSessionThreadLocal = new ThreadLocal<>();
    static{
        try {
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            factory = new SqlSessionFactoryBuilder().build(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static SqlSession getSqlSession(){
        SqlSession sqlSession = sqlSessionThreadLocal.get();
        if(null==sqlSession){
            sqlSession = factory.openSession();
            sqlSessionThreadLocal.set(sqlSession);
        }
        return sqlSession;
    }

    public static <T extends Object>T getMapper(Class<T> c){
        return getSqlSession().getMapper(c);
    }
}

七、事务管理

SqlSession对象

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

7.1手动提交事务

  • sqlSession.commit();提交事务
  • sqlSession.roolback();事务回滚
@Test
public void insertStudent() {
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    try {
        //通过会话获取DAO对象
        //当我们获取sqlSession对象时,就默认开启了事务
        StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
        //测试StudentDAO中的方法
        Student student = new Student(0, (Integer.parseInt(studentDAO.selectStudentMaxStuNum()) + 1) + "", "张六", "男", 21);
        int i = studentDAO.insertStudent(student);
        //操作完成之后,手动提交
        sqlSession.commit();
    }catch (Exception e){
        //当操作出现异常,调用rollback进行回滚
        sqlSession.rollback();
    }
}

7.2自动提交事务

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

  • 自动提交事务:factory.openSession(true)
  • 手动提交事务:factory.openSession();/factory.openSession(false)

MyBatisUtil优化

package com.le_tian.study.util;

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 {
    public static SqlSessionFactory factory;
    public static final ThreadLocal<SqlSession> sqlSessionThreadLocal = new ThreadLocal<>();
    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);
    }
    
    private static SqlSession getSqlSession(boolean isAutocommit){
        SqlSession sqlSession = sqlSessionThreadLocal.get();
        if(null==sqlSession){
            sqlSession = factory.openSession(isAutocommit);
            sqlSessionThreadLocal.set(sqlSession);
        }
        return sqlSession;
    }

    public static <T extends Object>T getMapper(Class<T> c){
        return getSqlSession(true).getMapper(c);
    }
}

测试操作

@Test
public void deleteStudent() {
    StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);
    studentDAO.deleteStudent("1002");
}

八、MyBatis主配置文件

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

8.1properties标签

  • 在resources目录下创建jdbc.properties文件,配置键值对如下:
jdbc.mysql.driver=com.mysql.jdbc.Driver
jdbc.mysql.url=jdbc:mysql://localhost:3306/le_tian_study?useSSL=false&amp;characterEncoding=UTF-8
jdbc.mysql.username=root
jdbc.mysql.password=root
  • mybatis-config.xml中通过properties标签引用jdbc.properties文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- properties标签:1.可以定义键值对 2.可以引用属性文件 -->
    <properties resource="jdbc.properties"/>

    <!-- 在environments配置数据库连接信息 -->
    <environments default="mysql">
        <environment id="mysql">
            <!-- transactionManager标签用于配置数据库管理方式 -->
            <transactionManager type="JDBC"/>
            <!-- POOLED使用MyBatis内置的连接池 -->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.mysql.driver}"/>
                <property name="url" value="${jdbc.mysql.url}"/>
                <property name="username" value="${jdbc.mysql.username}"/><property name="password" value="${jdbc.mysql.password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mappers/StudentMapper.xml"/>
    </mappers>
</configuration>

8.2setting标签

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

8.3typeAliases标签

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

8.4plugins标签

<!-- plugins标签,用于配置MyBatis插件(分页插件) -->
<plugins>
	<plugin interceptor=""></plugin>
</plugins>

8.5environments标签

<!-- 在environments配置数据库连接信息 -->
<!-- 在environments标签中可以定义多个environment标签,每个environment标签可以定义一套连接配置 -->
<!-- default属性,用来指定使用哪个environment标签 -->
<environments default="mysql">
    <environment id="mysql">
        <!-- transactionManager标签用于配置数据库管理方式
                type="JDBC"  可以进行事务的提交和回滚操作
                type="MANAGED"  依赖容器完成事务管理,本身不进行事务的提交和回滚操作
             -->
        <transactionManager type="JDBC"/>
        <!-- dataSource标签用来配置数据库连接信息 POOLED|UNPOOLED -->
        <dataSource type="POOLED">
            <property name="driver" value="${jdbc.mysql.driver}"/>
            <property name="url" value="${jdbc.mysql.url}"/>
            <property name="username" value="${jdbc.mysql.username}"/><property name="password" value="${jdbc.mysql.password}"/>
        </dataSource>
    </environment>
</environments>

8.6mappers标签

加载映射配置(映射文件、DAO注解)

<mappers>
    <mapper resource="mappers/StudentMapper.xml"/>
</mappers>

九、映射文件

9.1MyBatis初始化

9.2mapper根标签

mapper文件相当于DAO接口的‘实现类’,namespace属性要指定实现DAO接口的全限定名

9.3insert标签

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

常用属性

id,绑定对应DAO接口中的方法

parameterType,用于指定接口中对应方法的参数类型(可省略)

useGeneratedKeys,设置添加操作是否需要回填生成的主键

keyPeoperty属性,指定回填的id设置到参数对象中的哪个属性

timeout,设置此操作的超时时间,不设置则一直等待

主键回填

<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>
<insert id="insertStudent">
    <selectKey keyProperty="stuID" resultType="java.lang.Integer">
        select  last_insert_id()
    </selectKey>
</insert>

9.4delete标签

声明删除操作

9.5update标签

声明修改操作

9.6select标签

声明查询操作

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

9.7resultMap标签

<!-- resultMap用于定义实体类与数据表的映射关系(ORM) -->
<resultMap id="StudentMap" type="student">
    <id column="stu_id" 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.8cache标签

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

<cache type="" size=“” readOnly=""/>

9.9sql和include

SQL片段

<sql id="wanglaoji">sid,stu_num,stu_name,stu_gender,stu_age/>
<select id="IistStudents" resultWap="studentMap">
	select <include refid="wanglaoji"/> from tb_students
</select>

10、分页插件

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

10.1添加分页插件的依赖

PageHelper

<!-- 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.PageHelper"></plugin>
</plugins>

10.3分页查询

pagenum:分页页数
pageSize:分页条数
size:查询的信息条数
startRow:信息在数据库开始的行数
endRow:信息在数据库结束的行数
pages:当前页数
prePage:上一页的页数
nextPage:下一页的页数
isFirstpage:是不是第一页
isLastpage:是不是最后一页
list:查询结果
@Test
public void selectStudent() {
    StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);
    PageHelper.startPage(2,4);
    List<Student> student = studentDAO.selectStudent();
    PageInfo<Student> pageInfo = new PageInfo<>(student);
    pageInfo.getList().forEach(student1 -> System.out.println(student1));
    assertNotNull(student);
}

十一、关联映射

11.1实体关系

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

例如:用户和角色/房屋和楼栋/订单和商品

实体关系分为以下四种

一对一关联

实例:人和身份证/学生和学生证

数据表关系

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

  • 唯一外键关联

  • 一对多关联/多对一关联

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

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

    数据表关系:在多的一端添加外键和一的一端进行关联

  • 多对多关联

    用户和角色/角色和权限/房屋和业主

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

11.2创建项目,部署MyBatis框架

  • 创建web项目(maven)

     <!-- javax.servlet-api -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
        <scope>provided</scope>
    </dependency>
    
    <!-- jsp-api -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.0</version>
        <scope>provided</scope>
    </dependency>
    
  • 部署MyBatis框架

    • 添加依赖
    <!-- mybatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.9</version>
    </dependency>
    
    <!-- mysql-connector-java -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.49</version>
    </dependency>
    
    • 配置文件

    • 帮助类

      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;
      import java.io.InputStream;
      
      public class MyBatisUtil {
          public static SqlSessionFactory factory;
          public static final ThreadLocal<SqlSession> sqlSessionThreadLocal = new ThreadLocal<>();
          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);
          }
      
          private static SqlSession getSqlSession(boolean isAutocommit){
              SqlSession sqlSession = sqlSessionThreadLocal.get();
              if(null==sqlSession){
                  sqlSession = factory.openSession(isAutocommit);
                  sqlSessionThreadLocal.set(sqlSession);
              }
              return sqlSession;
          }
      
          public static <T extends Object>T getMapper(Class<T> c){
              return getSqlSession(true).getMapper(c);
          }
      }
      

11.3一对一关联

实例:用户----详情

11.3.1创建数据表

-- 用户信息表
cteate table users(
	user_id int primary key auto_increament,
    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_incrment,
    user_addr varchar(50) not null,
    user_tel char(11) not null,
    user_desc varchar(200),
    uid int not null unique
	-- constraint FK_USER foreign key(uid) references users(user_id)
)

11.3.2创建实体类

User.java

package com.mybatis.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private int userID;
    private String userName;
    private String userPwd;
    private String userRealName;
    private String userImg;
}

Detail.java

package com.mybatis.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Detail {
    private int detailID;
    private String userAddress;
    private String userTel;
    private String userDesc;
    private int uid;
}

11.3.3添加操作

11.3.4一对一关联查询

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

实体

User Detail

映射文件

  • 连接查询

    <resultMap id="UserMap" type="com.mybatis.pojo.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.userAddress"/>
        <result column="user_tel" property="detail.userTel"/>
        <result column="user_desc" property="detail.userDesc"/>
        <result column="uid" property="detail.uid"/>
    </resultMap>
    <select id="selectUserByName" resultMap="UserMap">
        select user_id,user_name,user_img,detail_id,user_addr,user_tel,user_desc,uid
        from users inner join details on users.user_id=details.uid where users.user_name=#{userName}
    </select>
    
  • 子查询

    <resultMap id="UserMap" type="com.mybatis.pojo.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.mybatis.dao.DetailDAO.selectDetailByUid" column="user_id"/>
    </resultMap>
    <select id="selectUserByName" resultMap="UserMap">
        select user_id,user_name,user_pwd,user_realname,user_img
        from users where user_name=#{userName} or user_realname=#{userName}
    </select>
    

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创建实体类

Clazz.java

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Clazz {
    private Integer cId;
    private String cName;
    private String cDesc;
    
    private List<Student> stus;//存储班级下的所有学生
}

Student.java

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Student {
    private String sId;
    private String sName;
    private Integer sAge;
    private Integer cId;
}

11.4.3关联查询

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

连接查询

<resultMap id="ClazzMap" type="Clazz">
    <id column="cid" property="cId"/>
    <result column="cname" property="cName"/>
    <result column="cdesc" property="cDesc"/>
    <!-- Clazz对象的students是一个List集合,需要使用collection标签 -->
    <!-- collection标签的ofType属性声明集合中元素的类型 -->
    <collection property="students" ofType="Student">
    <result column="sid" property="sId"/>
    <result column="sname" property="sName"/>
    <result column="sage" property="sAge"/>
    <result column="scid" property="sCid"/>
    </collection>
    </resultMap>
    <select id="selectClass" resultMap="ClazzMap">
    select cid,cname,cdesc,sid,sname,sage,scid from classes c inner join students s on c.cid=s.scid where cid=#{classId}
</select>

子查询

<resultMap id="ClazzMap" type="Clazz">
    <id column="cid" property="cId"/>
    <result column="cname" property="cName"/>
    <result column="cdesc" property="cDesc"/>
    <!-- Clazz对象的students是一个List集合,需要使用collection标签
        collection标签的ofType属性声明集合中元素的类型 -->
    <collection property="students" select="com.mybatis.dao.StudentDAO.selectStudentByCid" column="cid"/>
</resultMap>
<select id="selectClass" resultMap="ClazzMap">
    select cid,cname,cdesc from classes c where cid=#{classId}
</select>

11.5多对一关联

实例:学生----班级

11.5.1创建实体类

Student Clazz

十二、动态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_gander char(2) not null,
    member_age int not null,
    member_city varchar(30) not null
);

12.2.2创建实体类

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

十四、MyBatis日志配置

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

14.1添加日志框架依赖

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

14.3日志信息的级别

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

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

十五、配置数据库连接池-整合Druid

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

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

如果使用三方数据库连接池,则需要自定义配置

15.1常见的连接池

  • DBCP
  • C3P0
  • Druid:性能比较好,提供了比较便携的监控系统
  • HIkari:性能最好
功能 dbcp druid c3p0 tomcat-jdbc HikariCP
是否支持PSCache
监控 jmx jmx/log/http jmx,log jmx jmx
扩展性
sql拦截及解析 支持
代码 简单 中等 复杂 简单 简单
更新时间 2015.8.6 2015.10.10 2015.12.09 2015.12.3
特点 依赖于common-pool 阿里开源,功能全面 历史久远,代码逻辑复杂,且不易维护 优化力度大,功能简单,起源于boneCP
连接池管理 LinkedBlockingDeque 数组 FairBlockingQueue threadlocal+CopyOnWriteArrayList

15.2添加Druid依赖

<!-- druid -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.12</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"/>
        <dataSource type="com.le_tian.utils.DruidDataSourceFactory">
            <property name="driverClass" value="${jdbc.mysql.driver}"/>
            <property name="jdbcUrl" value="${jdbc.mysql.url}"/>
            <property name="username" value="${jdbc.mysql.username}"/>
            <property name="password" value="${jdbc.mysql.password}"/>
        </dataSource>
    </environment>
</environments>

十六、MyBatis缓存

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

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

16.1缓存的工作原理

16.2MyBatis缓存

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

16.2.1一级缓存

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

特性:

如果多次查询使用的是同一个SqlSeesion对象,则第一次查询之后数据会存放到缓存,后续的查询则优先查询缓存中存储的数据

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

再次查询时想要跳过缓存直接查询数据库,则可以通过sqlSession.clearCaChe();来清除当前SqlSession的缓存

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

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

16.2.3二级缓存

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

特性:

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

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

  • mybatis-config.xml开启使用二级缓存

    <settings>
    	<setting name="cacheEnabled" value="true"/>
    </settings>
    
  • 在需要使用二级缓存的Mapper文件中配置cache标签使用功能二级缓存

    <cache/>
    
  • 被缓存的实体类序列化接口

    @Data
    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    public class Member implements Serializable {
        private int memberId;
        private String memberNick;
        private String memberGender;
        private int memberAge;
        private String memberCity;
    }
    
  • 测试

    @Test
    public void testQueryMemberById(){
        SqlSessionFactory factory = MyBatisUtil.getSqlSessionFactory();
        //多个SqlSession对象必须来自同一个SqlSessionFactory
        SqlSession sqlSession1 = factory.openSession(true);
        SqlSession sqlSession2 = factory.openSession(true);
    
        MemberDAO memberDao1 = sqlSession1.getMapper(MemberDAO.class);
        Member member1 = memberDao1.queryMemberById(1);
        System.out.println(member1);
        //将当前sqlSession查询结果暂存到二级缓存需要执行commit();
        sqlSession1.commit();
    
        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>

十七、延迟加载

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

posted @ 2023-04-20 11:22  Byron_Zora  阅读(22)  评论(0)    收藏  举报