百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

【MyBatis系列教程二】MyBatis全局配置文件详解

wxin55 2024-11-24 22:36 10 浏览 0 评论

在上篇文章中粗浅地讨论了MyBatis的组成和它们大致的用法,这里的任务是详细讨论MyBatis的配置。
先来看一下MyBatis配置XML文件的层次结构。注意,这些层次是不能够颠倒顺序的,如果颠倒顺序,MyBatis在解析XML文件的时候就会出现异常。先来了解一下MyBatis配置XML文件的层次结构。

<?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></properties>
  <!--全局设置-->
  <settings></settings>
  <!--类型别名-->
  <typeAliases></typeAliases>
  <!--类型处理器-->
  <typeHandlers></typeHandlers>
  <!--对象工厂-->
  <objectFactory></objectFactory>
  <!--插件-->
  <plugins></plugins>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
      </dataSource>
    </environment>
  </environments>
  <databaseIdProvider></databaseIdProvider>
  <mappers></mappers>
</configuration>

需要注意的是在上面的配置文件中所有属性的层次顺序是不能颠倒的,也就是说在configuration标签中第一个必须是properties标签,其他的标签依次向下不能调换顺序。这是因为当使用Mybatis时,Mybatis会先读取该配置文件并按照顺序依次解析将配置值封装进Configuration对象,如果顺序不对,则会导致Mybatis解析配置文件异常。
下面将接每个属性进行解释。

2.1 Properties属性

Properties属性用于配置连接数据库的信息,在JDBC的学习中可以知道在开发过程中为了方便程序维护及扩展,通常将数据库连接信息单独写在配置文件中,Properties属性为开发者提供了2种方式,用于配置数据库连接信息。

2.1.1 Properties子元素

通过Properties子元素可以配置数据库连接信息,由于项目打包时会将配置文件拷贝到classes目录下,当需要更换数据库或者项目维护时就需要重写修改该文件,并重写打包,因此并不推荐使用这种方式。

<properties>
    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai"/>
    <property name="username" value="root"/>
    <property name="password" value="root"/>
</properties>

在上面的代码中已经配置好了数据库连接信息,这些信息一般会在datasource标签中使用,使用方式如下:

<dataSource type="POOLED">
  <property name="driver" value="${driver}"/>
  <property name="url" value="${url}"/>
  <property name="username" value="${username}"/>
  <property name="password" value="${password}"/>
</dataSource>

2.2.2 Properties配置文件

更多的时候会使用Properties配置文件来配置属性,以方便程序的扩展和维护,配置文件中内容如下:

url = jdbc:mysql://localhost:3306/mybatis_demo?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
driver = com.mysql.cj.jdbc.Driver
username = root
password = root

配置文件编写完毕后,将该文件放置在resource目录下(Maven项目中resource目录专门用于存放配置文件)。并在properties标签中进行配置:

<properties resource="data.properties"></properties>

当Mybatis运行时会自动寻找该文件,并解析文件中配置的数据。使用时同上一小节的使用方式一致。
在上述的两种配置方式中使用resource/url属性中指定的配置文件的优先级要高于properties元素中指定的属性。
从Mybatis 3.4.2开始,可以为占位符指定一个默认值。

<dataSource type="POOLED">
  <!-- 如果属性 'username' 没有被配置,'username' 属性的值将为 'ut_user' -->
  <property name="username" value="${username:ut_user}"/> 
</dataSource>

这个特性默认是关闭的。要启用这个特性,需要添加一个特定的属性来开启这个特性。例如:

<properties resource="org/mybatis/example/config.properties">
  <!-- 启用默认值特性 -->
  <property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/> 
</properties>

2.2 Settings设置属性

Settings是Mybatis中最重要的配置内容之一,它会改变Mybatis运行时的行为,即使不配置settings,Mybatis也可以正常的工作,但是了解Settings的配置内容以及作用是十分必要的。

设置名

描述

有效值

默认值

cacheEnabled

全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。

true | false

true

lazyLoadingEnabled

延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。

true | false

false

aggressiveLazyLoading

开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods)。

true | false

false (在 3.4.1 及之前的版本中默认为 true)

multipleResultSetsEnabled

是否允许单个语句返回多结果集(需要数据库驱动支持)。

true | false

true

useColumnLabel

使用列标签代替列名。实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察。

true | false

true

useGeneratedKeys

允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。

true | false

False

autoMappingBehavior

指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。

NONE, PARTIAL, FULL

PARTIAL

autoMappingUnknownColumnBehavior

指定发现自动映射目标未知列(或未知属性类型)的行为。

  • NONE: 不做任何反应
  • WARNING: 输出警告日志('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARN
  • FAILING: 映射失败 (抛出 SqlSessionException)

NONE, WARNING, FAILING

NONE

defaultExecutorType

配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(PreparedStatement); BATCH 执行器不仅重用语句还会执行批量更新。

SIMPLE REUSE BATCH

SIMPLE

defaultStatementTimeout

设置超时时间,它决定数据库驱动等待数据库响应的秒数。

任意正整数

未设置 (null)

defaultFetchSize

为驱动的结果集获取数量(fetchSize)设置一个建议值。此参数只可以在查询设置中被覆盖。

任意正整数

未设置 (null)

defaultResultSetType

指定语句默认的滚动策略。(新增于 3.5.2)

FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(等同于未设置)

未设置 (null)

safeRowBoundsEnabled

是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。

true | false

False

safeResultHandlerEnabled

是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为 false。

true | false

True

mapUnderscoreToCamelCase

是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。

true | false

False

localCacheScope

MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。

SESSION | STATEMENT

SESSION

jdbcTypeForNull

当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型。 某些数据库驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。

JdbcType 常量,常用值:NULL、VARCHAR 或 OTHER。

OTHER

lazyLoadTriggerMethods

指定对象的哪些方法触发一次延迟加载。

用逗号分隔的方法列表。

equals,clone,hashCode,toString

defaultScriptingLanguage

指定动态 SQL 生成使用的默认脚本语言。

一个类型别名或全限定类名。

org.apache.ibatis.scripting.xmltags.XMLLanguageDriver

defaultEnumTypeHandler

指定 Enum 使用的默认 TypeHandler 。(新增于 3.4.5)

一个类型别名或全限定类名。

org.apache.ibatis.type.EnumTypeHandler

callSettersOnNulls

指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这在依赖于 Map.keySet() 或 null 值进行初始化时比较有用。注意基本类型(int、boolean 等)是不能设置成 null 的。

true | false

false

returnInstanceForEmptyRow

当返回行的所有列都是空时,MyBatis默认返回 null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集(如集合或关联)。(新增于 3.4.2)

true | false

false

logPrefix

指定 MyBatis 增加到日志名称的前缀。

任何字符串

未设置

logImpl

指定 MyBatis 所用日志的具体实现,未指定时将自动查找。

SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING

未设置

proxyFactory

指定 Mybatis 创建可延迟加载对象所用到的代理工具。

CGLIB | JAVASSIST

JAVASSIST (MyBatis 3.3 以上)

vfsImpl

指定 VFS 的实现

自定义 VFS 的实现的类全限定名,以逗号分隔。

未设置

useActualParamName

允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1)

true | false

true

configurationFactory

指定一个提供 Configuration 实例的类。 这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。 这个类必须包含一个签名为static Configuration getConfiguration() 的方法。(新增于 3.2.3)

一个类型别名或完全限定类名。

未设置

shrinkWhitespacesInSql

从SQL中删除多余的空格字符。请注意,这也会影响SQL中的文字字符串。 (新增于 3.5.5)

true | false

false

defaultSqlProviderType

Specifies an sql provider class that holds provider method (Since 3.5.6). This class apply to the type(or value) attribute on sql provider annotation(e.g. @SelectProvider), when these attribute was omitted.

A type alias or fully qualified class name

Not set

一个完整的settings元素的示例如下:

<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

关于Settings的常用配置,会在后续的内容中讲解,此处做了解即可。

2.3 typeAliases别名属性

别名是一个指代的名称,因为在开发中在很多地方需要配置类的全限定名,例如在使用Mybatis做查询时需要告诉Mybatis将查询结果映射成哪个实体类对象。但是通常类的全限定名比较长,需要有一个简短的名称来替代这全限定名。别名在Mybatis中分为系统定义别名和自定义别名两类。需要注意的是Mybatis中别名是不区分大小写的。一个typeAliases的实例是在解析配置文件时生成的,然后长期保存在Configuration对象中。

2.3.1 系统定义别名

Mybatis系统定义了一些常用的类型的别名,例如:数字、字符串、日期和集合等,可以在Mybatis中直接使用。
首先来看一下Mybatis已经定义好的别名(数组类型的只要加“[]”即可),如下表所示:

别名

映射的类型

_byte

byte

_long

long

_short

short

_int

int

_integer

int

_double

double

_float

float

_boolean

boolean

string

String

byte

Byte

long

Long

short

Short

int

Integer

integer

Integer

double

Double

float

Float

boolean

Boolean

date

Date

decimal

BigDecimal

bigdecimal

BigDecimal

object

Object

map

Map

hashmap

HashMap

list

List

arraylist

ArrayList

collection

Collection

iterator

Iterator

这些配置信息都在Mybatis源码org.apache.ibatis.type.TypeAliasRegistry中。

public class TypeAliasRegistry {
public TypeAliasRegistry() {
    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);
  }

}

2.3.2 自定义别名

系统定义的别名通常是不够用的,因为不同的应用有不同的需要,Mybatis也允许自定义别名,配置方式如下,例如第一章中的查询,就可以使用别名来替代全限定名。配置方式如下:

<typeAliases>
  <typeAlias alias="student" type="cn.bytecollege.entity.Student"/>
</typeAliases>

为了方便开发,通常会为所有的实体类定义别名,如果实体类较少的情况下使用上述方式配置别名不会有任何问题,但是当实体类较多时上述的配置方式就显得很烦琐,因此Mybatis又提供了一种配置方式,可以直接指定一个包名,Mybatis会为配置包下所有类自动定义别名,别名为类名首字母小写。配置方式如下:

<typeAliases>
  <package name="cn.bytecollege.entity"/>
</typeAliases>

如果通过上述方式配置了别名,会为cn.bytecollege.entity包下的所有的类定义别名,例如cn.bytecollege.entity.Student类的别名就是student。
除此以外,Mybatis还提供了注解的方式配置别名,配置方式如下:

@Alias("student")
public class Student {
    ...
}

虽然Mybatis提供了以上3种方式配置类的别名,但是切记不要混用,否则很容易导致映射文件解析异常。

2.4 typeHandlers属性

在前面的章节中知道Mybatis可以将查询出的数据自动映射进配置文件中绑定的实体类对象,在映射的过程中就存在一个问题,如何将数据库的数据类型和Java类型对应起来,并将数据库的数据类型转换成Java的数据类型,而typeHandlers的作用就是将从数据库取出的数据以合适的方式转换成Java类型。Mybatis自带了一些默认的类型处理器,如下表所示:

类型处理器

Java 类型

JDBC 类型

BooleanTypeHandler

java.lang.Boolean, boolean

数据库兼容的 BOOLEAN

ByteTypeHandler

java.lang.Byte, byte

数据库兼容的 NUMERIC 或 BYTE

ShortTypeHandler

java.lang.Short, short

数据库兼容的 NUMERIC 或 SMALLINT

IntegerTypeHandler

java.lang.Integer, int

数据库兼容的 NUMERIC 或 INTEGER

LongTypeHandler

java.lang.Long, long

数据库兼容的 NUMERIC 或 BIGINT

FloatTypeHandler

java.lang.Float, float

数据库兼容的 NUMERIC 或 FLOAT

DoubleTypeHandler

java.lang.Double, double

数据库兼容的 NUMERIC 或 DOUBLE

BigDecimalTypeHandler

java.math.BigDecimal

数据库兼容的 NUMERIC 或 DECIMAL

StringTypeHandler

java.lang.String

CHAR, VARCHAR

ClobReaderTypeHandler

java.io.Reader

-

ClobTypeHandler

java.lang.String

CLOB, LONGVARCHAR

NStringTypeHandler

java.lang.String

NVARCHAR, NCHAR

NClobTypeHandler

java.lang.String

NCLOB

BlobInputStreamTypeHandler

java.io.InputStream

-

ByteArrayTypeHandler

byte[]

数据库兼容的字节流类型

BlobTypeHandler

byte[]

BLOB, LONGVARBINARY

DateTypeHandler

java.util.Date

TIMESTAMP

DateOnlyTypeHandler

java.util.Date

DATE

TimeOnlyTypeHandler

java.util.Date

TIME

SqlTimestampTypeHandler

java.sql.Timestamp

TIMESTAMP

SqlDateTypeHandler

java.sql.Date

DATE

SqlTimeTypeHandler

java.sql.Time

TIME

ObjectTypeHandler

Any

OTHER 或未指定类型

EnumTypeHandler

Enumeration Type

VARCHAR 或任何兼容的字符串类型,用来存储枚举的名称(而不是索引序数值)

EnumOrdinalTypeHandler

Enumeration Type

任何兼容的 NUMERIC 或 DOUBLE 类型,用来存储枚举的序数值(而不是名称)。

SqlxmlTypeHandler

java.lang.String

SQLXML

InstantTypeHandler

java.time.Instant

TIMESTAMP

LocalDateTimeTypeHandler

java.time.LocalDateTime

TIMESTAMP

LocalDateTypeHandler

java.time.LocalDate

DATE

LocalTimeTypeHandler

java.time.LocalTime

TIME

OffsetDateTimeTypeHandler

java.time.OffsetDateTime

TIMESTAMP

OffsetTimeTypeHandler

java.time.OffsetTime

TIME

ZonedDateTimeTypeHandler

java.time.ZonedDateTime

TIMESTAMP

YearTypeHandler

java.time.Year

INTEGER

MonthTypeHandler

java.time.Month

INTEGER

YearMonthTypeHandler

java.time.YearMonth

VARCHAR 或 LONGVARCHAR

JapaneseDateTypeHandler

java.time.chrono.JapaneseDate

DATE

.4.1 自定义typeHandlers

通常而言,Mybatis系统提供使用的TypeHandler已经能给应对绝大部分使用场景,但是并不能保证不会出现不够用的情况,如果出现这种情况,开发者就可以自定义typeHandler进行转换,自定义typeHandler通常需要继承TypeHandler接口,同时Mybatis也提供了BaseTypeHandler继承了TypeHandler接口,也就是说,要实现自定义typeHandler,只需要集成BaseTypeHandler抽象类即可。下面通过示例来学习如何自定义typeHandlers。

package cn.bytecollege.typeHandler;

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.log4j.Logger;

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

/**
 * @author MR.W
 */
@MappedJdbcTypes(JdbcType.VARCHAR)
public class MyStringHandler extends BaseTypeHandler<String> {
    Logger logger = Logger.getLogger(MyStringHandler.class);
    //1
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        logger.info("setNonNullParameter 为占位符赋值");
        ps.setString(i,parameter);
    }
    //2
    @Override
    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
        logger.info("setNonNullParameter 根据列名获取值");
        return rs.getString(columnName);
    }
    //3
    @Override
    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        logger.info("getNullableResult 根据列索引获取值");
        return rs.getString(columnIndex);
    }
    //4.
    @Override
    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        logger.info("getNullableResult");
        return cs.getString(columnIndex);
    }
}

在上例中自定义了String类型的转换器,为了简单起见,方法内只是进行了简单标记,并调用了父类的同名方法。代码注释处方法含义如下:

  1. 根据占位符的索引,为占位符赋值
  2. 根据数据库字段名称从结果集中获取值
  3. 根据字段索引从结果集中获取数据
  4. 根据字段索引从存储过程结果中获取值

在定义完typeHandler后该typeHandler并不能正常工作,还需要进行配置,配置有两步:

  1. 在自定义typeHandler类上添加注解@MappedJdbcTypes,注解内的值是该typeHandler可以转换的数据库数据类型
  2. 在全局配置文件中进行配置,告诉Mybatis自定义的typeHandler可以将数据库的数据类型转换成Java的何种类型。
<typeHandlers>
    <typeHandler handler="cn.bytecollege.typeHandler.MyStringHandler" jdbcType="VARCHAR"></typeHandler>
</typeHandlers>

2.5 ObjectFactory对象工厂属性

在上篇文章的示例中可以看出,当要做查询操作时,只需要在标签上配置resultType即可,Mybatis会自动帮开发者创建对象,并将查询出的数据映射到对象中,每次 MyBatis 创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成实例化工作。

默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认无参构造方法,要么通过存在的参数映射来调用带有参数的构造方法。 如果想覆盖对象工厂的默认行为,可以通过创建自己的对象工厂来实现。

通常,如果要创建自己的对象工厂,需要继承ObjectFactory接口,ObjectFactory 接口很简单,它包含两个创建实例化的方法,一个是处理默认无参构造方法的,另外一个是处理带参数的构造方法的,Mybatis为该接口提供了一个实现类DefaultObjectFactory,也就是说创建对象工厂时也可以继承DefaultObjectFactory类。

package cn.bytecollege.factory;

import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.log4j.Logger;

import java.util.List;

public class ExampleObjectFactory extends DefaultObjectFactory {
    private Logger logger = Logger.getLogger(ExampleObjectFactory.class);

    public ExampleObjectFactory() {
        super();
    }

    @Override
    public <T> T create(Class<T> type) {
        logger.info("public <T> T create(Class<T> type)");
        return super.create(type);
    }

    @Override
    public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        logger.info("public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs)");
        return super.create(type, constructorArgTypes, constructorArgs);
    }

    @Override
    protected Class<?> resolveInterface(Class<?> type) {
        logger.info("protected Class<?> resolveInterface(Class<?> type)");
        return super.resolveInterface(type);
    }

    @Override
    public <T> boolean isCollection(Class<T> type) {
        logger.info("public <T> boolean isCollection(Class<T> type) {");
        return super.isCollection(type);
    }
}

当创建完对象工厂后,还需要在Mybatis配置文件中进行配置:

<!-- mybatis-config.xml --><objectFactory type="org.mybatis.example.ExampleObjectFactory"> <property name="someProperty" value="100"/></objectFactory> 

2.6 plugins插件属性

MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。

2.7 environment环境属性

配置环境可以注册多个数据源,例如,日常开发中通常会有开发库、测试库、生产库、并且需要在这几个库中切换,此时配置多个数据源就非常有必要。每个数据源大概分为:数据库连接信息的配置和数据库事务的配置。 一份完整的配置示例如下:

<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="${jdbc.driverClassName}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </dataSource>
    </environment>
    <environment id="production">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="${prod.driverClassName}"/>
            <property name="url" value="${prod.url}"/>
            <property name="username" value="${prod.username}"/>
            <property name="password" value="${prod.password}"/>
        </dataSource>
    </environment>
</environments>

注意一些关键点:

  • 默认使用的环境 ID(比如:default="development")。
  • 每个 environment 元素定义的环境 ID(比如:id="development")。
  • 事务管理器的配置(比如:type="JDBC")。
  • 数据源的配置(比如:type="POOLED")。

默认环境和环境 ID 顾名思义。 环境可以随意命名,但务必保证默认的环境 ID 要匹配其中一个环境 ID。

2.7.1 事务配置

在 MyBatis 中有3种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):

  • JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
  • MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。
  • 自定义:开发者自定义数据库事务的管理办法
import cn.bytecollege.entity.Student;
import cn.bytecollege.mapper.StudentMapper;
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 java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class TransactionDemo {
        public static void main(String[] args) {
            SqlSession session = null;
            Student student = new Student();
            student.setStudentName("郭靖");
            student.setStudentAge(18);
            student.setStudentGender("男");
            try{
                InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
                SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
                session = sqlSessionFactory.openSession();
                StudentMapper mapper = session.getMapper(StudentMapper.class);
                mapper.addStudent(student);
                //<1>
                session.commit();
            }catch (Exception e){
                //<2>
                session.rollback();
                e.printStackTrace();
            }
        }
}

从上例代码中可以看出当执行完数据库操作后,调用session对象的commit()方法提交事务,如果发生异常时则调用rollback()方法进行事务回滚。

2.7.2 数据源

dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。Mybatis提供了3种数据源类型,下面就这种类型进行简单介绍:

  1. UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择。 性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。
  2. POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。这种方式是开发中常用的方式。
  3. JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。

开发者只需要把数据源的属性type定义为:UNPOOLEN、POOLED、JNDI即可。 如果需要使用自定义数据源,需要实现org.apache.ibatis.datasource.DatatSourceFactory接口,例如要是用Druid数据源,就需要自定义数据源。

package cn.bytecollege.datasource;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.apache.ibatis.datasource.DataSourceFactory;

import javax.sql.DataSource;
import java.util.Properties;

public class MyDruidDataSource implements DataSourceFactory {
    private Properties properties = null;
    @Override
    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    @Override
    public DataSource getDataSource() {
        DruidDataSource druidDataSource = null;
        try {
            druidDataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return druidDataSource;
    }
}

数据源定义完毕后还需要在全局配置文件中进行配置,配置完成后就可以使用自定义数据源了,配置如下:

<!--1-->
<dataSource type="cn.bytecollege.datasource.MyDruidDataSource">
    <property name="driver" value="${driver}"/>
    <property name="url" value="${url}"/>
    <property name="username" value="${username}"/>
    <property name="password" value="${password}"/>
</dataSource>

在上述配置中注释1处,只需要将type修改为自定义数据源的全限定名即可。

2.8 databaseIdProvider数据库厂商标识属性

在相同数据库厂商的环境下,数据库厂商标识没有任何意义,在实际开发中使用的较少。如果使用了不同厂商的数据库,Mybatis可以根据不同的数据库厂商执行不同的语句。这种支持是基于配置文件中的databaseId属性。Mybatis会加载带有匹配当前数据库databaseId属性的语句。

2.9 mappers映射器属性

映射器是Mybatis最核心的组件之一,在本小节内主要讨论如何在全局配置文件中引入映射器,映射器中的参数类型、动态SQL、缓存等内容将会在第3章及第4章中详细讲解。 Mybatis提供了4种映射器的引入方式

2.9.1 resource引入

resource导入是使用mapper标签的resource属性引入映射器。这种方式也是使用最多的方式。配置如下:

<mappers>
  <mapper resource="mapper/StudentMapper.xml"/>
  <mapper resource="mapper/UserMapper.xml"/>
</mappers>

如果是使用Maven构建的项目,为了方便管理映射器,通常在resource目录下新建mapper目录,然后将映射器放入该目录中,resource只需配置相对路径即可完成映射。

2.9.2 class引入

class引入则是使用mapper标签的class属性引入映射器,这种方式通常需要将映射器放在Mapper接口相同的目录中(Eclipse项目),如果是Maven项目,则需要在resource目录中新建和包名相同的路径,然后将映射器放入。配置如下:

<mappers>
  <mapper class="cn.bytecollege.mapper.StudentMapper"/>
  <mapper class="cn.bytecollege.mapper.UserMapper"/>
</mappers>

2.9.3 url引入

url引用方式在开发中使用的较少,因为这种方式是使用映射器的绝对路径进行引入,引入方式如下:

<mappers>
    <mapper url="file:///E:\project\idea\mybaits\chapter02\src\main\resources\cn\bytecollege\mapper\StudentMapper.xml"></mapper>
</mappers>

需要注意的是这种引入方式需要在文件的路径前添加“file:///”,否则会引发异常。

2.9.4 package引入

package引入方式也是开发中比较常用的一种引入方式,此种方式是使用package标签配置映射器所在的路径。这种方式只需要配置包名,Mybatis即可扫描到该路径下的映射器。需要注意的是这种方式也需要在resource目录下建立和Mapper接口相同的路径名,并将映射器放入才能使用。配置方式如下:

<mappers>
  <package name="cn.bytecollege.mapper"/>
</mappers>

总结

在本文内系统学习了Mybatis的全局配置文件,在整个Mybatis框架内,全局配置文件可以说是该框架的核心之一,该文件中常用配置有数据库与对象字段映射,缓存,以及映射器(即分离后的SQL语句)。因此掌握全局文件的配置也就相当于掌握了一半Mybatis框架。

相关推荐

ES6中 Promise的使用场景?(es6promise用法例子)

一、介绍Promise,译为承诺,是异步编程的一种解决方案,比传统的解决方案(回调函数)更加合理和更加强大在以往我们如果处理多层异步操作,我们往往会像下面那样编写我们的代码doSomething(f...

JavaScript 对 Promise 并发的处理方法

Promise对象代表一个未来的值,它有三种状态:pending待定,这是Promise的初始状态,它可能成功,也可能失败,前途未卜fulfilled已完成,这是一种成功的状态,此时可以获取...

Promise的九大方法(promise的实例方法)

1、promise.resolv静态方法Promise.resolve(value)可以认为是newPromise方法的语法糖,比如Promise.resolve(42)可以认为是以下代码的语...

360前端一面~面试题解析(360前端开发面试题)

1.组件库按需加载怎么做的,具体打包配了什么-按需加载实现:借助打包工具(如Webpack的require.context或ES模块动态导入),在使用组件时才引入对应的代码。例如在V...

前端面试-Promise 的 finally 怎么实现的?如何在工作中使用?

Promise的finally方法是一个非常有用的工具,它无论Promise是成功(fulfilled)还是失败(rejected)都会执行,且不改变Promise的最终结果。它的实现原...

最简单手写Promise,30行代码理解Promise核心原理和发布订阅模式

看了全网手写Promise的,大部分对于新手还是比较难理解的,其中几个比较难的点:状态还未改变时通过发布订阅模式去收集事件实例化的时候通过调用构造函数里传出来的方法去修改类里面的状态,这个叫Re...

前端分享-Promise可以中途取消啦(promise可以取消吗)

传统Promise就像一台需要手动组装的设备,每次使用都要重新接线。而Promise.withResolvers的出现,相当于给开发者发了一个智能遥控器,可以随时随地控制异步操作。它解决了三大...

手写 Promise(手写输入法 中文)

前言都2020年了,Promise大家肯定都在用了,但是估计很多人对其原理还是一知半解,今天就让我们一起实现一个符合PromiseA+规范的Promise。附PromiseA+规范地址...

什么是 Promise.allSettled()!新手老手都要会?

Promise.allSettled()方法返回一个在所有给定的promise都已经fulfilled或rejected后的promise,并带有一个对象数组,每个对象表示对应的pr...

前端面试-关于Promise解析与高频面试题示范

Promise是啥,直接上图:Promise就是处理异步函数的API,它可以包裹一个异步函数,在异步函数完成时抛出完成状态,让代码结束远古时无限回掉的窘境。配合async/await语法糖,可...

宇宙厂:为什么前端离不开 Promise.withResolvers() ?

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发。1.为什么需要Promise.with...

Promise 新增了一个超实用的 API!

在JavaScript的世界里,Promise一直是处理异步操作的神器。而现在,随着ES2025的发布,Promise又迎来了一个超实用的新成员——Promise.try()!这个新方法简...

一次搞懂 Promise 异步处理(promise 异步顺序执行)

PromisePromise就像这个词的表面意识一样,表示一种承诺、许诺,会在后面给出一个结果,成功或者失败。现在已经成为了主流的异步编程的操作方式,写进了标准里面。状态Promise有且仅有...

Promise 核心机制详解(promise机制的实现原理)

一、Promise的核心状态机Promise本质上是一个状态机,其行为由内部状态严格管控。每个Promise实例在创建时处于Pending(等待)状态,此时异步操作尚未完成。当异步操作成功...

javascript——Promise(js实现promise)

1.PromiseES6开始支持,Promise对象用于一个异步操作的最终完成(包括成功和失败)及结果值的表示。简单说就是处理异步请求的。之所以叫Promise,就是我承诺,如果成功则怎么处理,失败怎...

取消回复欢迎 发表评论: