MyBatis之配置

        上文介绍了MyBatis的简介以及核心组件,详情请看《MyBatis之简介及其核心组件》。本篇博客主要讲解MyBatis的相关配置。   

1 概述

        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>

        这里需要注意的是,MyBatis配置项的顺序不能颠倒。如果颠倒了他们的顺序,那么在MyBatis启动阶段就会发生异常,导致程序无法运行。

2 properties属性

         properties属性可以给系统配置一些运行参数,可以放在XML文件或者properties文件中,而不是放在Java编码中,这样的好处在于方便参数修改,而不会引起代码的重新编译。一般而言,MyBatis提供了3种方式让我们使用properties:

  • property子元素
  • properties文件
  • 程序代码传递

2.1 property子元素

<?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>
		<property name="database.driver"
			value="com.mysql.cj.jdbc.Driver" />
		<property name="database.url"
			value="jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC&useSSL=false" />
		<property name="database.username" value="root" />
		<property name="database.password" value="root" />
	</properties>
	<!-- 别名 -->
	<typeAliases>
		<typeAlias alias="role"
			type="com.hys.mybatis.example1.pojo.Role" />
	</typeAliases>
	<!--数据库环境 -->
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${database.driver}" />
				<property name="url" value="${database.url}" />
				<property name="username" value="${database.username}" />
				<property name="password" value="${database.password}" />
			</dataSource>
		</environment>
	</environments>
	<!-- 映射文件 -->
	<mappers>
		<mapper
			resource="com/hys/mybatis/example1/mapper/RoleMapper.xml" />
	</mappers>
</configuration>

        这里使用了元素<properties>下的子元素<property>定义,用字符串database.username定义数据库用户名,然后就可以在数据库定义中引入这个已经定义好的属性参数,如${database.username},这样定义一次就可以到处引用了。但是如果属性参数有成百上千个,显然使用这样的方式就不是一个很好的选择,这个时候可以使用properties文件。

2.2 使用properties文件

        使用properties文件是比较普遍的方法,一方面这个文件十分简单,其逻辑就是键值对应,我们可以配置多个键值放在一个properties文件中,也可以把多个键值放到多个properties文件中,这些都是允许的,它方便日后维护和修改。

properties文件如下:

database.driver=com.mysql.cj.jdbc.Driver
database.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC&useSSL=false
database.username=root
database.password=root

在MyBatis配置文件中通过<properties>的属性resource来引用properties文件:

<properties
		resource="com/hys/mybatis/example2/config/jdbc.properties" />

        这种方式也可以按${database.username}的方法引入properties文件的属性参数到MyBatis配置文件中。这个时候通过维护properties文件就可以维护我们的配置内容了。建议使用该方式来实现properties属性。

2.3 使用程序传递方式传递参数

        在真实的生产环境中,数据库的用户密码是对开发人员和其他人员保密的。运维人员为了保密,一般都需要把用户和密码经过加密成为密文后,配置到properties文件中。对于开发人员及其他人员而言,就不知道其真实的用户密码了,数据库也不可能使用已经加密的字符串来连接,此时往往需要通过解密才能得到真实的用户名和密码。

        String resource = "com/hys/mybatis/example2/config/mybatis-config.xml";
        InputStream inputStream;
        InputStream in = Resources.getResourceAsStream("com/hys/mybatis/example2/config/jdbc.properties");
        Properties props = new Properties();
        props.load(in);
        String username = props.getProperty("database.username");
        String password = props.getProperty("database.password");
        //解密用户和密码,并在属性中重置
        props.put("database.username", CodeUtils.decode(username));
        props.put("database.password", CodeUtils.decode(password));
        inputStream = Resources.getResourceAsStream(resource);
        //使用程序传递的方式覆盖原有的properties属性参数
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, props);
        首先使用Resources对象读取了一个jdbc.properties配置文件,然后获取了它原来配置的用户名和密码,用给定的解密规则进行解密并重置,最后使用SqlSessionFactoryBuilder的build方法,传递多个properties参数来完成,这将覆盖之前配置的密文,这样就能连接数据库了,同时也满足了运维人员对数据库用户和密码的要求。

3 settings设置

        settings是MyBatis中最复杂的配置,它能深刻影响MyBatis底层的运行,但是在大部分情况下使用默认值便可以运行,所以在大部分情况下不需要大量配置它,只需要修改一些常用的规则即可,比如自动映射、驼峰命名映射、级联规则、是否启动缓存、执行器(Executor)类型等。一些常用的setting配置项说明如下:

配置项 作用 配置选项说明 默认值
cacheEnabled         该配置影响所有映射器中配置缓存的全局开关 true|false true
lazyLoadingEnabled         延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。在特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态 true|false false
aggressiveLazyLoading         当启用时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载;反之,每种属性将会按需加载 true|false 版本3.4.1(不包含)之前true,之后false
autoMappingBehavior         指定MyBatis应如何自动映射列到字段或属性。NONE表示取消自动映射;PARTIAL表示只会自动映射,没有定义嵌套结果集和映射结果集;FULL会自动映射任意复杂的结果集(无论是否嵌套) NONE、PARTIAL、FULL PARTIAL
defaultExecutorType         配置默认的执行器。SIMPLE是普通的执行器;REUSE会重用预处理语句(perpared statements);BATCH执行器将重用语句并执行批量更新 SIMPLE、REUSE、BATCH SIMPLE
mapUnderscoreToCamelCase         是否开启自动驼峰命名规则映射,即从经典数据库列名A_COLUMN到经典Java属性名aColumn的类似映射 true|false false
配置样例:
        <settings>
		<setting name="cacheEnabled" value="true" />
		<setting name="autoMappingBehavior" value="PARTIAL" />
		<setting name="defaultExecutorType" value="SIMPLE" />
	</settings>

4 typeAliases别名

        由于类的全限定名称很长,需要大量使用的时候,总写那么长的名称不方便。在MyBatis中允许定义一个简写来代表这个类,这就是别名,别名分为系统别名和自定义别名。在MyBatis中别名由类TypeAliasRegistry(org.apache.ibatis.type.TypeAliasRegistry)来定义。注意,在MyBatis中别名不区分大小写。

4.1 系统定义别名

部分系统定义别名如下(完整系统定义别名请查看相关文档):

别名 Java类型 是否支持数组
_int int
_double double
_boolean boolean
string String
int Integer
double Double
boolean Boolean
date Date
bigdecimal BigDecimal
object Object
hashmap HashMap
list List
iterator Iterator
ResultSet ResultSet

4.2 自定义别名

        由于在实际开发中存在很多对象,比如User这个对象有时候需要大量重复地使用,因此MyBatis也提供了用户自定义别名的规则。我们可以通过TypeAliasRegistry类的registerAlias方法注册,也可以采用配置文件或者扫描方式来自定义它。如下:

        <typeAliases>
		<typeAlias alias="role"
			type="com.hys.mybatis.example1.pojo.Role" />
		<typeAlias alias="user"
			type="com.hys.mybatis.example2.pojo.User" />
	</typeAliases>

这样就可以定义一个别名了。如果有很多类需要定义别名,那么用这样的方式进行配置就不那么轻松了。MyBatis还支持扫描别名。如下:

        <typeAliases>
		<package name="com.hys.mybatis.example2" />
	</typeAliases>

这样MyBatis将扫描这个包下面的所有类,将其第一个字母变为小写作为其别名。但是这样的规则,有时候会出现重名,导致出现异常,这个时候可以使用MyBatis提供的注解@Ailas进行区分,如下:

import org.apache.ibatis.type.Alias;

@Alias("user1")
public class User {
    //do something...
}

这样就能够避免因为别名重名导致的扫描失败的问题。

5 typeHandler类型转换器

        在JDBC中,需要在PreparedStatement对象中设置那些已经预编译过的SQL语句的参数。执行SQL后,会通过ResultSet对象获取得到数据库的数据,而这些MyBatis是根据数据的类型通过typeHandler来实现的。在typeHandler中,分为jdbcType和javaType,其中jdbcType用于定义数据库类型,而javaType用于定义Java类型,那么typeHandler的作用就是根据jdbcType和javaType之间的相互转换。在很多情况下我们并不需要去配置typeHandler、jdbcType、javaType,因为MyBatis会探测应该使用什么类型的typeHandler进行处理,但是有些场景无法探测到。对于那些需要使用自定义枚举的场景,或者数据库使用特殊数据类型的场景,可以使用自定义的typeHandler来处理类型之间的转换问题。

5.1 系统定义的typeHandler

      MyBatis内部定义了许多有用的typeHandler,部分如下(完整typeHandler请查看相关文档):

类型处理器 Java类型 JDBC类型
IntegerTypeHandler java.lang.Integer,int 数据库兼容的NUMERIC或INTEGER
DoubleTypeHandler java.lang.Double,double 数据库兼容的NUMERIC或DOUBLE
BigDecimalTypeHandler java.math.BigDecimal 数据库兼容的NUMERIC或DECIMAL
StringTypeHandler java.lang.String CHAR、VARCHAR
ClobTypeHandler java.lang.String CLOB、LONGVARCHAR
DateTypeHandler java.util.Date TIMESTAMP
EnumTypeHandler Enumeration Type VARCHAR任何兼容的字符串类型,存储枚举的名称(而不是索引)

5.2 自定义typeHandler

        在MyBatis中typeHandler都要实现接口org.apache.ibatis.type.TypeHandler,或者继承org.apache.ibatis.type.BaseTypeHandler(实际上,BaseTypeHandler实现了typeHandler接口。自定义typeHandler类继承BaseTypeHandler类需要实现BaseTypeHandler类的4个抽象方法。这里使用了模板方法模式,如若对模板方法模式不熟悉,可以查看笔者之前的博客《设计模式之模板方法模式》)。这里笔者实现了一个类似StringTypeHandler功能的typeHandler,代码如下:

package com.hys.mybatis.example2.typehandler;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;

public class MyTypeHandler implements TypeHandler<String> {

    @Override
    public void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        System.out.println("设置string参数【" + parameter + "】");
        ps.setString(i, parameter);
    }

    @Override
    public String getResult(ResultSet rs, String columnName) throws SQLException {
        String result = rs.getString(columnName);
        System.out.println("读取string参数1【" + result + "】");
        return result;
    }

    @Override
    public String getResult(ResultSet rs, int columnIndex) throws SQLException {
        String result = rs.getString(columnIndex);
        System.out.println("读取string参数2【" + result + "】");
        return result;
    }

    @Override
    public String getResult(CallableStatement cs, int columnIndex) throws SQLException {
        String result = cs.getString(columnIndex);
        System.out.println("读取string参数3【" + result + "】");
        return result;
    }
}
  • setParameter方法,是使用typeHandler通过PreparedStatement对象进行设置SQL参数的时候使用的具体方法,其中i是参数在SQL的下标,parameter是参数,jdbcType是数据库类型。
  • 其中有3个getResult的方法,它的作用是从JDBC结果集中获取数据进行转换,要么使用列名(columnName)要么使用下标(columnIndex)获取数据库的数据,其中最后一个getResult方法是存储过程专用的。

        mybatis-config.xml中需要配置typeHandler,如下:

        <typeHandlers>
		<typeHandler jdbcType="VARCHAR" javaType="string"
			handler="com.hys.mybatis.example2.typehandler.MyTypeHandler" />
	</typeHandlers>

        现在UserMapper.xml中就可以显示启用自定义的typeHandler了,如下:

<?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">
<mapper namespace="com.hys.mybatis.example2.mapper.UserMapper">
	<resultMap type="user" id="userMapper">
		<result property="id" column="id" />
		<result property="userName" column="user_name"
			jdbcType="VARCHAR" javaType="string" />
		<result property="password" column="password"
			typeHandler="com.hys.mybatis.example2.typehandler.MyTypeHandler" />
		<result property="sex" column="sex" />
		<result property="mobile" column="mobile" />
		<result property="tel" column="tel" />
		<result property="email" column="email" />
		<result property="note" column="note" />
	</resultMap>
	<select id="getUser" parameterType="string"
		resultMap="userMapper">
		select
		id,user_name,password,sex,mobile,tel,email,note from
		t_user where
		user_name=#{user_name,jdbcType=VARCHAR,javaType=string}
	</select>
	<select id="getUser1" parameterType="string"
		resultMap="userMapper">
		select
		id,user_name,password,sex,mobile,tel,email,note from
		t_user where
		user_name=#{user_name,typeHandler=com.hys.mybatis.example2.typehandler.MyTypeHandler}
	</select>
</mapper>

要么指定了与自定义typeHandler一致的jdbcType和javaType,要么直接使用typeHandler指定具体的实现类。运行结果如下:

log4j:WARN No appenders could be found for logger (org.apache.ibatis.logging.LogFactory).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
设置string参数【Robert Hou】
读取string参数1【Robert Hou】
读取string参数1【1234】
User [id=1, userName=Robert Hou, password=1234, sex=MALE, mobile=185, tel=833, email=hys@qq, note=Programmer]

5.3 枚举typeHandler

        MyBatis已经定义了两个类作为枚举类型的支持,分别是EnumOrdinalTypeHandler和EnumTypeHandler。

  • EnumOrdinalTypeHandler:是按MyBatis根据枚举数组下标索引的方式进行匹配的,也是枚举类型的默认转换类,它要求数据库返回一个整数作为其下标,它会根据下标找到对应的枚举类型。
  • EnumTypeHandler:会把使用的名称转化为对应的枚举,比如它会根据数据库返回的字符串"MALE"进行Enum.valueOf(SexEnum.class, "MALE");转换。

        但是这两个typeHandler的作用不大,所以在大部分情况下,我们都不用它们,这里也不做过多介绍。下面来自定义实现一个枚举typeHandler。

        首先定义一个枚举类SexEnum,如下:

package com.hys.mybatis.example2.pojo;

public enum SexEnum {
    MALE(1, "男"), FEMALE(0, "女");

    private int    id;
    private String name;

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    private SexEnum(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public static SexEnum getSexById(int id) {
        for (SexEnum sex : SexEnum.values()) {
            if (sex.getId() == id) {
                return sex;
            }
        }
        return null;
    }
}

POJO:

package com.hys.mybatis.example2.pojo;

public class User {

    private Long    id;
    private String  userName;
    private String  password;
    private SexEnum sex;
    private String  mobile;
    private String  tel;
    private String  email;
    private String  note;

    public Long getId() {
        return id;
    }

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

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public SexEnum getSex() {
        return sex;
    }

    public void setSex(SexEnum sex) {
        this.sex = sex;
    }

    public String getMobile() {
        return mobile;
    }

    public void setMobile(String mobile) {
        this.mobile = mobile;
    }

    public String getTel() {
        return tel;
    }

    public void setTel(String tel) {
        this.tel = tel;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getNote() {
        return note;
    }

    public void setNote(String note) {
        this.note = note;
    }

    @Override
    public String toString() {
        return "User [id=" + id + ", userName=" + userName + ", password=" + password + ", sex=" + sex + ", mobile=" + mobile + ", tel=" + tel + ", email=" + email + ", note=" + note + "]";
    }
}

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">
<mapper namespace="com.hys.mybatis.example2.mapper.UserMapper">
	<resultMap type="user" id="userMapper">
		<result property="id" column="id" />
		<result property="userName" column="user_name" />
		<result property="password" column="password" />
		<result property="sex" column="sex"
			typeHandler="sexEnumTypeHandler" />
		<result property="mobile" column="mobile" />
		<result property="tel" column="tel" />
		<result property="email" column="email" />
		<result property="note" column="note" />
	</resultMap>
	<select id="getUser" parameterType="string"
		resultMap="userMapper">
		select
		id,user_name,password,sex,mobile,tel,email,note from
		t_user where
		user_name=#{user_name}
	</select>

</mapper>

6 environments(运行环境)

        在MyBatis中,运行环境主要的作用是配置数据库信息,它可以配置多个数据库,一般而言之需要配置其中的一个就可以了。它下面又分为两个可配置的元素:事务管理器(transactionManager)和数据源(dataSource)。在实际的工作中,大部分情况下会采用Spring对数据源和数据库的事务进行管理。这里我们先讨论MyBatis自身实现的类。

6.1 transactionManager(事务管理器)

        在MyBatis中,transactionManager提供了两个实现类,它需要实现接口org.apache.ibatis.transaction.Transaction。MyBatis为Transaction提供了两个实现类:JdbcTransaction和ManagedTransaction。于是它对应着两种工厂:JdbcTransactionFactory和ManagedTransactionFactory,这个工厂需要实现TransactionFactory接口,通过它们会生成对应的Transaction对象。于是事务管理器配置成为以下两种方式:

        <transactionManager type="JDBC" />
	<transactionManager type="MANAGED" />
  • JDBC使用JdbcTransactionFactory生成的JdbcTransaction对象实现。它是以JDBC的方式对数据库的提交和回滚进行操作。
  • MANAGED使用ManagedTransactionFactory生成的ManagedTransaction对象实现。它的提交和回滚方法不用任何操作,而是把事务交给容器处理。在默认情况下,它会关闭连接,然而一些容器并不希望这样,因此需要将closeConnection属性设置为false来阻止它默认的关闭行为。
        事务管理器也可以自定义实现,需要实现自定义事务工厂和自定义事务类。这里不做过多介绍。

6.2 environment数据源环境

        environment的主要作用是配置数据库,在MyBatis中,数据库通过PooledDataSourceFactory、UnpooledDataSourceFactory和JndiDataSourceFactory三个工厂类来提供,前两者对应产生PooledDataSource、UnpooledDataSpurce类对象,而JndiDataSourceFactory则会根据JNDI的信息拿到外部容器实现的数据库连接对象。无论如何这三个工厂类,最后生成的产品都会是一个实现了DataSource接口的数据库连接对象。

        <dataSource type="UNPOOLED">
	<dataSource type="POOLED">
	<dataSource type="JNDI">
  • UNOPOOLED:采用非数据库池的管理方式,每次请求都会打开一个新的数据库连接,所以创建会比较慢。在一些对性能没有很高要求的场合可以使用它。对有些数据库而言,使用连接池并不重要,那么它也是一个比较理想的选择。
  • POOLED:数据源POOLED利用“池”的概念将JDBC的Connection对象组织起来,它开始会有一些空置,并且已经连接好的数据库连接,所以请求时,无需再建立和验证,省去了创建新的连接实例时所必需的初始化和认证时间。它还控制最大连接数,避免过多的连接导致系统瓶颈。
  • JNDI:数据源JNDI的实现是为了能在如EJB或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个个JNDI上下文的引用。

        MyBatis也支持第三方数据源,例如使用DBCP数据源,那么需要提供一个自定义的DataSourceFactory。

7 引入映射器的方法

        映射器定义命名空间(namespace)的方法,命名空间对应的是一个接口的全路径,而不是实现类。引入映射器的方法有很多,一般分为以下几种:
  • 用文件路径引入映射器
        <mappers>
		<mapper
			resource="com/hys/mybatis/example2/mapper/UserMapper.xml" />
	</mappers>
  • 用包名引入映射器
        <mappers>
		<package name="com.hys.mybatis.example2.mapper" />
	</mappers>
  • 用类注册引入映射器
        <mappers>
		<mapper class="com.hys.mybatis.example2.mapper.UserMapper" />
	</mappers>
  • 用userMapper.xml引入映射器
<mappers>
		<mapper url="file:///E:/EclipseWorkspace/SSM/mybatis/src/com/hys/mybatis/example2/mapper/UserMapper.xml" />
	</mappers>

参考资料:[1]杨开振 周吉文 梁华辉 谭茂华.Java EE 互联网轻量级框架整合开发:SSM框架(Spring MVC+Spring+MyBatis)和Redis实现[M].北京:电子工业出版社,2017.7

猜你喜欢

转载自blog.csdn.net/weixin_30342639/article/details/81046525