Back

MyBatis(一)

MyBatis概念以及基本使用

MyBatis(一)

MyBatis概括

  • 什么是MyBatis

    • MyBatis 是一款优秀的持久层框架
    • MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的过程
    • MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 实体类 【Plain Old Java Objects,普通的 Java对象】映射成数据库中的记录。
    • MyBatis 本是apache的一个开源项目ibatis, 2010年这个项目由apache 迁移到了google code,并且改名为MyBatis 。
    • 2013年11月迁移到Github .
    • Mybatis官方文档 : http://www.mybatis.org/mybatis-3/zh/index.html
    • GitHub : https://github.com/mybatis/mybatis-3
  • 什么是持久化

    持久化就是将程序数据在持久状态和瞬时状态间转化的机制。

    • 即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中、XML数据文件中等等。
    • JDBC就是一种持久化机制。文件IO也是一种持久化机制。
    • 在生活中 : 将鲜肉冷藏,吃的时候再解冻的方法也是。将水果做成罐头的方法也是。
  • 为什么需要持久化服务呢?

    那是由于内存本身的缺陷引起的

    • 内存断电后数据会丢失,但有一些对象是无论如何都不能丢失的,比如银行账号等,遗憾的是,人们还无法保证内存永不掉电。
    • 内存过于昂贵,与硬盘、光盘等外存相比,内存的价格要高2~3个数量级,而且维持成本也高,至少需要一直供电吧。所以即使对象不需要永久保存,也会因为内存的容量限制不能一直呆在内存中,需要持久化来缓存到外存。
  • 什么是持久层?

    持久层:完成持久化工作的代码块 . —-> dao层 【DAO (Data Access Object) 数据访问对象】

    • 大多数情况下特别是企业级应用,数据持久化往往也就意味着将内存中的数据保存到磁盘上加以固化,而持久化的实现过程则大多通过各种关系数据库来完成。
    • 不过**这里有一个字需要特别强调,也就是所谓的“层”。**对于应用系统而言,数据持久功能大多是必不可少的组成部分。也就是说,我们的系统中,已经天然的具备了“持久层”概念?也许是,但也许实际情况并非如此。之所以要独立出一个“持久层”的概念,而不是“持久模块”,“持久单元”,也就意味着,我们的系统架构中,应该有一个相对独立的逻辑层面,专注于数据持久化逻辑的实现.
    • 与系统其他部分相对而言,这个层面应该具有一个较为清晰和严格的逻辑边界。【说白了就是用来操作数据库存在的!】
  • 为什么需要Mybatis

    • Mybatis就是帮助程序猿将数据存入数据库中 , 和从数据库中取数据 .

    • 传统的jdbc操作 , 有很多重复代码块 .比如 : 数据取出时的封装 , 数据库的建立连接等等… , 通过框架可以减少重复代码,提高开发效率 .

    • MyBatis 是一个半自动化的ORM框架 (Object Relationship Mapping) –>对象关系映射

    • 所有的事情,不用Mybatis依旧可以做到,只是用了它,所有实现会更加简单!技术没有高低之分,只有使用这个技术的人有高低之别

    • MyBatis的优点

      • 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件就可以了,易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
      • 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
      • 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
      • 提供xml标签,支持编写动态sql。

第一个MyBatis程序

  • 示例代码

    1. 搭建实验数据库

      CREATE DATABASE `mybatis`;
      
      USE `mybatis`;
      
      DROP TABLE IF EXISTS `user`;
      
      CREATE TABLE `user` (
      `id` int(20) NOT NULL,
      `name` varchar(30) DEFAULT NULL,
      `pwd` varchar(30) DEFAULT NULL,
      PRIMARY KEY (`id`)
      ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
      
      insert  into `user`(`id`,`name`,`pwd`) values (1,'狂神','123456'),(2,'张三','abcdef'),(3,'李四','987654');
      
    2. 导入MyBatis相关jar包

      <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
      <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.7</version>
      </dependency>
      <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
      <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.27</version>
      </dependency>
      
    3. 编写MyBatis核心配置文件

      <?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 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?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
                      <property name="username" value="root"/>
                      <property name="password" value="777777"/>
                  </dataSource>
              </environment>
          </environments>
          <mappers>
              <mapper resource="com/heng/dao/Mapper.xml"/>
          </mappers>
      </configuration>
      

      注意<mapper resource="com/heng/dao/Mapper.xml"/>的路径

    4. 编写MyBatis的工具类

      package com.heng.utils;
      
      import org.apache.ibatis.session.Configuration;
      import org.apache.ibatis.session.SqlSession;
      import org.apache.ibatis.session.SqlSessionFactory;
      import org.apache.ibatis.io.Resources;
      import org.apache.ibatis.session.SqlSessionFactoryBuilder;
      
      import java.io.IOException;
      import java.io.InputStream;
      
      /**
       * @Author: minster
       * @Date: 2021/10/26 10:24
       */
      //SqlSessionFactory  工厂--生产->产品 SqlSession,即该方法可以用来生成sqlSession
      public class MybatisUtils  {
          public static SqlSessionFactory sqlSessionFactory;
          static {
              try {
                  //使用Mybatis第一步:获取sqlSessionFactory对象(建议使用类路径下的资源文件配置)
                  String resource = "mybatis-config.xml";
                  InputStream inputStream = Resources.getResourceAsStream(resource);
                  sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
          //既然有了sqlSessionFactory,顾名思义,我们就可以从中获得sqlSession的实例了
          //SqlSession完全包含了面向数据库执行SQL命令所需的所有方法
          public static SqlSession getSession(){
              return sqlSessionFactory.openSession();
          }
      }
      

      什么是SqlSession?

      • 简单理解SqlSession,就是一次操作数据库的会话过程,通过它可以与数据库进行交

      • 使用Mybatis执行数据库操作,首先要获取SqlSession,通过它进一步获取Mapper接口代理对象,最后通过代理对象发起数据库操作

      • 通过构建DataSource、TransactionFactory、Environment、Configuration并将它们组装在一起获得SqlSessionFactory,此后就可以通过它获取SqlSession

      • 从try-with-resource里的openSession方法,在这里可以看到获取SqlSession时Mybatis会创建事务工厂TransactionFactory、执行器Executor结合在启动Mybatis时创建的Configuration返回一个DefaultSqlSession

      • 其中Mybatis利用执行器Executor执行数据库操作。

      • Mybatis获取SqlSession的过程结束,通过它为当前的数据库操作创建一个专属的执行器Executor和事务工厂TransactionFactory,令当前线程的数据库操作处于一个会话状态

        https://www.jianshu.com/p/48dfeb0b79b6

    5. 创建实体类

      public class User {
      
         private int id;  //id
         private String name;   //姓名
         private String pwd;   //密码
      
         //构造,有参,无参
         //set/get
         //toString()
      
      }
      
    6. 编写Mapper接口类

      package com.heng.dao;
      
      import com.heng.pojo.User;
      
      import java.util.List;
      
      /**
       * @Author: minster
       * @Date: 2021/10/26 10:33
       */
      public interface UserMapper {
          List<User> selectUser();
      }
      
    7. 编写Mapper.xml配置文件

      namespace非常重要!!不能写错

      <?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">
      <!-- namespace绑定一个对应的Dao/Mapper接口 -->
      <!-- 配置文件和UserMapper接口绑定起来了 -->
      <mapper namespace="com.heng.dao.UserMapper">
          <select id="selectUser" resultType="com.heng.pojo.User">
              select * from user
          </select>
      </mapper>
      
    8. 编写测试类

      import com.heng.dao.UserMapper;
      import com.heng.pojo.User;
      import com.heng.utils.MybatisUtils;
      import org.apache.ibatis.session.SqlSession;
      import org.junit.Test;
      
      import java.util.List;
      
      /**
       * @Author: minster
       * @Date: 2021/10/26 10:34
       */
      public class MyTest {
          方法1
          @Test
          public void selectUser1(){
              SqlSession session = MybatisUtils.getSession();
              List<User> users = session.selectList("com.heng.dao.UserMapper.selectUser");
              for (User user : users) {
                  System.out.println(user);
              }
              session.close();
          }
          //方法2
          @Test
          public void selectUser2(){
              SqlSession session = MybatisUtils.getSession();
              UserMapper mapper = session.getMapper(UserMapper.class);
              List<User> users = mapper.selectUser();
              for (User user : users) {
                  System.out.println(user);
              }
          }
      }
      
    9. 编译,测试,出现Maven静态资源过滤问题,在pom.xml文件上添加下面配置

      <build>
          <resources>
              <resource>
                  <directory>src/main/java</directory>
                  <includes>
                      <include>**/*.properties</include>
                      <include>**/*.xml</include>
                  </includes>
                  <filtering>false</filtering>
              </resource>
              <resource>
                  <directory>src/main/resources</directory>
                  <includes>
                      <include>**/*.properties</include>
                      <include>**/*.xml</include>
                  </includes>
                  <filtering>false</filtering>
              </resource>
          </resources>
      </build>
      

MyBatis核心对象

MyBatis 有三个基本要素:

  • 核心接口和类
  • MyBatis核心配置文件(mybatis-config.xml)
  • SQL映射文件(mapper.xml)

下面首先介绍 MyBatis 的核心接口和类,如下所示。

image04

每个 MyBatis 应用程序都以一个 SqlSessionFactory 对象的实例为核心。

首先获取SqlSessionBuilder对象,可以根据XML配置文件或者Configuration类的实例构建该对象

然后获取SqlSessionFactory对象,该对象实例可以通过SqlSessionBuilder对象来获取

有了SqlSessionFactory对象后,就可以进而获取SqlSession实例。SqlSession对象中完全包含数据库为背景的所有执行SQL操作的方法,用该实例可以直接执行已映射的SQL语句。

  • SqlSessionFactoryBuidler

    SqlSessionFactoryBuidler会根据配置信息或者代码生成SqlSessionFactory,并且提供了多个build()方法重载,如图

    image05

    通过源码分析,可以发现以上方法都是在调用同一签名方法,即

    public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
        //...
    }
    

    由于参数environment和properties都可以为null,所以我们可以去除重复的方法,真正的重载其实只有以下三种:

    • build(InputStream inputStream, String environment, Properties properties)
    • build(Reader reader, String environment, Properties properties)
    • build(Configuration config)

    通过以上分析,我们可以发现配置信息可以以三种形式给SqlSessionFactoryBuilder的build()方法,分别是InputStream(字节流)、Reader(字符流)、Configuration(类)。

    由于字节流和字符流都属于读取配置文件的方式,所以就很容易想到构建一个SqlSessionFactory有两种方式:即读取XML配置文件和编写代码。常用的是采取XML配置文件的方式来构造SqlSessionFactory,这样一方面可以避免硬编码,另一方面方便日后配置人员修改,避免重复编译代码

  • SqlSessionFactoryBuilder的生命周期和作用域

    SqlSessionFactoryBuilder的最大特点是用过即丢。创建SqlSessionFactory对象之后,这个类就不存在了,因此SqlSessionFactoryBuilder的最佳作用范围就是存在于方法体内,也就是局部变量

    观察下面代码便可理解

    static {
        try {
            //使用Mybatis第一步:获取sqlSessionFactory对象(建议使用类路径下的资源文件配置)
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
  • SqlSessionFactory SqlSessionFactory 是工厂接口而不是现实类,他的任务就是创建 SqlSession。

    所有的MyBatis应用都以SqlSessionFactory实例为中心,SqlSessionFactory的实例可以通过SqlSessionFactoryBuilder对象来获取。有了它之后,顾名思义,就可以通过SqlSession提供的openSession()方法来获取SqlSession实例,源码如下:

    package org.apache.ibatis.session;
    
    import java.sql.Connection;
    
    public interface SqlSessionFactory {
        SqlSession openSession();
        SqlSession openSession(boolean var1);
        SqlSession openSession(Connection var1);
        SqlSession openSession(TransactionIsolationLevel var1);
        SqlSession openSession(ExecutorType var1);
        SqlSession openSession(ExecutorType var1, boolean var2);
        SqlSession openSession(ExecutorType var1, TransactionIsolationLevel var2);
        SqlSession openSession(ExecutorType var1, Connection var2);
        Configuration getConfiguration();
    }
    
  • SqlSessionFactory的生命周期和作用域

    SqlSessionFactory对象一旦创建,就会在整个应用程序过程中始终存在。没有理由去销毁或再创建它,并且在应用程序运行中也不建议多次创建SqlSessionFactory。因此SqlSessionFactory的最佳作用域是Application,即随着应用程序的生命周期一直存在。这种“存在于整个应用运行期间,并且只有一个对象实例”的模式就是所谓的单例模式(指在运行期间有且仅有一个实例)

  • SqlSession

    SqlSession是用于执行持久化操作的对象,类似于JDBC中的Connection。它提供了面向数据库执行SQL命令所需要的的所有方法,可以通过SqlSession实例直接运行已映射的SQL语句。

    image06

    SqlSession的用途主要有两种:

    1. 获取映射器。让映射器通过命名空间和方法名称找到对应的SQL,并发送给数据库,执行返回结果。
    2. 直接通过“命名空间(namespace)+SQL id”的方式执行SQL,不需要获取映射器。这是iBatis版本留下的方式。例如《第一个MyBatis程序》一节的实例就是用这种方法执行的SQL语句。
  • SqlSession的生命周期和作用域

    SqlSession对应一次数据库会话。由于数据库会话不是永久的,因此SqlSession的生命周期也不是永久的,每次访问数据库时都需要创建SqlSession对象。

    需要注意的是:每个线程都有自己的SqlSession实例,SqlSession实例是不能被共享的,也不是线程安全的。因此SqlSession的作用域范围是request作用域或方法体作用域内。

MyBatis详细的执行流程

image11

MyBatis Mapper(映射器)

映射器是MyBatis中最重要的文件,文件包含一组SQL语句(例如查询、添加、删除、修改),这些语句称为映射语句或映射SQL语句。

映射器由Java接口和XML文件(或注解)共同组成,它的作用如下:

  1. 定义参数类型
  2. 配置缓存
  3. 提供SQL语句和动态SQL
  4. 定义查询结果和POJO的映射关系1

映射器有一下两种实现方式:

  1. 通过XML文件方式实现,比如我们在mybatis-config.xml文件中描述的XML文件,可以用来生成mapper。
  2. 通过注解的方式实现,使用Configuration对象注册mapper接口

如果SQL语句存在动态SQL或者比较复杂,使用注解写在Java文件里可读性差,且增加了维护成本。所以一般建议使用XML文件配置的方式,可以避免重复编写SQL语句。

  • XML实现映射器

    XML定义映射器分为两个部分:接口和XML。下面定义接口UserMapper

    package com.heng.dao;
    
    import com.heng.pojo.User;
    
    import java.util.List;
    
    /**
     * @Author: minster
     * @Date: 2021/10/26 10:33
     */
    public interface UserMapper {
        List<User> selectUser();
    }
    

    UserMapper.xml代码如下

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!-- namespace绑定一个对应的Dao/Mapper接口 -->
    <!-- 配置文件和UserMapper接口绑定起来了 -->
    <mapper namespace="com.heng.dao.UserMapper">
        <select id="selectUser" resultType="com.heng.pojo.User">
            select * from user
        </select>
    </mapper>
    

    下面对上述 XML 文件进行讲解。

    • namespace 用来定义命名空间,该命名空间和定义接口的全限定名一致。
    • <select> 元素表明这是一条查询语句,属性 id 用来标识这条 SQL。resultType 表示返回的是一个 User类型的值。

    在MyBatis配置文件中添加以下代码:

    <mappers>
        <mapper resource="com/heng/dao/userMapper.xml"/>
    </mappers>
    

    该语句用来引入XML文件,MyBatis会读取userMapper.xml文件,生成映射器。

  • 注解实现映射器

    使用注解的方式实现映射器,只需要在接口中使用Java注解,注入SQL即可。如下所示:

    package com.heng.dao;
    
    import com.heng.pojo.User;
    
    import java.util.List;
    
    /**
     * @Author: minster
     * @Date: 2021/10/26 10:33
     */
    public interface UserMapper {
        @Select(value = "select * from user")
        List<User> selectUser();
    }
    

    这里我们使用了@Select注解,并且注入了和XML中相同的SQL语句。

    如果同时使用了注解和XML文件两种方式实现映射器,那么XML方式将覆盖掉注解方式。

    虽然XML看起来比注解复杂很多,但是现实中我们遇到的SQL会比上述例子的SQL语句复杂很多。如果SQL语句中有多个表的关联、多个查询条件、级联、条件分支等,显然这条SQL就会复杂很多,所以并不建议使用注解的这种方式实现。

    此外,XML 可以相互引入,而注解是不可以的,所以在一些比较复杂的场景下,使用 XML 方式会更加灵活和方便。因此大部分的企业都以 XML 为主,本教程也会保持一致,以 XML 方式来创建映射器。当然在一些简单的表和应用中使用注解方式也会比较简单。

  • MyBatis映射器的主要元素

    • mapper:映射文件的根节点,只有namescape一个属性。

      namescape作用如下:

      • 用于区分不同的 mapper,全局唯一
      • 绑定DAO接口,即面向接口编程。当 namescape 绑定某一接口后,可以不用写该接口的实现类,MyBatis 会通过接口的完整限定名查找到对应的 mapper 配置来执行 SQL 语句。因此 namescape 的命名必须要跟接口同名。
    • select:查询语句,最常用、最复杂的元素之一。可以自定义参数,返回结果集等

    • insert:插入语句。执行后返回一个整数,代表插入的条数

    • update:更新语句。执行后返回一个整数,代表更新的条数

    • delete:删除语句。执行后返回一个整数,代表删除的条数

    • parameterMap:定义参数映射关系,即被删除的元素,不建议使用

    • sql:允许定义一部分的 SQL,然后在各个地方引用它

    • resultMap:用来描述数据库结果集与对象的对应关系,它是最复杂、最强大的元素

    • cache:配置给定命名空间的缓存

    • cache-ref:其他命名空间缓存配置的引用

MyBatis实现CURD

  • 查询语句

    • elect标签是mybatis中最常用的标签之一

    • select语句有很多属性可以详细配置每一条SQL语句

      • SQL语句返回值类型。【完整的类名或者别名】
      • 传入SQL语句的参数类型 。【万能的Map,可以多尝试使用】
      • 命名空间中唯一的标识符
      • 接口中的方法名与映射文件中的SQL语句ID 一一对应
      • id:对应的namespace的方法名
      • resultType:Sql语句执行的返回值
      • parameterType:参数类型!

    需求:根据id查询用户

    1. 在userMapper中添加对应方法

      public interface UserMapper {
          List<User> selectUser();
          User selectUserById(int id);
      }
      
    2. 在UserMapper.xml中添加Select语句

      <select id="selectUserById" resultType="com.heng.pojo.User">
          select * from user where id = #{id}
      </select>
      
    3. 测试类测试

          @Test
          public void selectUserById(){
              SqlSession session = MybatisUtils.getSession();
              UserMapper mapper = session.getMapper(UserMapper.class);
              User user = mapper.selectUserById(1);
              System.out.println(user);
              session.close();
          }
      }
      

      image01

    需求:根据密码和名字查询用户

    方法一:直接在方法中传递参数

    1. 在接口方法的参数前加@Param属性

    2. Sql语句编写的时候,直接取@Param中设置的值即可,不需要单独设置参数类型

      UserMapper接口增加方法

      User selectUserByNP1(@Param("username") String username,@Param("pwd") String pwd);
      

      在Mapper.xml配置文件增加查询语句

      <select id="selectUserByNP1" resultType="com.heng.pojo.User">
          select * from user where name = #{username} and pwd = #{pwd}
      </select>
      

      编写测试类

      public void selectUserByNP01(){
          SqlSession session = MybatisUtils.getSession();
          UserMapper mapper = session.getMapper(UserMapper.class);
          User jack = mapper.selectUserByNP1("jack", "123456");
          System.out.println(jack);
          session.close();
      }
      

      测试,程序成功输出

    方法二:使用万能的Map集合

    1. 实体类或数据库中的表字段或参数过多,应当考虑Map!(企业常用)

    2. 不需要知道用户表里有什么也能增删改查

    3. Map传递参数,直接在SQL中取出key即可!

      在UserMapper接口中增加方法

      User selectUserByNP2(Map<String, Object> map);
      

      在Mapper.xml编写SQL语句时,需要传递参数类型,类型为map

      <select id="selectUserByNP2" parameterType="map" resultType="com.heng.pojo.User">
          select * from user where name = #{username} and pwd = #{pwd}
      </select>
      

      编写测试代码时,只需要填写key为sql中取的值即可(即key对应sql语句的字段)

      @Test
      public void selectUserByNP02(){
          SqlSession session = MybatisUtils.getSession();
          UserMapper mapper = session.getMapper(UserMapper.class);
          Map<String, Object> map = new HashMap<String, Object>();
          map.put("username","张三");
          map.put("pwd","abcdef");
          User user = mapper.selectUserByNP2(map);
          System.out.println(user);
          session.close();
      }
      

      查询结果

      image02

  • 增、删、改语句

    • insert标签:进行插入操作
    • delete标签:进行删除操作
    • update标签:进行更新操作

    代码实现增、删、改

    1. 编写接口方法

      public interface UserMapper {
          int addUser(User user);
          int updateUser(User user);
          int deleteUser(int Id);
      }
      
    2. 在Mapper.xml编写SQL语句

      <!--增加一个用户-->
      <insert id="addUser" parameterType="com.heng.pojo.User">
          insert into user (id,name,pwd) value (#{id},#{name},#{pwd})
      </insert>
      <!--修改用户信息-->
      <update id="updateUser" parameterType="com.heng.pojo.User">
          update user set name = #{name},pwd = #{pwd} where id = #{id}
      </update>
      <!--删除一个用户-->
      <delete id="deleteUser" parameterType="com.heng.pojo.User">
          delete from user where id = #{id}
      </delete>
      
    3. 编写测试类

      增加一个用户

      @Test
      public void addUser(){
          SqlSession session = MybatisUtils.getSession();
          UserMapper mapper = session.getMapper(UserMapper.class);
          User tom = new User(4, "tom", "777777");
          int i = mapper.addUser(tom);
          List<User> users = mapper.selectUser();
          for (User user : users) {
              System.out.println(user);
          }
          session.commit();
          session.close();
      }
      

      修改一个用户的信息

      @Test
      public void updateUser(){
          SqlSession session = MybatisUtils.getSession();
          UserMapper mapper = session.getMapper(UserMapper.class);
          User mary = new User(4, "mary", "777776");
          int i = mapper.updateUser(mary);
          User user = mapper.selectUserById(4);
          System.out.println(user);
          session.commit();
          session.close();
      }
      

      删除一个用户

      @Test
      public void deleteUser(){
          SqlSession session = MybatisUtils.getSession();
          UserMapper mapper = session.getMapper(UserMapper.class);
          int i = mapper.deleteUser(4);
          List<User> users = mapper.selectUser();
          for (User user : users) {
              System.out.println(user);
          }
          session.commit();
          session.close();
      }
      

    切记:进行增删改操作时,一定要提交事务session.commit();

    如果把工具类MybatisUtils的openSession()的参数设置为true,程序会自动提交事务!

    public static SqlSession getSession(){
        return sqlSessionFactory.openSession(true);
    }
    
  • 小结:

    • 所有增删改操作都需要提交事务!
    • 接口所有的普通参数,尽量都写上@Param参数,尤其是多个参数时,必须写上!
    • 有时候根据业务的需求,可以考虑使用map集合传递参数!
    • 为了规范操作,在SQL的配置文件中,我们尽量将Parameter参数和resultType参数都写上!
  • 模糊查询like语句该怎么写?

    1. 第一种:在Java代码中添加SQL通配符。

      //UserMapper接口增加方法
      List<User> selectLikeByName(@Param("username") String username);
      //测试方法
      @Test
          public void selectLikeByName(){
              SqlSession session = MybatisUtils.getSession();
              UserMapper mapper = session.getMapper(UserMapper.class);
              //增加SQL通配符
              List<User> user = mapper.selectLikeByName("%a%");
              System.out.println(user);
              session.close();
          }
      

      xml文件

      <select id="selectLikeByName" resultType="com.heng.pojo.User">
          select * from user where name like #{username}
      </select>
      
    2. 第二种:在sql语句中拼接通配符(不建议使用,会引起SQL注入问题!)

      <select id="selectLikeByName" resultType="com.heng.pojo.User">
          select * from user where name like "%"#{username}"%"
      </select>
      

MyBatis核心配置文件解析

  • mybatis-config.xml 系统核心配置文件

  • MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。

  • 能配置的内容如下:

    <?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 /><!-- 属性 -->
        <settings /><!-- 设置 -->
        <typeAliases /><!-- 类型命名 -->
        <typeHandlers /><!-- 类型处理器 -->
        <objectFactory /><!-- 对象工厂 -->
        <plugins /><!-- 插件 -->
        <environments><!-- 配置环境 -->
            <environment><!-- 环境变量 -->
                <transactionManager /><!-- 事务管理器 -->
                <dataSource /><!-- 数据源 -->
            </environment>
        </environments>
        <databaseIdProvider /><!-- 数据库厂商标识 -->
        <mappers /><!-- 映射器 -->
    </configuration>
    

    image03

    ​ 我们可以阅读 mybatis-config.xml 上面的dtd的头文件!

  • environments标签

    <environments default="development">
     <environment id="development">
       <transactionManager type="JDBC">
         <property name="..." value="..."/>
       </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>
    

    在 environments 标签中,可以配置 MyBatis 的多套运行环境,将 SQL 映射到多个不同的数据库上。

    environment 是 environments 的子标签,用来配置 MyBatis 的一套运行环境,需指定运行环境 ID、事务管理、数据源配置等相关信息。

    我们可以通过配置多个 environment 标签来连接多个数据库,需要注意的是必须指定其中一个为默认运行环境(通过default指定)。

    environment 标签提供了两个子标签,即 transactionManager 和 dataSource。

    • transactionManager标签

      MyBatis 支持两个事务管理器,即 JDBC 和 MANAGED。

      如果使用 JDBC 类型的事务管理器,则应用程序服务器负责事务管理操作,例如提交、回滚等。如果使用 MANAGED 类型的事务管理器,则应用程序服务器负责管理连接生命周期。

    • dataSource标签

      用于配置数据库的连接属性,例如要连接的数据库的驱动程序名称、URL、用户名和密码等。

      dataSource 中的 type 属性用于指定数据源类型,有以下 3 种类型。

      <dataSource type = "[UNPOOLED|POOLED|JNDI]"></dataSource>
      
      1. UNPOOLED

        UNPOOLED 没有数据库连接池,效率低下。MyBatis 需要打开和关闭每个数据库操作的连接,它有点慢,通常应用于简单的应用程序。

      2. POOLED

        对于 POOLED 数据源类型,MyBatis 将维护一个数据库连接池。并且对于每个数据库的操作,MyBatis 都会使用连接池中的连接,并在操作完成后将它们返回到池中。减少了创建新连接所需的初始连接和身份验证时间。

      3. JNDI

        对于 JNDI 的数据源类型,MyBatis 将从 JNDI 数据源中获取连接。

      dataSource的示例代码如下:

      <dataSource type="POOLED">
      	<!-- MySQL数据库驱动 -->
      	<property name="driver" value="com.mysql.jdbc.Driver" />
      	<!-- 连接数据库的URL -->
      	<property name="url"
      	value="jdbc:mysql://localhost:3306/test?characterEncoding=utf8" />
      	<property name="username" value="root" />
      	<property name="password" value="root" />
      </dataSource>
      
  • Mappers标签

    mappers 标签用于指定 MyBatis SQL 映射文件的路径。

    mapper 是 mappers 的子标签,mapper 中的 resource 属性用于指定 SQL 映射文件的路径(类资源路径)

    例如,SQL 映射文件的名称是 Student.xml,它位于名为 net.biancheng.mapper 的包中,那么您可以这样配置:

    <!-- 使用相对于类路径的资源引用 -->
    <mappers>
    	<mapper resource = "net/biancheng/mapper/Student.xml"></mapper>
    </mappers>
    <!-- 使用完全限定资源定位符(URL) -->
    <mappers>
     <mapper url="file:///net/biancheng/mapper/Student.xml"/>
    </mappers>
    <!--
    使用映射器接口实现类的完全限定类名
    需要配置文件名称和接口名称一致,并且位于同一目录下
    -->
    <mappers>
     <mapper class="net.biancheng.mapper.Student"/>
    </mappers>
    <!--
    将包内的映射器接口实现全部注册为映射器
    但是需要配置文件名称和接口名称一致,并且位于同一目录下
    -->
    <mappers>
     <package name="net.biancheng.mapper"/>
    </mappers>
    
  • Properties标签

    roperties 标签可以通过 resource 属性指定外部 properties 文件(database.properties),也可以通过 properties 子元素配置。

    现在,我们优化一下我们的配置文件

    1. 在资源目录下新建一个db.properties文件

      driver=com.mysql.jdbc.Driver
      url=jdbc:mysql://localhost:3306/mybatis?userSSL=true&userUnicode=true&characterEncoding=utf8
      username=root
      password=777777
      
    2. 将文件导入到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 resource="db.properties"></properties>
          <environments default="development">
              <environment id="development">
                  <transactionManager type="JDBC"/>
                  <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>
          <mappers>
              <mapper resource="com/heng/dao/Mapper.xml"/>
          </mappers>
      </configuration>
      
  • typeAliases标签

    为了不在任何地方都指定类的全限定名,我们可以使用 typeAliases 标签定义一个别名。

    例如,在 com.heng.pojo包中有一个 User类,则该类的全限定名称为 com.heng.pojo.User。使用 typeAliases 标签定义别名,这样就不用每次都书写类的全限定名称了,代码如下。

    <typeAliases>
        <typeAlias type="com.heng.pojo.User" alias="user"></typeAlias>
    </typeAliases
    

    当这样配置时,User可以用在任何使用com.heng.pojo.User的地方。

    也可以指定一个包名,MyBatis会在包名下面搜索需要的Java Bean,比如

    <typeAliases>
        <package name ="com.heng.pojo">
    </typeAliases>
    

    每一个在包 com.heng.pojo 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。\例如 Student 别名为 student,User 别名为 user。

    若有注解,则别名为其注解值。见下面的例子:

    //别名为user
    @Alias("user")
    public class User {
      ...
    }
    
  • settings标签

    settings 标签用于配置 MyBatis 的运行时行为,它能深刻的影响 MyBatis 的底层运行,一般不需要大量配置,大部分情况下使用其默认值即可。

    settings 的配置项很多,但是真正用到的不会太多,我们把常用的配置项研究清楚就可以了。settings 配置项说明如下表所示(表中红色字体的配置项为常用配置项)。

    配置项 作用 配置选项 默认值
    cacheEnabled 该配置影响所有映射器中配置缓存的全局开关 true|false true
    lazyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。在特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态 true|false false
    aggressiveLazyLoading 当启用时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载;反之,每种属性将会按需加载 true|false 版本3.4.1 (不包含) 之前默认值为 true,之后为 false
    multipleResultSetsEnabled 是否允许单一语句返回多结果集(需要兼容驱动) true|false true
    useColumnLabel 使用列标签代替列名。不同的驱动会有不同的表现,具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果 true|false true
    useGeneratedKeys 允许JDBC 支持自动生成主键,需要驱动兼容。如果设置为 true,则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby) true|false false
    autoMappingBehavior 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射。 PARTIAL 表示只会自动映射,没有定义嵌套结果集和映射结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套) NONE、PARTIAL、FULL PARTIAL
    autoMappingUnkno wnColumnBehavior 指定自动映射当中未知列(或未知属性类型)时的行为。 默认是不处理,只有当日志级别达到 WARN 级别或者以下,才会显示相关日志,如果处理失败会抛出 SqlSessionException 异常 NONE、WARNING、FAILING NONE
    defaultExecutorType 配置默认的执行器。SIMPLE 是普通的执行器;REUSE 会重用预处理语句(prepared statements);BATCH 执行器将重用语句并执行批量更新 SIMPLE、REUSE、BATCH SIMPLE
    defaultStatementTimeout 设置超时时间,它决定驱动等待数据库响应的秒数 任何正整数 Not Set (null)
    defaultFetchSize 设置数据库驱动程序默认返回的条数限制,此参数可以重新设置 任何正整数 Not Set (null)
    safeRowBoundsEnabled 允许在嵌套语句中使用分页(RowBounds)。如果允许,设置 false true|false false
    safeResultHandlerEnabled 允许在嵌套语句中使用分页(ResultHandler)。如果允许,设置false true|false true
    mapUnderscoreToCamelCase 是否开启自动驼峰命名规则映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射 true|false false
    localCacheScope MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速联复嵌套査询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlScssion 的不同调用将不会共享数据 SESSION|STATEMENT SESSION
    jdbcTypeForNull 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER NULL、VARCHAR、OTHER OTHER
    lazyLoadTriggerMethods 指定哪个对象的方法触发一次延迟加载 equals、clone、hashCode、toString
    defaultScriptingLanguage 指定动态 SQL 生成的默认语言 org.apache.ibatis .script.ing.xmltags .XMLDynamicLanguageDriver
    callSettersOnNulls 指定当结果集中值为 null 时,是否调用映射对象的 setter(map 对象时为 put)方法,这对于 Map.kcySet() 依赖或 null 值初始化时是有用的。注意,基本类型(int、boolean 等)不能设置成 null true|false false
    logPrefix 指定 MyBatis 增加到日志名称的前缀 任何字符串 Not set
    loglmpl 指定 MyBatis 所用日志的具体实现,未指定时将自动査找 SLF4J|LOG4J|LOG4J2|JDK_LOGGING |COMMONS_LOGGING |ST DOUT_LOGGING|NO_LOGGING Not set
    proxyFactory 指定 MyBatis 创建具有延迟加栽能力的对象所用到的代理工具 CGLIB|JAVASSIST JAVASSIST (MyBatis 版本为 3.3 及以上的)
    vfsImpl 指定 VFS 的实现类 提供 VFS 类的全限定名,如果存在多个,可以使用逗号分隔 Not set
    useActualParamName 允许用方法参数中声明的实际名称引用参数。要使用此功能,项目必须被编译为 Java 8 参数的选择。(从版本 3.4.1 开始可以使用) true|false true

    下面给出一个全量的配置样例,如下所示。

    <settings>
     <setting name="cacheEnabled" value="true"/>
     <setting name="lazyLoadingEnabled" value="true"/>
     <setting name="multipleResultSetsEnabled" value="true"/>
     <setting name="useColumnLabel" value="true"/>
     <setting name="useGeneratedKeys" value="false"/>
     <setting name="autoMappingBehavior" value="PARTIAL"/>
     <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
     <setting name="defaultExecutorType" value="SIMPLE"/>
     <setting name="defaultStatementTimeout" value="25"/>
     <setting name="defaultFetchSize" value="100"/>
     <setting name="safeRowBoundsEnabled" value="false"/>
     <setting name="mapUnderscoreToCamelCase" value="false"/>
     <setting name="localCacheScope" value="SESSION"/>
     <setting name="jdbcTypeForNull" value="OTHER"/>
     <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
    </settings>
    
  • 类型处理器

    • 无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。
    • 你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。【了解即可】
  • 对象工厂

    • MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。
    • 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过有参构造方法来实例化。
    • 如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现。【了解即可】
Licensed under CC BY-NC-SA 4.0
Built with Hugo
Theme Stack designed by Jimmy