深入理解 MyBatis 的核心配置

时间:2022-11-09 09:56:20

一、学习目标

① 了解 MyBatis 核心对象的作用;
② 掌握 MyBatis 核心配置文件及其元素的使用;
③ 掌握 MyBatis 映射文件及其元素的使用。

通过上一篇的学习,我们对 MyBatis 框架已经有了一个初步的了解,但是要想熟练地使用 MyBatis 框架进行实际开发,只会简单的配置肯定是不行的,我们还需要对框架中的核心对象、核心配置文件以及映射文件有更深入的了解,这就是我们接下来要学习的目标。

二、MyBatis 核心对象

1.SqlSessionFactoryBuilder 对象

SqlSessionFactoryBuilder 构建 build() 方法的形式有三种:

① 参数 inputStream 是字节流,它封装了 XML 文件形式的配置信息,参数 environment 和参数 properties 为可选参数,其中,参数 environment 决定将要加载的环境,包括数据源和事务管理器,参数 properties 决定将要加载的 properties 文件。

build(InputStream inputStream, String environment, Properties properties)

② 第二种形式的 build() 方法参数作用与第一种形式大体上一致,唯一不同的是,第一种形式的方法使用 InputStream 字节流封装了 XML 文件形式的配置信息,而第二种形式的方法使用 Reader 字符流 封装了 XML 文件形式的配置信息。

build(Reader reader, String environment, Properties properties)

③ 以配置对象的形式。

build(Configuration config)

2.SqlSessionFactory 对象

SqlSessionFactory 是一个工厂对象,作用就是用来用来构造 SqlSession。
SqlSessionFactory 对象是线程安全的,它一旦被创建,在整个应用程序执行期间都会存在,如果我们多次创建同一个数据库的 SqlSessionFactory 对象,那么该数据库的资源将很容易被耗尽。通常每一个数据库都只创建一个 SqlSessionFactory 对象,所以在构建 SqlSessionFactory 对象时,建议使用单例模式。

通过读取 XML 配置文件的方式构造 SqlSessionFactory 对象的关键代码如下所示:

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

SqlSessionFactory 的 openSession(ExecutorType execType, Connection connection) 方法如下:

深入理解 MyBatis 的核心配置

(1)execType 执行器类型有三个可选值:
① ExecutorType.SIMPLE,表示为每条语句创建一条新的预处理语句;
② ExecutorType.REUSE,表示会复用预处理语句;
③ ExecutorType.BATCH,表示会批量执行所有更新语句。
(2)参数 connection 可提供自定义连接。

openSession()方法也可以空参,不传参数!

3.SqlSession 对象

SqlSession 是 MyBatis 框架中的一个非常重要的对象,它是应用程序与持久层之间执行交互操作的一个单线程对象,主要作用是执行持久化操作,说白了就是用来操作数据库的,类似于 JDBC 中的 Connection。SqlSession 对象包含了执行 SQL 操作的方法,由于其底层封装了 JDBC 连接,所以可以直接使用 SqlSession 对象来执行已映射的 SQL 语句。

SqlSession 对象中常用的方法:

深入理解 MyBatis 的核心配置

深入理解 MyBatis 的核心配置

每一个线程都应该有一个自己的 SqlSession 对象,并且该对象不能共享。SqlSession 对象是线程不安全的,因此其使用范围最好在一次请求或一个方法中,绝不能将其放在类的静态字段、对象字段或任何类型的管理范围中使用。SqlSession 对象使用完之后,要及时关闭,SqlSession 对象通常放在 finally 块中关闭,代码如下所示。

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

三、MyBatis 核心配置文件

1.核心配置文件中的主要元素

configuration 元素是整个 XML 配置文件的根元素,相当于 MyBatis 各元素的管理员,configuration 有很多子元素,MyBatis 的核心配置就是通过这些子元素完成的。
深入理解 MyBatis 的核心配置

① properties 用来加载外部配置文件,重点;
② setting 用来做一些配置从而改变 MyBatis 的 默认行为,重点;
③ typeAliases 别名映射,查询的结果集的映射,重点;
④ typeHandlers 类型处理器,MyBatis 有默认的类型处理器,当然我们也可以自定义;
⑤ objectFactory 工厂对象;
⑥ plugins 插件,MyBatis 支持第三方插件的配置;
⑦ environments 用于配置多组数据库相关的内容,重点;
⑧ transactionManager 事务管理方式;
⑨ dataSource 数据源相关内容;
⑩ mappers 管理 MyBatis 的映射文件,非常重要。

configuration 的子元素必须按照上图由上到下的顺序进行配置,否则在解析 XML 配置文件时会报错!

2.properties 元素

① 假设现在有一个配置文件 db.properties,该文件配置了数据库的连接信息,具体如下:

jdbc.driver = com.mysql.cj.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/mybatis
jdbc.username = root
jdbc.password = root

② 接下来可以在 MyBatis 的核心配置文件 MybatisConfig.xml 中使用 properties 元素引入 db.properties 文件,具体代码如下:

<properties resource = "db.properties" />

③ 引入 db.properties 文件后,如果希望动态获取 db.properties 文件中的数据库连接信息,可以使用 property 元素配置,示例代码如下:

<dataSource type = "POOLED">
  <!-- 数据库驱动 -->
  <property name="driver" value = "${jdbc.driver}" />
  <!-- 连接数据库的URL -->
  <property name = "url" value = "${jdbc.url}" />
  <!-- 连接数据库的用户名 -->
  <property name = "username" value = "${jdbc.username}" />
  <!-- 连接数据库的密码 -->
  <property name = "password" value = "${jdbc.password}" />
</dataSource>

④ 完成上述配置后,dataSource 元素中连接数据库的4个属性(driver、url、username 和 password)值将会由 db.properties 文件中对应的值来动态替换。这样,properties 元素就实现了动态参数的配置。

使用动态参数配置的优点是方便后期的管理和维护!

3.setting 元素

改变 MyBatis 默认行为,比如 MyBatis 实体的属性名和表中的列名默认是应该保持一致的。但是在实际开发中我们是有命名规范的,属性名用驼峰命名法来命名,而数据库的列名命名规范是不同单词之间下划线隔开,这就导致实体属性名和表中的列名不一致,此时用 setting 元素就可以解决这个问题。

深入理解 MyBatis 的核心配置

<settings>
  <!-- 是否开启缓存,默认true -->
  <setting name="cacheEnabled" value="true" />
  <!-- 是否开启延迟加载,如果开启,所有关联对象都会延迟加载,默认false -->
  <setting name="lazyLoadingEnabled" value="true" />
  <!-- 是否开启关联对象属性的延迟加载,默认false -->
  <setting name="aggressiveLazyLoading" value="true" />
  <!-- 是否开启驼峰映射,默认false -->
  <setting name="mapUnderscoreToCamelCase" value="true" />
</settings>

4.typeAliases 元素

核心配置文件若要引用一个 POJO 实体类,需要输入 POJO 实体类的全限定类名,而全限定类名比较冗长,如果直接输入,很容易拼写错误。 例如,POJO 实体类 User 的全限定类名是 com.tyut.pojo.User,未设置别名前,映射文件的 select 语句块若要引用 User 类,必须使用其全限定类名,引用代码如下:

<select id="findById" parameterType="int" resultType="com.tyut.pojo.User">
  select * from users where uid = #{id}
</select>  

① 方式一,在 typeAliases 元素下,使用多个 typeAlias 元素为每一个全限定类逐个配置别名。

<typeAliases>
  <typeAlias alias="User" type="com.tyut.pojo.User"/>
  <typeAlias alias="Student" type="com.tyut.pojo.Student"/>
  <typeAlias alias="Employee" type="com.tyut.pojo.Employee"/>
</typeAliases>

② 方式二,通过自动扫描包的形式自定义别名。

<typeAliases>
  <package name="com.tyut,pojo"/>
</typeAliases>

默认实体的类名就是别名,大小写不区分!

除了可以使用 typeAliases 元素为实体类自定义别名外,MyBatis 框架还为许多常见的 Java 类型(如数值、字符串、日期和集合等)提供了相应的默认别名。例如别名 _byte 映射类型 byte、_long 映射类型 long 等,别名可以在 MyBatis 中直接使用,但是由于别名不区分大小写,所以在使用时要注意重复定义的覆盖问题。

5.environments 元素

不同的运行环境可以通过 environments 元素来配置,但不管增加几套运行环境,都必须要选择出当前要用的唯一的一个运行环境。
MyBatis 的运行环境信息包括事务管理器和数据源。environment 元素有两个子元素,transactionManager 元素和 DataSource 元素,transactionManager 用于配置运行环境的事务管理器,DataSource 用于配置运行环境的数据源信息。

① transactionManager 元素可以配置两种类型的事务管理器,分别是 JDBC 和 MANAGED。
JDBC 配置直接使用 JDBC 的提交和滚回设置,它依赖于从数据源得到的连接来管理事务的作用域;
MANAGED 配置不提交或滚回一个连接,而是让容器来管理事务的整个生命周期,默认情况下它会关闭连接。

② 对于数据源的配置,MyBatis 提供了 UNPOOLED、POOLED 和 JNDI 三种数据源类型。
UNPOOLED 表示数据源为无连接池类型,配置此数据源类型后,程序在每次被请求时会打开和关闭数据库连接,它适用于对性能要求不高的简单应用程序;
POOLED 表示数据源为连接池类型,POOLED 数据源将 JDBC 连接对象组织起来,节省了在创建新的连接对象时初始化和认证的时间,它使得并发 Web 应用可以快速的响应请求,是当前比较流行的数据源配置类型;
JNDI 表示数据源可以在 EJB 或应用服务器等容器中使用。

6.mappers 元素

引入映射文件有四种方法:

<!--使用类路径引入-->
<mappers>
  <mapper resource="com/tyut/Mapper/UserMapper.xml"/>  
<mappers>
<!--使用本地文件引入-->
<mappers>
  <mapper url="file:///D:/com/tyut/Mapper/UserMapper.xml"/>
</mappers>  
<!--使用接口类引入-->
<mappers>
  <mapper class="com.tyut.mapper.UserMapper"/>  
</mappers>
<!--使用包名引入-->
<mappers>
  <package name="com.tyut.mapper.Mapper"/>  
</mappers>

四、映射文件常用元素

深入理解 MyBatis 的核心配置

1.namespace 属性

namespace 属性有两个作用:
① 用于区分不同的 mapper,全局唯一;
② 绑定 DAO接口,即面向对象接口编程。当 namespace 绑定某一接口之后,可以不用写该接口的实现类,MyBatis 会通过接口的全限定名查找到对应的 mapper 配置来执行 SQL 语句,因此 namespace 的命名必须是接口的全路径。

在不同的映射文件中,mapper 子元素的 id 可以相同,MyBatis 通过 mapper 元素的 namespace 属性值和子元素的 id 联合区分不同的 Mapper.xml 文件,接口中的方法与映射文件中 SQL 语句 id 应一一对应。

2.select 元素

select 元素用来映射查询语句,它可以从数据库中查询数据并返回。

<!--查询操作-->
<select id="findUserById" parameterType="Integer" resultType="com.tyut.pojo.User">
  select * from users where id = #{id}
</select>

深入理解 MyBatis 的核心配置

前四个常用,后面的了解即可!

3.insert 元素

insert 元素用于映射插入语句,在执行完 insert 元素中定义的 SQL 语句后,会返回插入记录的数量。

<!--插入操作-->
<insert id="addUser" parameterType="com.tyut.pojo.User">
  insert into users(uid,uname,uage) values(#{uid},#{uname},#{uage})
</insert>

下面举一个例子,插入一组数据并全部显示,如下:

/*映射文件 UserMapper.xml*/

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tyut.pojo.User">
    <insert id="addUser" parameterType="com.tyut.pojo.User">
        insert into users(uid,uname,uage) values(#{uid},#{uname},#{uage})
    </insert>
    <select id="allUser" resultType="com.tyut.pojo.User">
        select * from users
    </select>
</mapper>    
//测试文件 UserTest.java

package com.tyut.Test;
import com.tyut.pojo.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.Reader;
import java.io.IOException;
import java.util.List;

public class UserTest {
    @Test
    public void userFindByTest() {
        String resources = "MybatisConfig.xml";
        Reader reader = null;
        try {
            reader = Resources.getResourceAsReader(resources);
        }catch (IOException e) {
            e.printStackTrace();
        }
        SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = sqlMapper.openSession();
        User newUser = new User();
        newUser.setUid(6);
        newUser.setUname("熊二");
        newUser.setUage(8);
        session.insert("addUser",newUser);
        List<User> list = session.selectList("allUser");
        System.out.println("姓名\t年龄");
        for (User user : list) {
            System.out.println(user.getUname()+"\t"+user.getUage());
        }
        session.commit();
    }
}

深入理解 MyBatis 的核心配置

实体类及其他具体的配置在这里就不多解释了,不清楚的请看我上篇博文!

有时候我们需要获取数据库中表的主键值,如何实现呢?

① 使用支持主键自增的数据库获取主键值
如 MySQL 和 SQL Server,可以通过 keyProperty 属性指定 POJO 类的某个属性来接收主键返回值(通常会设置到 id 属性上),然后将 useGeneratedKeys 的属性值设置为 true。

<insert id="addUser" parameterType="com.tyut.pojo.User" keyProperty="uid" useGeneratedKeys="true">
  insert into users(uid,uname,uage) values(#{uid},#{uname},#{uage})
</insert>

② 使用不支持主键自增的数据库获取主键值

<insert id="addUser" parameterType="com.tyut.pojo.User"> 
  <selectKey keyProperty="id" resultType="Integer" order="BEFORE" statementType="PREPARED">
    SELECT LAST_INSERT_ID()
  </selectKey>
</insert>  

在上述 selectKey 元素的属性中,order 属性可以被设置为 BEFORE 或 AFTER。如果设置为 BEFORE,那么它会首先执行 selectKey 元素中的配置来设置主键,然后执行插入语句,如果设置为 AFTER,那么它先执行插入语句,然后执行 selectKey 元素中的配置内容。

4.update 元素

更新数据库中的数据。

<!--更新操作-->
<update id="updateUser" parameterType="com.tyut.pojo.User">
  update users set uname = #{uname}, uage = #{uage} where uid = #{uid}
</update>

5.delete 元素

映射删除数据。

<!--删除操作-->
<delete id="deleteUser" parameterType="Integer">
  delete from users where uid = #{uid}
</delete>

6.sql 元素

在一个映射文件中,通常需要定义多条 SQL 语句,这些 SQL 语句的组成可能有一部分是相同的(如多条 select 语句中都查询相同的 id,username 字段),如果每一个 SQL 语句都重写一遍相同的部分,势必会增加代码量,针对此问题,可以在映射文件中使用 MyBatis 所提供的 sql 元素,将这些 SQL 语句中相同的组成部分抽取出来,然后在需要的地方引用。

深入理解 MyBatis 的核心配置

7.resultMap 元素

resultMap 元素表示结果映射集,是 MyBatis 中最重要也是功能最强大的元素。
默认情况下,MyBatis 程序在运行时会自动将查询到的数据与需要返回的对象的属性进行匹配赋值(数据表中的列名与对象的属性名完全一致时才能匹配成功并赋值)。然而实际开发时,数据表中的列和需要返回的对象属性可能不会完全一致,这种情况下 MyBatis 不会自动赋值,这就需要使用 resultMap 元素进行结果集映射。

<mapper namespace="com.tyut.mapper.StudentMapper">
  <resultMap type="com.tyut.pojo.Student" id="studentMap">
    <id property="id" column="sid"/>
    <result property="name" column="sname"/>
    <result property="age" column="sage"/>
  </resultMap>
  <select id="findAllStudent" resultMap="studentMap">
      select * from t_student
  </select>
</mapper>