diff --git a/src/site/site_zh.xml b/src/site/site_zh.xml
index 3d868162851..a8c7007e7af 100644
--- a/src/site/site_zh.xml
+++ b/src/site/site_zh.xml
@@ -45,7 +45,7 @@
注意在使用自动发现功能的时候,只能通过注解方式来指定 JDBC 的类型。 你可以创建一个能够处理多个类的泛型类型处理器。为了使用泛型类型处理器,
- 需要增加一个接受该类的 class 作为参数的构造器,这样在构造一个类型处理器的时候
- MyBatis 就会传入一个具体的类。 你可以创建能够处理多个类的泛型类型处理器。为了使用泛型类型处理器,
+ 需要增加一个接受该类的 class 作为参数的构造器,这样 MyBatis
+ 会在构造一个类型处理器实例的时候传入一个具体的类。 若想映射枚举类型 Enum
,则需要从 EnumTypeHandler
- 或者 EnumOrdinalTypeHandler
中选一个来使用。EnumOrdinalTypeHandler
中选择一个来使用。
比如说我们想存储取近似值时用到的舍入模式。默认情况下,MyBatis 会利用
EnumTypeHandler
来把 Enum
值转换成对应的名字。
EnumTypeHandler
- 在某种意义上来说是比较特别的,其他的处理器只针对某个特定的类,而它不同,它会处理任意继承了
+ 在某种意义上来说是比较特别的,其它的处理器只针对某个特定的类,而它不同,它会处理任意继承了
Enum
的类。
- 不过,我们可能不想存储名字,相反我们的 DBA 会坚持使用整形值代码。那也一样轻而易举:
- 在配置文件中把 EnumOrdinalTypeHandler
加到 typeHandlers
中即可,
+
不过,我们可能不想存储名字,相反我们的 DBA 会坚持使用整形值代码。那也一样简单:在配置文件中把
+ EnumOrdinalTypeHandler
加到 typeHandlers
中即可,
这样每个 RoundingMode
将通过他们的序数值来映射成对应的整形数值。
但是怎样能将同样的 Enum
既映射成字符串又映射成整形呢?
但要是你想在一个地方将 Enum
映射成字符串,在另外一个地方映射成整形值呢?
- 自动映射器(auto-mapper)会自动地选用 EnumOrdinalTypeHandler
来处理,
+ 自动映射器(auto-mapper)会自动地选用 EnumOrdinalTypeHandler
来处理枚举类型,
所以如果我们想用普通的 EnumTypeHandler
,就必须要显式地为那些 SQL 语句设置要使用的类型处理器。
(下一节才开始介绍映射器文件,如果你是首次阅读该文档,你可能需要先跳过这里,过会再来看。)
@@ -1461,13 +1460,13 @@ public class GenericTypeHandler注意,这里的 select 语句强制使用 resultMap
来代替 resultType
。
注意,这里的 select 语句必须指定 resultMap
而不是 resultType
。
MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。 - 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。 - 如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现。比如:
+每次 MyBatis 创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成实例化工作。 + 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认无参构造方法,要么通过存在的参数映射来调用带有参数的构造方法。 + 如果想覆盖对象工厂的默认行为,可以通过创建自己的对象工厂来实现。比如:
ObjectFactory 接口很简单,它包含两个创建用的方法,一个是处理默认构造方法的,另外一个是处理带参数的构造方法的。 - 最后,setProperties 方法可以被用来配置 ObjectFactory,在初始化你的 ObjectFactory 实例后, +
ObjectFactory 接口很简单,它包含两个创建实例用的方法,一个是处理默认无参构造方法的,另外一个是处理带参数的构造方法的。 + 另外,setProperties 方法可以被用来配置 ObjectFactory,在初始化你的 ObjectFactory 实例后, objectFactory 元素体中定义的属性会被传递给 setProperties 方法。
- MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括: + MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 发行包中的源代码。 如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。 - 因为如果在试图修改或重写已有方法的行为的时候,你很可能在破坏 MyBatis 的核心模块。 - 这些都是更低层的类和方法,所以使用插件的时候要特别当心。
+ 因为在试图修改或重写已有方法的行为时,很可能会破坏 MyBatis 的核心模块。 + 这些都是更底层的类和方法,所以使用插件的时候要特别当心。通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。
上面的插件将会拦截在 Executor 实例中所有的 “update” 方法调用, - 这里的 Executor 是负责执行低层映射语句的内部对象。
+ 这里的 Executor 是负责执行底层映射语句的内部对象。提示 覆盖配置类
-除了用插件来修改 MyBatis 核心行为之外,还可以通过完全覆盖配置类来达到目的。只需继承后覆盖其中的每个方法,再把它传递到 SqlSessionFactoryBuilder.build(myConfig) 方法即可。再次重申,这可能会严重影响 MyBatis 的行为,务请慎之又慎。
+除了用插件来修改 MyBatis 核心行为以外,还可以通过完全覆盖配置类来达到目的。只需继承配置类后覆盖其中的某个方法,再把它传递到 SqlSessionFactoryBuilder.build(myConfig) 方法即可。再次重申,这可能会极大影响 MyBatis 的行为,务请慎之又慎。
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, - 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中 - 使用相同的 SQL 映射。有许多类似的使用场景。
+ 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema + 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory
实例只能选择一种环境。
@@ -1580,13 +1579,13 @@ public class ExamplePlugin implements Interceptor {
如果忽略了环境参数,那么默认环境将会被加载,如下所示: +
如果忽略了环境参数,那么将会加载默认环境,如下所示:
环境元素定义了如何配置环境。 +
environments 元素定义了如何配置环境。
- 注意这里的关键点: + 注意一些关键点:
- 默认的环境和环境 ID 是自解释的,因此一目了然。 - 你可以对环境随意命名,但一定要保证默认的环境 ID 要匹配其中一个环境 ID。 + 默认环境和环境 ID 顾名思义。 + 环境可以随意命名,但务必保证默认的环境 ID 要匹配其中一个环境 ID。
事务管理器(transactionManager)
-在 MyBatis 中有两种类型的事务管理器(也就是 type=”[JDBC|MANAGED]”):
+在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
- 提示如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器, - 因为 Spring 模块会使用自带的管理器来覆盖前面的配置。 + 提示 + 如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器,因为 + Spring 模块会使用自带的管理器来覆盖前面的配置。
- 这两种事务管理器类型都不需要设置任何属性。它们其实是类型别名,换句话说,你可以使用 - TransactionFactory 接口的实现类的完全限定名或类型别名代替它们。 + 这两种事务管理器类型都不需要设置任何属性。它们其实是类型别名,换句话说,你可以用 + TransactionFactory 接口实现类的全限定名或类型别名代替它们。
任何在 XML 中配置的属性在实例化之后将会被传递给 setProperties() - 方法。你也需要创建一个 Transaction 接口的实现类,这个接口也很简单:
+在事务管理器实例化后,所有在 XML 中配置的属性将会被传递给 setProperties() + 方法。你的实现还需要创建一个 Transaction 接口的实现类,这个接口也很简单:
dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
有三种内建的数据源类型(也就是 type=”[UNPOOLED|POOLED|JNDI]”):
+有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]"):
- UNPOOLED– 这个数据源的实现只是每次被请求时打开和关闭连接。虽然有点慢,但对于在数据库连接可用性方面没有太高要求的简单应用程序来说,是一个很好的选择。 - 不同的数据库在性能方面的表现也是不一样的,对于某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。UNPOOLED 类型的数据源具有以下属性。:
+ UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择。 + 性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。UNPOOLED 类型的数据源仅仅需要配置以下 5 种属性:driver
– 这是 JDBC 驱动的 Java 类的完全限定名(并不是 JDBC 驱动中可能包含的数据源类)。
+ driver
– 这是 JDBC 驱动的 Java 类全限定名(并不是 JDBC 驱动中可能包含的数据源类)。
url
– 这是数据库的 JDBC URL 地址。
defaultTransactionIsolationLevel
– 默认的连接事务隔离级别。
defaultNetworkTimeout
– The default network timeout value in milliseconds to wait for the database operation to complete. See the API documentation of java.sql.Connection#setNetworkTimeout()
for details.
+ defaultNetworkTimeout
– 等待数据库操作完成的默认网络超时时间(单位:毫秒)。查看 java.sql.Connection#setNetworkTimeout()
的 API 文档以获取更多信息。
作为可选项,你也可以传递属性给数据库驱动。只需在属性名加上“driver.”前缀即可,例如: @@ -1695,30 +1696,29 @@ SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, propert
driver.encoding=UTF8
这将通过 DriverManager.getConnection(url,driverProperties) 方法传递值为 +
这将通过 DriverManager.getConnection(url, driverProperties) 方法传递值为
UTF8
的 encoding
属性给数据库驱动。
POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 - 这是一种使得并发 Web 应用快速响应请求的流行处理方式。 + 这种处理方式很流行,能使并发 Web 应用快速响应请求。
除了上述提到 UNPOOLED 下的属性外,还有更多属性用来配置 POOLED 的数据源:
poolMaximumActiveConnections
– 在任意时间可以存在的活动(也就是正在使用)连接数量,默认值:10
+ poolMaximumActiveConnections
– 在任意时间可存在的活动(正在使用)连接数量,默认值:10
poolMaximumIdleConnections
– 任意时间可能存在的空闲连接数。
-
poolMaximumCheckoutTime
– 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒)
poolTimeToWait
– 这是一个底层设置,如果获取连接花费了相当长的时间,连接池会打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直安静的失败),默认值:20000 毫秒(即 20 秒)。
+ poolTimeToWait
– 这是一个底层设置,如果获取连接花费了相当长的时间,连接池会打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直失败且不打印日志),默认值:20000 毫秒(即 20 秒)。
poolMaximumLocalBadConnectionTolerance
– 这是一个关于坏连接容忍度的底层设置,
作用于每一个尝试从缓存池获取连接的线程。
如果这个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超过 poolMaximumIdleConnections
- 与 poolMaximumLocalBadConnectionTolerance
之和。 默认值:3 (新增于 3.4.5)
+ 与 poolMaximumLocalBadConnectionTolerance
之和。 默认值:3(新增于 3.4.5)
poolPingQuery
– 发送到数据库的侦测查询,用来检验连接是否正常工作并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动失败时带有一个恰当的错误消息。
+ poolPingQuery
– 发送到数据库的侦测查询,用来检验连接是否正常工作并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动出错时返回恰当的错误消息。
poolPingEnabled
– 是否启用侦测查询。若开启,需要设置 poolPingQuery
属性为一个可执行的 SQL 语句(最好是一个速度非常快的 SQL 语句),默认值:false。
- JNDI – 这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。这种数据源配置只需要两个属性: + JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。这种数据源配置只需要两个属性:
initial_context
– 这个属性用来在 InitialContext 中寻找上下文(即,initialContext.lookup(initial_context))。这是个可选属性,如果忽略,那么将会直接从 InitialContext 中寻找 data_source 属性。
data_source
– 这是引用数据源实例位置的上下文的路径。提供了 initial_context 配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找。
+ data_source
– 这是引用数据源实例位置的上下文路径。提供了 initial_context 配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找。
和其他数据源配置类似,可以通过添加前缀“env.”直接把属性传递给初始上下文。比如: +
和其他数据源配置类似,可以通过添加前缀“env.”直接把属性传递给 InitialContext。比如:
env.encoding=UTF8
这就会在初始上下文(InitialContext)实例化时往它的构造方法传递值为 UTF8
的 encoding
属性。
+
这就会在 InitialContext 实例化时往它的构造方法传递值为 UTF8
的 encoding
属性。
- 你可以通过实现接口 org.apache.ibatis.datasource.DataSourceFactory
来使用第三方数据源:
+ 你可以通过实现接口 org.apache.ibatis.datasource.DataSourceFactory
来使用第三方数据源实现:
为了令其工作,记得为每个希望 MyBatis 调用的 setter 方法在配置文件中增加对应的属性。 +
为了令其工作,记得在配置文件中为每个希望 MyBatis 调用的 setter 方法增加对应的属性。 下面是一个可以连接至 PostgreSQL 数据库的例子:
MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId
属性。
- MyBatis 会加载不带 databaseId
属性和带有匹配当前数据库 databaseId
属性的所有语句。
+ MyBatis 会加载带有匹配当前数据库 databaseId
属性和所有不带 databaseId
属性的语句。
如果同时找到带有 databaseId
和不带 databaseId
的相同语句,则后者会被舍弃。
- 为支持多厂商特性只要像下面这样在 mybatis-config.xml 文件中加入 databaseIdProvider
即可:
+ 为支持多厂商特性,只要像下面这样在 mybatis-config.xml 文件中加入 databaseIdProvider
即可:
- DB_VENDOR 对应的 databaseIdProvider 实现会将 databaseId 设置为
+ databaseIdProvider 对应的 DB_VENDOR 实现会将 databaseId 设置为
DatabaseMetaData#getDatabaseProductName()
返回的字符串。
- 由于通常情况下这些字符串都非常长而且相同产品的不同版本会返回不同的值,所以你可能想通过设置属性别名来使其变短,如下:
+ 由于通常情况下这些字符串都非常长,而且相同产品的不同版本会返回不同的值,你可能想通过设置属性别名来使其变短:
- 在提供了属性别名时,DB_VENDOR 的 databaseIdProvider 实现会将 databaseId
- 设置为第一个数据库产品名与属性中的名称相匹配的值,如果没有匹配的属性将会设置为 “null”。
+ 在提供了属性别名时,databaseIdProvider 的 DB_VENDOR 实现会将 databaseId
+ 设置为数据库产品名与属性中的名称第一个相匹配的值,如果没有匹配的属性,将会设置为 “null”。
在这个例子中,如果 getDatabaseProductName()
返回“Oracle (DataDirect)”,databaseId 将被设置为“oracle”。
- 既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要定义 SQL 映射语句了。
- 但是首先我们需要告诉 MyBatis 到哪里去找到这些语句。
- Java 在自动查找这方面没有提供一个很好的方法,所以最佳的方式是告诉 MyBatis 到哪里去找映射文件。
- 你可以使用相对于类路径的资源引用,
- 或完全限定资源定位符(包括 file:///
的 URL),或类名和包名等。例如:
+ 既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。
+ 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。
+ 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉
+ MyBatis 到哪里去找映射文件。
+ 你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:///
形式的 URL),或类名和包名等。例如:
这些配置会告诉了 MyBatis 去哪里找映射文件,剩下的细节就应该是每个 SQL +
这些配置会告诉 MyBatis 去哪里找映射文件,剩下的细节就应该是每个 SQL 映射文件了,也就是接下来我们要讨论的。
- 这些情况下,MyBatis 会在幕后自动创建一个 ResultMap
,再基于属性名来映射列到
- JavaBean 的属性上。如果列名和属性名没有精确匹配,可以在 SELECT
- 语句中对列使用别名(这是一个基本的 SQL 特性)来匹配标签。比如:
+ 在这些情况下,MyBatis 会在幕后自动创建一个 ResultMap
,再根据属性名来映射列到
+ JavaBean 的属性上。如果列名和属性名不能匹配上,可以在 SELECT
+ 语句中设置列别名(这是一个基本的 SQL 特性)来完成匹配。比如:
- ResultMap
最优秀的地方在于,虽然你已经对它相当了解了,但是根本就不需要显式地用到他们。
- 上面这些简单的示例根本不需要下面这些繁琐的配置。
- 但出于示范的原因,让我们来看看最后一个示例中,如果使用外部的
+ 在学习了上面的知识后,你会发现上面的例子没有一个需要显式配置
+ ResultMap
,这就是 ResultMap
+ 的优秀之处——你完全可以不用显式地配置它们。
+ 虽然上面的例子不用显式配置 ResultMap
。
+ 但为了讲解,我们来看看如果在刚刚的示例中,显式使用外部的
resultMap
会怎样,这也是解决列名不匹配的另外一种方式。
- 而在引用它的语句中使用 resultMap
属性就行了(注意我们去掉了
+ 然后在引用它的语句中设置 resultMap
属性就行了(注意我们去掉了
resultType
属性)。比如:
- 如果世界总是这么简单就好了。 + 如果这个世界总是这么简单就好了。
MyBatis 创建时的一个思想是:数据库不可能永远是你所想或所需的那个样子。 - 我们希望每个数据库都具备良好的第三范式或 BCNF 范式,可惜它们不总都是这样。 - 如果能有一种完美的数据库映射模式,所有应用程序都可以使用它,那就太好了,但可惜也没有。 + 我们希望每个数据库都具备良好的第三范式或 BCNF 范式,可惜它们并不都是那样。 + 如果能有一种数据库映射模式,完美适配所有的应用程序,那就太好了,但可惜也没有。 而 ResultMap 就是 MyBatis 对这个问题的答案。
@@ -821,8 +827,8 @@ public class User {你可能想把它映射到一个智能的对象模型,这个对象表示了一篇博客,它由某位作者所写,有很多的博文,每篇博文有零或多条的评论和标签。 - 我们来看看下面这个完整的例子,它是一个非常复杂的结果映射(假设作者,博客,博文,评论和标签都是类型别名)。 - 不用紧张,我们会一步一步来说明。虽然它看起来令人望而生畏,但其实非常简单。 + 我们先来看看下面这个完整的例子,它是一个非常复杂的结果映射(假设作者,博客,博文,评论和标签都是类型别名)。 + 不用紧张,我们会一步一步地来说明。虽然它看起来令人望而生畏,但其实非常简单。
association
– 一个复杂类型的关联;许多结果将包装成这种类型
resultMap
元素,或者从别处引用一个resultMap
元素,或是对其它结果映射的引用collection
– 一个复杂类型的集合
resultMap
元素,或者从别处引用一个resultMap
元素,或是对其它结果映射的引用case
– 基于某些值的结果映射
case
本身可以是一个 resultMap
元素,因此可以具有相同的结构和元素,或者从别处引用一个case
也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射autoMapping
最佳实践 - 最好一步步地建立结果映射。单元测试可以在这个过程中起到很大帮助。 - 如果你尝试一次创建一个像上面示例那样的巨大的结果映射,那么很可能会出现错误而且很难去使用它来完成工作。 - 从最简单的形态开始,逐步迭代。而且别忘了单元测试! - 使用框架的缺点是有时候它们看上去像黑盒子(无论源代码是否可见)。 - 为了确保你实现的行为和想要的一致,最好的选择是编写单元测试。提交 bug - 的时候它也能起到很大的作用。 + 最好逐步建立结果映射。单元测试可以在这个过程中起到很大帮助。 + 如果你尝试一次性创建像上面示例那么巨大的结果映射,不仅容易出错,难度也会直线上升。 + 所以,从最简单的形态开始,逐步迭代。而且别忘了单元测试! + 有时候,框架的行为像是一个黑盒子(无论是否开源)。因此,为了确保实现的行为与你的期望相一致,最好编写单元测试。 + 并且单元测试在提交 bug 时也能起到很大的作用。
@@ -945,14 +950,14 @@ public class User {
- 这些是结果映射最基本的内容。id 和 result + 这些元素是结果映射的基础。id 和 result 元素都将一个列的值映射到一个简单数据类型(String, int, double, Date 等)的属性或字段。
这两者之间的唯一不同是,id - 元素表示的结果将是对象的标识属性,这会在比较对象实例时用到。 + 元素对应的属性会被标记为对象的标识符,在比较对象实例时使用。 这样可以提高整体的性能,尤其是进行缓存和嵌套结果映射(也就是连接映射)的时候。
@@ -972,9 +977,9 @@ public class User {property
javaType
typeHandler