mybatis-高階結果對映之一對一(多種方式, 有沒提到的你找我)
版權宣告:本文為博主原創文章,未經博主允許不得轉載。https://blog.csdn.net/weixin_37139197/article/details/83045858
mybatis的高階結果對映可以很輕鬆的幫助我們處理一對一, 一對多的資料關係。
1 資料準備
1.1 資料庫
建立以下的名為mybatis 的資料庫, 並在其下建立4個表。
在此就不貼出來建表的 SQL 語句了 , 感興趣的可以去ofollow,noindex" target="_blank">我的 Github:mybatis-mapping 中獲取。
1.2 實體類, 介面和XML
使用mybatis-程式碼生成器 生成相應的實體類, 介面和XML。
以上為生成的專案結構。
2 一對一對映
建立的表中, author 和 blog 就是一對一的關係。
我們希望找到一個blog, 然後就會把它的作者資訊也關聯出來。
2.1 resultType 方式一
注意:resultType方式要求獲取到的列和成員變數名一致。
2.1.1 建立物件
建立一個類BlogCustom
, 該類繼承於Blog
類, 在該類上新增Author
物件作為成員變數
2.1.2 建立介面方法和XML 語句
BlogBO selectBoById(int id); /** * 根據部落格的 id 獲取部落格及作者的資訊 * @param id * @return */ BlogCustom selectCutomById(int id);
對應的 XML 語句:
<select id="selectCutomById" parameterType="java.lang.Integer" resultType="com.homejim.mybatis.entity.BlogCustom"> SELECT b.id, b.title, b.author_id AS authorId, a.id AS "author.id", a.username AS "author.username", a.password AS "author.password" , a.email AS "author.email" FROM blog b LEFT JOIN author a ON b.author_id=a.id where b.id = #{id,jdbcType=INTEGER} </select>
通過author.username
這種方式可以設定author
物件中的username
屬性。
2.1.3 測試
@Test public void testSelectCustomById() { SqlSession sqlSession = sqlSessionFactory.openSession(); BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class); BlogCustom blogCustom = blogMapper.selectCutomById(1); System.out.println(ToStringBuilder.reflectionToString(blogCustom, ToStringStyle.MULTI_LINE_STYLE)); }
執行後的結果
有時候, 我們獲取的另外的屬性不多, 則我們也可以選擇下面的方式二。
2.2 resultType 方式二
2.2.1 建立物件
建立一個類BlogBO
, 該類繼承於Blog
類, 在該類上新增Author
中的成員變數作為該類自己的成員變數。
2.2.2 建立介面方法和XML 語句
介面方法
/** * 根據部落格的 id 獲取部落格及作者的資訊 * @param id * @return */ BlogBO selectBoById(int id);
XML 對應的 SQL , 其中 resultType 是上面定義的實體物件。
<select id="selectBoById" parameterType="java.lang.Integer" resultType="com.homejim.mybatis.entity.BlogBO"> select b.id, b.title, b.author_id as authorId, a.id as userId, a.username, a.email from blog b left join author a on b.author_id=a.id where b.id = #{id,jdbcType=INTEGER} </select>
2.2.3 測試
建立一個測試例子。
@Test public void testSelectBoById() { SqlSession sqlSession = sqlSessionFactory.openSession(); BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class); BlogBO blogBO = blogMapper.selectBoById(1); System.out.println(ToStringBuilder.reflectionToString(blogBO, ToStringStyle.MULTI_LINE_STYLE)); }
測試結果
2.3 resultMap 方式
2.3.1 建立物件
建立一個類BlogCustom
, 該類繼承於Blog
類, 在該類上新增Author
物件作為成員變數
2.3.2 建立對應 resultMap
對應建立一個resultMap
<resultMap id="BOResultMap" type="com.homejim.mybatis.entity.BlogCustom" extends="BaseResultMap"> <result column="username" jdbcType="VARCHAR" property="author.username" /> <result column="user_id" jdbcType="VARCHAR" property="author.id" /> <result column="email" jdbcType="VARCHAR" property="author.email" /> </resultMap>
2.3.3 建立介面方法和XML 語句
/** * 根據部落格的 id 獲取部落格及作者的資訊, resultMap 方式 * @param id * @return */ BlogCustom selectCutomByIdMap(int id);
SQL 語句得出的列名與BOResultMap 一致。
<select id="selectCutomByIdMap" parameterType="java.lang.Integer" resultMap="BOResultMap"> SELECT b.id, b.title, b.author_id AS authorId, a.id AS "user_id", a.username, a.email FROM blog b LEFT JOIN author a ON b.author_id=a.id where b.id = #{id,jdbcType=INTEGER} </select>
2.3.4 測試
/** *resultMap 方式一測試 */ @Test public void testSelectCustomByIdMap() { SqlSession sqlSession = sqlSessionFactory.openSession(); BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class); BlogCustom blogCustom = blogMapper.selectCutomByIdMap(1); System.out.println(ToStringBuilder.reflectionToString(blogCustom, ToStringStyle.MULTI_LINE_STYLE)); }
得出的結果
其實該型別也可以配置對應BlogBO
類對應的方式, 在此不做過多的講解。
2.4 resultMap + association 方式
2.4.1 建立物件
建立一個類BlogCustom
, 該類繼承於Blog
類, 在該類上新增Author
物件作為成員變數
2.4.2 建立 resultMap
<resultMap id="CustomResultMap" type="com.homejim.mybatis.entity.BlogCustom" extends="BaseResultMap"> <association property="author" javaType="com.homejim.mybatis.entity.Author"> <id column="user_id" jdbcType="INTEGER" property="id" /> <result column="username" jdbcType="VARCHAR" property="username" /> <result column="email" jdbcType="VARCHAR" property="email" /> </association> </resultMap>
或者, 可以引用別的 Mapper 中的結果集 。
<resultMap id="CustomResultMap" type="com.homejim.mybatis.entity.BlogCustom" extends="BaseResultMap"> <association property="author" javaType="com.homejim.mybatis.entity.Author" resultMap="com.homejim.mybatis.mapper.AuthorMapper.BaseResultMap"> </association> </resultMap>
2.4.3 建立介面方法和XML 語句
/** * 根據部落格的 id 獲取部落格及作者的資訊, resultMap + association方式 * @param id * @return */ BlogCustom selectCutomByIdAMap(int id);
SQL 語句
<select id="selectCutomByIdAMap" parameterType="java.lang.Integer" resultMap="CustomResultMap"> SELECT b.id, b.title, b.author_id AS authorId, a.id AS "user_id", a.username, a.email FROM blog b LEFT JOIN author a ON b.author_id=a.id where b.id = #{id,jdbcType=INTEGER} </select>
2.4.4 測試
/** *resultMap + association 方式測試 */ @Test public void testSelectCustomByIdAMap() { SqlSession sqlSession = sqlSessionFactory.openSession(); BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class); BlogCustom blogCustom = blogMapper.selectCutomByIdAMap(1); System.out.println(ToStringBuilder.reflectionToString(blogCustom, ToStringStyle.MULTI_LINE_STYLE)); }
結果
2.5 resultMap + association 巢狀查詢
以上幾種方法都是通過left join 的 SQL 語句獲取多個物件的結果。 還可以經過簡單的 SQL 語句, 多次查詢而轉化為我們需要的結果。
2.5.1 建立物件
建立一個類BlogCustom
, 該類繼承於Blog
類, 在該類上新增Author
物件作為成員變數
2.5.2 建立 resultMap
<resultMap id="blogAuthorMap" type="com.homejim.mybatis.entity.BlogCustom" extends="BaseResultMap"> <association property="author" column="author_id" select="com.homejim.mybatis.mapper.AuthorMapper.selectById" fetchType="eager" /> </resultMap>
該結果集與之前的不同了,association 標籤中使用了select 屬性。
select: 另一個查詢的 id, mybatis 會額外執行這個查詢來獲取巢狀的結果
column: 列名(別名), 將主查詢中的列作為巢狀查詢的引數, 多個引數使用逗號(英文)分隔開。
fetchType: 資料載入的方式, 可選擇為 lazy(延遲載入) 或者 eager(積極載入), 該配置會覆蓋全域性的 lazyLoadingEnabled 配置。
2.5.3 建立介面方法和XML 語句
/** * 根據部落格的 id 獲取部落格及作者的資訊, resultMap + association巢狀方式 * @param id * @return */ BlogCustom selectBlogAndAuthorByIdSelect(int id);
獲取的資料是部落格和使用者的資訊, 以下的 SQL 只是獲取部落格資訊, 使用者資訊通過com.homejim.mybatis.mapper.AuthorMapper.selectById
獲取。
<select id="selectBlogAndAuthorByIdSelect" parameterType="java.lang.Integer" resultMap="blogAuthorMap"> SELECT b.id, b.title, b.author_id FROM blog b where b.id = #{id,jdbcType=INTEGER} </select>
com.homejim.mybatis.mapper.AuthorMapper.selectById
是一個全限定名, 即AuthorMapper
下的selectById
方法
/** * 巢狀查詢使用的方法 * @param id * @return */ Author selectById(Integer id);
對應的 SQL
<select id="selectById" parameterType="java.lang.Integer" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from author where id = #{id} </select>
2.5.4 測試
使用時, 呼叫selectBlogAndAuthorByIdSelect
方法即可。
/** *resultMap + association 巢狀查詢方式測試 */ @Test public void testSelectBlogAndAuthorByIdSelect() { SqlSession sqlSession = sqlSessionFactory.openSession(); BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class); BlogCustom blogCustom = blogMapper.selectBlogAndAuthorByIdSelect(1); System.out.println(ToStringBuilder.reflectionToString(blogCustom, ToStringStyle.MULTI_LINE_STYLE)); Assert.assertNotNull(blogCustom); Assert.assertNotNull(blogCustom.getAuthor()); }
輸出, 會發送兩次SQL 語句。
可以看到, 上面的結果示意圖中, 傳送了兩次 SQL 。
2.5.5 延遲載入
如果是一個物件中只是包含一兩個物件, 使用上面的方式還好。 但是如果包含有很多, 那要一次性發送很多次SQL , 效能上就會很有影響。延遲載入可以解決此類的問題。
延遲載入就是說,只有在呼叫內部的物件時, 才會把獲取該物件的SQL 傳送出去。
更改結果集
<resultMap id="blogAuthorMapLazy" type="com.homejim.mybatis.entity.BlogCustom" extends="BaseResultMap"> <association fetchType="lazy" property="author" column="author_id" select="com.homejim.mybatis.mapper.AuthorMapper.selectById" /> </resultMap>
將上面的查詢中的結果集更改resultMap="blogAuthorMapLazy"
<select id="selectBlogAndAuthorByIdSelect" parameterType="java.lang.Integer" resultMap="blogAuthorMapLazy"> <!--resultMap="blogAuthorMap"--> SELECT b.id, b.title, b.author_id FROM blog b where b.id = #{id,jdbcType=INTEGER} </select>
更改延遲載入總開關
<setting name="lazyLoadingEnabled" value="true"/> <setting name="aggressiveLazyLoading" value="false"/>
測試
注意: 延遲載入是在SqlSession
的宣告週期內的, 如果超出該宣告週期, 如 spring 中, 只能在 Service 層使用延遲載入的物件, 如果返回Controller層在獲取延遲載入屬性, 則會丟擲異常。
有時候, 我們配置了延遲載入, 但是卻想要一次性載入, 怎麼辦?
有一個配置屬性可以幫我們解決lazyLoadTriggerMethods , 它的預設配置如下:
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
就是說我們使用上面配置中的任何一個方法(上面的是預設的, 我們可以不配置), 就可以載入屬性啦。
測試
/** *resultMap + association 巢狀查詢方式測試(延遲載入不延遲lazyLoadTriggerMethods) */ @Test public void testSelectBlogAndAuthorByIdSelectTrigger() { SqlSession sqlSession = sqlSessionFactory.openSession(); BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class); BlogCustom blogCustom = blogMapper.selectBlogAndAuthorByIdSelect(1); blogCustom.equals(null); Assert.assertNotNull(blogCustom); sqlSession.close(); System.out.println("開始使用author物件"); Assert.assertNotNull(blogCustom.getAuthor()); }
結果
3. 程式碼
本來還要寫的一對多, 鑑別器的, 但由於篇幅的原因, 後續繼續吧。