第3篇:MyBatis配置

149 阅读6分钟

MyBatis使用核心配置文件来管理相关的配置信息,本篇文章主要介绍:

  • 属性配置:<properties.../>
  • 设置配置:<settings.../>
  • 类型别名:<typeAliases.../>
  • 对象工厂:<objectFactory.../>
  • 加载Mapper:<mappers.../>

1. 属性配置

前面章节介绍过,MyBatis在配置数据库datasource时,使用<property>元素定义相关的属性。具体到MyBatis配置文件,可以在<properties>元素中配置相关的属性。

通过使用属性,可以将核心配置文件中的部分信息提取到属性文件中统一管理,例如,将之前定义在<datasoure>元素中的相关配置,统一集中在<properties>中管理;或者以参数的形式传递给SqlSessionFactorybuild方法。

MyBatis允许在三个地方设置属性:

  1. <properties.../>元素中定一个<property.../>子元素配置,每个子元素配置一个属性;
  2. 额外定义一个配置文件,再使用<properties.../>元素加载这个文件;
  3. SqlAlchemyFactorybuild方法中传入Properties参数。

如果在多个地方设置了同名属性,优先级高的会覆盖优先级低的值,优先级排序:**3 > 2 > 1**。具体来看下面的示例:

首先,定义额外的属性文件,文件名为CustomProperty.properties,所在位置:src根目录,内容为:

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/example
user=root

然后,修改MyBatis的配置文件mybatis-config.xml文件,具体内容如下:

...
<configuration>
  <!-- 使用CustomProperty.properties文件 -->
  <properties resource="CustomProperty.properties">
    <property name="user" value="admin"/>
    <property name="password" value="admin"/>
  </properties>
  <!-- 默认指定的数据库环境为MySQL,引用其中一个environment元素的id -->
  <environments default="mysql">
    <!-- 配置名为mysql(名称可任意取)的环境 -->
    <environment id="mysql">
      <!-- 配置事务管理器,JDBC代表使用JDBC自带的事务提交和回滚 -->
      <transactionManager type="JDBC"/>
      <!-- datasource配置数据源,此处使用MyBatis内置的数据源 -->
      <datasource type="POOLED">
        <!-- 配置连接数据库的驱动、URL、用户名和密码 -->
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${user}"/>
        <property name="password" value="${password}"/>
      </datasource>
    </environment>
  </environments>
</configuration>

其中:

  • 在第4行,通过指定properties元素的resource属性,加载额外定义的配置文件;
    • 除了使用resource属性加载外部的配置文件外,还可使用url属性来加载
    • resource属性:指定从类加载路径下搜索属性文件,这种方法更实用一些
    • url属性:指定加载URL对应的属性文件,可使用HTTP协议加载网络上的属性文件,也可使用file:///<file_path>的格式加载指定磁盘路径下的文件
  • 在第5行,定义了与额外的配置文件中同名的user配置,在后续执行时,会使用额外配置文件中的root覆盖此处的admin值;
  • 在17-20行,使用${}的形式引用定义的属性值。

最后,在调用SqlSessionFactoryBuilderbuild方法时传入Properties对象,例如:

public class UserManager {
    public static void main(String[] args) throws Exception {
        String configFileSource = "mybatis-config.xml";
        var inputStream = Resources.getResourceAsStream(configFileSource);

        var props = Properties();
        prop.setProperty("password""admin123");

        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, props);

        // 创建session
        var sqlSession = sqlSessionFactory.openSession();

        // 创建新的用户
        createUser(sqlSession);
    }
    
    public static void createUser(SqlSession sqlSession) {
        ...
    }

    public static void updateUser(SqlSession sqlSession) {
        ...
    }

    public static void deleteUser(SqlSession sqlSession) {
        ...
    }
    
    public static void queryUser(SqlSession sqlSession) {
        ...
    }
}

其中:

  • 在第7行,定义了password的属性,因为它的优先级最高,它会覆盖之前定义的password属性的值;
  • 在第9行,调用build方法时,传入属性。

MyBatis还支持在引用属性值时提供默认值,具体的语法格式为:${属性名:默认值}。但是,在引用属性时指定默认值默认是关闭的,需要显式地开启,需要在<properties.../>元素中添加如下子元素:

<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>

默认情况下,属性名和默认值是通过:分隔,也可以自定义这个分隔符,需要在<properties.../>元素中添加如下子元素,此时分隔符为-

<property name="org.apache.ibatis.parsing.PropertyParser.default-value-separator" value="-"/>

2. 设置配置

MyBatis有一些全局行为需要配置,这些配置是存放在<settings.../>中,每个<setting.../>子元素配置一个设置,例如:

<settings>
  <setting name="lazyLoaingEnabled" value="true"/>
</settings>

MyBatis支持的设置项及其有效值、默认值如下:

img

img

img

img

img

img

3. 为类型配置别名

上一篇中,在介绍如何使用Mapper对象时,先定义了一个领域类,然后在Mapper中引用该类,当时使用的是类的全限定类型(即:包路径+类文件名称)。当涉及的领域类很多时,会造成代码的冗长,并且十分繁琐,给开发人员带来烦恼。

Mybatis允许在<typeAliases.../>元素内为Java类指定别名,主要有2种方式:

  • <typeAlias.../>:为单个Java类指定别名;
  • <package.../>:为指定包下面的所有Java类指定别名。

3.1 MyBatis内置的别名

MyBatis为许多Java类都内置了别名,具体如下:

img

img

3.2 使用<typeAlias.../>

使用<typeAlias.../>元素可以为单个Java类指定对应的别名,核心配置文件的配置如下:

...
<configuration>
  ...
  <typeAliases>
    <typeAlias alias="user" value="org.example.app.entity.User" />
  </typeAliases>
  ...
</configuration>

接下来,在使用该Java类的地方就可以使用对应的别名了,例如:

<select id="selectUser" resultType="user">
  select id as is_alias, nickname sd nickname_alias from user where id = #{id}
</select>

使用别名之前的调用方式如下:

<select id="selectUser" resultType="org.exmaple.app.entity.User">
  select id as is_alias, nickname sd nickname_alias from user where id = #{id}
</select>

代码是不是清爽很多?

3.3 使用<package.../>

使用<package.../>可以为指定包下面的所有Java类指定别名。在没有@Alias注解的情况下,MyBatis会自动将所有类的类名首字母小写(即将大驼峰转为小驼峰)来作为别名。使用方式如下:

...
<configuration>
  ...
  <typeAliases>
    <package name="org.example.app.entity" />
  </typeAliases>
  ...
</configuration>

之前的示例中,该包下面有User类,MyBatis会自动设置其别名为user

4. 对象工厂

前面介绍过,当MyBatis将ResultSet映射为具体的Java对象时,需要为每一行的记录创建一个对象,这个过程是由对象工程(Object Factory)负责的。

MyBatis中内置了DefaultObjectFactory来负责将每一行的记录创建一个对象,它是继承了ObjectFactory。开发者一般情况下无需特别关注,MyBatis会自动完成这个过程。

但是,一些特殊情况下,开发者希望能够自定义实现对象工厂,从而执行某些特别的行为。开发自定义对象工厂需要如下步骤:

  1. 定义一个实现ObjectFactory接口的类,该类可作为对象工厂;
  2. 在MyBatis核心配置文件中,使用<objectFactory.../>元素注册对象工厂。

4.1 自定义对象工厂

第1个步骤,可以继承MyBatis中的DefaultObjectFactory(因为该类是继承ObejctFactory并实现了相关的函数)并重写对应的方法即可,这也是推荐的做法。

需要实现ObjectFactory或者可以重写DefaultObjectFactory的方法有4个:

  1. <T> T create(Class<T> type):负责创建对象,当MyBatis通过无参数的构造器创建对象时,实际上是由工厂方法调用该函数创建的;
  2. <T> T create(Class<T> type, List<class<?>> constructorArgTypes, List<Object> constructorArgs):负责创建对象,当MyBatis通过有参数的构造器创建对象时,实际上是由工厂方法调用该函数创建的,其中,constructorArgTypes表示构造器参数列表的类型,constructorArgs表示构造器参数列表的值;
  3. <T> boolean isCollection(Class<T> type):如果创建的对象是列表,则返回True;
  4. void setProperties(Properties properties):负责将配置对象工厂时配置的多个属性以Properties整体传入。

示例:定义一个对象工厂,在处理User查询的ResultSet时,为该对象添加age和operationTime属性:

  1. 修改User类:增加meta属性
public class User {
    private Integer id;
    private String nickname;
    private String fullname;
    private Map<StringString> meta;

    public User() {}

    public User(String nickname, String fullname) {
        this.nickname = nickname;
        this.fullname = fullname;
    }

    public Integer getId() {
        return id;
    }

    public String getNickname() {
        return nickname;
    }

    public String getFullname() {
        return fullname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    public void setFullname(String fullname) {
        this.fullname = fullname;
    }

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

    public Map<StringStringgetMeta() {
        return meta;
    }
}
  1. 自定义对象工厂,文件名称:UserObjectFactory,所在包:org.exmaple.app.ObjectFactory
public class UserObejctFactory extends DefaultObjectFactory {
    private Integer age;
    
    public Object create(Class type) {
        var obj = super.create(type);
        return processObject(obj);
    }

    public Object create(Class type, List constructorArgTypes, List constructorArgs) {
        var obj = super.create(type, constructorArgs, constructorArgs);
        return processObject(obj);
    }

    public void setProperties(Properties properties) {
        super.setProperties(properties);

        this.age = properties.getProperty("age");
    }

    public <T> boolean isCollection(Class<T> type) {
        return super.isCollection(type);
    }

    private Object processObject(Object obj) {
        // 如果type是User的子类或者本身
        if (User.class.isAssignableFrom(obj.getClass()) {
            var use = (User) obj;

            user.getMeta().put("age"this.age);
            user.getMeta().put("operationTime"new Date());
        }

        return obj
    }
}

4.2 配置自定义对象工厂

自定义对象工厂代码编写完成后,在MyBatis核心配置文件中可以使用<objectFactory.../>元素配置自定义的对象工厂,示例如下:

...
<configuration>
  ...
  <typeAliases>
    <package name="org.example.app.entity" />
  </typeAliases>
  <objectFactory type="org.exmaple.app.ObjectFactory.UserObjectFactory">
    <property name="age" value="29"/>
  </objectFactory>
  ...
</configuration>

其中:

  • 在第7行,配置自定义对象工厂源码位置
  • 在第8行,使用<property.../>元素指定相关的配置

5. 加载Mapper

MyBatis支持4中加载Mapper的配置方式,用来将Mapper组件配置到核心配置文件中,具体如下:

  1. <mapper.../>指定resource属性加载文件,属性值为具体的文件路径;
  2. <mapper.../>指定url属性来加载文件, 可以使用file:///的方式加载指定磁盘目录下的文件;
  3. <mapper.../>指定class属性来加载文件,属性值为Mapper接口;
  4. 使用<package.../>属性执行加载某个包下的所有Mapper文件。

前三种大同小异,第4种在平常情况下使用的频率最高,配置示例如下:

...
<configuration>
  ...
  <typeAliases>
    <package name="org.example.app.entity" />
  </typeAliases>
  <objectFactory type="org.exmaple.app.ObjectFactory.UserObjectFactory">
    <property name="age" value="29"/>
  </objectFactory>
  <mappers>
    <package name="org.example.app.dao"/>
  </mappers>
  ...
</configuration>