第 7 章 MyBatis 的核心配置

通过上一章的学习,大家对 MyBatis 框架的使用已经有了一个初步的了解,但是要想熟练地使用 MyBatis 框架进行实际开发,只会简单的配置是不行的,我们还需要对框架中的核心对象, 以及映射文件和配置文件有更加深入的了解。 接下来,本章将对这些内容进行详细的讲解。

MyBatis 的核心对象

在使用 MyBatis 框架时,主要涉及两个核心对象: SqlSessionFactory 和 SqlSession ,它们 在 MyBatis 框架中起着至关重要的作用。 本节将对这两个对象进行详细讲解。

  • SqlSessionFactory

SqlSessionFactory 是 MyBatis 框架中十分重要的对象,它是单个数据库映射关系经过编译后的内存镜像,其主要作用是创建 SqlSession。SqlSessionFactory 对象的实例可以通过 SqlSessionFactoryBuilder 对象来构建,而 SqlSessionFactoryBuilder 则可以通过 XML 配置文件或一个预先定义好的 Configuration 实例构建出 SqlSessionFactory 的实例。 我们所讲解的就是通过 XML 配置文件构建出的 SqlSessionFactory 实例,其实现代码如下:

  // 读取配置文件
  InputStream inputStream = Resources.getResourceAsStream("配置文件位置");
  // 根据配置文件构建 Sq1SessionFactory
  SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

SqISessionFactory 对象是线程安全的,它一旦被创建,在整个应用执行期间都会存在。 如果我们多次地创建同一个数据库的 SqlSessionFactory,那么此数据库的资源将很容易被耗尽。 为了解决此问题,通常每一个数据库都会只对应一个 SqlSessionFactory,所以在构建 SqlSessionFactory 实例时,建议使用单列模式。

  • SqlSession

SqlSession 是 MyBatis 框架中另一个重要的对象,它是应用程序与持久层之间执行交互操作的一个单线程对象,其主要作用是执行持久化操作。 SqlSession 对象包含了数据库中所有执 行 SQL 操作的方法,由于其底层封装了 JDBC 连接,所以可以直接使用其实例来执行己映射的 SQL 语句。
每一个线程都应该有一个自己的 SqlSession 实例,并且该实例是不能被共享的。 同时, SqlSession 实例也是线程不安全的,因此其使用范围最好在一次请求或一个方法中,绝不能将其放在一个类的静态字段、实例字段或任何类型的管理范围(如 Servlet 的 HttpSession )中使用。 使用完 SqlSession 对象之后,要及时地关闭它,通常可以将其放在 finally 块中关闭,代码如下所示。

      SqlSession sqlSession = sqlSessionFactory.openSession();
      try{
          //此处执行持久化操作
      }finally{
          sqlSession.close();
      }

SqlSession 对象中包含了很多方法,其常用方法如下所示。

  • T selectOne ( String statement ); 查询方法。 参数 statement 是在配置文件中定义的元素的 id。 使用该方法后,会返 回执行 SOL 语句查询结果的泛型对象的集合。

  • List selectList ( String statement, Object parameter ); 查询方法。 参数 statement 是在配置文件中定义的

  • List selectList ( String statement, Object parameter, RowBounds rowBounds ); 查询方法。 参数 statement 是在配置文件中定义的元素

元素执行查询操作非常简单,其示例如下。

  

上述语句中的唯一标识为 findCustomerByld ,它接收一个 Integer 类型的参数,并返回一个 Customer 类型的对象。
元素的属性大部分相同 , 但还包含了 3 个特有属性,这 3 个属性的描述如表所示。

属性 说明
keyProperty (仅对 insert和 update 有用)此属性的作用是将插入或更新操作时的返回值赋值给 PO 类的某个属性,通常会设置为主键对应的属性。 如果需要设置联合主键,可以在多个值之间用逗号隔开
keyColumn (仅对 insert和 update 有用)此属性用于设置第几列是主键,当主键列不是表中的第一列时需要设置。在需要主键联合时,值可以用逗号隔开
useGeneratedKeys (仅对 insert和 update 有用)此属性会使 MyBatis 使用 JDBC 的 getGeneratedKeys() 方法来获取由数据库内部生产的主键,如 MySQL 和 SQL Server等自动递增的字段,其默认值为 false

执行插入操作后,很多时候我们会需要返回插入成功的数据生成的主键值,此时就可以通过上面所讲解的 3 个属性来实现。
如果使用的数据库支持主键自动增长(如 MySQL ),那么可以通过 keyProperty 属性指定 PO 类的某个属性接收主键返回值 ( 通常会设置到 id 属性上 ),然后将 useGeneratedKeys 的属性值设置为 true , 其使用示例如下。

  
      insert into t_customer(username,jobs,phone)
      values(#{username},#{jobs},#{phone})
  

使用上述配置执行插入后,会返回插入成功的行数,以及插入行的主键值。 为了验证此配置, 可以通过如下代码测试。

  @Test
  public void addCustomerTest(){
      SqlSession sqlSession = MybatisUtils.getSession();
      System.out.println(sqlSession);
      Customer customer = new Customer();
      customer.setUsername("rose1");
      customer.setJobs("student1");
      customer.setPhone("13300007777");
      int rows = sqlSession.insert("com.neuedu.mapper.CustomerMapper.addCustomer",customer);
      //输出插入数据的主键 id值
      System.out.println(customer.getId());
      if(rows > 0){
          System.out.println("您成功插入了"+rows+"条数据!");
      }else{
          System.out.println("执行插入数据失败!!!");
      }
      sqlSession.commit(); 
      sqlSession.close();
  }

执行程序后,控制台的输出结果如图所示。



如果使用的数据库不支持主键自动增长(如 Oracle ),或者支持增长的数据库取消了主键自增的规则时,也可以使用 MyBatis 提供的另一种方式来自定义生成主键,具体配置示例如下。

 
  
      select if(max(id) is null,1,max(id)+1) as newId from t_customer
  
  insert into t_customer(id,username,jobs,phone)
   values(#{id},#{username},#{jobs},#{phone}) 
 

在执行上述示例代码时, 元素会首先运行,它会通过自定义的语句来设置数据 表中的主键(如果 t_customer 表中没有记录,则将 id 设置为 1 ,否则就将 id 的最大值加 1 ,来作为新的主键),然后再调用插入语句。
元素在使用时可以设置以下几种属性。

  

在上述元素的几个属性中, keyProperty、 resultType 和 statementType 的作用与前面讲解的相同,这里不重复介绍。 order 属性可以被设置为 BEFORE 或 AFTER。 如果设置 为 BEFORE ,那么它会首先执行元素中的配置来设置主键,然后执行插入语句;如果设置为 AFTER ,那么它会先执行插入语句,然后执行元素中的配置内容。

  • 元慧和元素

元素的使用比较简单,它们的属性配置也基本相同( 元素中不包含上面提到的 3 个属性),其常用属性如下所示。

  
  

从上述配置代码中可以看出, 元素的属性基本与 select from t_customer where id = #{id}

在上述代码中,使用元素的 refid 属性引用了自定义的代码片段, refid 的属性值为自定义代码片段的 id。
上面示例只是一个简单的引用查询。 在实际开发中,可以更加灵活地定义 SOL 片段,其示例如下。


 
  ${prefix}customer 
 
 
from 
 
 
 
 
id,username,jobs,phone 

  
  

上述代码中,定义了 3 个代码片段,分别为表的前缀名、要查询的表和需要查询的列。前两个代码片段中,分别获取了 子元素 中的值,其中第 1 个代码片段中的 "${prefix) "会获取 name 为 prefix 的值 "t_" ,获取后所组成的表名为 "t_customer" ; 而第 2 个代码片段中的 "${include_target)"会获取 name 为 include_target 的值 "tablename" ,由于 tablename 为第 1 个 SOL 片段的 id 值,所以最后要查询的表为 "t_customer"。所有的 SOL 片段在程序运行时,都会由 MyBatis 组合成 SOL 语句来执行需要的操作。
执行程序后,控制台的输出结果如图所示。


  • 元素

元素表示结果映射集,是 MyBatis 中最重要也是最强大的元素。它的主要作回是定义映射规则、级联的更新以及定义类型转化器等。
元素中包含了一些子元素,它的元素结构如下所示。



元素的 type 属性表示需要映射的 POJO , id 属性是这个 resultMap 的唯一标识。 它的子元素用于配置构造方法(当一个 POJO 中未定义无参的构造方法时,就可 以使用元素进行配置)。 子元素用于表示哪个列是主键,而用于表示 POJO 和数据表中普通列的映射关系。 用于处理多表时的关联关 系,而元素主要用于处理一个单独的数据库查询返回很多不同数据类型结果集的情况。
在默认情况下, MyBatis 程序在运行时会自动地将查询到的数据与需要返回的对象的属性进行匹配赋值(需要表中的列名与对象的属性名称完全一致)。 然而实际开发时,数据表中的列和需要返回的对象的属性可能不会完全一致,这种情况下 MyBatis 是不会自动赋值的。 此时,就可以使用元素进行处理。
接下来,通过一个具体的案例来演示元素在此种情况的使用,具体步骤如下。
( 1 )在 mybatis 数据库中,创建一个 t_user 表,并插入几条测试数据。



( 2 )在 com.neuedu.po 包中,创建持久化类 User,并在类中定义 id、 name 和 age 属性, 以及其 getter/setter 方法和 toString()方法,文件如下所示。
package com.neuedu.po;
public class User {
  private Integer id;
  private String name;
  private Integer age;
  public Integer getId() {
      return id;
  }
  public void setId(Integer id) {
      this.id = id;
  }
  public String getName() {
      return name;
  }
  public void setName(String name) {
      this.name = name;
  }
  public Integer getAge() {
      return age;
  }
  public void setAge(Integer age) {
      this.age = age;
  }
  @Override
  public String toString() {
      return "User [id=" + id + ", name=" + name + ", age=" + age + "]";
  }   
}

( 3 )在 com.neuedu.mapper 包下,创建映射文件 UserMapper.xml ,并在映射文件中编写映射查询语句,文件如下所示。

 
 

  
      
      
      
  
  

在上述文件中, 的子元素的 property 属性表示 User 类的属性名, column 属性表示数据表 t_user 的列名。