Mybatis学习笔记(四)属性描述property映射到列结果的字段或属性。如果匹配的是存在的,和给定名称相同的Java
Mybatis学习笔记(四)
属性
描述
property
映射到列结果的字段或属性。如果匹配的是存在的,和给定名称相同的JavaBeans的属性,那么就会使用。否则MyBatis将会寻找给定名称的字段。这两种情形你可以使用通常点式的复杂属性导航。比如,你可以这样映射一些东西:“username”,或者映射到一些复杂的东西:“address.street.number”。
column
从数据库中得到的列名,或者是列名的重命名标签。这也是通常和会传递给resultSet.getString(columnName)方法参数中相同的字符串。
javaType
一个Java类的完全限定名,或一个类型别名(参加上面内建类型别名的列表)。如果你映射到一个JavaBean,MyBatis通常可以断定类型。然而,如果你映射到的是HashMap,那么你应该明确地指定javaType来保证所需的行为。
jdbcType
在这个表格之后的所支持的JDBC类型列表中的类型。JDBC类型是仅仅需要对插入,更新和删除操作可能为空的列进行处理。这是JDBC的需要,而不是MyBatis的。如果你直接使用JDBC编程,你需要指定这个类型-但仅仅对可能为空的值。
typeHandler
我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的类型处理器。这个属性值是类的完全限定名或者是一个类型处理器的实现,或者是类型别名。
?
?
构造方法
[html]?view plaincopy
- <constructor>??
- ????<idArg?column="id"?javaType="int"/>??
- ????<arg?column=”username”?javaType=”String”/>??
- </constructor>??
对于大多数数据传输对象(Data Transfer Object,DTO)类型,属性可以起作用,而且像绝大多数的领域模型,指令也许是你想使用一成不变的类的地方。通常包含引用或查询数据的表很少或基本不变的话对一成不变的类来说是合适的。构造方法注入允许你在初始化时为类设置属性的值,而不用暴露出公有方法。MyBatis也支持私有属性和私有JavaBeans属性来达到这个目的,但是一些人更青睐构造方法注入。Constructor(构造方法)元素支持这个。
?
[java]?view plaincopy
- public?class?User?{??
- ????//…??
- ????public?User(int?id,?String?username)?{??
- ????????//…??
- ????}??
- ????//…??
- }??
为了向这个构造方法中注入结果,MyBatis需要通过它的参数的类型来标识构造方法。Java没有自查(或反射)参数名的方法。所以当创建一个构造方法元素时,保证参数是按顺序排列的,而且数据类型也是确定的。
?
?
[java]?view plaincopy
- <constructor>??
- ????<idArg?column="id"?javaType="int"/>??
- ????<arg?column=”username”?javaType=”String”/>??
- </constructor>??
剩余的属性和规则和固定的id和result元素是相同的。
关联?
[html]?view plaincopy
- <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在这方面会有两种不同的方式:
(1) 嵌套查询:通过执行另外一个SQL映射语句来返回预期的复杂类型。
(2) 嵌套结果:使用嵌套结果映射来处理重复的联合结果的子集。
首先,然让我们来查看这个元素的属性。
?
?
属性
描述
property
映射到列结果的字段或属性。如果匹配的是存在的,和给定名称相同的JavaBeans的属性,那么就会使用。否则MyBatis将会寻找给定名称的字段。这两种情形你可以使用通常点式的复杂属性导航。比如,你可以这样映射一些东西:“username”,或者映射到一些复杂的东西:“address.street.number”。
column
来自数据库的列名,或重命名的列标签。这和通常传递给resultSet.getString(columnName)方法的字符串是相同的。
注意:要处理复合主键,你可以指定多个列名通过column=”{prop1=col1,prop2=col2}”这种语法来传递给嵌套查询语句。这会引起prop1和prop2以参数对象形式来设置给目标嵌套查询语句。
javaType
一个Java类的完全限定名,或一个类型别名(参加上面内建类型别名的列表)。如果你映射到一个JavaBean,MyBatis通常可以断定类型。然而,如果你映射到的是HashMap,那么你应该明确地指定javaType来保证所需的行为。
jdbcType
在这个表格之前的所支持的JDBC类型列表中的类型。JDBC类型是仅仅需要对插入,更新和删除操作可能为空的列进行处理。这是JDBC的需要,而不是MyBatis的。如果你直接使用JDBC编程,你需要指定这个类型-但仅仅对可能为空的值。
typeHandler
我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的类型处理器。这个属性值是类的完全限定名或者是一个类型处理器的实现,或者是类型别名。
?
?
关联的嵌套查询
?
select ? ? ? ? ? ? ? ? ? ? ? ?
另外一个映射语句的ID,可以加载这个属性映射需要的复杂类型。获取的在列属性中指定的列的值将被传递给目标select语句作为参数。表格后面有一个详细的示例。
注意:要处理复合主键,你可以指定多个列名通过column=”{prop1=col1,prop2=col2}”这种语法来传递给嵌套查询语句。这会引起prop1和prop2以参数对象形式来设置给目标嵌套查询语句。
示例:
?
?
?
[html]?view plaincopy
- <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查询问题可以是这样引起的:
?? ?(1)你执行了一个单独的SQL语句来获取结果列表(就是“+1”)。
?? ?(2)对返回的每条记录,你执行了一个查询语句来为每个加载细节(就是“N”)。
?
关联的嵌套结果
resultMap
这是结果映射的ID,可以映射关联的嵌套结果到一个合适的对象图中。这是一种替代方法来调用另外一个查询语句。这允许你联合多个表来合成到一个单独的结果集。这样的结果集可能包含重复,数据的重复组需要被分解,合理映射到一个嵌套的对象图。为了使它变得容易,MyBatis让你“链接”结果映射,来处理嵌套结果。例子会很容易来仿照,这个表格后面也有一个示例。
?
?
[html]?view plaincopy
- <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>??
注意这个联合查询,以及采取保护来确保所有结果被唯一而且清晰的名字来重命名。这使得映射非常简单。现在我们可以映射这个结果:
[html]?view plaincopy
- <resultMap?id="blogResult"?type="Blog">??
- ????<id?property=”blog_id”?column="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>??
非常重要:在嵌套结果映射中id元素扮演了非常重要的角色。应该通常指定一个或多个属性,它们可以用来唯一标识结果。实际上就是如果你不使用它(id元素),但是会产生一个严重的性能问题,不过MyBatis仍然可以正常工作。选择的属性越少越好,它们可以唯一地标识结果。主键就是一个显而易见的选择(即便是联合主键)。
现在,上面的示例用了外部的结果映射元素来映射关联。这使得Author结果映射可以重用。然而,如果你不需要重用它的话,或者你仅仅引用你所有的结果映射合到一个单独描述的结果映射中。你可以嵌套结果映射。这里给出使用这种方式的相同示例:
?
?
[html]?view plaincopy
- <resultMap?id="blogResult"?type="Blog">??
- ????<id?property=”blog_id”?column="id"?/>??
- ????<result?property="title"?column="blog_title"/>??
- ????<association?property="author"?column="blog_author_id"??
- ????????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>??
集合
[html]?view plaincopy
- <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中,我们使用集合元素。就像关联元素一样,我们可以从连接中使用嵌套查询,或者嵌套结果。
集合的嵌套查询
首先,让我们看看使用嵌套查询来为博客加载文章。
[html]?view plaincopy
- <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="Author">??
- ????SELECT?*?FROM?POST?WHERE?BLOG_ID?=?#{id}??
- </select>??
首先,你应该注意我们使用的是集合元素。然后要注意那个新的“ofType”属性。这个属性用来区分JavaBean(或字段)属性类型和集合包含的类型来说是很重要的。所以你可以读出下面这个映射:
[html]?view plaincopy
- <collection?property="posts"?javaType=”ArrayList”?column="blog_id"??
- ????ofType="Post"?select=”selectPostsForBlog”/>??
读作:“在Post类型的ArrayList中的posts的集合。”
集合的嵌套结果
[html]?view plaincopy
- <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>??
现在用文章映射集合映射博客,可以简单写为:
[html]?view plaincopy
- <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元素的重要性。
如果你引用更长的形式允许你的结果映射的更多重用,你可以使用下面这个替代的映
[html]?view plaincopy
- <resultMap?id="blogResult"?type="Blog">??
- ????<id?property=”id”?column="blog_id"?/>??
- ????<result?property="title"?column="blog_title"/>??
- ????<collection?property="posts"?ofType="Post"?resultMap=”blogPostResult”/>??
- </resultMap>??
- <resultMap?id="blogPostResult"?type="Post">??
- ????<id?property="id"?column="post_id"/>??
- ????<result?property="subject"?column="post_subject"/>??
- ????<result?property="body"?column="post_body"/>??
- </resultMap>??
鉴别器
[html]?view plaincopy
- <discriminator?javaType="int"?column="draft">??
- ????<case?value="1"?resultType="DraftPost"/>??
- </discriminator>??
有时一个单独的数据库查询也许返回很多不同(但是希望有些关联)数据类型的结果集。鉴别器元素就是被设计来处理这个情况的,还有包括类的继承层次结构。鉴别器非常容易理解,因为它的表现很像Java语言中的switch语句。
定义鉴别器指定了column和javaType属性。列是MyBatis查找比较值的地方。JavaType是需要被用来保证等价测试的合适类型(尽管字符串在很多情形下都会有用)。比如:
[html]?view plaincopy
- <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>??
- <resultMap?id="carResult"?type="Car">??
- ????<result?property=”doorCount”?column="door_count"?/>??
- </resultMap>??
上面示例中,MyBatis会从结果集中得到每条记录,然后比较它的vehicle类型的值。如果它匹配任何一个鉴别器的实例,那么就使用这个实例指定的结果映射。
还有另外一种语法来做简洁的映射风格。比如:
[html]?view plaincopy
- <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>??