MyBatis嵌套查询解析
Mybatis表現關聯關系比hibernate簡單,沒有分那么細致one-to-many、many-to-one、one-to-one。而是只有兩種association(一)、collection(多),表現很簡潔。下面通過一個實例,來展示一下Mybatis對于常見的一對多和多對一關系復雜映射是怎樣處理的。
以最簡單的用戶表訂單表這個最簡單的一對多做示例:
對應的JavaBean:
User:
public class User {private int id;private String name;private Double age;private List<User_orders> orders;// get set 省}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
User_orders:
public class User_orders {private int id;private String name;// get set 省 }- 1
- 2
- 3
- 4
- 5
對應的數據庫:
mysql> desc user; +-------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name | varchar(20) | NO | | NULL | | | age | double | YES | | NULL | | +-------+-------------+------+-----+---------+----------------+ 3 rows in set (0.00 sec)mysql> desc user_orders; +---------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +---------+-------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name | varchar(20) | NO | | NULL | | | user_id | int(5) | YES | MUL | NULL | | +---------+-------------+------+-----+---------+----------------+ 3 rows in set (0.00 sec)- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
現在查詢一個user的id查詢出所有信息.如果不考慮關聯查詢,我們會先根據user的id在user表中查詢出name,age然后設置給User類的時候,再根據該user的id在user_orders表中查詢出所有訂單并設置給User類。這樣的話,在底層最起碼調用兩次查詢語句,得到需要的信息,然后再組裝User對象。
嵌套語句查詢
mybatis提供了一種機制,叫做嵌套語句查詢,可以大大簡化上述的操作,加入配置及代碼如下:
<resultMap type="domain.User" id="user"><id column="id" property="id"/><result column="age" property="age"/><collection column="id" property="orders" ofType="domain.User_orders"select="selectOrderByUser"> <id column="id" property="id"/><result column="name" property="name"/></collection> </resultMap> <select id="selectOrderByUser" parameterType="integer" resultType="domain.User_orders">select id,name from user_orders where user_id = #{id} </select><select id="findById" resultMap="user" parameterType="integer">select * from user where id = #{id}</select>- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
測試(可以成功查詢到所有信息):
String config = "sqlMapConfig.xml"; InputStream inputStream = Resources.getResourceAsStream(config); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession session = sqlSessionFactory.openSession(); // 執行在bean配置文件中定義的sql語句 User user = session.selectOne("UserMapper.findById", 1); //一句即可獲取到復雜的User對象。 System.out.println(user); session.commit(); session.close();- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
嵌套語句查詢的原理
在上面的代碼中,Mybatis會執行以下流程:
-
1.先執行 findById 對應的語句從User表里獲取到ResultSet結果集;
-
2.取出ResultSet下一條有效記錄,然后根據resultMap定義的映射規格,通過這條記錄的數據來構建對應的一個User 對象。
- 當要對User中的orders屬性進行賦值的時候,發現有一個關聯的查詢,此時Mybatis會先執行這個select查詢語句,得到返回的結果,將結果設置到user的orders屬性上
這種關聯的嵌套查詢,有一個非常好的作用就是:可以重用select語句,通過簡單的select語句之間的組合來構造復雜的對象。想如上的兩個select完全可以獨立使用。
嵌套查詢的多對一?
上面的關聯查詢查詢其實是對于一對多的查詢,即從user中查出user_order的信息。?
現在從user_order中查user的信息.?
在User_order表中增加字段user:
- 1
- 2
- 3
- 4
- 5
- 6
配置select:
<resultMap type="domain.User_orders" id="user_order"><id column="id" property="id"/><result column="name" property="name"/><association property="user" column="user_id" javaType="domain.User" select="selectUserByOrderId"><id column="id" property="id"/><result column="age" property="age"/></association> </resultMap><select id="selectUserByOrderId" parameterType="INTEGER" resultType="domain.User">select id,age from user where id = #{id}</select><select id="findOne" resultMap="user_order" parameterType="integer">select * from user_orders where id=#{id}</select>- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
測試:
SqlSession session = sqlSessionFactory.openSession();// 執行在bean配置文件中定義的sql語句User_orders user_orders= session.selectOne("User_ordersMapper.findOne", 1);System.out.println(user_orders);//查詢到了user_order對應的user的信息session.commit();session.close();- 1
- 2
- 3
- 4
- 5
- 6
- 7
嵌套查詢的N+1問題
盡管嵌套查詢大量的簡化了存在關聯關系的查詢,但它的弊端也比較明顯:即所謂的N+1問題。關聯的嵌套查詢顯示得到一個結果集,然后根據這個結果集的每一條記錄進行關聯查詢。
現在假設嵌套查詢就一個(即resultMap 內部就一個association標簽),現查詢的結果集返回條數為N,那么關聯查詢語句將會被執行N次,加上自身返回結果集查詢1次,共需要訪問數據庫N+1次。如果N比較大的話,這樣的數據庫訪問消耗是非常大的!所以使用這種嵌套語句查詢的使用者一定要考慮慎重考慮,確保N值不會很大。
以上面一對多(根據user的id查詢order)的例子為例,select 語句本身會返回user條數為1 的結果集,由于它存在有1條關聯的語句查詢,它需要共訪問數據庫 1*(1+1)=2次數據庫。
嵌套結果查詢
嵌套語句的查詢會導致數據庫訪問次數不定,進而有可能影響到性能。Mybatis還支持一種嵌套結果的查詢:即對于一對多,多對多,多對一的情況的查詢,Mybatis通過聯合查詢,將結果從數據庫內一次性查出來,然后根據其一對多,多對一,多對多的關系和ResultMap中的配置,進行結果的轉換,構建需要的對象。
重新定義User的結果映射 resultMap
<resultMap type="domain.User" id="user_auto"> <id column="id" property="id"/><result column="age" property="age"/><collection column="id" property="orders" ofType="domain.User_orders"> <id column="order_id" property="id"/><result column="name" property="name"/></collection> </resultMap>- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
對應的sql語句如下:
<select id="findAuth" resultMap="user_auto">select u.id,u.age,o.id as order_id ,o.name,o.user_id as user_id from user u left outer join user_orders oon o.user_id = u.id</select>- 1
- 2
- 3
- 4
嵌套結果查詢的執行步驟:
-
1.根據表的對應關系,進行join操作,獲取到結果集;
- 根據結果集的信息和user 的resultMap定義信息,對返回的結果集在內存中進行組裝、賦值,構造User;
- 返回構造出來的結果List 結果。
對于關聯的結果查詢,如果是多對一的關系,則通過形如?<association property="user" column="user_id" javaType="domain.User" >進行配置,Mybatis會通過column屬性對應的user_id 值去從內存中取數據,并且封裝成User_order對象;
如果是一對多的關系,就如User和User_order之間的關系,通過形如?<collection column="id" property="orders" ofType="domain.User_orders">進行配置,MyBatis通過 id去內存中取User_orders對象,封裝成List;
對于關聯結果的查詢,只需要查詢數據庫一次,然后對結果的整合和組裝全部放在了內存中。
以上是通過查詢User表所有信息來演示了一對多和多對一的映射對象處理。
總結
以上是生活随笔為你收集整理的MyBatis嵌套查询解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c#.net课程设计:ZCMU通讯录(待
- 下一篇: ACM竞赛