mybatis xml详解

MyBatis 真正的力量是在映射语句中。这里是奇迹发生的地方。对于全部的力量,SQL 映射的 XML 文件是至关的简单。固然若是你将它们和对等功能的 JDBC 代码来比较,你会发现映射文件节省了大约 95%的代码量。MyBatis 的构建就是聚焦于 SQL 的,使其远离于普通的方式。html

SQL 映射文件有不多的几个顶级元素(按照它们应该被定义的顺序):java

  • cache – 配置给定命名空间的缓存。
  • cache-ref – 从其余命名空间引用缓存配置。
  • resultMap – 最复杂,也是最有力量的元素,用来描述如何从数据库结果集中来加载你的对象。
  • parameterMap – 已经被废弃了!老式风格的参数映射。内联参数是首选,这个元素可能在未来被移除。这里不会记录。
  • sql – 能够重用的 SQL 块,也能够被其余语句引用。
  • insert – 映射插入语句
  • update – 映射更新语句
  • delete – 映射删除语句
  • select – 映射查询语句

下一部分将从语句自己开始来描述每一个元素的细节。算法

select

查询语句是使用 MyBatis 时最经常使用的元素之一。直到你从数据库取出数据时才会发现将数据存在数据库中是多么的有价值, 因此许多应用程序查询要比更改数据多的多。对于每次插入,更新或删除,那也会有不少的查询。这是 MyBatis 的一个基本原则,也是将重心和努力放到查询和结果映射的缘由。对简单类别的查询元素是很是简单的。好比:sql

<select id="selectPerson" parameterType="int" resultType="hashmap">   SELECT * FROM PERSON WHERE ID = #{id}</select>

这个语句被称做 selectPerson, 使用一个 int (或 Integer) 类型的参数, 并返回一个 HashMap 类型的对象,其中的键是列名,值是列对应的值。数据库

注意参数注释:编程

#{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 元素有不少属性容许你配置,来决定每条语句的做用细节。session

<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 将会传入这条语句的参数类的彻底限定名或别名。
parameterMap 这是引用外部 parameterMap 的已经被废弃的方法。使用内联参数映射和 parameterType 属性。
resultType 从这条语句中返回的指望类型的类的彻底限定名或别名。注意集合情形,那应该是集合能够包含的类型,而不能是集合自己。使用 resultType 或 resultMap,但不能同时使用。
resultMap 命名引用外部的 resultMap。返回 map 是 MyBatis 最具力量的特性, 对其有一个很好的理解的话, 许多复杂映射的情形就能被解决了。使用 resultMap 或 resultType,但不能同时使用。
flushCache 将其设置为 true,不论语句何时被带哦用,都会致使缓存被清空。默认值:false。
useCache 将其设置为 true, 将会致使本条语句的结果被缓存。默认值: true。
timeout 这个设置驱动程序等待数据库返回请求结果,并抛出异常时间的最大等待值。默认不设置(驱动自行处理)
fetchSize 这是暗示驱动程序每次批量返回的结果行数。默认不设置(驱动自行处理)。
statementType STA TEMENT,PREPARED 或 CALLABLE 的一种。这会让 MyBatis 使用选择使用 Statement,PreparedStatement 或 CallableStatement。默认值:PREPARED。
resultSetType FORWARD_ONLY|SCROLL_SENSITIVE|SCROLL_INSENSITIVE 中的一种。默认是不设置(驱动自行处理)。
databaseId In case there is a configured databaseIdProvider, 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.

insert, update and delete

数据变动语句 insert,update 和 delete 在它们的实现中很是类似:mybatis

<insert   id="insertAuthor"   parameterType="domain.blog.Author"   flushCache="true"   statementType="PREPARED"   keyProperty=""   keyColumn=""   useGeneratedKeys=""   timeout="20000"> <update   id="insertAuthor"   parameterType="domain.blog.Author"   flushCache="true"   statementType="PREPARED"   timeout="20000"> <delete   id="insertAuthor"   parameterType="domain.blog.Author"   flushCache="true"   statementType="PREPARED"   timeout="20000">
Insert, Update and Delete Attributes
属性 描述
id 在命名空间中惟一的标识符,能够被用来引用这条语句。
parameterType 将会传入这条语句的参数类的彻底限定名或别名。
parameterMap 这是引用外部 parameterMap 的已经被废弃的方法。使用内联参数映射和 parameterType 属性。
flushCache 将其设置为 true,不论语句何时被带哦用,都会致使缓存被清空。默认值:false。
timeout 这个设置驱动程序等待数据库返回请求结果, 并抛出异常时间的最大等待值。默认不设置(驱动自行处理)。
statementType STA TEMENT,PREPARED 或 CALLABLE 的一种。这会让 MyBatis 使用选择使用 Statement,PreparedStatement 或 CallableStatement。默认值:PREPARED。
useGeneratedKeys ( 仅 对 insert 有 用 ) 这 会 告 诉 MyBatis 使 用 JDBC 的 getGeneratedKeys 方法来取出由数据(好比:像 MySQL 和 SQL Server 这样的数据库管理系统的自动递增字段)内部生成的主键。默认值:false。
keyProperty (仅对 insert 有用) 标记一个属性, MyBatis 会经过 getGeneratedKeys 或者经过 insert 语句的 selectKey 子元素设置它的值。默认: 不设置。
keyColumn (仅对 insert 有用) 标记一个属性, MyBatis 会经过 getGeneratedKeys 或者经过 insert 语句的 selectKey 子元素设置它的值。默认: 不设置。

下面就是 insert,update 和 delete 语句的示例:

<insert id="insertAuthor" parameterType="domain.blog.Author">   insert into Author (id,username,password,email,bio)
  values (#{id},#{username},#{password},#{email},#{bio})</insert> <update id="updateAuthor" parameterType="domain.blog.Author">   update Author set
    username = #{username},
    password = #{password},
    email = #{email},
    bio = #{bio}
  where id = #{id}</update> <delete id="deleteAuthor" parameterType="int">   delete from Author where id = #{id}</delete>

如前所述,插入语句有一点多,它有一些属性和子元素用来处理主键的生成。

首先,若是你的数据库支持自动生成主键的字段(好比 MySQL 和 SQL Server) ,那么你能够设置 useGeneratedKeys=”true”,并且设置 keyProperty 到你已经作好的目标属性上。例如,若是上面的 Author 表已经对 id 使用了自动生成的列类型,那么语句能够修改成:

<insert id="insertAuthor" parameterType="domain.blog.Author" useGeneratedKeys="true"     keyProperty="id">   insert into Author (username,password,email,bio)
  values (#{username},#{password},#{email},#{bio})</insert>

MyBatis 有另一种方法来处理数据库不支持自动生成类型,或者可能 JDBC 驱动不支持自动生成主键时的主键生成问题。

这里有一个简单(甚至很傻)的示例,它能够生成一个随机 ID(可能你不会这么作, 可是这展现了 MyBatis 处理问题的灵活性,由于它并不真的关心 ID 的生成):

<insert id="insertAuthor" parameterType="domain.blog.Author">   <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 Attributes
属性 描述
keyProperty selectKey 语句结果应该被设置的目标属性。
resultType 结果的类型。MyBatis 一般能够算出来,可是写上也没有问题。 MyBatis 容许任何简单类型用做主键的类型,包括字符串。
order 这能够被设置为 BEFORE 或 AFTER。若是设置为 BEFORE,那么它会首先选择主键, 设置 keyProperty 而后执行插入语句。若是设置为 AFTER,那么先执行插入语句,而后是 selectKey 元素- 这和如 Oracle 数据库类似,能够在插入语句中嵌入序列调用。
statementType 和前面的相 同,MyBatis 支持 STA TEMENT ,PREPARED 和 CALLABLE 语句的映射类型,分别表明 PreparedStatement 和 CallableStatement 类型。

sql

这个元素能够被用来定义可重用的 SQL 代码段,能够包含在其余语句中。好比:

<sql id="userColumns"> id,username,password </sql>

这个 SQL 片断能够被包含在其余语句中,例如:

<select id="selectUsers" parameterType="int" resultType="hashmap">   select <include refid="userColumns"/>   from some_table
  where id = #{id}</select>

Parameters

在以前的语句中, 你已经看到了一些简单参数的示例。 MyBatis 中参数是很是强大的在元素。对于简单的作法,大概 90%的状况,是不用太多的,好比:

<select id="selectUsers" parameterType="int" resultType="User">   select id, username, password
  from users
  where id = #{id}</select>

上面的这个示例说明了一个很是简单的命名参数映射。参数类型被设置为“int” ,所以这个参数能够被设置成任何内容。原生的类型或简单数据类型, 好比整型和没有相关属性的字符串,所以它会彻底用参数来替代。然而,若是你传递了一个复杂的对象,那么 MyBatis 的处理方式就会有一点不一样。好比:

<insert id="insertUser" parameterType="User" >   insert into users (id, username, password)
  values (#{id}, #{username}, #{password})</insert>

若是 User 类型的参数对象传递到了语句中, username 和 password 属性将会被查找, id、而后它们的值就被传递到预处理语句的参数中。

这点对于传递参数到语句中很是好。可是对于参数映射也有一些其余的特性。

首先,像 MyBatis 的其余部分,参数能够指定一个肯定的数据类型。

#{property,javaType=int,jdbcType=NUMERIC}

像 MyBatis 的剩余部分,javaType 一般能够从参数对象中来去顶,除非对象是一个 HashMap。那么 javaType 应该被肯定来保证使用正确类型处理器。

注意 若是 null 被看成值来传递,对于全部可能为空的列,JDBC Type 是须要的。以能够本身经过阅读预处理语句的 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 来映射结果集到参数类型。要注意这里的 javaType 属性是可选的,若是左边的空白是 jdbcType 的 CURSOR 类型,它会自动地被设置为结果集。

#{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 建立预处理语句属性并以它为背景设置安全的值(好比?) 。这样作很安全,很迅速也是首选作法,有时你只是想直接在 SQL 语句中插入一个不改变的字符串。好比,像 ORDER BY,你能够这样来使用:

ORDER BY ${columnName}

这里 MyBatis 不会修改或转义字符串。

重要 接受从用户输出的内容并提供给语句中不变的字符串,这样作是不安全的。这会致使潜在的 SQL 注入攻击,所以你不该该容许用户输入这些字段,或者一般自行转义并检查。

Result Maps

resultMap 元素是 MyBatis 中最重要最强大的元素。它就是让你远离 90%的须要从结果集中取出数据的 JDBC 代码的那个东西, 并且在一些情形下容许你作一些 JDBC 不支持的事情。事实上, 编写类似于对复杂语句联合映射这些等同的代码, 也许能够跨过上千行的代码。 ResultMap 的设计就是简单语句不须要明确的结果映射,而不少复杂语句确实须要描述它们的关系。

你已经看到简单映射语句的示例了,但没有明确的 resultMap。好比:

<select id="selectUsers" parameterType="int" resultType="hashmap">   select id, username, hashedPassword
  from some_table
  where id = #{id}</select>

这样一个语句简单做用于全部列被自动映射到 HashMap 的键上,这由 resultType 属性指定。这在不少状况下是有用的,可是 HashMap 不能很好描述一个领域模型。那样你的应用程序将会使用 JavaBeans 或 POJOs(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 能够被映射到结果集,就像映射到 HashMap 同样简单。

<select id="selectUsers" parameterType="int" 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" parameterType="int" resultType="User">   select id, username, hashedPassword
  from some_table
  where id = #{id}</select>

这些状况下,MyBatis 会在幕后自动建立一个 ResultMap,基于属性名来映射列到 JavaBean 的属性上。若是列名没有精确匹配,你能够在列名上使用 select 字句的别名(一个基本的 SQL 特性)来匹配标签。好比:

<select id="selectUsers" parameterType="int" 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="username"/>   <result property="password" column="password"/> </resultMap>

引用它的语句使用 resultMap 属性就好了(注意咱们去掉了 resultType 属性)。好比:

<select id="selectUsers" parameterType="int" resultMap="userResultMap">   select user_id, user_name, hashed_password
  from some_table
  where id = #{id}</select>

若是世界老是这么简单就行了。

高级结果映射

MyBatis 建立的一个想法:数据库不用永远是你想要的或须要它们是什么样的。而咱们最喜欢的数据库最好是第三范式或 BCNF 模式,但它们有时不是。若是可能有一个单独的数据库映射,全部应用程序均可以使用它,这是很是好的,但有时也不是。结果映射就是 MyBatis 提供处理这个问题的答案。

好比,咱们如何映射下面这个语句?

<!-- Very Complex Statement --> <select id="selectBlogDetails" parameterType="int" 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>

你可能想把它映射到一个智能的对象模型,包含一个做者写的博客,有不少的博文,每篇博文有零条或多条的评论和标签。下面是一个完整的复杂结果映射例子 (假设做者, 博客, 博文, 评论和标签都是类型的别名) 咱们来看看, 。可是不用紧张, 咱们会一步一步来讲明。当天最初它看起来使人生畏,但实际上很是简单。

<!-- Very Complex 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

  • constructor - 类在实例化时,用来注入结果到构造方法中
    • idArg - ID 参数;标记结果做为 ID 能够帮助提升总体效能
    • arg - 注入到构造方法的一个普通结果
  • id – 一个 ID 结果;标记结果做为 ID 能够帮助提升总体效能
  • result – 注入到字段或 JavaBean 属性的普通结果
  • association – 一个复杂的类型关联;许多结果将包成这种类型
    • 嵌入结果映射 – 结果映射自身的关联,或者参考一个
  • collection – 复杂类型的集
    • 嵌入结果映射 – 结果映射自身的集,或者参考一个
  • discriminator – 使用结果值来决定使用哪一个结果映射
    • case – 基于某些值的结果映射
      • 嵌入结果映射 – 这种情形结果也映射它自己,所以能够包含不少相 同的元素,或者它能够参照一个外部的结果映射。

最佳实践 一般逐步创建结果映射。单元测试的真正帮助在这里。若是你尝试建立一次建立一个向上面示例那样的巨大的结果映射, 那么可能会有错误并且很难去控制它来工做。开始简单一些,一步一步的发展。并且要进行单元测试!使用该框架的缺点是它们有时是黑盒(是否可见源代码) 。你肯定你实现想要的行为的最好选择是编写单元测试。它也能够你帮助获得提交时的错误。

下面一部分将详细说明每一个元素。

id & result

<id property="id" column="post_id"/> <result property="subject" column="post_subject"/>

这些是结果映射最基本内容。id 和 result 都映射一个单独列的值到简单数据类型(字符串,整型,双精度浮点数,日期等)的单独属性或字段。

这二者之间的惟一不一样是 id 表示的结果将是当比较对象实例时用到的标识属性。这帮助来改进总体表现,特别是缓存和嵌入结果映射(也就是联合映射) 。

每一个都有一些属性:

Id and Result Attributes
属性 描述
property 映射到列结果的字段或属性。若是匹配的是存在的,和给定名称相同的 JavaBeans 的属性,那么就会使用。不然 MyBatis 将会寻找给定名称 property 的字段。这两种情形你可使用一般点式的复杂属性导航。好比,你能够这样映射一些东西: “username” ,或者映射到一些复杂的东西: “address.street.number” 。
column 从数据库中获得的列名,或者是列名的重命名标签。这也是一般和会传递给 resultSet.getString(columnName)方法参数中相同的字符串。
javaType 一个 Java 类的彻底限定名,或一个类型别名(参加上面内建类型别名的列表) 。若是你映射到一个 JavaBean,MyBatis 一般能够判定类型。然而,若是你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证所需的行为。
jdbcType 在这个表格以后的所支持的 JDBC 类型列表中的类型。JDBC 类型是仅仅须要对插入,更新和删除操做可能为空的列进行处理。这是 JDBC jdbcType 的须要,而不是 MyBatis 的。若是你直接使用 JDBC 编程,你须要指定这个类型-但仅仅对可能为空的值。
typeHandler 咱们在前面讨论过默认的类型处理器。使用这个属性,你能够覆盖默认的类型处理器。这个属性值是类的彻底限定名或者是一个类型处理器的实现,或者是类型别名。

支持的 JDBC 类型

为了将来的参考,MyBatis 经过包含的 jdbcType 枚举型,支持下面的 JDBC 类型。

BIT FLOAT CHAR TIMESTAMP OTHER UNDEFINED
TINYINT REAL VARCHAR BINARY BLOG NVARCHAR
SMALLINT DOUBLE LONGVARCHAR VARBINARY CLOB >NCHAR
INTEGER NUMERIC DATE LONGVARBINARY BOOLEAN NCLOB
BIGINT DECIMAL TIME NULL CURSOR ARRAY

构造方法

<constructor>    <idArg column="id" javaType="int"/>    <arg column="username" javaType="String"/> </constructor>

对于大多数数据传输对象(Data Transfer Object,DTO)类型,属性能够起做用,并且像你绝大多数的领域模型, 指令也许是你想使用一成不变的类的地方。一般包含引用或查询数据的表不多或基本不变的话对一成不变的类来讲是合适的。构造方法注入容许你在初始化时为类设置属性的值,而不用暴露出公有方法。MyBatis 也支持私有属性和私有 JavaBeans 属性来达到这个目的,可是一些人更青睐构造方法注入。构造方法元素支持这个。

看看下面这个构造方法:

public class User {    //...    public User(int id, String username) {      //...   } //... }

为了向这个构造方法中注入结果,MyBatis 须要经过它的参数的类型来标识构造方法。 Java 没有自查(反射)参数名的方法。因此当建立一个构造方法元素时,保证参数是按顺序排列的,并且数据类型也是肯定的。

<constructor>    <idArg column="id" javaType="int"/>    <arg column="username" javaType="String"/> </constructor>

剩余的属性和规则和固定的 id 和 result 元素是相同的。

属性 描述
column 来自数据库的类名,或重命名的列标签。这和一般传递给 resultSet.getString(columnName)方法的字符串是相同的。
javaType 一个 Java 类的彻底限定名,或一个类型别名(参加上面内建类型别名的列表)。若是你映射到一个 JavaBean,MyBatis 一般能够判定类型。然而,若是你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证所需的行为。
jdbcType 在这个表格以前的所支持的 JDBC 类型列表中的类型。JDBC 类型是仅仅须要对插入, 更新和删除操做可能为空的列进行处理。这是 JDBC 的须要, jdbcType 而不是 MyBatis 的。若是你直接使用 JDBC 编程,你须要指定这个类型-但仅仅对可能为空的值。
typeHandler 咱们在前面讨论过默认的类型处理器。使用这个属性,你能够覆盖默认的类型处理器。这个属性值是类的彻底限定名或者是一个类型处理器的实现, 或者是类型别名。
select The ID of another mapped statement that will load the complex type required by this property mapping. The values retrieved from columns specified in the column attribute will be passed to the target select statement as parameters. See the Association element for more.
resultMap This is the ID of a ResultMap that can map the nested results of this argument into an appropriate object graph. This is an alternative to using a call to another select statement. It allows you to join multiple tables together into a single ResultSet. Such a ResultSet will contain duplicated, repeating groups of data that needs to be decomposed and mapped properly to a nested object graph. To facilitate this, MyBatis lets you "chain" result maps together, to deal with the nested results. See the Association element below for more.

关联

<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 在这方面会有两种不一样的方式:

  • 嵌套查询:经过执行另一个 SQL 映射语句来返回预期的复杂类型。
  • 嵌套结果:使用嵌套结果映射来处理重复的联合结果的子集。首先,然让咱们来查看这个元素的属性。全部的你都会看到,它和普通的只由 select 和

resultMap 属性的结果映射不一样。

属性 描述
property 映射到列结果的字段或属性。若是匹配的是存在的,和给定名称相同的 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 以参数对象形式来设置给目标嵌套查询语句。

示例:

<resultMap id="blogResult" type="Blog">   <association property="author" column="blog_author_id" javaType="Author" select="selectAuthor"/> </resultMap> <select id="selectBlog" parameterType="int" resultMap="blogResult">   SELECT * FROM BLOG WHERE ID = #{id}</select> <select id="selectAuthor" parameterType="int" 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 When joining multiple tables, you would have to use column alias to avoid duplicated column names in the ResultSet. Specifying columnPrefix allows you to map such columns to an external resultMap. Please see the example explained later in this section.

在上面你已经看到了一个很是复杂的嵌套关联的示例。下面这个是一个很是简单的示例来讲明它如何工做。代替了执行一个分离的语句,咱们联合博客表和做者表在一块儿,就像:

<select id="selectBlog" parameterType="int" 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>

What if the blog has a co-author? The select statement would look like:

<select id="selectBlog" parameterType="int" resultMap="blogResult">   select
    B.id            as blog_id,
    B.title         as blog_title,
    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,
    CA.id           as co_author_id,
    CA.username     as co_author_username,
    CA.password     as co_author_password,
    CA.email        as co_author_email,
    CA.bio          as co_author_bio
  from Blog B
  left outer join Author A on B.author_id = A.id
  left outer join Author CA on B.co_author_id = CA.id
  where B.id = #{id}</select>

Recall that the resultMap for Author is defined as follows.

<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>

Because the column names in the results differ from the columns defined in the resultMap, you need to specify columnPrefix to reuse the resultMap for mapping co-author results.

<resultMap id="blogResult" type="Blog">   <id property="id" column="blog_id" />   <result property="title" column="blog_title"/>   <association property="author"     resultMap="authorResult" />   <association property="coAuthor"     resultMap="authorResult"     columnPrefix="co_" /> </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="blog_id" ofType="Post" select="selectPostsForBlog"/> </resultMap> <select id="selectBlog" parameterType="int" resultMap="blogResult">   SELECT * FROM BLOG WHERE ID = #{id}</select> <select id="selectPostsForBlog" parameterType="int" resultType="Blog">   SELECT * FROM POST WHERE BLOG_ID = #{id}</select>

这里你应该注意不少东西,但大部分代码和上面的关联元素是很是类似的。首先,你应该注意咱们使用的是集合元素。而后要注意那个新的“ofType”属性。这个属性用来区分 JavaBean(或字段)属性类型和集合包含的类型来讲是很重要的。因此你能够读出下面这个映射:

<collection property="posts" javaType="ArrayList" column="blog_id" ofType="Post" select="selectPostsForBlog"/>

读做: “在 Post 类型的 ArrayList 中的 posts 的集合。”

javaType 属性是不须要的,由于 MyBatis 在不少状况下会为你算出来。因此你能够缩短写法:

<collection property="posts" column="blog_id" ofType="Post" select="selectPostsForBlog"/>

集合的嵌套结果

至此,你能够猜想集合的嵌套结果是如何来工做的,由于它和关联彻底相同,除了它应用了一个“ofType”属性

First, let's look at the SQL:

<select id="selectBlog" parameterType="int" 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 将会为你自动匹配列和属性。因此这些例子中的大部分是很冗长的,而实际上是不须要的。也就是说,不少数据库是很复杂的,咱们不太可能对全部示例都能依靠它。

Auto-mapping

As you have already seen in the previous sections, in simple cases MyBatis can auto-map the results for you and in others you will need to build a result map. But as you will see in this section you can also mix both strategies. Let's have a deeper look at how auto-mapping works.

When auto-mapping results MyBatis will get the column name and look for a property with the same name ignoring case. That means that if a column named ID and property named id are found, MyBatis will set the id property with the ID column value.

Usually database columns are named using uppercase letters and underscores between words and java properties often follow the camelcase naming covention. To enable the auto-mapping between them set the setting mapUnderscoreToCamelCase to true.

Auto-mapping works even when there is an specific result map. When this happens, for each result map, all columns that are present in the ResultSet that have not a manual mapping will be auto-mapped, then manual mappings will be processed. In the following sample id and userName columns will be auto-mapped and hashed_password column will be mapped.

<select id="selectUsers" parameterType="int" resultType="User">   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>

There are three auto-mapping levels:

  • NONE - disables auto-mapping. Only manually mapped properties will be set.
  • PARTIAL - will auto-map results except those that have nested result mappings defined inside (joins).
  • FULL - auto-maps everything.

The default value is PARTIAL, and it is so for a reason. When FULL is used auto-mapping will be performed when processing join results and joins retrieve data of several different entities in the same row hence this may result in undesired mappings. To understand the risk have a look at the following sample:

<select id="selectBlog" parameterType="int" 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" javaType="Author" resultMap="authorResult"/> </resultMap> <resultMap id="authorResult" type="Author">   <result property="username" column="author_username"/> </resultMap>

With this result map both Blog and Author will be auto-mapped. But note that Author has an idproperty and there is a column named id in the ResultSet so Author's id will be filled with Blog's id, and that is not what you were expecting. So use the FULL option with caution.

缓存

MyBatis 包含一个很是强大的查询缓存特性,它能够很是方便地配置和定制。MyBatis 3 中的缓存实现的不少改进都已经实现了,使得它更增强大并且易于配置。

默认状况下是没有开启缓存的,除了局部的 session 缓存,能够加强变现并且处理循环依赖也是必须的。要开启二级缓存,你须要在你的 SQL 映射文件中添加一行:

<cache/>

字面上看就是这样。这个简单语句的效果以下:

  • 映射语句文件中的全部 select 语句将会被缓存。
  • 映射语句文件中的全部 insert,update 和 delete 语句会刷新缓存。
  • 缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回。
  • 根据时间表(好比 no Flush Interval,没有刷新间隔), 缓存不会以任什么时候间顺序 来刷新。
  • 缓存会存储列表集合或对象(不管查询方法返回什么)的 1024 个引用。
  • 缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,并且能够安全地被调用者修改,而不干扰其余调用者或线程所作的潜在修改。

全部的这些属性均可以经过缓存元素的属性来修改。好比:

<cache   eviction="FIFO"   flushInterval="60000"   size="512"   readOnly="true"/>

这个更高级的配置建立了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,并且返回的对象被认为是只读的,所以在不一样线程中的调用者之间修改它们会致使冲突。

可用的收回策略有:

  • LRU – 最近最少使用的:移除最长时间不被使用的对象。
  • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
  • SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
  • WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

默认的是 LRU。

flushInterval(刷新间隔)能够被设置为任意的正整数,并且它们表明一个合理的毫秒形式的时间段。默认状况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。

size(引用数目)能够被设置为任意正整数,要记住你缓存的对象数目和你运行环境的可用内存资源数目。默认值是 1024。

readOnly(只读)属性能够被设置为 true 或 false。只读的缓存会给全部调用者返回缓存对象的相同实例。所以这些对象不能被修改。这提供了很重要的性能优点。可读写的缓存会返回缓存对象的拷贝(经过序列化) 。这会慢一些,可是安全,所以默认是 false。

使用自定义缓存

除了这些自定义缓存的方式, 你也能够经过实现你本身的缓存或为其余第三方缓存方案建立适配器来彻底覆盖缓存行为。

<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();   ReadWriteLock getReadWriteLock(); }

要配置你的缓存, 简单和公有的 JavaBeans 属性来配置你的缓存实现, 并且是经过 cache 元素来传递属性, 好比, 下面代码会在你的缓存实现中调用一个称为 “setCacheFile(String file)” 的方法:

<cache type="com.domain.something.MyCustomCache">   <property name="cacheFile" value="/tmp/my-custom-cache.tmp"/> </cache>

你可使用全部简单类型做为 JavaBeans 的属性,MyBatis 会进行转换。

记得缓存配置和缓存实例是绑定在 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"/>
 

转至: https://blog.csdn.net/zhll3377/article/details/8203440

相关文章
相关标签/搜索