zoukankan      html  css  js  c++  java
  • java版云笔记(八)之关联映射

    Mybatis关联映射

    通过数据库对象之间的关联关系,反映到到实体对象之间的引用。

    加载多个表中的关联数据,封装到我们的实体对象中。

    当业务对数据库进行关联查询。

    关联

    <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 以参数对象形式来设置给目标嵌套查询语句。
    fetchType 可选的。有效值为** lazy和eager。 如果使用了,它将取代全局配置参数lazyLoadingEnabled**。

    **示例: **

    <resultMap id="blogResult" type="Blog">
    	<!-- property映射到读者实体类 -->
      <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>
    

      非常重要: 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" javaType="Author" select="authorResult">
    	    <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>	
    	
    	<!--select的值和id的值要一样 -->
    	<select id="authorResult" resultType="Author"> 
    		<!-- sql语句 --->
    	</select> 
    	<!-- resultMap的id和resultMap值一样 -->
    	<select id="getById" parameterType="int" resultMap="blogResult" > 
    		<!-- sql语句 --->
    	</select> 
    
    

    如果blog有一个co-author怎么办? select语句将看起来这个样子

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

    因为结果中的列名与resultMap中的列名不同。 你需要指定columnPrefix去重用映射co-author结果的resultMap。

    <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="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 让你后来可以改变想法,而不对你的代码造成很小(或任何)影响。

  • 相关阅读:
    LeetCode-求最长回文子序列
    C++四种类型转换总结
    kmp算法分析和C++实现
    把二叉树打印成多行
    考研数据结构笔记—堆排序
    天勤考研数据结构笔记—栈的C语言实现
    合并两个排序的链表递归和非递归C++实现
    二叉树的线索化
    单链表的基本操作实现
    OpenFaceswap 入门教程(3): 软件参数篇!
  • 原文地址:https://www.cnblogs.com/yangliguo/p/7624854.html
Copyright © 2011-2022 走看看