--- title: 核心配置文件 author: 查尔斯 date: 2020/12/26 14:48 categories: - MyBatis快速入门 tags: - MyBatis - ORM框架 --- # 核心配置文件 ## 前言 **C:** 在上一篇,笔者带大家对 MyBatis 的核心对象做了介绍。本篇,笔者将继续带你学习 MyBatis,掌握对核心配置文件的使用。 MyBatis 的核心/全局配置文件 mybatis-config.xml ,顾名思义就是对 MyBatis 系统的核心设置文件。包含有 MyBatis 运行时行为配置、类型别名配置、环境配置等。 下方是核心配置文件的标签模板,笔者将对其中常用的一些标签的常用使用方式进行介绍。 configuration 根节点 - **properties** 属性配置 - settings 运行时行为配置 - **typeAliases** 类型别名配置 - typeHandlers 类型处理器 - objectFactory 对象工厂 - plugins 插件配置 - **environments** 环境配置 - environment 单个环境配置 - transactionManager 事务管理器配置 - dataSource 数据源配置 - databaseIdProvider 数据库厂商标识 - **mappers** 映射器配置 ::: tip 笔者说 这些标签在使用时一定要注意标签的顺序和允许使用次数。Eclipse 中可以通过在标签上按 F2 查看该标签下的内容模型,即标签的顺序和允许使用次数。你看下图中画圈处就是各个标签的顺序,后面的 ?号 代表指定标签最多允许使用一次。 ::: ![202012262251170](../../../public/img/2020/12/26/202012262251170.png) ## properties元素 如果你学过 Maven,那 properties 元素应该不难理解。在 MyBatis 的核心配置文件中,有很多配置是可能经常需要变动或复用的,如果直接将值硬编码在对应位置,将不利于统一维护管理和复用。 properties 元素的作用就体现出来了,它的使用方式有两种。 ### 内部编写 **第一种使用方式,是内部编写配置** ,示例如下: ```xml ``` ### 外部引入 **第二种使用方式,是在外部配置文件编写配置,然后通过 properties 引入外部配置** ,示例如下: 在 classpath 下 添加 一个 properties 配置文件,记录各种配置信息。(此处笔者记录的是数据源信息) ``` # MySQL mysql.driver=com.mysql.jdbc.Driver mysql.url=jdbc:mysql://localhost:3306/mybatis_demo_db mysql.username=root mysql.password=root ``` ```xml ``` ::: tip 笔者说 大家猜一下,如果这两种方式在同时使用时遇到了相同配置,那么哪种方式的配置会生效呢? 测试思路: 可以先故意改错内部配置方式的 mysql.password 值,如果测试运行正常,说明外部配置生效了,反之则内部配置生效了。 也可以再故意调错外部的值试试。 ::: ```xml ``` ## settings元素(下篇讲解) settings 元素是用来设置和改变 MyBatis 在运行时的一些行为的。 | **设置项** | **描述** | **允许值** | **默认值** | | ------------------- | :----------------------------------------------------------: | :-------------------: | :--------: | | cacheEnabled | 对在此配置文件下的所有cache进行全局性开/关设置 | true \| false | true | | lazyLoadingEnabled | 全局性设置懒加载。如果设为false,则所有相关联的都会被初始化加载 | true \| false | true | | autoMappingBehavior | MyBatis对于resultMap自动映射匹配级别 | NONE\|PARTIAL \|FULL | PARTIAL | | **……(9个)** | **......** | **......** | **......** | ## typeAlias元素 在 SQL 映射文件中,我们在使用到某些类型时,需要编写好对应的全类名,大量的使用时,繁琐不说还容易错,如下 resultType 属性示例。 ```xml ``` 而typeAlias 元素就可以解决此问题,通过它的配置,可以为指定类型配置好别名,这样在 SQL 映射文件中就可以不用写全限定类名,而是直接使用配置的类型别名了。它的使用方式也有两种。 ### 单个配置 **第一种使用方式:挨个对不同类型进行别名配置。** ```xml ``` ### 包扫描 **第二种使用方式:当要配置别名的类型都在指定的 package 下时,可以直接开启包扫描,批量实现别名自动配置。** ```xml ``` ### 使用效果 下方是有了类型别名配置之后,SQL 映射文件内使用类型的效果。 ```xml ``` ::: tip 笔者说 在 MyBatis 中有一个类 TypeAliasRegistry ,它的作用就是进行类型别名注册和解析,Java 中常见的类型都已经被它注册好了别名。 ::: ```java package org.apache.ibatis.type; // ...略... public class TypeAliasRegistry { /** Map<类型别名, 对应类型的Class对象> */ private final Map> typeAliases = new HashMap<>(); /** 在创建对象时进行常用 Java 类型的别名注册 */ public TypeAliasRegistry() { // String 类型注册的别名为 string registerAlias("string", String.class); registerAlias("byte", Byte.class); registerAlias("long", Long.class); registerAlias("short", Short.class); registerAlias("int", Integer.class); registerAlias("integer", Integer.class); registerAlias("double", Double.class); registerAlias("float", Float.class); registerAlias("boolean", Boolean.class); registerAlias("byte[]", Byte[].class); registerAlias("long[]", Long[].class); registerAlias("short[]", Short[].class); registerAlias("int[]", Integer[].class); registerAlias("integer[]", Integer[].class); registerAlias("double[]", Double[].class); registerAlias("float[]", Float[].class); registerAlias("boolean[]", Boolean[].class); registerAlias("_byte", byte.class); registerAlias("_long", long.class); registerAlias("_short", short.class); registerAlias("_int", int.class); registerAlias("_integer", int.class); registerAlias("_double", double.class); registerAlias("_float", float.class); registerAlias("_boolean", boolean.class); registerAlias("_byte[]", byte[].class); registerAlias("_long[]", long[].class); registerAlias("_short[]", short[].class); registerAlias("_int[]", int[].class); registerAlias("_integer[]", int[].class); registerAlias("_double[]", double[].class); registerAlias("_float[]", float[].class); registerAlias("_boolean[]", boolean[].class); registerAlias("date", Date.class); registerAlias("decimal", BigDecimal.class); registerAlias("bigdecimal", BigDecimal.class); registerAlias("biginteger", BigInteger.class); registerAlias("object", Object.class); registerAlias("date[]", Date[].class); registerAlias("decimal[]", BigDecimal[].class); registerAlias("bigdecimal[]", BigDecimal[].class); registerAlias("biginteger[]", BigInteger[].class); registerAlias("object[]", Object[].class); registerAlias("map", Map.class); registerAlias("hashmap", HashMap.class); registerAlias("list", List.class); registerAlias("arraylist", ArrayList.class); registerAlias("collection", Collection.class); registerAlias("iterator", Iterator.class); registerAlias("ResultSet", ResultSet.class); } /** * 解析别名 * @param string 要解析的别名 * @return 该别名对应的类型的Class对象 */ public Class resolveAlias(String string) { try { if (string == null) { return null; } // MyBatis 在【别名自动配置】和【解析映射文件中别名】时,对别名进行了小写转换。 // 所以在使用别名的时候才不区分大小写。 String key = string.toLowerCase(Locale.ENGLISH); Class value; if (typeAliases.containsKey(key)) { value = (Class) typeAliases.get(key); } else { value = (Class) Resources.classForName(string); } return value; } catch (ClassNotFoundException e) { throw new TypeException("Could not resolve type alias '" + string + "'. Cause: " + e, e); } } // ...略... } ``` ## environments元素 MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中,现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。 **不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。** 所以,如果你想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例,依此类推,记起来很简单: - **每个数据库对应一个 SqlSessionFactory 实例** [1] ```xml ``` ## mappers元素 mappers 元素的作用就是用来告诉 MyBatis 去哪找我们编写的 SQL 语句,它的使用方式有两大类。 ### 指定映射文件 这类方式主要是告诉 MyBatis 我们所编写的 SQL 映射文件的地址,我们之前在 [《快速入门》](./01-快速入门) 中使用的就是属于这类方式。它有两种实现: ```xml ``` ```xml ``` ### 指定Mapper接口[重要] 这类方式还要涉及到 SqlSession 对象的另一个使用形式:**Mapper接口开发** 我们之前使用 SqlSession 是让它来直接执行指定的 SQL 语句。这种方式需要指明 SQL 映射文件 namespace的名字以及 SQL 语句的 id。因为是硬编码在代码中,维护时有诸多不便,例如:容易写错不说,还不利于我们在持久层采用面向接口编程思想。 ```java // 执行 SQL 语句 List userList = sqlSession.selectList("userMapper.selectList"); ``` 而 Mapper 接口开发就可以有效解决此问题,实现方式如下: **第一步:先创建一个 Mapper 接口。** 前期就养成一个开发习惯,保持 Mapper 接口和对应 SQL 映射文件同名同包(虽然目前不同名也没事,但是先听话,养成习惯)。 ```java public interface UserMapper { /** * 查询用户列表 * @return */ List selectList(); } ``` ![202012262252221](../../../public/img/2020/12/26/202012262252221.png) **第二步:将 SQL 映射文件的 namespace 值改为对应 Mapper 接口的全限定类名。** ```xml ``` **第三步:将 SQL 映射文件中的 SQL 语句和 Mapper 接口中的方法进行绑定。** ```xml ``` **最后,我们在核心配置文件中,再配置好 Mapper 接口的全限定类名即可。** ```xml ``` **测试一下。** ```java @Test void testSelectList() throws IOException { // 获取SqlSession对象 try (SqlSession sqlSession = MyBatisUtils.openSession()){ // 获取 Mapper 接口,而不再直接执行 SQL 语句 // 和以前 DAO 模式就非常相像了,只不过是 DAO 实现类变为了 SQL 映射文件 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 执行方法 List userList = userMapper.selectList(); // 遍历数据 userList.forEach(System.out::println); } catch (Exception e) { e.printStackTrace(); } } ``` 另外,在核心配置文件中,大量的配置 Mapper 接口全限定类名,还是有些麻烦,所以 MyBatis 在这也支持包扫描配置。 ```xml ``` ::: warning 笔者说 Mapper 接口开发是我们以后主要使用的方式,必须掌握! ::: ## 参考文献 [1]MyBatis 官网. MyBatis 配置[EB/OL]. https://mybatis.org/mybatis-3/zh/configuration.html. 2020-12-26 ## 后记 ::: info 笔者说 对于技术的学习,笔者一贯遵循的步骤是:先用最最简单的 demo 让它跑起来,然后学学它的最最常用 API 和 配置让自己能用起来,最后熟练使用的基础上,在空闲时尝试阅读它的源码让自己能够洞彻它的运行机制,部分问题出现的原因,同时借鉴这些技术实现来提升自己的代码高度。 所以在笔者的文章中,前期基本都是小白文,仅仅穿插很少量的源码研究。当然等小白文更新多了,你们还依然喜欢,后期会不定时专门对部分技术的源码进行解析。 :::