国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Java > 正文

實例講解Java的MyBatis框架對MySQL中數(shù)據(jù)的關(guān)聯(lián)查詢

2019-11-26 14:15:50
字體:
供稿:網(wǎng)友

mybatis 提供了高級的關(guān)聯(lián)查詢功能,可以很方便地將數(shù)據(jù)庫獲取的結(jié)果集映射到定義的Java Bean 中。下面通過一個實例,來展示一下Mybatis對于常見的一對多和多對一關(guān)系復(fù)雜映射是怎樣處理的。
設(shè)計一個簡單的博客系統(tǒng),一個用戶可以開多個博客,在博客中可以發(fā)表文章,允許發(fā)表評論,可以為文章加標簽。博客系統(tǒng)主要有以下幾張表構(gòu)成:
Author表:作者信息表,記錄作者的信息,用戶名和密碼,郵箱等。
Blog表   :  博客表,一個作者可以開多個博客,即Author和Blog的關(guān)系是一對多。
Post表  : 文章記錄表,記錄文章發(fā)表時間,標題,正文等信息;一個博客下可以有很多篇文章,Blog 和Post的關(guān)系是一對多。
Comments表:文章評論表,記錄文章的評論,一篇文章可以有很多個評論:Post和Comments的對應(yīng)關(guān)系是一對多。
Tag表:標簽表,表示文章的標簽分類,一篇文章可以有多個標簽,而一個標簽可以應(yīng)用到不同的文章上,所以Tag和Post的關(guān)系是多對多的關(guān)系;(Tag和Post的多對多關(guān)系通過Post_Tag表體現(xiàn))
Post_Tag表: 記錄 文章和標簽的對應(yīng)關(guān)系。

201661153922692.png (889×595)

一般情況下,我們會根據(jù)每一張表的結(jié)構(gòu) 創(chuàng)建與此相對應(yīng)的JavaBean(或者Pojo),來完成對表的基本CRUD操作。

201661154013811.png (819×626)

上述對單個表的JavaBean定義有時候不能滿足業(yè)務(wù)上的需求。在業(yè)務(wù)上,一個Blog對象應(yīng)該有其作者的信息和一個文章列表,如下圖所示:

201661154042218.png (563×241)

如果想得到這樣的類的實例,則最起碼要有一下幾步:
1. 通過Blog 的id 到Blog表里查詢Blog信息,將查詢到的blogId 和title 賦到Blog對象內(nèi);
2. 根據(jù)查詢到到blog信息中的authorId 去 Author表獲取對應(yīng)的author信息,獲取Author對象,然后賦到Blog對象內(nèi);
3. 根據(jù) blogId 去 Post表里查詢 對應(yīng)的 Post文章列表,將List<Post>對象賦到Blog對象中;
這樣的話,在底層最起碼調(diào)用三次查詢語句,請看下列的代碼:

/*  * 通過blogId獲取BlogInfo對象  */ public static BlogInfo ordinaryQueryOnTest(String blogId) {  BigDecimal id = new BigDecimal(blogId);  SqlSession session = sqlSessionFactory.openSession();  BlogInfo blogInfo = new BlogInfo();  //1.根據(jù)blogid 查詢Blog對象,將值設(shè)置到blogInfo中  Blog blog = (Blog)session.selectOne("com.foo.bean.BlogMapper.selectByPrimaryKey",id);  blogInfo.setBlogId(blog.getBlogId());  blogInfo.setTitle(blog.getTitle());    //2.根據(jù)Blog中的authorId,進入數(shù)據(jù)庫查詢Author信息,將結(jié)果設(shè)置到blogInfo對象中  Author author = (Author)session.selectOne("com.foo.bean.AuthorMapper.selectByPrimaryKey",blog.getAuthorId());  blogInfo.setAuthor(author);    //3.查詢posts對象,設(shè)置進blogInfo中  List posts = session.selectList("com.foo.bean.PostMapper.selectByBlogId",blog.getBlogId());  blogInfo.setPosts(posts);  //以JSON字符串的形式將對象打印出來  JSONObject object = new JSONObject(blogInfo);  System.out.println(object.toString());  return blogInfo; } 

從上面的代碼可以看出,想獲取一個BlogInfo對象比較麻煩,總共要調(diào)用三次數(shù)據(jù)庫查詢,得到需要的信息,然后再組裝BlogInfo對象。

嵌套語句查詢
mybatis提供了一種機制,叫做嵌套語句查詢,可以大大簡化上述的操作,加入配置及代碼如下:

<resultMap type="com.foo.bean.BlogInfo" id="BlogInfo">  <id column="blog_id" property="blogId" />  <result column="title" property="title" />  <association property="author" column="blog_author_id"   javaType="com.foo.bean.Author" select="com.foo.bean.AuthorMapper.selectByPrimaryKey">  </association>  <collection property="posts" column="blog_id" ofType="com.foo.bean.Post"   select="com.foo.bean.PostMapper.selectByBlogId">  </collection> </resultMap>  <select id="queryBlogInfoById" resultMap="BlogInfo" parameterType="java.math.BigDecimal">  SELECT  B.BLOG_ID,  B.TITLE,  B.AUTHOR_ID AS BLOG_AUTHOR_ID  FROM LOULUAN.BLOG B  where B.BLOG_ID = #{blogId,jdbcType=DECIMAL} </select> 
/*  * 通過blogId獲取BlogInfo對象  */ public static BlogInfo nestedQueryOnTest(String blogId) {  BigDecimal id = new BigDecimal(blogId);  SqlSession session = sqlSessionFactory.openSession();  BlogInfo blogInfo = new BlogInfo();  blogInfo = (BlogInfo)session.selectOne("com.foo.bean.BlogMapper.queryBlogInfoById",id);  JSONObject object = new JSONObject(blogInfo);  System.out.println(object.toString());  return blogInfo; } 

通過上述的代碼完全可以實現(xiàn)前面的那個查詢。這里我們在代碼里只需要 blogInfo = (BlogInfo)session.selectOne("com.foo.bean.BlogMapper.queryBlogInfoById",id);一句即可獲取到復(fù)雜的blogInfo對象。

嵌套語句查詢的原理
在上面的代碼中,Mybatis會執(zhí)行以下流程:
1.先執(zhí)行 queryBlogInfoById 對應(yīng)的語句從Blog表里獲取到ResultSet結(jié)果集;
2.取出ResultSet下一條有效記錄,然后根據(jù)resultMap定義的映射規(guī)格,通過這條記錄的數(shù)據(jù)來構(gòu)建對應(yīng)的一個BlogInfo 對象。
3. 當要對BlogInfo中的author屬性進行賦值的時候,發(fā)現(xiàn)有一個關(guān)聯(lián)的查詢,此時Mybatis會先執(zhí)行這個select查詢語句,得到返回的結(jié)果,將結(jié)果設(shè)置到BlogInfo的author屬性上;
4. 對BlogInfo的posts進行賦值時,也有上述類似的過程。
5. 重復(fù)2步驟,直至ResultSet. next () == false;
以下是blogInfo對象構(gòu)造賦值過程示意圖:

201661154146597.png (933×418)

這種關(guān)聯(lián)的嵌套查詢,有一個非常好的作用就是:可以重用select語句,通過簡單的select語句之間的組合來構(gòu)造復(fù)雜的對象。上面嵌套的兩個select語句com.foo.bean.AuthorMapper.selectByPrimaryKey和com.foo.bean.PostMapper.selectByBlogId完全可以獨立使用。

N+1問題
它的弊端也比較明顯:即所謂的N+1問題。關(guān)聯(lián)的嵌套查詢顯示得到一個結(jié)果集,然后根據(jù)這個結(jié)果集的每一條記錄進行關(guān)聯(lián)查詢。
現(xiàn)在假設(shè)嵌套查詢就一個(即resultMap 內(nèi)部就一個association標簽),現(xiàn)查詢的結(jié)果集返回條數(shù)為N,那么關(guān)聯(lián)查詢語句將會被執(zhí)行N次,加上自身返回結(jié)果集查詢1次,共需要訪問數(shù)據(jù)庫N+1次。如果N比較大的話,這樣的數(shù)據(jù)庫訪問消耗是非常大的!所以使用這種嵌套語句查詢的使用者一定要考慮慎重考慮,確保N值不會很大。
以上面的例子為例,select 語句本身會返回com.foo.bean.BlogMapper.queryBlogInfoById 條數(shù)為1 的結(jié)果集,由于它有兩條關(guān)聯(lián)的語句查詢,它需要共訪問數(shù)據(jù)庫 1*(1+1)=3次數(shù)據(jù)庫。

嵌套結(jié)果查詢
嵌套語句的查詢會導(dǎo)致數(shù)據(jù)庫訪問次數(shù)不定,進而有可能影響到性能。Mybatis還支持一種嵌套結(jié)果的查詢:即對于一對多,多對多,多對一的情況的查詢,Mybatis通過聯(lián)合查詢,將結(jié)果從數(shù)據(jù)庫內(nèi)一次性查出來,然后根據(jù)其一對多,多對一,多對多的關(guān)系和ResultMap中的配置,進行結(jié)果的轉(zhuǎn)換,構(gòu)建需要的對象。
重新定義BlogInfo的結(jié)果映射 resultMap

<resultMap type="com.foo.bean.BlogInfo" id="BlogInfo">  <id column="blog_id" property="blogId"/>  <result column="title" property="title"/>  <association property="author" column="blog_author_id" javaType="com.foo.bean.Author">   <id column="author_id" property="authorId"/>   <result column="user_name" property="userName"/>   <result column="password" property="password"/>   <result column="email" property="email"/>   <result column="biography" property="biography"/>  </association>  <collection property="posts" column="blog_post_id" ofType="com.foo.bean.Post">   <id column="post_id" property="postId"/>   <result column="blog_id" property="blogId"/>   <result column="create_time" property="createTime"/>   <result column="subject" property="subject"/>   <result column="body" property="body"/>   <result column="draft" property="draft"/>  </collection>   </resultMap> 

對應(yīng)的sql語句如下:

<select id="queryAllBlogInfo" resultMap="BlogInfo">  SELECT   B.BLOG_ID,   B.TITLE,   B.AUTHOR_ID AS BLOG_AUTHOR_ID,   A.AUTHOR_ID,   A.USER_NAME,   A.PASSWORD,   A.EMAIL,   A.BIOGRAPHY,   P.POST_ID,   P.BLOG_ID AS BLOG_POST_ID ,  P.CREATE_TIME,   P.SUBJECT,   P.BODY,   P.DRAFT FROM BLOG B LEFT OUTER JOIN AUTHOR A  ON B.AUTHOR_ID = A.AUTHOR_ID LEFT OUTER JOIN POST P  ON P.BLOG_ID = B.BLOG_ID </select> 
/*  * 獲取所有Blog的所有信息  */ public static BlogInfo nestedResultOnTest() {  SqlSession session = sqlSessionFactory.openSession();  BlogInfo blogInfo = new BlogInfo();  blogInfo = (BlogInfo)session.selectOne("com.foo.bean.BlogMapper.queryAllBlogInfo");  JSONObject object = new JSONObject(blogInfo);  System.out.println(object.toString());  return blogInfo; } 

嵌套結(jié)果查詢的執(zhí)行步驟:
1.根據(jù)表的對應(yīng)關(guān)系,進行join操作,獲取到結(jié)果集;
2. 根據(jù)結(jié)果集的信息和BlogInfo 的resultMap定義信息,對返回的結(jié)果集在內(nèi)存中進行組裝、賦值,構(gòu)造BlogInfo;
3. 返回構(gòu)造出來的結(jié)果List<BlogInfo> 結(jié)果。
對于關(guān)聯(lián)的結(jié)果查詢,如果是多對一的關(guān)系,則通過形如 <association property="author" column="blog_author_id" javaType="com.foo.bean.Author"> 進行配置,Mybatis會通過column屬性對應(yīng)的author_id 值去從內(nèi)存中取數(shù)據(jù),并且封裝成Author對象;
如果是一對多的關(guān)系,就如Blog和Post之間的關(guān)系,通過形如 <collection property="posts" column="blog_post_id" ofType="com.foo.bean.Post">進行配置,MyBatis通過 blog_Id去內(nèi)存中取Post對象,封裝成List<Post>;
對于關(guān)聯(lián)結(jié)果的查詢,只需要查詢數(shù)據(jù)庫一次,然后對結(jié)果的整合和組裝全部放在了內(nèi)存中。
以上是通過查詢Blog所有信息來演示了一對多和多對一的映射對象處理。

ps:自身關(guān)聯(lián)映射示例:
實體類

public class Module {   private int id;  private String key;  private String name;  private Module parentModule;  private List<Module> childrenModules;  private String url;  private int sort;  private String show;  private String del;   public int getId() {   return id;  }   public void setId(int id) {   this.id = id;  }   public String getKey() {   return key;  }   public void setKey(String key) {   this.key = key;  }   public String getName() {   return name;  }   public void setName(String name) {   this.name = name;  }   public Module getParentModule() {   return parentModule;  }   public void setParentModule(Module parentModule) {   this.parentModule = parentModule;  }   public String getUrl() {   return url;  }   public void setUrl(String url) {   this.url = url;  }   public int getSort() {   return sort;  }   public void setSort(int sort) {   this.sort = sort;  }   public String getShow() {   return show;  }   public void setShow(String show) {   this.show = show;  }   public String getDel() {   return del;  }   public void setDel(String del) {   this.del = del;  }   public List<Module> getChildrenModules() {   return childrenModules;  }   public void setChildrenModules(List<Module> childrenModules) {   this.childrenModules = childrenModules;  } } 
XML代碼:
<mapper namespace="com.sagaware.caraccess.mapper.ModuleMapper">   <resultMap type="Module" id="moduleResultMap">   <id property="id" column="module_id"/>   <result property="key" column="module_key"/>   <result property="name" column="module_name"/>   <result property="url" column="module_url"/>   <result property="sort" column="module_sort"/>   <result property="show" column="module_show"/>   <result property="del" column="module_del"/>      <!-- 查詢父模塊 -->   <association property="parentModule" column="module_parent_id" select="getModulesById" />      <!-- 查詢子模塊 -->   <collection property="childrenModules" column="module_id" select="getChildrenModues" />     </resultMap>    <select id="getModules" parameterType="String" resultMap="moduleResultMap">   select * from tb_module where module_id=2  </select>    <select id="getModulesById" parameterType="int" resultMap="moduleResultMap">   select * from tb_module where module_id = #{module_id}  </select>    <select id="getChildrenModues" parameterType="int" resultMap="moduleResultMap">   select * from tb_module where module_parent_id = #{module_id}  </select> </mapper> 

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 新绛县| 普兰县| 东乡县| 广河县| 赫章县| 桓台县| 桑植县| 金沙县| 资源县| 甘谷县| 涿州市| 安泽县| 扎兰屯市| 营口市| 印江| 茶陵县| 宁都县| 韩城市| 孙吴县| 澜沧| 扬中市| 那曲县| 廊坊市| 盐城市| 赤壁市| 白玉县| 尖扎县| 巴林左旗| 裕民县| 贵南县| 商河县| 新绛县| 华蓥市| 杨浦区| 石棉县| 海原县| 农安县| 搜索| 大余县| 松溪县| 吉水县|