配置

MyBatis 配置包含对 MyBatis 行为产生重大影响的设置和属性。文档的高级结构如下

属性

这些是可外部化、可替换的属性,可以在典型的 Java Properties 文件实例中配置,或通过 properties 元素的子元素传递。例如

<properties resource="org/mybatis/example/config.properties">
  <property name="username" value="dev_user"/>
  <property name="password" value="F2Fa3!33TYyg"/>
</properties>

然后可以在整个配置文件中使用属性来替换需要动态配置的值。例如

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

此示例中的用户名和密码将替换为 properties 元素中设置的值。driver 和 url 属性将替换为 config.properties 文件中包含的值。这提供了许多配置选项。

还可以将属性传递到 SqlSessionFactoryBuilder.build() 方法中。例如

SqlSessionFactory factory =
  sqlSessionFactoryBuilder.build(reader, props);

// ... or ...

SqlSessionFactory factory =
  new SqlSessionFactoryBuilder.build(reader, environment, props);

如果某个属性存在于多个此类位置,MyBatis 将按以下顺序加载它们

  • 首先读取 properties 元素正文中指定的属性,
  • 其次读取从 properties 元素的 classpath 资源或 url 属性加载的属性,并覆盖已指定的任何重复属性,
  • 最后读取作为方法参数传递的属性,并覆盖可能已从 properties 正文和资源/url 属性加载的任何重复属性。

因此,最高优先级的属性是作为方法参数传递的属性,其次是资源/URL 属性,最后是属性元素正文中指定的属性。

自 MyBatis 3.4.2 起,您可以将默认值指定到占位符中,如下所示

<dataSource type="POOLED">
  <!-- ... -->
  <property name="username" value="${username:ut_user}"/> <!-- If 'username' property not present, username become 'ut_user' -->
</dataSource>

此功能默认禁用。如果您将默认值指定到占位符中,则应通过添加特殊属性来启用此功能,如下所示

<properties resource="org/mybatis/example/config.properties">
  <!-- ... -->
  <property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/> <!-- Enable this feature -->
</properties>

注意这将与属性键中的 ":" 字符(例如 db:username)或 SQL 定义中的 OGNL 表达式的三元运算符(例如 ${tableName != null ? tableName : 'global_constants'})冲突。如果您使用其中任何一个并希望使用默认属性值,则必须通过添加此特殊属性来更改默认值分隔符

<properties resource="org/mybatis/example/config.properties">
  <!-- ... -->
  <property name="org.apache.ibatis.parsing.PropertyParser.default-value-separator" value="?:"/> <!-- Change default value of separator -->
</properties>
<dataSource type="POOLED">
  <!-- ... -->
  <property name="username" value="${db:username?:ut_user}"/>
</dataSource>

设置

这些是极重要的调整,它们会修改 MyBatis 在运行时的行为。下表描述了设置、它们的含义及其默认值。

设置 说明 有效值 默认值
cacheEnabled 全局启用或禁用在此配置下任何映射器中配置的任何缓存。 true | false true
lazyLoadingEnabled 全局启用或禁用延迟加载。启用后,所有关系都将延迟加载。可以使用该关系上的 fetchType 属性来替代此值以针对特定关系使用。 true | false false
aggressiveLazyLoading 启用后,任何方法调用都将加载对象的全部延迟属性。否则,每个属性将按需加载(另请参见 lazyLoadTriggerMethods)。 true | false false (true in ≤3.4.1)
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
请注意,当 autoMappingBehavior 设置为 FULL 时,可能会出现误报。
NONE、WARNING、FAILING NONE
defaultExecutorType 配置默认执行器。SIMPLE 执行器不执行任何特殊操作。REUSE 执行器重复使用已准备好的语句。BATCH 执行器重复使用语句并批处理更新。 SIMPLE REUSE BATCH SIMPLE
defaultStatementTimeout 设置驱动程序等待数据库响应的秒数。 任何正整数 未设置(null)
defaultFetchSize 设置驱动程序提示,以控制返回结果的获取大小。此参数值可通过查询设置覆盖。 任何正整数 未设置(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 使用本地缓存来防止循环引用并加快重复嵌套查询的速度。默认情况下(SESSION),会话期间执行的所有查询都会被缓存。如果 localCacheScope=STATEMENT,则本地会话将仅用于语句执行,不会在对同一 SqlSession 的两次不同调用之间共享数据。 SESSION | STATEMENT SESSION
jdbcTypeForNull 当未为参数提供特定 JDBC 类型时,指定 null 值的 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 指定当检索到的值为空时,是否将调用 setter 或 map 的 put 方法。当您依赖于 Map.keySet() 或空值初始化时,此设置非常有用。请注意,基本类型(例如 (int,boolean 等))不会被设置为 null。 true | false false
returnInstanceForEmptyRow 默认情况下,当返回行中的所有列都为 NULL 时,MyBatis 将返回 null。启用此设置后,MyBatis 将返回一个空实例。请注意,此设置也适用于嵌套结果(即集合和关联)。自 3.4.2 起 true | false false
logPrefix 指定 MyBatis 将添加到记录器名称的前缀字符串。 任意字符串 未设置
logImpl 指定 MyBatis 应使用的日志记录实现。如果不存在此设置,则将自动发现日志记录实现。 SLF4J | LOG4J(自 3.5.9 起已弃用)| LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING 未设置
proxyFactory 指定 MyBatis 将用于创建能够延迟加载对象的代理工具。 CGLIB(自 3.5.10 起已弃用)| JAVASSIST JAVASSIST(MyBatis 3.3 或更高版本)
vfsImpl 指定 VFS 实现 以逗号分隔的自定义 VFS 实现的完全限定类名。 未设置
useActualParamName 允许通过方法签名中声明的实际名称引用语句参数。要使用此功能,您的项目必须使用 -parameters 选项在 Java 8 中编译。(自 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 指定一个包含提供程序方法的 SQL 提供程序类(自 3.5.6 起)。此类适用于 SQL 提供程序注释上的 type(或 value)属性(例如 @SelectProvider),当这些属性被省略时。 类型别名或完全限定类名 未设置
nullableOnForEach 指定“foreach”标签上“nullable”属性的默认值。(自 3.5.9 起) true | false false
argNameBasedConstructorAutoMapping 应用构造函数自动映射时,使用参数名称搜索要映射的列,而不是依赖于列顺序。(自 3.5.10 起) true | false false

以下是对设置元素进行完全配置的示例

<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="aggressiveLazyLoading" 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="safeResultHandlerEnabled" value="true"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
  <setting name="defaultScriptingLanguage" value="org.apache.ibatis.scripting.xmltags.XMLLanguageDriver"/>
  <setting name="defaultEnumTypeHandler" value="org.apache.ibatis.type.EnumTypeHandler"/>
  <setting name="callSettersOnNulls" value="false"/>
  <setting name="returnInstanceForEmptyRow" value="false"/>
  <setting name="logPrefix" value="exampleLogPreFix_"/>
  <setting name="logImpl" value="SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING"/>
  <setting name="proxyFactory" value="CGLIB | JAVASSIST"/>
  <setting name="vfsImpl" value="org.mybatis.example.YourselfVfsImpl"/>
  <setting name="useActualParamName" value="true"/>
  <setting name="configurationFactory" value="org.mybatis.example.ConfigurationFactory"/>
</settings>

类型别名

类型别名只是 Java 类型的一个较短名称。它仅与 XML 配置相关,并且仅存在于减少完全限定类名的冗余输入。例如

<typeAliases>
  <typeAlias alias="Author" type="domain.blog.Author"/>
  <typeAlias alias="Blog" type="domain.blog.Blog"/>
  <typeAlias alias="Comment" type="domain.blog.Comment"/>
  <typeAlias alias="Post" type="domain.blog.Post"/>
  <typeAlias alias="Section" type="domain.blog.Section"/>
  <typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>

使用此配置,现在可以在 domain.blog.Blog 可以使用的地方使用 Blog

您还可以指定 MyBatis 将搜索 bean 的包。例如

<typeAliases>
  <package name="domain.blog"/>
</typeAliases>

如果在 domain.blog 中找到的每个 bean 都没有注释,则将使用 bean 的非限定类名的非大写形式将其注册为别名。即 domain.blog.Author 将注册为 author。如果找到 @Alias 注释,则其值将用作别名。请参阅以下示例

@Alias("author")
public class Author {
    ...
}

对于常见的 Java 类型,有许多内置类型别名。它们都是不区分大小写的,请注意由于重载名称而对基本类型的特殊处理。

别名 映射类型
_byte byte
_char(自 3.5.10 起) char
_character(自 3.5.10 起) char
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
char(自 3.5.10 起) Character
character(自 3.5.10 起) Character
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
biginteger BigInteger
object Object
date[] Date[]
十进制[] BigDecimal[]
bigdecimal[] BigDecimal[]
biginteger[] BigInteger[]
object[] Object[]
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iterator

类型处理器

每当 MyBatis 在 PreparedStatement 上设置参数或从 ResultSet 中检索值时,都会使用 TypeHandler 以适合 Java 类型的方式检索值。下表描述了默认 TypeHandler。

注意从 3.4.5 版本开始,MyBatis 默认支持 JSR-310(日期和时间 API)。

类型处理器 Java 类型 JDBC 类型
BooleanTypeHandler java.lang.Booleanboolean 任何兼容的 BOOLEAN
ByteTypeHandler java.lang.Bytebyte 任何兼容的 NUMERICBYTE
ShortTypeHandler java.lang.Shortshort 任何兼容的 NUMERICSMALLINT
IntegerTypeHandler java.lang.Integerint 任何兼容的 NUMERICINTEGER
LongTypeHandler java.lang.Longlong 任何兼容的 NUMERICBIGINT
FloatTypeHandler java.lang.Floatfloat 任何兼容的 NUMERICFLOAT
DoubleTypeHandler java.lang.Doubledouble 任何兼容的 NUMERICDOUBLE
BigDecimalTypeHandler java.math.BigDecimal 任何兼容的 NUMERICDECIMAL
StringTypeHandler java.lang.String CHARVARCHAR
ClobReaderTypeHandler java.io.Reader -
ClobTypeHandler java.lang.String CLOBLONGVARCHAR
NStringTypeHandler java.lang.String NVARCHARNCHAR
NClobTypeHandler java.lang.String NCLOB
BlobInputStreamTypeHandler java.io.InputStream -
ByteArrayTypeHandler byte[] 任何兼容的字节流类型
BlobTypeHandler byte[] BLOBLONGVARBINARY
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 任何 OTHER 或未指定
EnumTypeHandler 枚举类型 VARCHAR 任何兼容的字符串类型,因为代码已存储(不是索引)。
EnumOrdinalTypeHandler 枚举类型 任何兼容的 NUMERICDOUBLE,因为位置已存储(不是代码本身)。
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 VARCHARLONGVARCHAR
JapaneseDateTypeHandler java.time.chrono.JapaneseDate DATE

你可以覆盖类型处理器或创建自己的类型处理器来处理不受支持或非标准类型。为此,实现接口 org.apache.ibatis.type.TypeHandler 或扩展便捷类 org.apache.ibatis.type.BaseTypeHandler,并根据需要将其映射到 JDBC 类型。例如

// ExampleTypeHandler.java
@MappedJdbcTypes(JdbcType.VARCHAR)
public class ExampleTypeHandler extends BaseTypeHandler<String> {

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i,
    String parameter, JdbcType jdbcType) throws SQLException {
    ps.setString(i, parameter);
  }

  @Override
  public String getNullableResult(ResultSet rs, String columnName)
    throws SQLException {
    return rs.getString(columnName);
  }

  @Override
  public String getNullableResult(ResultSet rs, int columnIndex)
    throws SQLException {
    return rs.getString(columnIndex);
  }

  @Override
  public String getNullableResult(CallableStatement cs, int columnIndex)
    throws SQLException {
    return cs.getString(columnIndex);
  }
}
<!-- mybatis-config.xml -->
<typeHandlers>
  <typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
</typeHandlers>

使用此类 TypeHandler 将覆盖 Java String 属性和 VARCHAR 参数及结果的现有类型处理器。请注意,MyBatis 不会内省数据库元数据来确定类型,因此你必须在参数和结果映射中指定它是一个 VARCHAR 字段,以连接正确的类型处理器。这是因为在执行语句之前,MyBatis 不知道数据类型。

MyBatis 将通过内省其泛型类型来了解你要用此 TypeHandler 处理的 Java 类型,但你可以通过两种方式覆盖此行为

  • 向 typeHandler 元素添加 javaType 属性(例如:javaType="String"
  • 向 TypeHandler 类添加 @MappedTypes 注释,指定要与其关联的 Java 类型列表。如果也指定了 javaType 属性,将忽略此注释。

关联的 JDBC 类型可以通过两种方式指定

  • 向 typeHandler 元素添加 jdbcType 属性(例如:jdbcType="VARCHAR")。
  • 向 TypeHandler 类添加 @MappedJdbcTypes 注释,指定要与其关联的 JDBC 类型列表。如果也指定了 jdbcType 属性,则将忽略此注释。

ResultMap 中决定使用哪个 TypeHandler 时,Java 类型已知(来自结果类型),但 JDBC 类型未知。因此,MyBatis 使用组合 javaType=[TheJavaType], jdbcType=null 来选择 TypeHandler。这意味着使用 @MappedJdbcTypes 注释会限制 TypeHandler 的范围,使其在未明确设置的情况下不可用于 ResultMap 中。要使 TypeHandler 可用于 ResultMap,请在 @MappedJdbcTypes 注释上设置 includeNullJdbcType=true。但是,自 Mybatis 3.4.0 以来,如果注册了单个 TypeHandler 来处理 Java 类型,则它将默认在使用此 Java 类型的 ResultMap 中使用(即,即使没有 includeNullJdbcType=true)。

最后,你可以让 MyBatis 搜索你的 TypeHandler

<!-- mybatis-config.xml -->
<typeHandlers>
  <package name="org.mybatis.example"/>
</typeHandlers>

请注意,在使用自动发现功能时,JDBC 类型只能使用注释指定。

你可以创建一个能够处理多个类的通用 TypeHandler。为此,添加一个接收类作为参数的构造函数,MyBatis 将在构造 TypeHandler 时传递实际类。

//GenericTypeHandler.java
public class GenericTypeHandler<E extends MyObject> extends BaseTypeHandler<E> {

  private Class<E> type;

  public GenericTypeHandler(Class<E> type) {
    if (type == null) throw new IllegalArgumentException("Type argument cannot be null");
    this.type = type;
  }
  ...

EnumTypeHandlerEnumOrdinalTypeHandler 是通用 TypeHandler。我们将在下一节中了解它们。

处理枚举

如果你想映射一个 Enum,你需要使用 EnumTypeHandlerEnumOrdinalTypeHandler

例如,假设我们需要存储如果需要四舍五入时应该使用的舍入模式。默认情况下,MyBatis 使用 EnumTypeHandlerEnum 值转换为其名称。

请注意,EnumTypeHandler 的特殊之处在于,它不像其他处理程序那样只处理一个特定类,而是处理任何扩展 Enum 的类

但是,我们可能不想存储名称。我们的 DBA 可能坚持使用整数代码。这同样简单:将 EnumOrdinalTypeHandler 添加到配置文件中的 typeHandlers,现在每个 RoundingMode 都将使用其序数值映射到一个整数。

<!-- mybatis-config.xml -->
<typeHandlers>
  <typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler"
    javaType="java.math.RoundingMode"/>
</typeHandlers>

但是,如果您想将同一个 Enum 在一个地方映射到一个字符串,而在另一个地方映射到一个整数,该怎么办?

自动映射器将自动使用 EnumOrdinalTypeHandler,因此,如果我们想恢复使用普通旧的 EnumTypeHandler,我们必须告诉它,通过明确设置要用于这些 SQL 语句的类型处理器。

(映射器文件直到下一节才会介绍,因此,如果您是第一次阅读文档,您可能希望暂时跳过此内容,稍后再回来阅读。)

<!DOCTYPE mapper
    PUBLIC "-//mybatis.ac.cn//DTD Mapper 3.0//EN"
    "https://mybatis.ac.cn/dtd/mybatis-3-mapper.dtd">

<mapper namespace="org.apache.ibatis.submitted.rounding.Mapper">
    <resultMap type="org.apache.ibatis.submitted.rounding.User" id="usermap">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="funkyNumber" property="funkyNumber"/>
        <result column="roundingMode" property="roundingMode"/>
    </resultMap>

    <select id="getUser" resultMap="usermap">
        select * from users
    </select>
    <insert id="insert">
        insert into users (id, name, funkyNumber, roundingMode) values (
            #{id}, #{name}, #{funkyNumber}, #{roundingMode}
        )
    </insert>

    <resultMap type="org.apache.ibatis.submitted.rounding.User" id="usermap2">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="funkyNumber" property="funkyNumber"/>
        <result column="roundingMode" property="roundingMode"
         typeHandler="org.apache.ibatis.type.EnumTypeHandler"/>
    </resultMap>
    <select id="getUser2" resultMap="usermap2">
        select * from users2
    </select>
    <insert id="insert2">
        insert into users2 (id, name, funkyNumber, roundingMode) values (
            #{id}, #{name}, #{funkyNumber}, #{roundingMode, typeHandler=org.apache.ibatis.type.EnumTypeHandler}
        )
    </insert>

</mapper>

请注意,这会迫使我们在选择语句中使用 resultMap 而不是 resultType

对象工厂

MyBatis 每次创建结果对象的新实例时,都会使用 ObjectFactory 实例来执行此操作。默认的 ObjectFactory 除了使用默认构造函数或在存在参数映射时使用参数化构造函数实例化目标类之外,几乎没有其他作用。如果您想覆盖 ObjectFactory 的默认行为,您可以创建自己的 ObjectFactory。例如

// ExampleObjectFactory.java
public class ExampleObjectFactory extends DefaultObjectFactory {
  @Override
  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) {
    return super.create(type, constructorArgTypes, constructorArgs);
  }

  @Override
  public void setProperties(Properties properties) {
    super.setProperties(properties);
  }

  @Override
  public <T> boolean isCollection(Class<T> type) {
    return Collection.class.isAssignableFrom(type);
  }
}
<!-- mybatis-config.xml -->
<objectFactory type="org.mybatis.example.ExampleObjectFactory">
  <property name="someProperty" value="100"/>
</objectFactory>

ObjectFactory 接口非常简单。它包含两个 create 方法,一个用于处理默认构造函数,另一个用于处理参数化构造函数。最后,setProperties 方法可用于配置 ObjectFactory。在 ObjectFactory 实例初始化后,objectFactory 元素主体中定义的属性将传递给 setProperties 方法。

插件

MyBatis 允许您在映射语句执行过程中的某些点拦截调用。默认情况下,MyBatis 允许插件拦截以下方法调用

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

可以通过查看每个方法的完整方法签名以及每个 MyBatis 版本附带的源代码来了解这些类方法的详细信息。您应该了解要覆盖的方法的行为,假设您做的不仅仅是监视调用。如果您尝试修改或覆盖给定方法的行为,则可能会破坏 MyBatis 的核心。这些是低级类和方法,因此请谨慎使用插件。

鉴于插件提供的功能,使用插件非常简单。只需实现 Interceptor 接口,确保指定您要拦截的签名即可。

// ExamplePlugin.java
@Intercepts({@Signature(
  type= Executor.class,
  method = "update",
  args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
  private Properties properties = new Properties();

  @Override
  public Object intercept(Invocation invocation) throws Throwable {
    // implement pre-processing if needed
    Object returnObject = invocation.proceed();
    // implement post-processing if needed
    return returnObject;
  }

  @Override
  public void setProperties(Properties properties) {
    this.properties = properties;
  }
}
<!-- mybatis-config.xml -->
<plugins>
  <plugin interceptor="org.mybatis.example.ExamplePlugin">
    <property name="someProperty" value="100"/>
  </plugin>
</plugins>

上面的插件将拦截对 Executor 实例上“update”方法的所有调用,Executor 实例是一个负责映射语句低级执行的内部对象。

注意 覆盖配置类

除了通过插件修改 MyBatis 核心行为外,还可以完全覆盖 Configuration 类。只需对其进行扩展并覆盖内部的任何方法,然后将其传递给 SqlSessionFactoryBuilder.build(myConfig) 方法的调用。不过,这可能会严重影响 MyBatis 的行为,因此请谨慎使用。

环境

MyBatis 可以使用多个环境进行配置。这有助于你因任何原因将 SQL 映射应用于多个数据库。例如,你可能为开发、测试和生产环境使用不同的配置。或者,你可能有多个共享相同架构的生产数据库,并且希望对两者使用相同的 SQL 映射。有许多用例。

不过,有一件重要的事情需要记住:虽然你可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一个环境。

因此,如果你想连接到两个数据库,则需要为每个数据库创建两个 SqlSessionFactory 实例。对于三个数据库,你需要三个实例,依此类推。这很容易记住

  • 每个数据库一个 SqlSessionFactory 实例

要指定要构建的环境,只需将其作为可选参数传递给 SqlSessionFactoryBuilder。接受环境的两个签名是

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties);

如果省略环境,则加载默认环境,如下所示

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, properties);

environments 元素定义了如何配置环境。

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

注意此处的关键部分

  • 默认环境 ID(例如 default=“development”)。
  • 为每个已定义环境指定的 Environment ID(例如 id=“development”)。
  • TransactionManager 配置(例如 type=“JDBC”)
  • DataSource 配置(例如 type=“POOLED”)

默认环境和环境 ID 不言自明。根据需要命名它们,只需确保默认环境与其中一个匹配即可。

事务管理器

MyBatis 中包含两种 TransactionManager 类型(即 type=“[JDBC|MANAGED]”)

  • JDBC – 此配置只是直接使用 JDBC 提交和回滚功能。它依赖于从 dataSource 检索的连接来管理事务的范围。默认情况下,它在关闭连接时启用自动提交以与某些驱动程序兼容。但是,对于某些驱动程序,启用自动提交不仅不必要,而且还是一项昂贵的操作。因此,自 3.5.10 版本以来,你可以通过将“skipSetAutoCommitOnClose”属性设置为 true 来跳过此步骤。例如

    <transactionManager type="JDBC">
      <property name="skipSetAutoCommitOnClose" value="true"/>
    </transactionManager>
    
  • 托管 - 此配置几乎不执行任何操作。它从不提交或回滚连接。相反,它允许容器管理事务的完整生命周期(例如 JEE 应用程序服务器上下文)。默认情况下,它会关闭连接。但是,某些容器不希望这样做,因此,如果您需要阻止它关闭连接,请将“closeConnection”属性设置为 false。例如

    <transactionManager type="MANAGED">
      <property name="closeConnection" value="false"/>
    </transactionManager>
    

注意如果您计划将 MyBatis 与 Spring 一起使用,则无需配置任何 TransactionManager,因为 Spring 模块将设置自己的模块,覆盖任何先前设置的配置。

这两种 TransactionManager 类型都不需要任何属性。但是,它们都是类型别名,换句话说,您可以使用自己的完全限定类名或引用您自己的 TransactionFactory 接口实现的类型别名,而不是使用它们。

public interface TransactionFactory {
  default void setProperties(Properties props) { // Since 3.5.2, change to default method
    // NOP
  }
  Transaction newTransaction(Connection conn);
  Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}

在 XML 中配置的任何属性都将在实例化后传递给 setProperties() 方法。您的实现还需要创建一个事务实现,这也是一个非常简单的接口

public interface Transaction {
  Connection getConnection() throws SQLException;
  void commit() throws SQLException;
  void rollback() throws SQLException;
  void close() throws SQLException;
  Integer getTimeout() throws SQLException;
}

使用这两个接口,您可以完全自定义 MyBatis 处理事务的方式。

数据源

dataSource 元素使用标准 JDBC DataSource 接口配置 JDBC 连接对象的来源。

大多数 MyBatis 应用程序会像示例中那样配置 dataSource。但是,这不是必需的。不过,请意识到,为了促进延迟加载,需要此 dataSource。

有三种内置的 dataSource 类型(即 type=“[UNPOOLED|POOLED|JNDI]”)

UNPOOLED - 此 DataSource 实现每次请求时都会打开和关闭一个连接。虽然它有点慢,但对于不需要立即可用连接的性能的简单应用程序来说,这是一个不错的选择。不同的数据库在此性能区域中也不同,因此对于某些数据库来说,池化可能不那么重要,此配置将是理想的。UNPOOLED DataSource 具有以下要配置的属性

  • driver - 这是 JDBC 驱动的完全限定 Java 类(如果不是您的驱动程序包含一个,则不是 DataSource 类的)。
  • url - 这是您数据库实例的 JDBC URL。
  • username - 用于登录的数据库用户名。
  • password - 用于登录的数据库密码。
  • defaultTransactionIsolationLevel – 连接的默认事务隔离级别。
  • defaultNetworkTimeout – 等待数据库操作完成的默认网络超时值(以毫秒为单位)。有关详细信息,请参阅 java.sql.Connection#setNetworkTimeout() 的 API 文档。

此外,您还可以将属性传递给数据库驱动程序。为此,请使用 driver. 为属性添加前缀,例如

  • driver.encoding=UTF8

这会通过 DriverManager.getConnection(url, driverProperties) 方法将属性 encoding(值 UTF8)传递给您的数据库驱动程序。

POOLED – 此 DataSource 实现会池化 JDBC Connection 对象,以避免创建新 Connection 实例所需的初始连接和身份验证时间。这是并发 Web 应用程序实现最快响应的常用方法。

除了上述 (UNPOOLED) 属性外,还有许多其他属性可用于配置 POOLED 数据源

  • poolMaximumActiveConnections – 这是任何给定时间内可以存在的活动(即正在使用)连接数。默认值:10
  • poolMaximumIdleConnections – 任何给定时间内可以存在的空闲连接数。
  • poolMaximumCheckoutTime – 这是 Connection 可以从池中“签出”的时间量,然后才会强制将其返回。默认值:20000ms(即 20 秒)
  • poolTimeToWait – 这是一个低级别设置,它让池有机会打印日志状态,并在获取连接花费异常长的时间时重新尝试获取连接(以避免在池配置错误时永远静默失败)。默认值:20000ms(即 20 秒)
  • poolMaximumLocalBadConnectionTolerance – 这是一个低级别设置,关于任何线程获得的错误连接的容忍度。如果线程获得了错误连接,它可能仍有机会重新尝试获取有效的连接。但重试次数不应超过 poolMaximumIdleConnectionspoolMaximumLocalBadConnectionTolerance 之和。默认值:3(自 3.4.5 起)
  • poolPingQuery – Ping 查询被发送到数据库,以验证连接处于正常工作状态并已准备好接受请求。默认值为“未设置 Ping 查询”,这将导致大多数数据库驱动程序失败并显示一个不错的错误消息。
  • poolPingEnabled – 这会启用或禁用 Ping 查询。如果启用,您还必须使用有效的 SQL 语句(最好是非常快的语句)设置 poolPingQuery 属性。默认值:false。
  • poolPingConnectionsNotUsedFor – 这会配置 poolPingQuery 的使用频率。这可以设置为与数据库连接的典型超时时间相匹配,以避免不必要的 Ping。默认值:0(即所有连接每次都会被 Ping - 但当然前提是 poolPingEnabled 为 true)。

JNDI – 此 DataSource 实现旨在与容器(例如 EJB 或应用程序服务器)配合使用,这些容器可能会集中或外部配置 DataSource,并将对它的引用置于 JNDI 上下文中。此 DataSource 配置仅需要两个属性

  • initial_context – 此属性用于从 InitialContext 查找上下文(即 initialContext.lookup(initial_context))。此属性是可选的,如果省略,则会直接在 InitialContext 中查找 data_source 属性。
  • data_source – 这是可以找到对 DataSource 实例的引用的上下文路径。它将在 initial_context 查找返回的上下文中查找,或者如果没有提供 initial_context,则直接在 InitialContext 中查找。

与其他 DataSource 配置类似,可以通过使用 env. 为这些属性添加前缀,将属性直接发送到 InitialContext,例如

  • env.encoding=UTF8

这会将属性 encoding 连同值 UTF8 在实例化时发送到 InitialContext 的构造函数。

您可以通过实现接口 org.apache.ibatis.datasource.DataSourceFactory 来插入任何第三方 DataSource

public interface DataSourceFactory {
  void setProperties(Properties props);
  DataSource getDataSource();
}

org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory 可用作构建新数据源适配器的超类。例如,这是插入 C3P0 所需的代码

import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0DataSourceFactory extends UnpooledDataSourceFactory {

  public C3P0DataSourceFactory() {
    this.dataSource = new ComboPooledDataSource();
  }
}

要设置它,请为希望 MyBatis 调用的每个 setter 方法添加一个属性。下面是一个连接到 PostgreSQL 数据库的示例配置

<dataSource type="org.myproject.C3P0DataSourceFactory">
  <property name="driver" value="org.postgresql.Driver"/>
  <property name="url" value="jdbc:postgresql:mydb"/>
  <property name="username" value="postgres"/>
  <property name="password" value="root"/>
</dataSource>

databaseIdProvider

MyBatis 能够根据您的数据库供应商执行不同的语句。多数据库供应商支持基于映射语句 databaseId 属性。MyBatis 将加载所有没有 databaseId 属性或具有与当前属性匹配的 databaseId 的语句。如果同时找到具有和不具有 databaseId 的相同语句,则将丢弃后者。要启用多供应商支持,请将 databaseIdProvider 添加到 mybatis-config.xml 文件,如下所示

<databaseIdProvider type="DB_VENDOR" />

DB_VENDOR 实现 databaseIdProvider 将 DatabaseMetaData#getDatabaseProductName() 返回的字符串设置为 databaseId。鉴于通常该字符串太长,并且同一产品的不同版本可能会返回不同的值,因此您可能希望通过添加以下属性将其转换为较短的字符串

<databaseIdProvider type="DB_VENDOR">
  <property name="SQL Server" value="sqlserver"/>
  <property name="DB2" value="db2"/>
  <property name="Oracle" value="oracle" />
</databaseIdProvider>

提供属性后,DB_VENDOR databaseIdProvider 将搜索与返回的数据库产品名称中找到的第一个键或“null”(如果没有匹配的属性)对应的属性值。在这种情况下,如果 getDatabaseProductName() 返回“Oracle (DataDirect)”,则 databaseId 将设置为“oracle”。

您可以通过实现接口 org.apache.ibatis.mapping.DatabaseIdProvider 并将其注册到 mybatis-config.xml 中来构建自己的 DatabaseIdProvider

public interface DatabaseIdProvider {
  default void setProperties(Properties p) { // Since 3.5.2, changed to default method
    // NOP
  }
  String getDatabaseId(DataSource dataSource) throws SQLException;
}

映射器

现在已经使用上述配置元素配置了 MyBatis 的行为,我们准备定义映射的 SQL 语句。但首先,我们需要告诉 MyBatis 在哪里找到它们。Java 在这方面实际上没有提供任何良好的自动发现方法,因此最好的方法是直接告诉 MyBatis 在哪里找到映射文件。您可以使用类路径相对资源引用、完全限定的 URL 引用(包括 file:/// URL)、类名或包名。例如

<!-- Using classpath relative resources -->
<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
  <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- Using url fully qualified paths -->
<mappers>
  <mapper url="file:///var/mappers/AuthorMapper.xml"/>
  <mapper url="file:///var/mappers/BlogMapper.xml"/>
  <mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
<!-- Using mapper interface classes -->
<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
  <mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- Register all interfaces in a package as mappers -->
<mappers>
  <package name="org.mybatis.builder"/>
</mappers>

这些语句只是告诉 MyBatis 从这里开始去哪里。其他详细信息在每个 SQL 映射文件中,而这正是下一部分将讨论的内容。