MyBatis 的真正强大在于它的映射语句,也是它的魔力所在。因为它的异常强大,映射器的 XML 文件就显得相对简单。若是拿它跟具备相同功能的 JDBC 代码进行对比,你会当即发现省掉了将近 95% 的代码。MyBatis 就是针对 SQL 构建的,而且比普通的方法作的更好。html
SQL 映射文件有不多的几个顶级元素(按照它们应该被定义的顺序):java
cache
– 给定命名空间的缓存配置。cache-ref
– 其余命名空间缓存配置的引用。resultMap
– 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。parameterMap
– 已废弃!老式风格的参数映射。内联参数是首选,这个元素可能在未来被移除,这里不会记录。sql
– 可被其余语句引用的可重用语句块。insert
– 映射插入语句update
– 映射更新语句delete
– 映射删除语句select
– 映射查询语句下一部分将从语句自己开始来描述每一个元素的细节。算法
查询语句是 MyBatis 中最经常使用的元素之一,光能把数据存到数据库中价值并不大,若是还能从新取出来才有用,多数应用也都是查询比修改要频繁。对每一个插入、更新或删除操做,一般对应多个查询操做。这是 MyBatis 的基本原则之一,也是将焦点和努力放到查询和结果映射的缘由。简单查询的 select 元素是很是简单的。好比:sql
<select id="selectPerson" parameterType="int" resultType="hashmap"> SELECT * FROM PERSON WHERE ID = #{id} </select>
这个语句被称做 selectPerson,接受一个 int(或 Integer)类型的参数,并返回一个 HashMap 类型的对象,其中的键是列名,值即是结果行中的对应值。数据库
注意参数符号:apache
#{id}
这就告诉 MyBatis 建立一个预处理语句参数,经过 JDBC,这样的一个参数在 SQL 中会由一个“?”来标识,并被传递到一个新的预处理语句中,就像这样:编程
// Similar JDBC code, NOT MyBatis… String selectPerson = "SELECT * FROM PERSON WHERE ID=?"; PreparedStatement ps = conn.prepareStatement(selectPerson); ps.setInt(1,id);
固然,这须要不少单独的 JDBC 的代码来提取结果并将它们映射到对象实例中,这就是 MyBatis 节省你时间的地方。咱们须要深刻了解参数和结果映射,细节部分咱们下面来了解。数组
select 元素有不少属性容许你配置,来决定每条语句的做用细节。缓存
<select id="selectPerson" parameterType="int" parameterMap="deprecated" resultType="hashmap" resultMap="personResultMap" flushCache="false" useCache="true" timeout="10000" fetchSize="256" statementType="PREPARED" resultSetType="FORWARD_ONLY">
Select Attributes安全
属性 | 描述 |
---|---|
id | 在命名空间中惟一的标识符,能够被用来引用这条语句。 |
parameterType | 将会传入这条语句的参数类的彻底限定名或别名。 这个属性是可选的,由于 MyBatis 能够经过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。 |
使用内联参数映射和 parameterType 属性。 |
|
resultType | 从这条语句中返回的指望类型的类的彻底限定名或别名。 注意若是是集合情形,那应该是集合能够包含的类型, 而不能是集合自己。使用 resultType 或 resultMap,但不能同时使用。 |
resultMap | 外部 resultMap 的命名引用。 结果集的映射是 MyBatis 最强大的特性, 对其有一个很好的理解的话,许多复杂映射的情形都能迎刃而解。 使用 resultMap 或 resultType,但不能同时使用。 |
flushCache | 将其设置为 true,任什么时候候只要语句被调用, 都会致使本地缓存和二级缓存都会被清空,默认值:false。 |
useCache | 将其设置为 true,将会致使本条语句的结果被二级缓存, 默认值:对 select 元素为 true。 |
timeout | 这个设置是在抛出异常以前, 驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动)。 |
fetchSize | 这是尝试影响驱动程序每次批量返回的结果行数和这个设置值相等。 默认值为 unset(依赖驱动)。 |
statementType | STATEMENT,PREPARED 或 CALLABLE 的一个。 这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement, 默认值:PREPARED。 |
resultSetType | FORWARD_ONLY,SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE 中的一个, 默认值为 unset (依赖驱动)。 |
databaseId | 若是配置了 databaseIdProvider, MyBatis 会加载全部的不带 databaseId 或匹配当前 databaseId 的语句; 若是带或者不带的语句都有,则不带的会被忽略。 |
resultOrdered | 这个设置仅针对嵌套结果 select 语句适用: 若是为 true,就是假设包含了嵌套结果集或是分组了, 这样的话当返回一个主结果行的时候, 就不会发生有对前面结果集的引用的状况。 这就使得在获取嵌套的结果集的时候不至于致使内存不够用。默认值:false。 |
resultSets | 这个设置仅对多结果集的状况适用, 它将列出语句执行后返回的结果集并每一个结果集给一个名称, 名称是逗号分隔的。 |
数据变动语句 insert,update 和 delete 的实现很是接近:
<insert id="insertAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" keyProperty="" keyColumn="" useGeneratedKeys="" timeout="20"> <update id="updateAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" timeout="20"> <delete id="deleteAuthor" parameterType="domain.blog.Author" flushCache="true" statementType="PREPARED" timeout="20">
Insert, Update, Delete 's Attributes
属性 | 描述 |
---|---|
id | 命名空间中的惟一标识符,可被用来表明这条语句。 |
这个属性是可选的,由于 MyBatis 能够经过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。 |
|
parameterMap | 这是引用外部 parameterMap 的已经被废弃的方法。 使用内联参数映射和 parameterType 属性。 |
flushCache | 将其设置为 true,任什么时候候只要语句被调用, 都会致使本地缓存和二级缓存都会被清空, 默认值:true(对应插入、更新和删除语句)。 |
timeout | 这个设置是在抛出异常以前,驱动程序等待数据库返回请求结果的秒数。 默认值为 unset(依赖驱动)。 |
statementType | STATEMENT,PREPARED 或 CALLABLE 的一个。 这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
useGeneratedKeys | (仅对 insert 和 update 有用)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(好比:像 MySQL 和 SQL Server 这样的关系数据库管理系统的自动递增字段),默认值:false。 |
keyProperty | (仅对 insert 和 update 有用)惟一标记一个属性, MyBatis 会经过 getGeneratedKeys 的返回值 或者经过 insert 语句的 selectKey 子元素设置它的键值, 默认:unset。若是但愿获得多个生成的列,也能够是逗号分隔的属性名称列表。 |
keyColumn | (仅对 insert 和 update 有用)经过生成的键值设置表中的列名, 这个设置仅在某些数据库(像 PostgreSQL)是必须的, 当主键列不是表中的第一列的时候须要设置。 若是但愿获得多个生成的列,也能够是逗号分隔的属性名称列表。 |
databaseId | 若是配置了 databaseIdProvider,MyBatis 会加载全部的不带 databaseId 或匹配当前 databaseId 的语句; 若是带或者不带的语句都有,则不带的会被忽略。 |
下面就是 insert,update 和 delete 语句的示例:
<insert id="insertAuthor"> insert into Author (id,username,password,email,bio) values (#{id},#{username},#{password},#{email},#{bio}) </insert> <update id="updateAuthor"> update Author set username = #{username}, password = #{password}, email = #{email}, bio = #{bio} where id = #{id} </update> <delete id="deleteAuthor"> delete from Author where id = #{id} </delete>
如前所述,插入语句的配置规则更加丰富,在插入语句里面有一些额外的属性和子元素用来处理主键的生成,并且有多种生成方式。
首先,若是你的数据库支持自动生成主键的字段(好比 MySQL 和 SQL Server),那么你能够设置 useGeneratedKeys=”true”,而后再把 keyProperty 设置到目标属性上就OK了。例如,若是上面的 Author 表已经对 id 使用了自动生成的列类型,那么语句能够修改成:
<insert id="insertAuthor" useGeneratedKeys="true" keyProperty="id"> insert into Author (username,password,email,bio) values (#{username},#{password},#{email},#{bio}) </insert>
若是你的数据库还支持多行插入, 你也能够传入一个Authors数组或集合,并返回自动生成的主键。
<insert id="insertAuthor" useGeneratedKeys="true" keyProperty="id"> insert into Author (username, password, email, bio) values <foreach item="item" collection="list" separator=","> (#{item.username}, #{item.password}, #{item.email}, #{item.bio}) </foreach> </insert>
对于不支持自动生成类型的数据库或可能不支持自动生成主键的 JDBC 驱动,MyBatis 有另一种方法来生成主键。
这里有一个简单(甚至很傻)的示例,它能够生成一个随机 ID(你最好不要这么作,但这里展现了 MyBatis 处理问题的灵活性及其所关心的广度):
<insert id="insertAuthor"> <selectKey keyProperty="id" resultType="int" order="BEFORE"> select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1 </selectKey> insert into Author (id, username, password, email,bio, favourite_section) values (#{id}, #{username}, #{password}, #{email}, #{bio}, #{favouriteSection,jdbcType=VARCHAR}) </insert>
在上面的示例中,selectKey 元素将会首先运行,Author 的 id 会被设置,而后插入语句会被调用。这给你了一个和数据库中来处理自动生成的主键相似的行为,避免了使 Java 代码变得复杂。
selectKey 元素描述以下:
<selectKey keyProperty="id" resultType="int" order="BEFORE" statementType="PREPARED">
selectKey 属性
属性 | 描述 |
---|---|
keyProperty | selectKey 语句结果应该被设置的目标属性。 若是但愿获得多个生成的列,也能够是逗号分隔的属性名称列表。 |
keyColumn | 匹配属性的返回结果集中的列名称。 若是但愿获得多个生成的列,也能够是逗号分隔的属性名称列表。 |
resultType | 结果的类型。 MyBatis 一般能够推算出来,可是为了更加肯定写上也不会有什么问题。 MyBatis 容许任何简单类型用做主键的类型,包括字符串。 若是但愿做用于多个生成的列,则可使用一个包含指望属性的 Object 或一个 Map。 |
order | 这能够被设置为 BEFORE 或 AFTER。 若是设置为 BEFORE,那么它会首先选择主键, 设置 keyProperty 而后执行插入语句。 若是设置为 AFTER,那么先执行插入语句,而后是 selectKey 元素 - 这和像 Oracle 的数据库类似,在插入语句内部可能有嵌入索引调用。 |
statementType | 与前面相同, MyBatis 支持 STATEMENT,PREPARED 和 CALLABLE 语句的映射类型, 分别表明 PreparedStatement 和 CallableStatement 类型。 |
这个元素能够被用来定义可重用的 SQL 代码段,能够包含在其余语句中。它能够被静态地(在加载参数) 参数化. 不一样的属性值经过包含的实例变化. 好比:
<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>
这个 SQL 片断能够被包含在其余语句中,例如:
<select id="selectUsers" resultType="map"> select <include refid="userColumns"><property name="alias" value="t1"/></include>, <include refid="userColumns"><property name="alias" value="t2"/></include> from some_table t1 cross join some_table t2 </select>
属性值也能够被用在 include 元素的 refid 属性里(
<include refid="${include_target}"/>
)或 include 内部语句中(
${prefix}Table
),例如:
<sql id="sometable"> ${prefix}Table </sql> <sql id="someinclude"> from <include refid="${include_target}"/> </sql> <select id="select" resultType="map"> select field1, field2, field3 <include refid="someinclude"> <property name="prefix" value="Some"/> <property name="include_target" value="sometable"/> </include> </select>
前面的全部语句中你所见到的都是简单参数的例子,实际上参数是 MyBatis 很是强大的元素,对于简单的作法,大概 90% 的状况参数都不多,好比:
<select id="selectUsers" resultType="User"> select id, username, password from users where id = #{id} </select>
上面的这个示例说明了一个很是简单的命名参数映射。参数类型被设置为 int,这样这个参数就能够被设置成任何内容。原生的类型或简单数据类型(好比整型和字符串)由于没有相关属性,它会彻底用参数值来替代。然而,若是传入一个复杂的对象,行为就会有一点不一样了。好比:
<insert id="insertUser" parameterType="User"> insert into users (id, username, password) values (#{id}, #{username}, #{password}) </insert>
若是 User 类型的参数对象传递到了语句中,id、username 和 password 属性将会被查找,而后将它们的值传入预处理语句的参数中。
这点相对于向语句中传参是比较好的,并且又简单,不过参数映射的功能远不止于此。
首先,像 MyBatis 的其余部分同样,参数也能够指定一个特殊的数据类型。
#{property,javaType=int,jdbcType=NUMERIC}
像 MyBatis 的剩余部分同样,javaType 一般能够由参数对象肯定,除非该对象是一个 HashMap。这时所使用的 TypeHandler
应该明确指明 javaType。
若是一个列容许 null 值,而且会传递值 null 的参数,就必需要指定 JDBC Type。阅读 PreparedStatement.setNull() 的 JavaDocs 文档来获取更多信息。
为了之后定制类型处理方式,你也能够指定一个特殊的类型处理器类(或别名),好比:
#{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler}
尽管看起来配置变得愈来愈繁琐,但实际上,不多须要去设置它们。
对于数值类型,还有一个小数保留位数的设置,来肯定小数点后保留的位数。
#{height,javaType=double,jdbcType=NUMERIC,numericScale=2}
最后,mode 属性容许你指定 IN,OUT 或 INOUT 参数。若是参数为 OUT 或 INOUT,参数对象属性的真实值将会被改变,就像你在获取输出参数时所指望的那样。若是 mode 为 OUT(或 INOUT),并且 jdbcType 为 CURSOR(也就是 Oracle 的 REFCURSOR),你必须指定一个 resultMap 来映射结果集 ResultMap 到参数类型。要注意这里的 javaType 属性是可选的,若是留空而且 jdbcType 是 CURSOR,它会被自动地被设为 ResultMap。
#{department, mode=OUT, jdbcType=CURSOR, javaType=ResultSet, resultMap=departmentResultMap}
MyBatis 也支持不少高级的数据类型,好比结构体,可是当注册 out 参数时你必须告诉它语句类型名称。好比(再次提示,在实际中要像这样不能换行):
#{middleInitial, mode=OUT, jdbcType=STRUCT, jdbcTypeName=MY_TYPE, resultMap=departmentResultMap}
尽管全部这些选项很强大,但大多时候你只须简单地指定属性名,其余的事情 MyBatis 会本身去推断,顶多要为可能为空的列指定 jdbcType。
#{firstName} #{middleInitial,jdbcType=VARCHAR} #{lastName}
默认状况下,使用 #{} 格式的语法会致使 MyBatis 建立 PreparedStatement 参数并安全地设置参数(就像使用 ? 同样)。这样作更安全,更迅速,一般也是首选作法,不过有时你就是想直接在 SQL 语句中插入一个不转义的字符串。好比,像 ORDER BY,你能够这样来使用:
ORDER BY ${columnName}
这里 MyBatis 不会修改或转义字符串。
用这种方式接受用户的输入,并将其用于语句中的参数是不安全的,会致使潜在的 SQL 注入攻击,所以要么不容许用户输入这些字段,要么自行转义并检验。
resultMap 元素是 MyBatis 中最重要最强大的元素。它可让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来, 并在一些情形下容许你作一些 JDBC 不支持的事情。 实际上,在对复杂语句进行联合映射的时候,它极可能能够代替数千行的同等功能的代码。 ResultMap 的设计思想是,简单的语句不须要明确的结果映射,而复杂一点的语句只须要描述它们的关系就好了。
你已经见过简单映射语句的示例了,但没有明确的 resultMap。好比:
<select id="selectUsers" resultType="map"> select id, username, hashedPassword from some_table where id = #{id} </select>
上述语句只是简单地将全部的列映射到 HashMap 的键上,这由 resultType 属性指定。 虽然在大部分状况下都够用,可是 HashMap 不是一个很好的领域模型。 你的程序更可能会使用 JavaBean 或 POJO(Plain Old Java Objects,普通 Java 对象)做为领域模型。 MyBatis 对二者都支持。看看下面这个 JavaBean:
package com.someapp.model; public class User { private int id; private String username; private String hashedPassword; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getHashedPassword() { return hashedPassword; } public void setHashedPassword(String hashedPassword) { this.hashedPassword = hashedPassword; } }
基于 JavaBean 的规范,上面这个类有 3 个属性:id,username 和 hashedPassword。这些属性会对应到 select 语句中的列名。
这样的一个 JavaBean 能够被映射到 ResultSet,就像映射到 HashMap 同样简单。
<select id="selectUsers" resultType="com.someapp.model.User"> select id, username, hashedPassword from some_table where id = #{id} </select>
类型别名是你的好帮手。使用它们,你就能够不用输入类的彻底限定名称了。好比:
<!-- In mybatis-config.xml file --> <typeAlias type="com.someapp.model.User" alias="User"/> <!-- In SQL Mapping XML file --> <select id="selectUsers" resultType="User"> select id, username, hashedPassword from some_table where id = #{id} </select>
这些状况下,MyBatis 会在幕后自动建立一个 ResultMap,再基于属性名来映射列到 JavaBean 的属性上。若是列名和属性名没有精确匹配,能够在 SELECT 语句中对列使用别名(这是一个 基本的 SQL 特性)来匹配标签。好比:
<select id="selectUsers" resultType="User"> select user_id as "id", user_name as "userName", hashed_password as "hashedPassword" from some_table where id = #{id} </select>
ResultMap 最优秀的地方在于,虽然你已经对它至关了解了,可是根本就不须要显式地用到他们。 上面这些简单的示例根本不须要下面这些繁琐的配置。 出于示范的缘由,让咱们来看看最后一个示例中,若是使用外部的 resultMap 会怎样,这也是解决列名不匹配的另一种方式。
<resultMap id="userResultMap" type="User"> <id property="id" column="user_id" /> <result property="username" column="user_name"/> <result property="password" column="hashed_password"/> </resultMap>
引用它的语句使用 resultMap 属性就好了(注意咱们去掉了 resultType 属性)。好比:
<select id="selectUsers" resultMap="userResultMap"> select user_id, user_name, hashed_password from some_table where id = #{id} </select>
若是世界老是这么简单就行了。
MyBatis 建立的一个想法是:数据库不可能永远是你所想或所需的那个样子。 咱们但愿每一个数据库都具有良好的第三范式或 BCNF 范式,惋惜它们不总都是这样。 若是有一个独立且完美的数据库映射模式,全部应用程序均可以使用它,那就太好了,但惋惜也没有。 ResultMap 就是 MyBatis 对这个问题的答案。
好比,咱们如何映射下面这个语句?
<!-- Very Complex Statement --> <select id="selectBlogDetails" resultMap="detailedBlogResultMap"> select B.id as blog_id, B.title as blog_title, B.author_id as blog_author_id, A.id as author_id, A.username as author_username, A.password as author_password, A.email as author_email, A.bio as author_bio, A.favourite_section as author_favourite_section, P.id as post_id, P.blog_id as post_blog_id, P.author_id as post_author_id, P.created_on as post_created_on, P.section as post_section, P.subject as post_subject, P.draft as draft, P.body as post_body, C.id as comment_id, C.post_id as comment_post_id, C.name as comment_name, C.comment as comment_text, T.id as tag_id, T.name as tag_name from Blog B left outer join Author A on B.author_id = A.id left outer join Post P on B.id = P.blog_id left outer join Comment C on P.id = C.post_id left outer join Post_Tag PT on PT.post_id = P.id left outer join Tag T on PT.tag_id = T.id where B.id = #{id} </select>
你可能想把它映射到一个智能的对象模型,这个对象表示了一篇博客,它由某位做者所写, 有不少的博文,每篇博文有零或多条的评论和标签。 咱们来看看下面这个完整的例子,它是一个很是复杂的 ResultMap (假设做者,博客,博文,评论和标签都是类型的别名)。 不用紧张,咱们会一步一步来讲明。 虽然它看起来使人望而生畏,但其实很是简单。
<!-- 超复杂的 Result Map --> <resultMap id="detailedBlogResultMap" type="Blog"> <constructor> <idArg column="blog_id" javaType="int"/> </constructor> <result property="title" column="blog_title"/> <association property="author" javaType="Author"> <id property="id" column="author_id"/> <result property="username" column="author_username"/> <result property="password" column="author_password"/> <result property="email" column="author_email"/> <result property="bio" column="author_bio"/> <result property="favouriteSection" column="author_favourite_section"/> </association> <collection property="posts" ofType="Post"> <id property="id" column="post_id"/> <result property="subject" column="post_subject"/> <association property="author" javaType="Author"/> <collection property="comments" ofType="Comment"> <id property="id" column="comment_id"/> </collection> <collection property="tags" ofType="Tag" > <id property="id" column="tag_id"/> </collection> <discriminator javaType="int" column="draft"> <case value="1" resultType="DraftPost"/> </discriminator> </collection> </resultMap>
resultMap 元素有不少子元素和一个值得讨论的结构。 下面是 resultMap 元素的概念视图。
ResultMap 属性
属性 | 描述 |
---|---|
id | 当前命名空间中的一个惟一标识,用于标识一个result map. |
type | 类的彻底限定名, 或者一个类型别名 (内置的别名能够参考上面的表格). |
autoMapping | 若是设置这个属性,MyBatis将会为这个ResultMap开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。默认值为:unset。 |
最佳实践 最好一步步地创建结果映射。单元测试能够在这个过程当中起到很大帮助。若是你尝试一次建立一个像上面示例那样的巨大的结果映射, 那么极可能会出现错误并且很难去使用它来完成工做。 从最简单的形态开始,逐步进化。并且别忘了单元测试!使用框架的缺点是有时候它们看上去像黑盒子(不管源代码是否可见)。 为了确保你实现的行为和想要的一致,最好的选择是编写单元测试。提交 bug 的时候它也能起到很大的做用。
下一部分将详细说明每一个元素。
id & result
<id property="id" column="post_id"/> <result property="subject" column="post_subject"/>
这些是结果映射最基本的内容。id 和 result 都将一个列的值映射到一个简单数据类型(字符串,整型,双精度浮点数,日期等)的属性或字段。
这二者之间的惟一不一样是, id 表示的结果将是对象的标识属性,这会在比较对象实例时用到。 这样能够提升总体的性能,尤为是缓存和嵌套结果映射(也就是联合映射)的时候。
两个元素都有一些属性:
Id 和 Result 的属性
属性 | 描述 |
---|---|
property | 映射到列结果的字段或属性。若是用来匹配的 JavaBeans 存在给定名字的属性,那么它将会被使用。 不然 MyBatis 将会寻找给定名称 property 的字段。 不管是哪种情形,你均可以使用一般的点式分隔形式进行复杂属性导航。 好比,你能够这样映射一些简单的东西: “username” , 或者映射到一些复杂的东西: “address.street.number” 。 |
column | 数据库中的列名,或者是列的别名。 通常状况下,这和 传递给 resultSet.getString(columnName) 方法的参数同样。 |
javaType | 一个 Java 类的彻底限定名,或一个类型别名(参考上面内建类型别名 的列表) 。 若是你映射到一个 JavaBean,MyBatis 一般能够判定类型。 然而,若是你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证指望的行为。 |
jdbcType | JDBC 类型,所支持的 JDBC 类型参见这个表格以后的“支持的 JDBC 类型”。 只须要在可能执行插入、更新和删除的容许空值的列上指定 JDBC 类型。 这是 JDBC 的要求而非 MyBatis 的要求。 若是你直接面向 JDBC 编程,你须要对可能为 null 的值指定这个类型。 |
typeHandler | 咱们在前面讨论过的默认类型处理器。 使用这个属性,你能够覆盖默 认的类型处理器。 这个属性值是一个类型处理 器实现类的彻底限定名,或者是类型别名。 |
支持的 JDBC 类型
为了将来的参考,MyBatis 经过包含的 jdbcType 枚举型,支持下面的 JDBC 类型。
BIT | FLOAT | CHAR | TIMESTAMP | OTHER | UNDEFINED |
TINYINT | REAL | VARCHAR | BINARY | BLOB | NVARCHAR |
SMALLINT | DOUBLE | LONGVARCHAR | VARBINARY | CLOB | NCHAR |
INTEGER | NUMERIC | DATE | LONGVARBINARY | BOOLEAN | NCLOB |
BIGINT | DECIMAL | TIME | NULL | CURSOR | ARRAY |
构造方法
经过修改对象属性的方式,能够知足大多数的数据传输对象(Data Transfer Object,DTO)以及绝大部分领域模型的要求。 但有些状况下你想使用不可变类。 一般来讲,不多或基本不变的、包含引用或查询数 据的表,很适合使用不可变类。 构造方法注入容许你在初始化时 为类设置属性的值,而不用暴露出公有方法。MyBatis 也支持私有属性和私有 JavaBeans 属 性来达到这个目的,但有一些人更青睐于构造方法注入。constructor 元素就是为此而生的。
public class User { //... public User(Integer id, String username, int age) { //... } //... }
为了将结果注入构造方法,MyBatis须要经过某种方式定位相应的构造方法。 在下面的例子中,MyBatis搜索一个声明了三个形参的的构造方法,以 java.lang.Integer, java.lang.String and int 的顺序排列。
<constructor> <idArg column="id" javaType="int"/> <arg column="username" javaType="String"/> <arg column="age" javaType="_int"/> </constructor>
当你在处理一个带有多个形参的构造方法时,很容易在保证 arg 元素的正确顺序上出错。 从版本 3.4.3 开始,能够在指定参数名称的前提下,以任意顺序编写 arg 元素。 为了经过名称来引用构造方法参数,你能够添加 @Param 注解,或者使用 '-parameters' 编译选项并启用 useActualParamName 选项(默认开启)来编译项目。 下面的例子对于同一个构造方法依然是有效的,尽管第二和第三个形参顺序与构造方法中声明的顺序不匹配。
<constructor> <idArg column="id" javaType="int" name="id" /> <arg column="age" javaType="_int" name="age" /> <arg column="username" javaType="String" name="username" /> </constructor>
若是类中存在名称和类型相同的属性,那么能够省略 javaType 。
剩余的属性和规则和普通的 id 和 result 元素是同样的。
属性 | 描述 |
---|---|
column | 数据库中的列名,或者是列的别名。 通常状况下,这和 传递给 resultSet.getString(columnName) 方法的参数同样。 |
javaType | 一个 Java 类的彻底限定名,或一个类型别名(参考上面内建类型别名的列表)。 若是你映射到一个 JavaBean,MyBatis 一般能够判定类型。 然而,如 果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证指望的 行为。 |
jdbcType | JDBC 类型,所支持的 JDBC 类型参见这个表格以前的“支持的 JDBC 类型”。 只须要在可能执行插入、更新和删除的容许空值的列上指定 JDBC 类型。 这是 JDBC 的要求而非 MyBatis 的要求。 若是你直接面向 JDBC 编程,你须要对可能为 null 的值指定这个类型。 |
typeHandler | 咱们在前面讨论过的默认类型处理器。 使用这个属性,你能够覆盖默 认的类型处理器。 这个属性值是一个类型处理 器实现类的彻底限定名,或者是类型别名。 |
select | 用于加载复杂类型属性的映射语句的 ID,它会从 column 属性中指定的列检索数据, 做为参数传递给此 select 语句。具体请参考 Association 标签。 |
resultMap | ResultMap 的 ID,能够将嵌套的结果集映射到一个合适的对象树中, 功能和 select 属性类似,它能够实现将多表链接操做的结果映射成一个单一的ResultSet。 这样的ResultSet将会将包含重复或部分数据重复的结果集正确的映射到嵌套的对象树中。 为了实现它, MyBatis容许你 “串联” ResultMap,以便解决嵌套结果集的问题。 想了解更多内容,请参考下面的Association元素。 |
name | 构造方法形参的名字。从3.4.3版本开始,经过指定具体的名字, 你能够以任意顺序写入arg元素。参看上面的解释。 |
关联--一对一
<association property="author" column="blog_author_id" javaType="Author"> <id property="id" column="author_id"/> <result property="username" column="author_username"/> </association>
关联元素处理“有一个”类型的关系。好比,在咱们的示例中,一个博客有一个用户。 关联映射就工做于这种结果之上。你指定了目标属性,来获取值的列,属性的 java 类型(很 多状况下 MyBatis 能够本身算出来) ,若是须要的话还有 jdbc 类型,若是你想覆盖或获取的 结果值还须要类型控制器。
关联中不一样的是你须要告诉 MyBatis 如何加载关联。MyBatis 在这方面会有两种不一样的 方式:
属性 | 描述 |
---|---|
property | 映射到列结果的字段或属性。 若是用来匹配的 JavaBeans 存在给定名字的属性,那么它将会被使用。 不然 MyBatis 将会寻找与给定名称相同的字段。 这两种情形你可使用一般点式的复杂属性导航。 好比,你能够这样映射 一 些 东 西 :“ username ”, 或 者 映 射 到 一 些 复 杂 的 东 西 : “address.street.number” 。 |
javaType | 一个 Java 类的彻底限定名,或一个类型别名(参考上面内建类型别名的列 表) 。 若是你映射到一个 JavaBean,MyBatis 一般能够判定类型。 然而,如 javaType 果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证所需的 行为。 |
jdbcType | 在这个表格以前的所支持的 JDBC 类型列表中的类型。 JDBC 类型是仅仅 须要对插入, 更新和删除操做可能为空的列进行处理。 这是 JDBC 的须要, jdbcType 而不是 MyBatis 的。 若是你直接使用 JDBC 编程,你须要指定这个类型-但 仅仅对可能为空的值。 |
typeHandler | 咱们在前面讨论过默认的类型处理器。 使用这个属性,你能够覆盖默认的 typeHandler 类型处理器。 这个属性值是类的彻底限定名或者是一个类型处理器的实现, 或者是类型别名。 |
"关联"的嵌套查询
属性 | 描述 |
---|---|
column | 来自数据库的列名,或重命名的列标签。 这和一般传递给 resultSet.getString(columnName)方法的字符串是相同的。 column 注 意 : 要 处 理 复 合 主 键 , 你 可 以 指 定 多 个 列 名 通 过 column= ” {prop1=col1,prop2=col2} ” 这种语法来传递给嵌套查询语 句。 这会引发 prop1 和 prop2 以参数对象形式来设置给目标嵌套查询语句。 |
select | 另一个映射语句的 ID,能够加载这个属性映射须要的复杂类型。 获取的 在列属性中指定的列的值将被传递给目标 select 语句做为参数。 表格后面 有一个详细的示例。 select 注 意 : 要 处 理 复 合 主 键 , 你 可 以 指 定 多 个 列 名 通 过 column= ” {prop1=col1,prop2=col2} ” 这种语法来传递给嵌套查询语 句。 这会引发 prop1 和 prop2 以参数对象形式来设置给目标嵌套查询语句。 |
fetchType | 可选的。有效值为 lazy和eager。 若是使用了,它将取代全局配置参数lazyLoadingEnabled。 |
示例:
<resultMap id="blogResult" type="Blog"> <association property="author" column="author_id" javaType="Author" select="selectAuthor"/> </resultMap> <select id="selectBlog" resultMap="blogResult"> SELECT * FROM BLOG WHERE ID = #{id} </select> <select id="selectAuthor" resultType="Author"> SELECT * FROM AUTHOR WHERE ID = #{id} </select>
咱们有两个查询语句:一个来加载博客,另一个来加载做者,并且博客的结果映射描 述了“selectAuthor”语句应该被用来加载它的 author 属性。
其余全部的属性将会被自动加载,假设它们的列和属性名相匹配。
这种方式很简单, 可是对于大型数据集合和列表将不会表现很好。 问题就是咱们熟知的 “N+1 查询问题”。归纳地讲,N+1 查询问题能够是这样引发的:
你执行了一个单独的 SQL 语句来获取结果列表(就是“+1”)。
对返回的每条记录,你执行了一个查询语句来为每一个加载细节(就是“N”)。
这个问题会致使成百上千的 SQL 语句被执行。这一般不是指望的。
MyBatis 能延迟加载这样的查询就是一个好处,所以你能够分散这些语句同时运行的消 耗。然而,若是你加载一个列表,以后迅速迭代来访问嵌套的数据,你会调用全部的延迟加 载,这样的行为多是很糟糕的。
因此还有另一种方法。
"关联"的嵌套结果
属性 | 描述 |
---|---|
resultMap | 这是结果映射的 ID,能够映射关联的嵌套结果到一个合适的对象图中。 这 是一种替代方法来调用另一个查询语句。 这容许你联合多个表来合成到 resultMap 一个单独的结果集。 这样的结果集可能包含重复,数据的重复组须要被分 解,合理映射到一个嵌套的对象图。 为了使它变得容易,MyBatis 让你“链 接”结果映射,来处理嵌套结果。 一个例子会很容易来仿照,这个表格后 面也有一个示例。 |
columnPrefix | 当链接多表时,你将不得不使用列别名来避免ResultSet中的重复列名。 指定columnPrefix容许你映射列名到一个外部的结果集中。 请看后面的例子。 |
notNullColumn | 默认状况下,子对象仅在至少一个列映射到其属性非空时才建立。 经过对这个属性指定非空的列将改变默认行为, 这样作以后Mybatis将仅在这些列非空时才建立一个子对象。 能够指定多个列名,使用逗号分隔。默认值:未设置(unset)。 |
autoMapping | 若是使用了,当映射结果到当前属性时,Mybatis将启用或者禁用自动映射。 该属性覆盖全局的自动映射行为。 注意它对外部结果集无影响, 因此在select or resultMap属性中这个是毫无心义的。 默认值:未设置(unset)。 |
在上面你已经看到了一个很是复杂的嵌套关联的示例。 下面这个是一个很是简单的示例 来讲明它如何工做。代替了执行一个分离的语句,咱们联合博客表和做者表在一块儿,就像:
<select id="selectBlog" resultMap="blogResult"> select B.id as blog_id, B.title as blog_title, B.author_id as blog_author_id, A.id as author_id, A.username as author_username, A.password as author_password, A.email as author_email, A.bio as author_bio from Blog B left outer join Author A on B.author_id = A.id where B.id = #{id} </select>
注意这个联合查询, 以及采起保护来确保全部结果被惟一并且清晰的名字来重命名。 这使得映射很是简单。如今咱们能够映射这个结果:
<resultMap id="blogResult" type="Blog"> <id property="id" column="blog_id" /> <result property="title" column="blog_title"/> <association property="author" column="blog_author_id" javaType="Author" resultMap="authorResult"/> </resultMap> <resultMap id="authorResult" type="Author"> <id property="id" column="author_id"/> <result property="username" column="author_username"/> <result property="password" column="author_password"/> <result property="email" column="author_email"/> <result property="bio" column="author_bio"/> </resultMap>
在上面的示例中你能够看到博客的做者关联表明着“authorResult”结果映射来加载做 者实例。
很是重要: id元素在嵌套结果映射中扮演着非 常重要的角色。你应该老是指定一个或多个能够惟一标识结果的属性。实际上若是你不指定它的话, MyBatis仍然能够工做,可是会有严重的性能问题。在能够惟一标识结果的状况下, 尽量少的选择属性。主键是一个显而易见的选择(即便是复合主键)。
如今,上面的示例用了外部的结果映射元素来映射关联。这使得 Author 结果映射能够 重用。然而,若是你不须要重用它的话,或者你仅仅引用你全部的结果映射合到一个单独描 述的结果映射中。你能够嵌套结果映射。这里给出使用这种方式的相同示例:
<resultMap id="blogResult" type="Blog"> <id property="id" column="blog_id" /> <result property="title" column="blog_title"/> <association property="author" javaType="Author"> <id property="id" column="author_id"/> <result property="username" column="author_username"/> <result property="password" column="author_password"/> <result property="email" column="author_email"/> <result property="bio" column="author_bio"/> </association> </resultMap>
上面你已经看到了如何处理“有一个”类型关联。可是“有不少个”是怎样的?下面这 个部分就是来讨论这个主题的。
集合--一对多
<collection property="posts" ofType="domain.blog.Post"> <id property="id" column="post_id"/> <result property="subject" column="post_subject"/> <result property="body" column="post_body"/> </collection>
集合元素的做用几乎和关联是相同的。实际上,它们也很类似,文档的异同是多余的。 因此咱们更多关注于它们的不一样。
咱们来继续上面的示例,一个博客只有一个做者。可是博客有不少文章。在博客类中, 这能够由下面这样的写法来表示:
private List<Post> posts;
要映射嵌套结果集合到 List 中,咱们使用集合元素。就像关联元素同样,咱们能够从 链接中使用嵌套查询,或者嵌套结果。
"集合"的嵌套查询
首先,让咱们看看使用嵌套查询来为博客加载文章。
<resultMap id="blogResult" type="Blog"> <collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectPostsForBlog"/> </resultMap> <select id="selectBlog" resultMap="blogResult"> SELECT * FROM BLOG WHERE ID = #{id} </select> <select id="selectPostsForBlog" resultType="Post"> SELECT * FROM POST WHERE BLOG_ID = #{id} </select>
这里你应该注意不少东西,但大部分代码和上面的关联元素是很是类似的。首先,你应 该注意咱们使用的是集合元素。而后要注意那个新的“ofType”属性。这个属性用来区分 JavaBean(或字段)属性类型和集合包含的类型来讲是很重要的。因此你能够读出下面这个 映射:
<collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectPostsForBlog"/>
读做: “在 Post 类型的 ArrayList 中的 posts 的集合。”
javaType 属性是不须要的,由于 MyBatis 在不少状况下会为你算出来。因此你能够缩短写法:
<collection property="posts" column="id" ofType="Post" select="selectPostsForBlog"/>
"集合"的嵌套结果
至此,你能够猜想集合的嵌套结果是如何来工做的,由于它和关联彻底相同,除了它应 用了一个“ofType”属性
首先, 让咱们看看 SQL:
<select id="selectBlog" resultMap="blogResult"> select B.id as blog_id, B.title as blog_title, B.author_id as blog_author_id, P.id as post_id, P.subject as post_subject, P.body as post_body, from Blog B left outer join Post P on B.id = P.blog_id where B.id = #{id} </select>
咱们又一次联合了博客表和文章表,并且关注于保证特性,结果列标签的简单映射。现 在用文章映射集合映射博客,能够简单写为:
<resultMap id="blogResult" type="Blog"> <id property="id" column="blog_id" /> <result property="title" column="blog_title"/> <collection property="posts" ofType="Post"> <id property="id" column="post_id"/> <result property="subject" column="post_subject"/> <result property="body" column="post_body"/> </collection> </resultMap>
一样,要记得 id 元素的重要性,若是你不记得了,请阅读上面的关联部分。
一样, 若是你引用更长的形式容许你的结果映射的更多重用, 你可使用下面这个替代 的映射:
<resultMap id="blogResult" type="Blog"> <id property="id" column="blog_id" /> <result property="title" column="blog_title"/> <collection property="posts" ofType="Post" resultMap="blogPostResult" columnPrefix="post_"/> </resultMap> <resultMap id="blogPostResult" type="Post"> <id property="id" column="id"/> <result property="subject" column="subject"/> <result property="body" column="body"/> </resultMap>
注意 这个对你所映射的内容没有深度,广度或关联和集合相联合的限制。当映射它们 时你应该在大脑中保留它们的表现。 你的应用在找到最佳方法前要一直进行的单元测试和性 能测试。好在 myBatis 让你后来能够改变想法,而不对你的代码形成很小(或任何)影响。
高级关联和集合映射是一个深度的主题。文档只能给你介绍到这了。加上一点联系,你 会很快清楚它们的用法。
鉴别器
<discriminator javaType="int" column="draft"> <case value="1" resultType="DraftPost"/> </discriminator>
有时一个单独的数据库查询也许返回不少不一样 (可是但愿有些关联) 数据类型的结果集。 鉴别器元素就是被设计来处理这个状况的, 还有包括类的继承层次结构。 鉴别器很是容易理 解,由于它的表现很像 Java 语言中的 switch 语句。
定义鉴别器指定了 column 和 javaType 属性。 列是 MyBatis 查找比较值的地方。 JavaType 是须要被用来保证等价测试的合适类型(尽管字符串在不少情形下都会有用)。好比:
<resultMap id="vehicleResult" type="Vehicle"> <id property="id" column="id" /> <result property="vin" column="vin"/> <result property="year" column="year"/> <result property="make" column="make"/> <result property="model" column="model"/> <result property="color" column="color"/> <discriminator javaType="int" column="vehicle_type"> <case value="1" resultMap="carResult"/> <case value="2" resultMap="truckResult"/> <case value="3" resultMap="vanResult"/> <case value="4" resultMap="suvResult"/> </discriminator> </resultMap>
在这个示例中, MyBatis 会从结果集中获得每条记录, 而后比较它的 vehicle 类型的值。 若是它匹配任何一个鉴别器的实例,那么就使用这个实例指定的结果映射。换句话说,这样 作彻底是剩余的结果映射被忽略(除非它被扩展,这在第二个示例中讨论) 。若是没有任何 一个实例相匹配,那么 MyBatis 仅仅使用鉴别器块外定义的结果映射。因此,若是 carResult 按以下声明:
<resultMap id="carResult" type="Car"> <result property="doorCount" column="door_count" /> </resultMap>
那么只有 doorCount 属性会被加载。这步完成后完整地容许鉴别器实例的独立组,尽管 和父结果映射可能没有什么关系。这种状况下,咱们固然知道 cars 和 vehicles 之间有关系, 如 Car 是一个 Vehicle 实例。所以,咱们想要剩余的属性也被加载。咱们设置的结果映射的 简单改变以下。
<resultMap id="carResult" type="Car" extends="vehicleResult"> <result property="doorCount" column="door_count" /> </resultMap>
如今 vehicleResult 和 carResult 的属性都会被加载了。
尽管曾经有些人会发现这个外部映射定义会多少有一些使人厌烦之处。 所以还有另一 种语法来作简洁的映射风格。好比:
<resultMap id="vehicleResult" type="Vehicle"> <id property="id" column="id" /> <result property="vin" column="vin"/> <result property="year" column="year"/> <result property="make" column="make"/> <result property="model" column="model"/> <result property="color" column="color"/> <discriminator javaType="int" column="vehicle_type"> <case value="1" resultType="carResult"> <result property="doorCount" column="door_count" /> </case> <case value="2" resultType="truckResult"> <result property="boxSize" column="box_size" /> <result property="extendedCab" column="extended_cab" /> </case> <case value="3" resultType="vanResult"> <result property="powerSlidingDoor" column="power_sliding_door" /> </case> <case value="4" resultType="suvResult"> <result property="allWheelDrive" column="all_wheel_drive" /> </case> </discriminator> </resultMap>
要记得 这些都是结果映射, 若是你不指定任何结果, 那么 MyBatis 将会为你自动匹配列和属性。因此这些例子中的大部分是很冗长的,而实际上是不须要的。也就是说,不少数据库 是很复杂的,咱们不太可能对全部示例都能依靠它。
正如你在前面一节看到的,在简单的场景下,MyBatis能够替你自动映射查询结果。 若是遇到复杂的场景,你须要构建一个result map。 可是在本节你将看到,你也能够混合使用这两种策略。 让咱们到深一点的层面上看看自动映射是怎样工做的。
当自动映射查询结果时,MyBatis会获取sql返回的列名并在java类中查找相同名字的属性(忽略大小写)。 这意味着若是Mybatis发现了ID列和id属性,Mybatis会将ID的值赋给id。
一般数据库列使用大写单词命名,单词间用下划线分隔;而java属性通常遵循驼峰命名法。 为了在这两种命名方式之间启用自动映射,须要将 mapUnderscoreToCamelCase设置为true。
自动映射甚至在特定的result map下也能工做。在这种状况下,对于每个result map,全部的ResultSet提供的列, 若是没有被手工映射,则将被自动映射。自动映射处理完毕后手工映射才会被处理。 在接下来的例子中, id 和 userName列将被自动映射, hashed_password 列将根据配置映射。
<select id="selectUsers" resultMap="userResultMap"> select user_id as "id", user_name as "userName", hashed_password from some_table where id = #{id} </select>
<resultMap id="userResultMap" type="User"> <result property="password" column="hashed_password"/> </resultMap>
有三种自动映射等级:
<select id="selectBlog" resultMap="blogResult"> select B.id, B.title, A.username, from Blog B left outer join Author A on B.author_id = A.id where B.id = #{id} </select>
<resultMap id="blogResult" type="Blog"> <association property="author" resultMap="authorResult"/> </resultMap> <resultMap id="authorResult" type="Author"> <result property="username" column="author_username"/> </resultMap>
在结果中Blog和Author均将自动映射。可是注意Author有一个id属性,在ResultSet中有一个列名为id, 因此Author的id将被填充为Blog的id,这不是你所期待的。因此须要谨慎使用FULL。
经过添加autoMapping属性能够忽略自动映射等级配置,你能够启用或者禁用自动映射指定的ResultMap。
<resultMap id="userResultMap" type="User" autoMapping="false"> <result property="password" column="hashed_password"/> </resultMap>
MyBatis 包含一个很是强大的查询缓存特性,它能够很是方便地配置和定制。MyBatis 3 中的缓存实现的不少改进都已经实现了,使得它更增强大并且易于配置。
默认状况下是没有开启缓存的,除了局部的 session 缓存,能够加强变现并且处理循环 依赖也是必须的。要开启二级缓存,你须要在你的 SQL 映射文件中添加一行:
<cache/>
字面上看就是这样。这个简单语句的效果以下:
NOTE The cache will only apply to statements declared in the mapping file where the cache tag is located. If you are using the Java API in conjunction with the XML mapping files, then statements declared in the companion interface will not be cached by default. You will need to refer to the cache region using the @CacheNamespaceRef annotation.
全部的这些属性均可以经过缓存元素的属性来修改。好比:
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
这个更高级的配置建立了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,并且返回的对象被认为是只读的,所以在不一样线程中的调用者之间修改它们会 致使冲突。
可用的收回策略有:
flushInterval(刷新间隔)能够被设置为任意的正整数,并且它们表明一个合理的毫秒 形式的时间段。默认状况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
size(引用数目)能够被设置为任意正整数,要记住你缓存的对象数目和你运行环境的 可用内存资源数目。默认值是 1024。
readOnly(只读)属性能够被设置为 true 或 false。只读的缓存会给全部调用者返回缓 存对象的相同实例。所以这些对象不能被修改。这提供了很重要的性能优点。可读写的缓存 会返回缓存对象的拷贝(经过序列化) 。这会慢一些,可是安全,所以默认是 false。
MyBatis 包含一个很是强大的查询缓存特性,它能够很是方便地配置和定制。MyBatis 3 中的缓存实现的不少改进都已经实现了,使得它更增强大并且易于配置。
默认状况下是没有开启缓存的,除了局部的 session 缓存,能够加强变现并且处理循环 依赖也是必须的。要开启二级缓存,你须要在你的 SQL 映射文件中添加一行:
除了这些自定义缓存的方式, 你也能够经过实现你本身的缓存或为其余第三方缓存方案 建立适配器来彻底覆盖缓存行为。
<cache type="com.domain.something.MyCustomCache"/>
这个示 例展 示了 如何 使用 一个 自定义 的缓 存实 现。type 属 性指 定的 类必 须实现 org.mybatis.cache.Cache 接口。这个接口是 MyBatis 框架中不少复杂的接口之一,可是简单 给定它作什么就行。
public interface Cache { String getId(); int getSize(); void putObject(Object key, Object value); Object getObject(Object key); boolean hasKey(Object key); Object removeObject(Object key); void clear(); }
要配置你的缓存, 简单和公有的 JavaBeans 属性来配置你的缓存实现, 并且是经过 cache 元素来传递属性, 好比, 下面代码会在你的缓存实现中调用一个称为 “setCacheFile(String file)” 的方法:
<cache type="com.domain.something.MyCustomCache"> <property name="cacheFile" value="/tmp/my-custom-cache.tmp"/> </cache>
你可使用全部简单类型做为 JavaBeans 的属性,MyBatis 会进行转换。 And you can specify a placeholder(e.g. ${cache.file}) to replace value defined at configuration properties.
从3.4.2版本开始,MyBatis已经支持在全部属性设置完毕之后能够调用一个初始化方法。若是你想要使用这个特性,请在你的自定义缓存类里实现 org.apache.ibatis.builder.InitializingObject 接口。
public interface InitializingObject { void initialize() throws Exception; }
记得缓存配置和缓存实例是绑定在 SQL 映射文件的命名空间是很重要的。所以,全部 在相同命名空间的语句正如绑定的缓存同样。 语句能够修改和缓存交互的方式, 或在语句的 语句的基础上使用两种简单的属性来彻底排除它们。默认状况下,语句能够这样来配置:
<select ... flushCache="false" useCache="true"/> <insert ... flushCache="true"/> <update ... flushCache="true"/> <delete ... flushCache="true"/>
由于那些是默认的,你明显不能明确地以这种方式来配置一条语句。相反,若是你想改 变默认的行为,只能设置 flushCache 和 useCache 属性。好比,在一些状况下你也许想排除 从缓存中查询特定语句结果,或者你也许想要一个查询语句来刷新缓存。类似地,你也许有 一些更新语句依靠执行而不须要刷新缓存。
回想一下上一节内容, 这个特殊命名空间的惟一缓存会被使用或者刷新相同命名空间内 的语句。也许未来的某个时候,你会想在命名空间中共享相同的缓存配置和实例。在这样的 状况下你可使用 cache-ref 元素来引用另一个缓存。
<cache-ref namespace="com.someone.application.data.SomeMapper"/>
本例涉及的知识包括上面的"集合"的嵌套查询小节。
相对于"集合"的嵌套查询小节的例子,本例多了一级嵌套。即类A包含类B的集合,而类B中又包含了类C的集合。
直接根据表table_a的ID直接映射返回类A对象。
此外,对应的也可使用"集合"的嵌套结果进行查询。在此就不给出实例了。
<!--根据ID查询返回A对象-BEGIN--> <!--一、根据table_a的ID查询返回A对象--> <!--定义返回的类A的结果集映射--> <!--A中包含3个属性:String id, String aName, List<B> bList <resultMap type="A" id="AMap"> <id column="ID" jdbcType="VARCHAR" property="id"/> <result column="A_NAME" jdbcType="DECIMAL" property="aName"/> <!--下面的column的值id指的是table_a表的主键,在table_b表中是外键a_id,在子查询getBList时用于匹配外键a_id--> <collection property="bList" column="id" ofType="B" select="getBList"/> </resultMap> <select id="findA" parameterType="java.lang.String" resultMap="AMap"> select a.* from table_a a where a.ID = #{id} </select> <!--二、查询B:根据table_b的外键a_id查询与table_a的ID匹配的记录--> <!--定义返回类B的结果映射--> <!--B中包含3个属性:String id, String bName, List<C> cList <resultMap type="B" id="BMap"> <id column="ID" jdbcType="VARCHAR" property="id"/> <result column="B_NAME" jdbcType="VARCHAR" property="bName"/> <!--下面的column的值id指的是table_b的主键id,在table_c表中是外键b_id,在子查询getCList时用于匹配外键b_id--> <collection property="cList" column="id" ofType="C" select="getCList"/> </resultMap> <!--在table_b表中根据外键a_id查询table_b表符合条件的记录--> <select id="getBList" parameterType="java.lang.String" resultMap="BMap"> select b.* from table_b b where b.a_id = #{id} </select> <!--三、查询C:根据table_c的外键b_id查询与table_b的ID匹配的记录--> <!--定义返回类C的结果映射--> <!--B中包含2个属性:String id, String cName <resultMap id="CMap" type="C"> <id column="ID" jdbcType="VARCHAR" property="id"/> <result column="C_NAME" jdbcType="VARCHAR" property="cName"/> </resultMap> <!--在table_c表中根据外键b_id查询table_c表符合条件的记录--> <select id="getCList" parameterType="java.lang.String" resultMap="CMap"> select c.* from table_c c where c.b_id =#{id} </select> <!--根据ID查询返回A对象-END-->
参考资料
Mybatis 级联查询 (一对多 )
MyBatis 3官方文档之SELECT
mybatis映射器配置细则