Mybatis学习笔记(传智播客)(完结)

说明:本文由大量的源代码,及截图,建议读者,操作一遍。ide:IDEA。文末有笔记及视频分享


Mybatis


数据表的sql下载 密码: xjjw

1、对原生态jdbc程序(单独使用jdbc开发)问题总结

先来看一部分代码

Public static void main(String[] args) {
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            ResultSet resultSet = null;

            try {
                //加载数据库驱动
                Class.forName("com.mysql.jdbc.Driver");

                //通过驱动管理类获取数据库链接
                connection =  DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "mysql");
                //定义sql语句 ?表示占位符
            String sql = "select * from user where username = ?";
                //获取预处理statement
                preparedStatement = connection.prepareStatement(sql);
                //设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
                preparedStatement.setString(1, "王五");
                //向数据库发出sql执行查询,查询出结果集
                resultSet =  preparedStatement.executeQuery();
                //遍历查询结果集
                while(resultSet.next()){
                    System.out.println(resultSet.getString("id")+"  "+resultSet.getString("username"));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }finally{
                //释放资源
                if(resultSet!=null){
                    try {
                        resultSet.close();
                    } catch (SQLException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                if(preparedStatement!=null){
                    try {
                        preparedStatement.close();
                    } catch (SQLException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                if(connection!=null){
                    try {
                        connection.close();
                    } catch (SQLException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }

            }

        }

上面代码有如下几个问题:

  • 数据库连接,使用时创建,不使用就关闭,对数据库进行频繁连接开启和关闭,造成数据库资源的浪费
    • 解决:使用数据库连接池管理数据库连接
  • 将sql 语句硬编码到Java代码中,如果sql语句修改,需要对java代码重新编译,不利于系统维护
    • 解决:将sql语句设置在xml配置文件中,即使sql变化,也无需重新编译
  • 向preparedStatement中设置参数,对占位符位置和设置参数值,硬编码到Java文件中,不利于系统维护
    • 解决:将sql语句及占位符,参数全部配置在xml文件中
  • 从resutSet中遍历结果集数据时,存在硬编码,将获取表的字段进行硬编码,不利于系统维护。
    • 解决:将查询的结果集,自动映射成java对象

2、Mybatis框架原理(掌握)

  • 什么是Mybatis?
    • mybatis是一个持久层的框架,是apache下的顶级项目。
    • mybatis托管到goolecode下,后来托管到github下:mybatis Github地址
    • mybatis让程序将主要精力放在sql上,通过mybatis提供的映射方式,自由灵活生成(半自动化,大部分需要程序员编写sql)满足需要sql语句。
    • mybatis可以将向 preparedStatement中的输入参数自动进行输入映射,将查询结果集灵活映射成java对象。(输出映射)
  • Mybatis原理图
    这里写图片描述

3、Mybatis入门程序

  • Mybatis和SpringMVC实现订单商品案例
  • 需求
    • 根据用户的id查询用户信息
    • 根据用户的名称模糊查询用户信息
    • 添加用户
    • 删除用户
    • 更新用户
  • Mybatis的运行环境(jar包)

    • 从这里点击下载

      笔者下载的是:
      Mybatis学习笔记(传智播客)(完结)_第1张图片
      下载mybatis-3.4.6.zip解压即可
      Mybatis学习笔记(传智播客)(完结)_第2张图片

      • lib下:依赖包
      • mybatis-3.4.6.jar:核心 包
      • mybatis-3.4.6.pdf,操作指南

    目录结构:
    Mybatis学习笔记(传智播客)(完结)_第3张图片
    jar包结构
    Mybatis学习笔记(传智播客)(完结)_第4张图片
    过程中需要数据库的连接,junit的调试,注意要将jar包build path!

log4j.properties

# Global logging configuration
# 开发环境下,日志级别要设置成DEBUG或者ERROR
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

SqlMapConfig.xml



<configuration>
    
    <environments default="development">
        <environment id="development">
            
            <transactionManager type="JDBC" />
            
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" />
                <property name="username" value="root" />
                <property name="password" value="" />
            dataSource>
        environment>
    environments>
    
    <mappers>
        <mapper resource="sqlmap/User.xml"/>
    mappers>
configuration>

User.xml




<mapper namespace="test">
    
    
    <select id="findUserById" parameterType="int" resultType="com.nuc.mybatis.po.User">
        select * from user where id=#{VALUE }
    select>




    
    <select id="findUserByName" parameterType="java.lang.String" resultType="com.nuc.mybatis.po.User">
        select * from user WHERE username LIKE '%${value}%'
    select>




    
    <insert id="insertUser" parameterType="com.nuc.mybatis.po.User">
        
        
      <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
          SELECT LAST_INSERT_ID()
      selectKey>
      insert into user (username,birthday,sex,address) value(#{username},#{birthday},#{sex},#{address})
    insert>


    <delete id="deleteUser" parameterType="java.lang.Integer">
        delete from user where id=#{id}
    delete>

    <update id="updateUser" parameterType="com.nuc.mybatis.po.User">
        UPDATE user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}
    update>
mapper>

User.java

package com.nuc.mybatis.po;

import java.util.Date;

public class User {
    //用户po
    //属性名和数据库字段名对应
    private int id;
    private String username;// 用户姓名
    private String sex;// 性别
    private Date birthday;// 生日
    private String address;// 地址

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

MybatisFirst.java

package com.nuc.mybatis.first;

import com.nuc.mybatis.po.User;
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;
import java.util.Date;
import java.util.List;

public class MybatisFirst {
    //根据id查询用户信息,得到一条记录
    @Test
    public void findUserByIdTest() throws IOException {
        //mybatis配置文件
        String resource = "SqlMapConfig.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //创建会话工厂
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //通过工厂得到SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //通过SqlSession操作数据库
        //第一个参数:映射文件中的statement的id,等于namespace+"."+statement的id
        //第二个参数:指定和映射文件中所匹配的所有parameterType的类型
        //sqlSession.selectOne()的结果是映射文件中所匹配的resultType类型的对象
        User user = sqlSession.selectOne("test.findUserById",1);
        System.out.println(user);
        //释放资源
        sqlSession.close();
    }
    //根据用户名称查询用户列表
    @Test
    public void findUserByName() throws IOException {
        //mybatis配置文件
        String resource = "SqlMapConfig.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //创建会话工厂
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //通过工厂得到SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        List list= sqlSession.selectList("test.findUserByName","小明");
        System.out.println(list);
        sqlSession.close();
    }
/*
    小结:
    selectOne和selectList:

    selectOne表示查询出一条记录进行映射。如果使用selectOne可以实现使用selectList也可以实现(list中只有一个对象)。
    selectList表示查询出一个列表(多条记录)进行映射。如果使用selectList查询多条记录,不能使用selectOne。
    如果使用selectOne报错:
    org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 4

 */
    @Test
    public void insertUserTest() throws IOException {
        //mybatis配置文件
        String resource = "SqlMapConfig.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //创建会话工厂
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //通过工厂得到SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        User user = new User();
        user.setUsername("宋江涛");
        user.setBirthday(new Date());
        user.setSex("男");
        user.setAddress("山西");
        //list中的user和映射文件User.xml中的resultType的类型一直
        sqlSession.insert("test.insertUser",user);
        //提交事务
        sqlSession.commit();
        //获取主键
        System.out.println(user.getId());
        sqlSession.close();
    }
    //删除用户
    @Test
    public void deleteUserTest() throws IOException {
        //mybatis配置文件
        String resource = "SqlMapConfig.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //创建会话工厂
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //通过工厂得到SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //list中的user和映射文件User.xml中的resultType的类型一直
        sqlSession.delete("test.deleteUser",30);
        //提交事务
        sqlSession.commit();
        sqlSession.close();
    }

    //更新用户
    @Test
    public void updateUserTest() throws IOException {
        //mybatis配置文件
        String resource = "SqlMapConfig.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //创建会话工厂
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //通过工厂得到SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        User user = new User();
        user.setId(27);
        user.setUsername("宋江涛new2");
        user.setBirthday(new Date());
        user.setSex("男");
        user.setAddress("山西太原new");
        //list中的user和映射文件User.xml中的resultType的类型一直
        sqlSession.update("test.updateUser",user);
        //提交事务
        sqlSession.commit();
        sqlSession.close();
    }
}

数据库的设计图
Mybatis学习笔记(传智播客)(完结)_第5张图片
Mybatis学习笔记(传智播客)(完结)_第6张图片
部分测试结果
Mybatis学习笔记(传智播客)(完结)_第7张图片
jdbc的测试程序,前面已经沾过。
重点内容在代码的注释中均已说明

mybatis和hibernate本质区别和应用场景

  • hibernate:是一个标准ORM框架(对象关系映射)。入门门槛较高的,不需要程序写sql,sql语句自动生成了。
    对sql语句进行优化、修改比较困难的。

    • 应用场景:
      • 适用与需求变化不多的中小型项目,比如:后台管理系统,erp、orm、oa。。
  • mybatis:专注是sql本身,需要程序员自己编写sql语句,sql修改、优化比较方便。mybatis是一个不完全 的ORM框架,虽然程序员自己写sql,mybatis 也可以实现映射(输入映射、输出映射)。

    • 应用场景:
      • 适用与需求变化较多的项目,比如:互联网项目。

企业进行技术选型,以低成本 高回报作为技术选型的原则,根据项目组的技术力量进行选择。

4、Mybatis开发dao的方法

  • 原始dao开发方法(程序需要编写dao接口和dao实现类)(掌握)

    • 在原有项目的基础上,src目录下创建com.nuc.mybatis.dao
    • 在这个包里有两个文件,一个是UserDao.java,另一个是UserDaoImpl.java这个类继承接口UserDao.java
    • 在创建一个和src同级的文件夹(记的把它标记为sources)具体方法见详情
    • 在test下创建com.nuc.mybatis.test,创建类UserDaoImplTest.java

      具体目录结构:
      Mybatis学习笔记(传智播客)(完结)_第8张图片
      结构中的相关mapper请先忽略

UserDao.java

package com.nuc.mybatis.dao;

import com.nuc.mybatis.po.User;

public interface UserDao {
    //dao原始开发
    //根据id查询用户信息
    public User findUserById(int id) throws Exception;
    //添加用户
    public void insertUser(User user) throws Exception;
    //删除用户
    public void deleteUser(int id) throws Exception;
}

UserDaoImpl.java

package com.nuc.mybatis.dao;

import com.nuc.mybatis.po.User;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSession;

public class UserDaoImpl implements UserDao {
    //原生态的dao
    //需要向dao实现类里注入SqlSessionFactory
    //通过构造方法
    private SqlSessionFactory sqlSessionFactory;

    public UserDaoImpl(SqlSessionFactory sqlSessionFactory){
        this.sqlSessionFactory=sqlSessionFactory;
    }
    @Override
    public User findUserById(int id) throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        User user = sqlSession.selectOne("test.findUserById",id);
        sqlSession.close();
        return user;
    }

    @Override
    public void insertUser(User user) throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        sqlSession.insert("test.insertUser",user);
        sqlSession.commit();
        sqlSession.close();

    }

    @Override
    public void deleteUser(int id) throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        sqlSession.insert("test.deleteUser",id);
        sqlSession.commit();
        sqlSession.close();
    }
}

UserDaoImplTest.java

package com.nuc.mybatis.test;

import com.nuc.mybatis.mapper.UserMapper;
import com.nuc.mybatis.po.User;
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.Before;
import org.junit.Test;

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

public class UserMapperTest {
    //原始dao的测试
    private SqlSessionFactory sqlSessionFactory;
    @Before
    public void setUp() throws Exception{
        String resource = "SqlMapConfig.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //创建会话工厂
        sqlSessionFactory = new
                SqlSessionFactoryBuilder().build(inputStream);
    }
    @Test
    public void testFindUserById() throws Exception{
      SqlSession sqlSession = sqlSessionFactory.openSession();
       //创建UserMapper的对象,mybatis自动调用
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user = userMapper.findUserById(1);
        System.out.println(user);

    }
    @Test
    public void testFindUserByName() throws Exception{
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //创建UserMapper的对象,mybatis自动调用
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List list= userMapper.findUserByName("宋江涛");
        System.out.println(list);
    }
    //其他的测试,可以照猫画虎
}

测试结果:
Mybatis学习笔记(传智播客)(完结)_第9张图片

  • Mybatis的mapper接口(相当于dao接口)代理开发方法(掌握)

    根据上面的结构图创建相应文件,源码如下:

UserMapper.java

package com.nuc.mybatis.mapper;

import com.nuc.mybatis.po.User;

import java.util.List;

public interface UserMapper {
    //mapper代理开发和dao开发对比
//    mapper接口,相当于dao接口,mybatis可以自动生成mapper接口实现类的代理对象
    //根据id查询用户信息
    public User findUserById(int id) throws Exception;
    //根据用户名查询用户列表
    public ListfindUserByName(String name) throws Exception;
    //添加用户
    public void insertUser(User user) throws Exception;
    //删除用户
    public void deleteUser(int id) throws Exception;
}

UserMapper.xml



<mapper namespace="com.nuc.mybatis.mapper.UserMapper">
    <select id="findUserById" parameterType="int" resultType="com.nuc.mybatis.po.User">
        select * from user where id=#{VALUE }
    select>
    <select id="findUserByName" parameterType="java.lang.String" resultType="com.nuc.mybatis.po.User">
        select * from user WHERE username LIKE '%${value}%'
    select>

    <insert id="insertUser" parameterType="com.nuc.mybatis.po.User">

      <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
          SELECT LAST_INSERT_ID()
      selectKey>
      insert into user (username,birthday,sex,address) value(#{username},#{birthday},#{sex},#{address})
    insert>


    <delete id="deleteUser" parameterType="java.lang.Integer">
        delete from user where id=#{id}
    delete>

    <update id="updateUser" parameterType="com.nuc.mybatis.po.User">
        UPDATE user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}
    update>
mapper>

在测试之前需要在SqlMapConfig.xml中加载mapper.xml这个映射文件
这里写图片描述
UserMapperTest.java

package com.nuc.mybatis.test;

import com.nuc.mybatis.mapper.UserMapper;
import com.nuc.mybatis.po.User;
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.Before;
import org.junit.Test;

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

public class UserMapperTest {
    //原始mapper的测试
    private SqlSessionFactory sqlSessionFactory;
    @Before
    public void setUp() throws Exception{
        String resource = "SqlMapConfig.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //创建会话工厂
        sqlSessionFactory = new
                SqlSessionFactoryBuilder().build(inputStream);
    }
    @Test
    public void testFindUserById() throws Exception{
      SqlSession sqlSession = sqlSessionFactory.openSession();
       //创建UserMapper的对象,mybatis自动调用
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user = userMapper.findUserById(1);
        System.out.println(user);

    }
    @Test
    public void testFindUserByName() throws Exception{
    //返回列表测试
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //创建UserMapper的对象,mybatis自动调用
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List list= userMapper.findUserByName("宋江涛");
        System.out.println(list);
    }
}

部分测试结果:
Mybatis学习笔记(传智播客)(完结)_第10张图片
总结:

  • 原始dao开发问题
    • dao接口实现类方法中存在大量模板方法,设想能否将这些代码提取出来,大大减轻程序员的工作量。
    • 调用sqlsession方法时将statement的id硬编码了
  • mapper开发

    • 只需要编写两个文件,mapper.java,mapper.xml。即可,不需要类来继承它。
    • mapper开发只需要遵守几个规范即可

      • 在mapper.xml中namespace等于mapper接口地址
        这里写图片描述
      • mapper.java接口中的方法名和mapper.xml中statement的id一致
        这里写图片描述
        这里写图片描述
      • mapper.java接口中的方法输入参数类型和mapper.xml中statement的parameterType指定的类型一致。
        Mybatis学习笔记(传智播客)(完结)_第11张图片
        这里写图片描述
      • mapper.java接口中的方法返回值类型和mapper.xml中statement的resultType指定的类型一致。
        这里写图片描述
        Mybatis学习笔记(传智播客)(完结)_第12张图片
    • 其实,以上开发规范主要是对下边的代码进行统一生成:

      User user = sqlSession.selectOne("test.findUserById", id);
      sqlSession.insert("test.insertUser", user);
      。。。。
    • mapper接口方法参数只能有一个是否影响系统 开发?mapper接口方法参数只能有一个,系统是否不利于扩展维护?

      • 系统 框架中,dao层的代码是被业务层公用的。
      • 即使mapper接口只有一个参数,可以使用包装类型的pojo满足不同的业务方法的需求。

      注意:持久层方法的参数可以包装类型、map。。。,service方法中建议不要使用包装类型(不利于业务层的可扩展)。

5、Mybatis配置文件SqlMapConfig.xml

  • properties(属性)

    • 将数据库连接的参数单独配置在,db.properties中,只需要在SqlMapConfig.xml中加载db.properties的属性值。在SqlMapConfig.xml中就不需要对数据库连接参数硬编码。
    • 好处:方便对参数进行统一管理,其它xml可以引用该db.properties
    • 特性: MyBatis 将按照下面的顺序来加载属性:

      • 在 properties 元素体内定义的属性首先被读取。
      • 然后会读取properties 元素中resource或 url 加载的属性,它会覆盖已读取的同名属性。
      • 最后读取parameterType传递的属性,它会覆盖已读取的同名属性。

      建议:

      • 不要在properties元素体内添加任何属性值,只将属性值定义在properties文件中。
      • 在properties文件中定义属性名要有一定的特殊性,如XXXXX.XXXXX.XXXX
        Mybatis学习笔记(传智播客)(完结)_第13张图片
        Mybatis学习笔记(传智播客)(完结)_第14张图片
  • settings(全局配置参数)
    • mybatis框架在运行时可以调整一些运行参数。比如:开启二级缓存、开启延迟加载。。全局参数将会影响mybatis的运行行为。具体如下:
      Mybatis学习笔记(传智播客)(完结)_第15张图片
      Mybatis学习笔记(传智播客)(完结)_第16张图片
      Mybatis学习笔记(传智播客)(完结)_第17张图片
  • typeAliases(类型别名)(重点)
    • 单个定义
      Mybatis学习笔记(传智播客)(完结)_第18张图片
    • 批量定义(常用)
      Mybatis学习笔记(传智播客)(完结)_第19张图片
      这样在其他地方就可以使用,例如:
      Mybatis学习笔记(传智播客)(完结)_第20张图片
    • mybatis默认支持的别名
      Mybatis学习笔记(传智播客)(完结)_第21张图片
  • typeHandlers(类型处理器)
    • mybatis中通过typeHandlers完成jdbc类型和java类型的转换。通常情况下,mybatis提供的类型处理器满足日常需要,不需要自定义.
    • mybatis支持的类型处理器
      Mybatis学习笔记(传智播客)(完结)_第22张图片
      Mybatis学习笔记(传智播客)(完结)_第23张图片
  • objectFactory(对象工厂)
    • 这个自行查看下载mybatis时附带的pdf文件,用的不多
  • plugins(插件)
  • environments(环境集合属性对象)
    • environment(环境子属性对象)
      • transactionManager(事务管理)
      • dataSource(数据源)
  • mappers(映射器)
    • 通过resource
      Mybatis学习笔记(传智播客)(完结)_第24张图片
    • 通过class
      Mybatis学习笔记(传智播客)(完结)_第25张图片
    • 通过package(推荐使用)
      这里写图片描述

6、Mybatis核心

  • Mybatis输入映射(掌握)

    • 通过parameterType指定输入参数的类型,类型可以是简单类型、hashmap、pojo的包装类型
      • 传递pojo的包装对象
        • 需求:完成用户信息的综合查询,需要传入查询条件很复杂(可能包括用户信息、其它信息,比如商品、订单的)
        • 针对上边需求,建议使用自定义的包装类型的pojo。
          在包装类型的pojo中将复杂的查询条件包装进去。

    目录结构
    Mybatis学习笔记(传智播客)(完结)_第26张图片
    UserCustom.java

package com.nuc.mybatis.po;

public class UserCustom extends User {
    //可扩展用户信息
}

UserQueryVo.java

package com.nuc.mybatis.po;

public class UserQueryVo {
    //这里包装所需的查询条件

    //用户查询条件
    private UserCustom userCustom;

    public UserCustom getUserCustom() {
        return userCustom;
    }

    public void setUserCustom(UserCustom userCustom) {
        this.userCustom = userCustom;
    }

    //可包装其他的查询条件,订单,商品。。。
}

UserMapper.xml中配置新的查询
Mybatis学习笔记(传智播客)(完结)_第27张图片
UserMapperTest.java中新增测试
Mybatis学习笔记(传智播客)(完结)_第28张图片
测试结果
Mybatis学习笔记(传智播客)(完结)_第29张图片

  • Mybatis输出映射(掌握)

    • 一、resultType
      • 使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功。
      • 如果查询出来的列名和pojo中的属性名全部不一致,没有创建pojo对象。
      • 只要查询出来的列名和pojo中的属性有一个一致,就会创建pojo对象

    在上面的输入中,属于每一列都成功映射
    Mybatis学习笔记(传智播客)(完结)_第30张图片
    当我们修改查询语句为:
    这里写图片描述
    其中id,起了别名叫做id_
    测试结果:
    Mybatis学习笔记(传智播客)(完结)_第31张图片
    很明显,由于查询条件,生日和地址为空,由于id的映射失败,导致查询id失败,只有sex一列映射成功,故查询成功(1代表男,2代表女)

    • 1.resultType的输出简单类型
      • 需求:用户信息的综合查询列表总数,通过查询总数和上边用户综合查询列表才可以实现分页。

如法炮制,对mapper.xml编辑和mapper.java编辑
Mybatis学习笔记(传智播客)(完结)_第32张图片
这里写图片描述
测试代码(复制修改)
Mybatis学习笔记(传智播客)(完结)_第33张图片
测试结果
Mybatis学习笔记(传智播客)(完结)_第34张图片
总结:查询出来的结果集只有一行且一列,可以使用简单类型进行输出映射。

  • 2.resultType的输出pojo对象和pojo列表

    • 不管是输出的pojo单个对象还是一个列表(list中包括pojo),在mapper.xml中resultType指定的类型是一样的。
      • 在mapper.java指定的方法返回值类型不一样:
        • 1、输出单个pojo对象,方法返回值是单个对象类型
        • 2、输出pojo对象list,方法返回值是List<简单java对象>
          Mybatis学习笔记(传智播客)(完结)_第35张图片
  • 二、resultMap

    • mybatis中使用resultMap完成高级输出结果映射。
    • 实例:
      • 1、在usermapper.xml定义resultMap
        Mybatis学习笔记(传智播客)(完结)_第36张图片
      • 2、在usermapper.xml中定义查询
        这里写图片描述
      • 3、在usermapper.java中定义方法
        这里写图片描述
      • 4、编写测试方法
        Mybatis学习笔记(传智播客)(完结)_第37张图片
      • 5、测试结果
        Mybatis学习笔记(传智播客)(完结)_第38张图片
      • 6、总结
        • 使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功
        • 如果查询出来的列名和pojo的属性名不一致,通过定义一个resultMap对列名和pojo属性名之间作一个映射关系。

7、Mybatis的动态sql(掌握)

  • 什么是动态sql?
    • mybatis核心 对sql语句进行灵活操作,通过表达式进行判断,对sql进行灵活拼接、组装。
    • 在原来的usermapper.xml中修改用户综合查询总数为下面代码
     
    <select id="findUserCount" parameterType="com.nuc.mybatis.po.UserQueryVo"
            resultType="int">
        select count(*) from user
        
        <where>
            <if test="userCustom!=null">
                <if test="userCustom.sex!=null and userCustom.sex!=''">
                    and user.sex=#{userCustom.sex}
                if>
                <if test="userCustom.username!=null and userCustom.username!=''">
                    and user.username=#{userCustom.username}
                if>
            if>
        where>
    select>
  • 修改测试类及测试结果
    Mybatis学习笔记(传智播客)(完结)_第39张图片
  • sql片段
    • 将上边实现的动态sql判断代码块抽取出来,组成一个sql片段。其它的statement中就可以引用sql片段。方便程序员进行开发
    • 先在mapper.xml中定义一个sql片段

    <sql id="Query_user">
        <if test="userCustom!=null">
            <if test="userCustom.sex!=null and userCustom.sex!=''">
                and user.sex=#{userCustom.sex}
            if>
            <if test="userCustom.username!=null and userCustom.username!=''">
                and user.username=#{userCustom.username}
            if>
        if>
    sql>
  • 将之前的sql语句修改如下:

    <select id="findUserCount" parameterType="com.nuc.mybatis.po.UserQueryVo"
            resultType="int">
        select count(*) from user
        
        <where>
            
            <include refid="Query_user"/>
        where>
    select>
  • 测试和之前的一样
  • foreach
    • 向sql传递数组或List,mybatis使用foreach解析
    • 在用户查询列表和查询总数的statement中增加多个id输入查询。即实现SELECT * FROM USER WHERE id=1 OR id=10 OR id=16
    • 先修改sql
<sql id="Query_user">
        <if test="userCustom!=null">
            <if test="userCustom.sex!=null and userCustom.sex!=''">
                and user.sex=#{userCustom.sex}
            if>
            <if test="userCustom.username!=null and userCustom.username!=''">
                and user.username=#{userCustom.username}
            if>
        if>
        <if test="ids!=null">
            
            <foreach collection="ids" item="user_id" open="and(" close=")" separator="OR">
              
                id=#{user_id}
            foreach>
        if>
    sql>
  • 添加属性,及其get set 方法
    Mybatis学习笔记(传智播客)(完结)_第40张图片
  • 修改测试类
//    用户综合查询总数
@Test
    public void testFindUser() throws Exception{
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //创建UserMapper的对象,mybatisCount自动调用
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        //创建包对象,设置查询条件
        UserQueryVo userQueryVo = new UserQueryVo();
        UserCustom userCustom = new UserCustom();
//        由于这里使用动态sql所以不设置某个值,就不会拼接在sql语句中
//        userCustom.setSex("1");
        userCustom.setUsername("小明");
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(10);
        list.add(16);
        userQueryVo.setIds(list);
        userQueryVo.setUserCustom(userCustom);
        int count= userMapper.findUserCount(userQueryVo);
        System.out.println(count);
    }
  • 测试结果
    Mybatis学习笔记(传智播客)(完结)_第41张图片
    sql语句成功修改!

8、订单商品数据模型分析

  • 数据模型分析思路

    • 1、每张表记录的数据内容
      • 分模块对每张表记录的内容进行熟悉,相当 于你学习系统 需求(功能)的过程
    • 2、每张表重要的字段设置
      • 非空字段、外键字段
    • 3、数据库级别表与表之间的关系
      • 外键关系
    • 4、表与表之间的业务关系
      • 在分析表与表之间的业务关系时一定要建立 在某个业务意义基础上去分析。

    下来拿之前的数据库的表为例,具体字段不作说明
    Mybatis学习笔记(传智播客)(完结)_第42张图片
    根据上面的步骤。
    第一步:每张表记录的数据内容

    • 用户表user:记录了购买商品的用户信息
    • 订单表:orders:记录了用户所创建的订单(购买商品的订单)
    • 订单明细表:orderdetail:记录了订单的详细信息即购买商品的信息
    • 商品表:items:记录了商品信息

    第二步:每张表重要的字段设置
    第三步:数据库级别表与表之间的关系

    • 表与表之间的业务关系:在分析表与表之间的业务关系时需要建立 在某个业务意义基础上去分析。

    先分析数据库级别之间有关系的表之间的业务关系:

    • usre和orders:

      • user—>orders:一个用户可以创建多个订单,一对多
      • orders—>user:一个订单只由一个用户创建,一对一
    • orders和orderdetail:

      • orders—>orderdetail:一个订单可以包括 多个订单明细,因为一个订单可以购买多个商品,每个商品的购买信息在orderdetail记录,一对多
      • orderdetail—> orders:一个订单明细只能包括在一个订单中,一对一
    • orderdetail和itesm:
      • orderdetail—>itesms:一个订单明细只对应一个商品信息,一对一
      • items–> orderdetail:一个商品可以包括在多个订单明细 ,一对多

    第四步:表与表之间的业务关系

    • 再分析数据库级别没有关系的表之间是否有业务关系:
      • orders和items:
        • orders和items之间可以通过orderdetail表建立 关系。

    Mybatis学习笔记(传智播客)(完结)_第43张图片

9、高级结果集映射

  • 一对一
    需求:查询订单信息,关联查询创建订单的用户信息

    分析:

    • 确定查询的主表:订单表
    • 确定查询的关联表:用户表
    • 关联查询使用内链接?还是外链接?
    • 由于orders表中有一个外键(user_id),通过外键关联查询用户表只能查询出一条记录,可以使用内链接。

      SELECT
      orders.*,
      USER.username,
      USER.sex,
      USER.address
      FROM
      orders,
      USER
      WHERE orders.user_id = user.id

    • 1、用resultType实现
      • 在po包中创建:Orders类
      • 在po包中创建:OrdersCustom类继承 Orders类
      • 在mapper包中创建:OrdersMapperCustom接口
      • 在mapper包中创建:OrdersMapperCustom.xml
      • 在test包中创建:OrdersMapperTest类

Orders.java

package com.nuc.mybatis.po;

import java.util.Date;
import java.util.List;

public class Orders {
    private Integer id;

    private Integer userId;

    private String number;

    private Date createtime;

    private String note;

    //用户信息
    private User user;

    //订单明细
    private List orderdetails;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number == null ? null : number.trim();
    }

    public Date getCreatetime() {
        return createtime;
    }

    public void setCreatetime(Date createtime) {
        this.createtime = createtime;
    }

    public String getNote() {
        return note;
    }

    public void setNote(String note) {
        this.note = note == null ? null : note.trim();
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public List getOrderdetails() {
        return orderdetails;
    }

    public void setOrderdetails(List orderdetails) {
        this.orderdetails = orderdetails;
    }    
}

OrdersCustom .java

package com.nuc.mybatis.po;

public class OrdersCustom extends Orders {
    private String username;
    private String sex;
    private String address;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }

}

OrdersMapperCustom.java

package com.nuc.mybatis.mapper;

import java.util.List;

public interface OrdersMapperCustom {
    public List findOrdersUser()throws Exception;
}

OrdersMapperCustom .xml



<mapper namespace="com.nuc.mybatis.mapper.OrdersMapperCustom">
    
    <select id="findOrdersUser" resultType="com.nuc.mybatis.po.OrdersCustom">
      SELECT
      orders.*,
      user.username,
      user.sex,
      user.address
      FROM
      orders,
      user
      WHERE orders.user_id = user.id
    select>

mapper>

OrdersMapperTest.java

package com.nuc.mybatis.test;

import com.nuc.mybatis.mapper.OrdersMapperCustom;
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.Before;
import org.junit.Test;

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

public class OrdersMapperTest {
    private SqlSessionFactory sqlSessionFactory;
    @Before
    public void setUp() throws Exception{
        String resource = "SqlMapConfig.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //创建会话工厂
        sqlSessionFactory = new
                SqlSessionFactoryBuilder().build(inputStream);
    }
    @Test
    public void testFindOrdersUser() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 创建代理对象
        OrdersMapperCustom ordersMapperCustom = sqlSession
                .getMapper(OrdersMapperCustom.class);
        // 调用maper的方法
        List list = ordersMapperCustom.findOrdersUser();
        System.out.println(list);
        sqlSession.close();
    }
}

测试结果
Mybatis学习笔记(传智播客)(完结)_第44张图片

  • 2、用resultMap实现
    • 实现思路:使用resultMap将查询结果中的订单信息映射到Orders对象中,在orders类中添加User属性,将关联查询出来的用户信息映射到orders对象中的user属性中。
      • 第一步:Orders类中添加User 的user属性。上面的代码已经添加
      • 第二步:mapper.xml中定义ResultMap及其查询
      • 第三步:接口中定义相应的方法
      • 最后测试

第三步:

<resultMap type="com.nuc.mybatis.po.Orders" id="OrdersUserResultMap">
        
        
        <id column="id" property="id"/>
        <result column="user_id" property="userId"/>
        <result column="number" property="number"/>
        <result column="createtime" property="createtime"/>
        <result column="note" property="note"/>
        
        
        <association property="user"  javaType="com.nuc.mybatis.po.User">
            
            <id column="user_id" property="id"/>
            <result column="username" property="username"/>
            <result column="sex" property="sex"/>
            <result column="address" property="address"/>
        association>
    resultMap>

    <select id="findOrdersUserResultMap" resultMap="OrdersUserResultMap">
        SELECT
        orders.*,
        USER.username,
        USER.sex,
        USER.address
        FROM
        orders,
        USER
        WHERE orders.user_id = user.id
    select>

第四步

//查询订单关联查询用户使用resultMap
    public List findOrdersUserResultMap()throws Exception;

测试


    @Test
    public void testFindOrdersResultMap() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 创建代理对象
        OrdersMapperCustom ordersMapperCustom = sqlSession
                .getMapper(OrdersMapperCustom.class);
        // 调用maper的方法
        List list = ordersMapperCustom.findOrdersUserResultMap();
        System.out.println(list);
        sqlSession.close();
    }

总结

  • resultType:使用resultType实现较为简单,如果pojo中没有包括查询出来的列名,需要增加列名对应的属性,即可完成映射。如果没有查询结果的特殊要求建议使用resultType。

  • resultMap:需要单独定义resultMap,实现有点麻烦,如果对查询结果有特殊的要求,使用resultMap可以完成将关联查询映射pojo的属性中。resultMap可以实现延迟加载,resultType无法实现延迟加载。

  • 一对多

    • 需求:查询订单及订单明细的信息。(根据数据库模型分析的结果来查询)
    • 使用resultMap:
      Mybatis学习笔记(传智播客)(完结)_第45张图片
      要求:对orders映射不能出现重复记录。
      解决思路:
      • 在orders.java类中添加List<>, orderDetails属性。
      • 最终会将订单信息映射到orders中,订单所对应的订单明细映射到orders中的orderDetails属性中。
      • 映射成的orders记录数为两条(orders信息不重复)
      • 每个orders中的orderDetails属性存储了该 订单所对应的订单明细

    添加属性在之前已经实现,且注释。
    只需要配置xml文件和接口,写一个测试类即可


    <resultMap type="com.nuc.mybatis.po.Orders" id="OrdersAndOrderDetailResultMap" extends="OrdersUserResultMap">
        
        
        

        
        <collection property="orderdetails" ofType="com.nuc.mybatis.po.Orderdetail">
            
            <id column="orderdetail_id" property="id"/>
            <result column="items_id" property="itemsId"/>
            <result column="items_num" property="itemsNum"/>
            <result column="orders_id" property="ordersId"/>
        collection>
    resultMap>
    
    <select id="findOrdersAndOrderDetailResultMap" resultMap="OrdersAndOrderDetailResultMap">
        SELECT
        orders.*,
        USER.username,
        USER.sex,
        USER.address,
        orderdetail.id orderdetail_id,
        orderdetail.items_id,
        orderdetail.items_num,
        orderdetail.orders_id
        FROM
        orders,
        USER,
        orderdetail
        WHERE orders.user_id = user.id AND orderdetail.orders_id=orders.id
    select>
 //查询订单(关联用户)及订单明细
    public List findOrdersAndOrderDetailResultMap() throws Exception;
@Test
    public void testfindOrdersAndOrderDetailResultMap() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 创建代理对象
        OrdersMapperCustom ordersMapperCustom = sqlSession
                .getMapper(OrdersMapperCustom.class);
        // 调用maper的方法
        List list = ordersMapperCustom.findOrdersAndOrderDetailResultMap();
        System.out.println(list);
        sqlSession.close();
    }

总结:

  • mybatis使用resultMap的collection对关联查询的多条记录映射到一个list集合属性中。
  • 使用resultType实现:将订单明细映射到orders中的orderdetails中,需要自己处理,使用双重循环遍历,去掉重复记录,将订单明细放在orderdetails中。
  • 多对多

    • 需求:查询用户及用户购买商品信息。
    • sql语句
      • 查询主表是:用户表
      • 关联表:由于用户和商品没有直接关联,通过订单和订单明细进行关联,所以关联表:orders、orderdetail、items
      • SELECT
        orders.*,
        USER.username,
        USER.sex,
        USER.address,
        orderdetail.id orderdetail_id,
        orderdetail.items_id,
        orderdetail.items_num,
        orderdetail.orders_id,
        items.name items_name,
        items.detail items_detail,
        items.price items_price
        FROM
        orders,
        USER,
        orderdetail,
        items
        WHERE orders.user_id = user.id AND orderdetail.orders_id=orders.id AND orderdetail.items_id = items.id
  • 映射思路

    • 将用户信息映射到user中。
    • 在user类中添加订单列表属性List orderslist,将用户创建的订单映射到orderslist
    • 在Orders中添加订单明细列表属性List<>orderdetials,将订单的明细映射到orderdetials
    • 在OrderDetail中添加Items属性,将订单明细所对应的商品映射到Items
  • mapper.xml

    
    
  • resultMap定义
   
    <resultMap type="com.nuc.mybatis.po.User" id="UserAndItemsResultMap">
        
        <id column="user_id" property="id"/>
        <result column="username" property="username"/>
        <result column="sex" property="sex"/>
        <result column="address" property="address"/>

        
        <collection property="ordersList" ofType="com.nuc.mybatis.po.Orders">
            <id column="id" property="id"/>
            <result column="user_id" property="userId"/>
            <result column="number" property="number"/>
            <result column="createtime" property="createtime"/>
            <result column="note" property="note"/>

            
            <collection property="orderdetails" ofType="com.nuc.mybatis.po.Orderdetail">
                <id column="orderdetail_id" property="id"/>
                <result column="items_id" property="itemsId"/>
                <result column="items_num" property="itemsNum"/>
                <result column="orders_id" property="ordersId"/>
                
                <association property="items" javaType="com.nuc.mybatis.po.Items">
                    <id column="items_id" property="id"/>
                    <result column="items_name" property="name"/>
                    <result column="items_detail" property="detail"/>
                    <result column="items_price" property="price"/>
                association>
            collection>
        collection>
    resultMap>
  • mapper.java
 //查询用户购买商品信息
    public List  findUserAndItemsResultMap()throws Exception;
  • 测试
    Mybatis学习笔记(传智播客)(完结)_第46张图片

    多对多查询总结

    • 将查询用户购买的商品信息明细清单,(用户名、用户地址、购买商品名称、购买商品时间、购买商品数量)
    • 针对上边的需求就使用resultType将查询到的记录映射到一个扩展的pojo中,很简单实现明细清单的功能。
    • 一对多是多对多的特例,如下需求:
      • 查询用户购买的商品信息,用户和商品的关系是多对多关系。
        • 需求1:查询字段:用户账号、用户名称、用户性别、商品名称、商品价格(最常见)企业开发中常见明细列表,用户购买商品明细列表,使用resultType将上边查询列映射到pojo输出。
        • 需求2:查询字段:用户账号、用户名称、购买商品数量、商品明细(鼠标移上显示明细)使用resultMap将用户购买的商品明细列表映射到user对象中。
  • 总结:使用resultMap是针对那些对查询结果映射有特殊要求的功能,比如特殊要求映射成list中包括 多个list

  • resultMap总结
    • resultType:
      • 作用:将查询结果按照sql列名pojo属性名一致性映射到pojo中。
      • 场合:常见一些明细记录的展示,比如用户购买商品明细,将关联查询信息全部展示在页面时,此时可直接使用resultType将每一条记录映射到pojo中,在前端页面遍历list(list中是pojo)即可。
    • resultMap:
      • 使用association和collection完成一对一和一对多高级映射(对结果有特殊的映射要求)。
      • association:
        • 作用:将关联查询信息映射到一个pojo对象中。
        • 场合:为了方便查询关联信息可以使用association将关联订单信息映射为用户对象的pojo属性中,比如:查询订单及关联用户信息。
        • 使用resultType无法将查询结果映射到pojo对象的pojo属性中,根据对结果集查询遍历的需要选择使用resultType还是resultMap。
      • collection:
        • 作用:将关联查询信息映射到一个list集合中。
        • 场合:为了方便查询遍历关联信息可以使用collection将关联信息映射到list集合中,比如:查询用户权限范围模块及模块下的菜单,可使用collection将模块映射到模块list中,将菜单列表映射到模块对象的菜单list属性中,这样的作的目的也是方便对查询结果集进行遍历查询。如果使用resultType无法将查询结果映射到list集合中。

10、Mybatis延迟加载

  • resultMap可以实现高级映射(使用association、collection实现一对一及一对多映射),association、collection具备延迟加载功能。

    • 需求:如果查询订单并且关联查询用户信息。如果先查询订单信息即可满足要求,当我们需要查询用户信息时再查询用户信息。把对用户信息的按需去查询就是延迟加载。
  • 延迟加载:先从单表查询、需要时再从关联表去关联查询,大大提高 数据库性能,因为查询单表要比关联查询多张表速度要快。

  • 不使用mybatis提供的association及collection中的延迟加载功能,如何实现延迟加载??

    • 实现方法如下:
      • 定义两个mapper方法:
        • 1、查询订单列表
        • 2、根据用户id查询用户信息
    • 实现思路:先去查询第一个mapper方法,获取订单信息列表。在程序中(service),按需去调用第二个mapper方法去查询用户信息。

    总之:使用延迟加载方法,先去查询简单的sql(最好单表,也可以关联查询),再去按需要加载关联查询的其它信息。

11、Mybatis查询缓存

  • 什么是查询缓存?
    • mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。
  • 为什么要用缓存?
    • 如果缓存中有数据就不用从数据库中获取,大大提高系统性能。
  • mybaits提供一级缓存,和二级缓存。
    Mybatis学习笔记(传智播客)(完结)_第47张图片
  • 一级缓存

    • 一级缓存是SqlSession级别的缓存。在操作数据库时需要构造 sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。
    • 工作原理
      Mybatis学习笔记(传智播客)(完结)_第48张图片

      • 第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。
      • 如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
      • 第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。
      • 测试:

        //mybatis默认支持一级缓存,不需要在配置文件去配置。
            @Test
            public void testCache1() throws Exception{
                SqlSession sqlSession = sqlSessionFactory.openSession();//创建代理对象
                UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
                //下边查询使用一个SqlSession
                //第一次发起请求,查询id为1的用户
                User user1 = userMapper.findUserById(1);
                System.out.println(user1);
        //      如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
                //更新user1的信息
                user1.setUsername("测试用户22");
                userMapper.updateUser(user1);
                //执行commit操作去清空缓存
                sqlSession.commit();
                //第二次发起请求,查询id为1的用户
                User user2 = userMapper.findUserById(1);
                System.out.println(user2);
                sqlSession.close();
        
            }
      • 一级缓存应用

        • 正式开发,是将mybatis和spring进行整合开发,事务控制在service中。
          一个service方法中包括 很多mapper方法调用。

          service{
              //开始执行时,开启事务,创建SqlSession对象
              //第一次调用mapper的方法findUserById(1)
          
              //第二次调用mapper的方法findUserById(1),从一级缓存中取数据
              //方法结束,sqlSession关闭
          }

          如果是执行两个service调用查询相同 的用户信息,不走一级缓存,因为session方法结束,sqlSession就关闭,一级缓存就清空。

  • 二级缓存

    • 二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
    • 原理
      Mybatis学习笔记(传智播客)(完结)_第49张图片
      • 首先开启mybatis的二级缓存。
      • sqlSession1去查询用户id为1的用户信息,查询到用户信息会将查询数据存储到二级缓存中。
      • 如果SqlSession3去执行相同 mapper下sql,执行commit提交,清空该 mapper下的二级缓存区域的数据。
      • sqlSession2去查询用户id为1的用户信息,去缓存中找是否存在数据,如果存在直接从缓存中取出数据。
      • 二级缓存与一级缓存区别,二级缓存的范围更大,多个sqlSession可以共享一个UserMapper的二级缓存区域。
      • UserMapper有一个二级缓存区域(按namespace分) ,其它mapper也有自己的二级缓存区域(按namespace分)。
      • 每一个namespace的mapper都有一个二缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同 的二级缓存区域中。
    • 开启二级缓存:
      • mybaits的二级缓存是mapper范围级别,除了在SqlMapConfig.xml设置二级缓存的总开关,还要在具体的mapper.xml中开启二级缓存。
        Mybatis学习笔记(传智播客)(完结)_第50张图片
        这里写图片描述
    • 调用pojo类实现序列化接口
      这里写图片描述
    • 测试
           // 二级缓存测试
          @Test
          public void testCache2() throws Exception {
              SqlSession sqlSession1 = sqlSessionFactory.openSession();
              SqlSession sqlSession2 = sqlSessionFactory.openSession();
              SqlSession sqlSession3 = sqlSessionFactory.openSession();
              // 创建代理对象
              UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
              // 第一次发起请求,查询id为1的用户
              User user1 = userMapper1.findUserById(1);
              System.out.println(user1);
      
              //这里执行关闭操作,将sqlsession中的数据写到二级缓存区域
              sqlSession1.close();
      
              //使用sqlSession3执行commit()操作
              UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);
              User user  = userMapper3.findUserById(1);
              user.setUsername("张明明");
              userMapper3.updateUser(user);
              //执行提交,清空UserMapper下边的二级缓存
              sqlSession3.commit();
              sqlSession3.close();
      
              UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
              // 第二次发起请求,查询id为1的用户
              User user2 = userMapper2.findUserById(1);
              System.out.println(user2);
      
              sqlSession2.close();
          }
  • useCache配置

    • 在statement中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。