MyBatis 的 XML 配置文件包含了影响 MyBatis 行为甚深的设置和属性信息。 XML 文档 的高层级结构如下:
这些是外部化的, 可替代的属性, 这些属性也可以配置在典型的 Java 属性配置文件中, 或者通过 properties 元素的子元素来传递。例如:
resource="org/mybatis/example/config.properties">
name="username" value="dev_user"/>
name="password" value="F2Fa3!33TYyg"/>
其中的属性就可以在整个配置文件中使用,使用可替换的属性来实现动态配置。比如:
type="POOLED">
name="driver" value="${driver}"/>
name="url" value="${url}"/>
name="username" value="${username}"/>
name="password" value="${password}"/>
这个例子中的 username 和 password 将会由 properties 元素中设置的值来替换。 driver 和 url 属性将会从包含进来的 config.properties 文件中的值来替换。这里提供很多配置的选项。
属性也可以被传递到 SqlSessionBuilder.build()方法中。例如:
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, props); // ... or ... SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment, props);
如果在这些地方,属性多于一个的话,MyBatis 按照如下的顺序加载它们:
因此, 最高优先级的属性是那些作为方法参数的, 然后是资源/url 属性, 最后是 properties 元素中指定的属性。
这些是极其重要的调整, 它们会修改 MyBatis 在运行时的行为方式。 下面这个表格描述 了设置信息,它们的含义和默认值。
设置参数 | 描述 | 有效值 | 默认值 |
---|---|---|---|
cacheEnabled | 这个配置使全局的映射器启用或禁用 缓存。 | true | false | true |
lazyLoadingEnabled | 全局启用或禁用延迟加载。当禁用时, 所有关联对象都会即时加载。 | true | false | true |
aggressiveLazyLoading | 当启用时, 有延迟加载属性的对象在被 调用时将会完全加载任意属性。否则, 每种属性将会按需要加载。 | true | false | true |
multipleResultSetsEnabled | 允许或不允许多种结果集从一个单独 的语句中返回(需要适合的驱动) | true | false | true |
useColumnLabel | 使用列标签代替列名。 不同的驱动在这 方便表现不同。 参考驱动文档或充分测 试两种方法来决定所使用的驱动。 | true | false | true |
useGeneratedKeys | 允许 JDBC 支持生成的键。 需要适合的 驱动。 如果设置为 true 则这个设置强制 生成的键被使用, 尽管一些驱动拒绝兼 容但仍然有效(比如 Derby) | true | false | False |
autoMappingBehavior | 指定 MyBatis 如何自动映射列到字段/ 属性。PARTIAL 只会自动映射简单, 没有嵌套的结果。FULL 会自动映射任 意复杂的结果(嵌套的或其他情况) 。 | NONE, PARTIAL, FULL | PARTIAL |
defaultExecutorType | 配置默认的执行器。SIMPLE 执行器没 有什么特别之处。REUSE 执行器重用 预处理语句。BATCH 执行器重用语句 和批量更新 | SIMPLE REUSE BATCH | SIMPLE |
defaultStatementTimeout | 设置超时时间, 它决定驱动等待一个数 据库响应的时间。 | Any positive integer | Not Set (null) |
safeRowBoundsEnabled | Allows using RowBounds on nested statements. | true | false | False |
mapUnderscoreToCamelCase | Enables automatic mapping from classic database column names A_COLUMN to camel case classic Java property names aColumn. | true | false | False |
localCacheScope | MyBatis uses local cache to prevent circular references and speed up repeated nested queries. By default (SESSION) all queries executed during a session are cached. If localCacheScope=STATEMENT local session will be used just for statement execution, no data will be shared between two different calls to the same SqlSession. | SESSION | STATEMENT | SESSION |
jdbcTypeForNull | Specifies the JDBC type for null values when no specific JDBC type was provided for the parameter. Some drivers require specifying the column JDBC type but others work with generic values like NULL, VARCHAR or OTHER. | JdbcType enumeration. Most common are: NULL, VARCHAR and OTHER | OTHER |
lazyLoadTriggerMethods | Specifies which Object's methods trigger a lazy load | A method name list separated by commas | equals,clone,hashCode,toString |
defaultScriptingLanguage | Specifies the language used by default for dynamic SQL generation. | A type alias or fully qualified class name. | org.apache.ibatis.scripting.xmltags.XMLDynamicLanguageDriver |
callSettersOnNulls | 当结果集中含有Null值时是否执行映射对象的setter或者Map对象的put方法。此设置对于原始类型如int,boolean等无效。 | true | false | false |
logPrefix | Specifies the prefix string that MyBatis will add to the logger names. | Any String | Not set |
logImpl | Specifies which logging implementation MyBatis should use. If this setting is not present logging implementation will be autodiscovered. | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | Not set |
proxyFactory | Specifies the proxy tool that MyBatis will use for creating lazy loading capable objects. | CGLIB | JAVASSIST | CGLIB |
一个设置信息元素的示例,完全的配置如下所示:
name="cacheEnabled" value="true"/> name="lazyLoadingEnabled" value="true"/> name="multipleResultSetsEnabled" value="true"/> name="useColumnLabel" value="true"/> name="useGeneratedKeys" value="false"/> name="autoMappingBehavior" value="PARTIAL"/> name="defaultExecutorType" value="SIMPLE"/> name="defaultStatementTimeout" value="25"/> name="safeRowBoundsEnabled" value="false"/> name="mapUnderscoreToCamelCase" value="false"/> name="localCacheScope" value="SESSION"/> name="jdbcTypeForNull" value="OTHER"/> name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
类型别名是为 Java 类型命名一个短的名字。 它只和 XML 配置有关, 只用来减少类完全 限定名的多余部分。例如:
alias="Author" type="domain.blog.Author"/> alias="Blog" type="domain.blog.Blog"/> alias="Comment" type="domain.blog.Comment"/> alias="Post" type="domain.blog.Post"/> alias="Section" type="domain.blog.Section"/> alias="Tag" type="domain.blog.Tag"/>
使用这个配置, “Blog”可以任意用来替代“domain.blog. Blog”所使用的地方。
You can also specify a package where MyBatis will search for beans. For example:
name="domain.blog"/>
Each bean found in domain.blog , if no annotation is found, will be registered as an alias using uncapitalized non-qualified class name of the bean. Thas is domain.blog.Author will be registered as Author . If the @Alias annotation is found its value will be used as an alias. See the example below:
@Alias("author") public class Author { ... }
对于普通的 Java 类型,有许多内建的类型别名。它们都是大小写不敏感的,由于重载 的名字,要注意原生类型的特殊处理。
别名 | 映射的类型 |
---|---|
_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 在预处理语句中设置一个参数, 还是从结果集中取出一个值时, 类型处 理器被用来将获取的值以合适的方式转换成 Java 类型。下面这个表格描述了默认的类型处 理器。
类型处理器 | Java 类型 | JDBC 类型 |
---|---|---|
BooleanTypeHandler | java.lang.Boolean, boolean | 任何兼容的布尔值 |
ByteTypeHandler | java.lang.Byte, byte | 任何兼容的数字或字节类型 |
ShortTypeHandler | java.lang.Short, short | 任何兼容的数字或短整型 |
IntegerTypeHandler | java.lang.Integer, int | 任何兼容的数字和整型 |
LongTypeHandler | java.lang.Long, long | 任何兼容的数字或长整型 |
FloatTypeHandler | java.lang.Float, float | 任何兼容的数字或单精度浮点型 |
DoubleTypeHandler | java.lang.Double, double | 任何兼容的数字或双精度浮点型 |
BigDecimalTypeHandler | java.math.BigDecimal | 任何兼容的数字或十进制小数类型 |
StringTypeHandler | java.lang.String | CHAR 和 VARCHAR 类型 |
ClobTypeHandler | java.lang.String | CLOB 和 LONGVARCHAR 类型 |
NStringTypeHandler | java.lang.String | NVARCHAR 和 NCHAR 类型 |
NClobTypeHandler | java.lang.String | NCLOB 类型 |
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 | 其他或未指定类型 |
EnumTypeHandler | Enumeration Type | VARCHAR-任何兼容的字符串类型, 作为代码存储(而不是索引) |
EnumOrdinalTypeHandler | Enumeration Type | Any compatible NUMERIC or DOUBLE, as the position is stored (not the code itself). |
你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。 //TODO translation needed To do so, simply extend theorg.apache.ibatis.type.BaseTypeHandler class and optionally map your new TypeHandler class to a JDBC type. 例如:
// 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); } }
handler="org.mybatis.example.ExampleTypeHandler"/>
使用这样的类型处理器将会覆盖已经存在的处理 Java 的 String 类型属性和 VARCHAR 参数及结果的类型处理器。 要注意 MyBatis 不会审视数据库元信息来决定使用哪种类型, 所 以你必须在参数和结果映射中指定那是 VARCHAR 类型的字段,来绑定到正确的类型处理 器上。这是因为 MyBatis 直到语句被执行都不知道数据类型的这个现实导致的。
// TODO translation needed MyBatis will know the the Java type that you want to handle with this TypeHandler by introspecting its generic type, but you can override this behavior by two means:
Associated JDBC type can be specified by two means:
And finally you can let MyBatis search for your TypeHandlers:
name="org.mybatis.example"/>
Note that when using the autodiscovery feature JDBC types can only be specified with annotations.
You can create a generic TypeHandler that is able to handle more than one class. For that purpose add a constructor that receives the class as a parameter and MyBatis will pass the actual class when constructing the 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; } ...
EnumTypeHandler and EnumOrdinalTypeHandler are generic TypeHandlers. We will learn about them in the following section.
If you want to map an Enum, you'll need to use either EnumTypeHandler or EnumOrdinalTypeHandler.
For example, let's say that we need to store the rounding mode that should be used with some number if it needs to be rounded. By default, MyBatis uses EnumTypeHandler to convert the Enum values to their names.
Note EnumTypeHandler is special in the sense that unlike other handlers, it does not handle just one specific class, but any class that extends EnumHowever, we may not want to store names. Our DBA may insist on an integer code instead. That's just as easy: add EnumOrdinalTypeHandler to thetypeHandlers in your config file, and now each RoundingMode will be mapped to an integer using its ordinal value.
handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="java.math.RoundingMode"/>
But what if you want to map the same Enum to a string in one place and to integer in another?
The auto-mapper will automatically use EnumOrdinalTypeHandler, so if we want to go back to using plain old ordinary EnumTypeHandler, we have to tell it, by explicitly setting the type handler to use for those SQL statements.
(Mapper files aren't covered until the next section, so if this is your first time reading through the documentation, you may want to skip this for now and come back to it later.)
namespace="org.apache.ibatis.submitted.rounding.Mapper"> type="org.apache.ibatis.submitted.rounding.User" id="usermap"> column="id" property="id"/> column="name" property="name"/> column="funkyNumber" property="funkyNumber"/> column="roundingMode" property="roundingMode"/> id="insert" parameterType="org.apache.ibatis.submitted.rounding.User"> insert into users (id, name, funkyNumber, roundingMode) values ( #{id}, #{name}, #{funkyNumber}, #{roundingMode} ) type="org.apache.ibatis.submitted.rounding.User" id="usermap2"> column="id" property="id"/> column="name" property="name"/> column="funkyNumber" property="funkyNumber"/> column="roundingMode" property="roundingMode" typeHandler="org.apache.ibatis.type.EnumTypeHandler"/> id="insert2" parameterType="org.apache.ibatis.submitted.rounding.User"> insert into users2 (id, name, funkyNumber, roundingMode) values ( #{id}, #{name}, #{funkyNumber}, #{roundingMode, typeHandler=org.apache.ibatis.type.EnumTypeHandler} )
Note that this forces us to use a resultMap instead of a resultType in our select statements.
MyBatis 每次创建结果对象新的实例时, 它使用一个 ObjectFactory 实例来完成。 如果参 数映射存在,默认的 ObjectFactory 不比使用默认构造方法或带参数的构造方法实例化目标 类做的工作多。如果你想重写默认的 ObjectFactory,你可以创建你自己的。比如:
// ExampleObjectFactory.java public class ExampleObjectFactory extends DefaultObjectFactory { public Object create(Class type) { return super.create(type); } public Object create(Class type, List<Class> constructorArgTypes, List<Object> constructorArgs) { return super.create(type, constructorArgTypes, constructorArgs); } public void setProperties(Properties properties) { super.setProperties(properties); } }
type="org.mybatis.example.ExampleObjectFactory"> name="someProperty" value="100"/>
ObjectFactory 接口很简单。它包含两个创建用的方法,一个是处理默认构造方法的,另 外一个是处理带参数构造方法的。最终,setProperties 方法可以被用来配置 ObjectFactory。 在 初 始化 你 的 ObjectFactory 实例 后 , objectFactory 元素 体 中定 义的 属 性会 被传 递 给 setProperties 方法。
MyBatis 允许你在某一点拦截已映射语句执行的调用。默认情况下,MyBatis 允许使用 插件来拦截方法调用:
这些类中方法的详情可以通过查看每个方法的签名来发现 ,而且它们的源代码在 MyBatis 的发行包中有。你应该理解你覆盖方法的行为,假设你所做的要比监视调用要多。 如果你尝试修改或覆盖一个给定的方法, 你可能会打破 MyBatis 的核心。 这是低层次的类和 方法,要谨慎使用插件。
使用插件是它们提供的非常简单的力量。 简单实现拦截器接口, 要确定你想拦截的指定 签名。
// ExamplePlugin.java @Intercepts({@Signature( type= Executor.class, method = "update", args = {MappedStatement.class,Object.class})}) public class ExamplePlugin implements Interceptor { public Object intercept(Invocation invocation) throws Throwable { return invocation.proceed(); } public Object plugin(Object target) { return Plugin.wrap(target, this); } public void setProperties(Properties properties) { } }
interceptor="org.mybatis.example.ExamplePlugin"> name="someProperty" value="100"/>
上面的插件将会拦截在 Executor 实例中所有的“update”方法调用,它也是负责低层次 映射语句执行的内部对象。
NOTE 覆盖配置类
除了用插件来修改 MyBatis 核心行为之外, 你也可以完全覆盖配置类。 简单扩展它, 然后覆盖其中的任意方法,之后传递它到 sqlSessionFactoryBuilder.build(myConfig)方法 的调用。这可能会严重影响 MyBatis 的行为,所以要小心。
MyBatis 可以配置多种环境。这会帮助你将 SQL 映射应用于多种数据库之中。例如, 你也许为开发要设置不同的配置, 测试和生产环境。 或者你可能有多种生产级数据库却共享 相同的模式,所以你会想对不同数据库使用相同的 SQL 映射。这种用例是很多的。
一个很重要的问题要记得:你可以配置多种环境,但你只能为每个 SqlSessionFactory 实例选择一个。
所以,如果你想连接两个数据库,你需要创建两个 SqlSessionFactory 实例,每个数据库 对应一个。而如果是三个数据库,你就需要三个实例,以此类推。记忆起来很简单:
为了明确创建哪种环境,你可以将它作为可选的参数传递给 SqlSessionFactoryBuilder。 可以接受环境配置的两个方法签名是:
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment); SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment,properties);
如果环境被忽略,那么默认环境将会被加载,如下进行:
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader); SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader,properties);
环境元素定义了如何配置环境。
default="development">
id="development">
type="JDBC">
name="..." value="..."/>
type="POOLED">
name="driver" value="${driver}"/>
name="url" value="${url}"/>
name="username" value="${username}"/>
name="password" value="${password}"/>
注意这里的键:
默认的环境和环境 ID 是自我解释的。你可以使用你喜欢的名称来命名,只要确定默认 的要匹配其中之一。
transactionManager
在 MyBatis 中有两种事务管理器类型(也就是 type=”[JDBC|MANAGED]”):
type="MANAGED">
name="closeConnection" value="false"/>
NOTE If you are planning to use MyBatis with Spring there is no need to configure any TransactionManager because the Spring module will set its own one overriding any previously set configuration.
这两种事务管理器都不需要任何属性。然而它们都是类型别名,要替换使用它们,你需 要放置将你自己的类的完全限定名或类型别名,它们引用了你对 TransactionFactory 接口的实现 类。
public interface TransactionFactory { void setProperties(Properties props); 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; }
使用这两个接口,你可以完全自定义 MyBatis 对事务的处理。
dataSource
dataSource 元素使用基本的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
有三种内建的数据源类型(也就是 type=”???”):
UNPOOLED – 这个数据源的实现是每次被请求时简单打开和关闭连接。它有一点慢, 这是对简单应用程序的一个很好的选择, 因为它不需要及时的可用连接。 不同的数据库对这 个的表现也是不一样的, 所以对某些数据库来说配置数据源并不重要, 这个配置也是闲置的。 UNPOOLED 类型的数据源仅仅用来配置以下 5 种属性:
作为可选项,你可以传递数据库驱动的属性。要这样做,属性的前缀是以“driver.”开 头的,例如:
这 样 就 会 传 递 以 值 “ UTF8 ” 来 传 递 属 性 “ encoding ”, 它 是 通 过 DriverManager.getConnection(url,driverProperties)方法传递给数据库驱动。
POOLED – 这是 JDBC 连接对象的数据源连接池的实现,用来避免创建新的连接实例 时必要的初始连接和认证时间。这是一种当前 Web 应用程序用来快速响应请求很流行的方 法。
除了上述(UNPOOLED)的属性之外,还有很多属性可以用来配置 POOLED 数据源:
JNDI – 这个数据源的实现是为了使用如 Spring 或应用服务器这类的容器, 容器可以集 中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。这个数据源配置只需要两个属 性:
和其他数据源配置相似, 它也可以通过名为 “env.” 的前缀直接向初始上下文发送属性。 比如:
在初始化之后,这就会以值“UTF8”向初始上下文的构造方法传递名为“encoding” 的属性。
MyBatis is able to execute different statements depending on your database vendor. The multi-db vendor support is based on the mapped statementsdatabaseId attribute. MyBatis will load all statements with no databaseId attribute or with a databaseId that matches the current one. If case the same statement if found with and without the databaseId the latter will be discarded. To enable the multi vendor support add a databaseIdProvider to mybatis-config.xml file as follows:
type="DB_VENDOR" />
The DB_VENDOR implementation databaseIdProvider sets as a databaseId the String returned by DatabaseMetaData#getDatabaseProductName(). As usually that string is too long and also, different versions of the same product return different values, so you may want to translate it to a shorter one by adding properties like follows:
type="DB_VENDOR">
name="SQL Server" value="sqlserver"/>
name="DB2" value="db2"/>
name="Oracle" value="oracle" />
When properties are provided, the DB_VENDOR databaseIdProvider will search the property value corresponding to the first key found in the returned database product name or "null" if there is not a matching property. In this case, if getDatabaseProductName() returns "Oracle (DataDirect)" the databaseId will be set to "oracle".
You can build your own database provider by implementing the interface org.apache.ibatis.mapping.DatabaseIdProvider and registerin it in mybatis-config.xml:
public interface DatabaseIdProvider { void setProperties(Properties p); String getDatabaseId(DataSource dataSource) throws SQLException; }
既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要定义 SQL 映射语句了。 但是, 首先我们需要告诉 MyBatis 到哪里去找到这些语句。 Java 在这方面没有提供一个很好 的方法, 所以最佳的方式是告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的 资源引用,或者字符表示,或 url 引用的完全限定名(包括 file:///URLs) 。例如:
resource="org/mybatis/builder/AuthorMapper.xml"/> resource="org/mybatis/builder/BlogMapper.xml"/> resource="org/mybatis/builder/PostMapper.xml"/>
url="file:///var/mappers/AuthorMapper.xml"/> url="file:///var/mappers/BlogMapper.xml"/> url="file:///var/mappers/PostMapper.xml"/>
class="org.mybatis.builder.AuthorMapper"/> class="org.mybatis.builder.BlogMapper"/> class="org.mybatis.builder.PostMapper"/>
name="org.mybatis.builder"/>
这些语句简单告诉了 MyBatis 去哪里找映射文件。其余的细节就是在每个 SQL 映射文 件中了,下面的部分我们来讨论 SQL 映射文件。