1. 程式人生 > >Mybatis多表查詢之一對一查詢的多種實現-XML配置

Mybatis多表查詢之一對一查詢的多種實現-XML配置

        Mybatis 中對於多表查詢提供了非常強大的實現方式,主要是通過resultMap的結果對映對於多表查詢後的返回值進行封裝,讓我們來看一下官網上對於resultMap的解釋:resultMap 元素是 MyBatis 中最重要最強大的元素。它可以讓你從 90% 的 JDBC ResultSets 資料提取程式碼中解放出來,並在一些情形下允許你進行一些 JDBC 不支援的操作。實際上,在為一些比如連線的複雜語句編寫對映程式碼的時候,一份 resultMap 能夠代替實現同等功能的長達數千行的程式碼。ResultMap 的設計思想是,對於簡單的語句根本不需要配置顯式的結果對映,而對於複雜一點的語句只需要描述它們的關係就行了。通過描述物件之間的關係將查詢後的結果對映到我們定義的實體類中。

  首先介紹一下本例中的實體類以及其對映關係,Demo中存在User類以及Account類,其關係為一個使用者對應零個、一個或者多個賬戶,賬戶中為了簡單單單儲存使用者的賬戶餘額以及所屬使用者的ID。我們實現的查詢的目標為:每次查詢一個賬戶的時候同時將其所屬的使用者資訊也展示出來。為了更好的幫助理解,我們將展示一種非mybatis方式以及兩種mybatis方式的實現來實現。User類以及Accoun類t的內容如下:

 1 import java.io.Serializable;
 2 
 3 public class Account implements Serializable{
 4     private Integer id;
 5     private Integer uid;
 6     private Double money;
 7     private User user;
 8 
 9     public User getUser() {
10         return user;
11     }
12 
13     public void setUser(User user) {
14         this.user = user;
15     }
16 
17     public Integer getId() {
18         return id;
19     }
20 
21     public void setId(Integer id) {
22         this.id = id;
23     }
24 
25     public Integer getUid() {
26         return uid;
27     }
28 
29     public void setUid(Integer uid) {
30         this.uid = uid;
31     }
32 
33     public Double getMoney() {
34         return money;
35     }
36 
37     public void setMoney(Double money) {
38         this.money = money;
39     }
40 
41     @Override
42     public String toString() {
43         return "Account{" +
44                 "id=" + id +
45                 ", uid=" + uid +
46                 ", money=" + money +
47                 '}';
48     }
49 }
View Code
 1 import java.io.Serializable;
 2 import java.util.Date;
 3 
 4 public class User implements Serializable{
 5     private Integer id;
 6     private String username;
 7     private Date birthday;
 8     private String sex;
 9     private String address;
10 
11     public Integer getId() {
12         return id;
13     }
14 
15     public void setId(Integer id) {
16         this.id = id;
17     }
18 
19     public String getUsername() {
20         return username;
21     }
22 
23     public void setUsername(String username) {
24         this.username = username;
25     }
26 
27     public Date getBirthday() {
28         return birthday;
29     }
30 
31     public void setBirthday(Date birthday) {
32         this.birthday = birthday;
33     }
34 
35     public String getSex() {
36         return sex;
37     }
38 
39     public void setSex(String sex) {
40         this.sex = sex;
41     }
42 
43     public String getAddress() {
44         return address;
45     }
46 
47     public void setAddress(String address) {
48         this.address = address;
49     }
50 
51     @Override
52     public String toString() {
53         return "User{" +
54                 "id=" + id +
55                 ", username='" + username + '\'' +
56                 ", birthday=" + birthday +
57                 ", sex='" + sex + '\'' +
58                 ", address='" + address + '\'' +
59                 '}';
60     }
61 }
View Code

資料庫的建表語句如下:

 1 DROP TABLE IF EXISTS user;
 2 
 3 CREATE TABLE user (
 4     id INT(11) NOT NULL auto_increment,
 5     username VARCHAR(32) NOT NULL COMMENT '使用者名稱稱',
 6     birthday datetime default NULL COMMENT '生日',
 7     sex char(1) default NULL COMMENT '性別',
 8     address varchar(256) default NULL COMMENT '地址',
 9     PRIMARY KEY (id)
10 )ENGINE=InnoDB default CHARSET=utf8
11 INSERT INTO `user` VALUES ('41', '老王', '2018-02-27 17:47:08', '男', '石家莊');
12 INSERT INTO `user` VALUES ('45', '老李', '2018-02-27 17:47:08', '男', '石家莊');
13 INSERT INTO `user` VALUES ('46', '老郭', '2018-02-27 17:47:08', '男', '石家莊');
14 
15 DROP TABLE IF EXISTS account;
16 CREATE TABLE account(
17 ID int(11) NOT NULL COMMENT '編號',
18 UID INT(11) DEFAULT NULL COMMENT '使用者編號',
19 MONEY DOUBLE DEFAULT NULL COMMENT  '金額',
20 PRIMARY KEY (ID),
21 KEY FK_Reference_8 (UID),
22 CONSTRAINT FK_Reference_8 FOREIGN KEY (UID) REFERENCES user (id)
23 )ENGINE=INNODB DEFAULT CHARSET=utf8
24 
25 INSERT INTO accountc (ID,UID,MONEY) VALUES (1,46,1000),(2,45,1000),(3,46,2000);

      搭建專案的過程就不展示了,主要的核心實體類和對應的資料庫表如上,接下來我們展示我們所要展示的三種方式實現一對一的聯表查詢。

      1.非mybatis的高階結果對映方式實現聯表查詢。

  這種方式的原理為通過建立一個新的類AccountUser類繼承Account類並在AccountUser類中新增我們想要查詢的User的資訊,並且在賬戶查詢的Dao.xml檔案中配置相應的sql語句即可實現。假如我們查詢Account的資訊的時候同時想要查詢使用者的名稱以及地址,那就在AccountUser的類中宣告使用者的名稱以及地址。這種實現方式只是作為一種拓展的實現方式,在實際使用過程中並不推薦使用。

  (1)宣告AccountUser類 

 1 public class AccountUser extends Account {
 2     private String username;
 3     private String address;
 4 
 5     public String getUsername() {
 6         return username;
 7     }
 8 
 9     public void setUsername(String username) {
10         this.username = username;
11     }
12 
13     public String getAddress() {
14         return address;
15     }
16 
17     public void setAddress(String address) {
18         this.address = address;
19     }
20 
21     @Override
22     public String toString() {
23         return super.toString() + "       "+"AccountUser{" +
24                 "username='" + username + '\'' +
25                 ", address='" + address + '\'' +
26                 '}';
27     }
28 }
View Code

需要注意的是該類繼承了Account類,聲明瞭我們需要的User類中的使用者名稱稱以及地址,對AccountUser類的toString()方法進行了改造,添加了super.toString(),方便我們列印的時候可以打印出從父類中繼承的屬性的屬性值。

  (2)在AccountDao類中宣告查詢賬戶資訊的方法

/**
     * 查詢所有賬戶同時包含使用者的姓名和地址
     * @return
     */
    List<AccountUser> findAllAccountUser();

  (3)在AccountDao.xml中配置findAllAccountUser方法的實現

 <select id="findAllAccountUser" resultType="com.example.domain.AccountUser">
        SELECT a.*,u.username,u.address FROM USER u,account a WHERE a.UID= u.id;
    </select>

  (4)測試該方法

@Test
    public void findAllAccounUsertTest(){
        List<AccountUser> accountList = accountDao.findAllAccountUser();
        for (AccountUser account:accountList){
            System.out.println(account);
        }
    }

  測試結果:

 

  2.通過Mybatis中的高階結果對映的resultMap的關聯屬性(association)來實現多表的一對一查詢。

  關聯屬性主要用來處理“有一個”型別的關係,關聯的關鍵之處是我們需要告訴 MyBatis 如何載入關聯。在MyBatis 中有兩種不同的方式載入關聯:一是巢狀 Select 查詢:通過執行另外一個 SQL 對映語句來載入期望的複雜型別。二是巢狀結果對映:使用巢狀的結果對映來處理連線結果的重複子集。通過這兩種不同的方式衍生出兩種不同的方式去實現多表的一對一查詢。

    1.關聯的巢狀SELECT查詢

    (1)因為我們要實現的是在查詢賬戶的時候期望可以得到賬戶所屬使用者的某些資訊,所以我們需要在Account類中宣告User物件,用來將查詢到的結果進行封裝。如下

private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    (2)AccountDao類中新增查詢的方法的宣告。

 /**
     * 查詢所有賬戶同時包含使用者的所有資訊
     * @return
     */
    List<Account> findAll();

    (3)在AccountDao.xml中配置findAll方法的的結果對映。首先宣告結果對映關係resultMap,resultMap的id為該結果對映的唯一標識,type為結果類的完全限定名,resultMap中的屬性說明:id 和 result 元素都將一個列的值對映到一個簡單資料型別(String, int, double, Date 等)的屬性或欄位。這兩者之間的唯一不同是,id 元素表示的結果將是物件的標識屬性,這會在比較物件例項時用到。 這樣可以提高整體的效能,尤其是進行快取和巢狀結果對映(也就是連線對映)的時候。id和result中的屬性說明:property

對映到列結果的欄位或屬性,其實就是實體類中屬性的名稱。column是指資料庫中的列名,對應實體類的屬性。在下面的<id property="id" column="aid"/>中的column屬性的值aid沒有完全匹配上資料中的id,是因為在查詢語句中對account中的id欄位設定了別名aid。association的屬性的 property為user對應實體類中宣告的user物件,其型別使用javaType屬性指定為User類,column為資料表的列名,並作為引數傳遞給此 select 語句。select屬性用於載入複雜型別屬性的對映語句的 ID,它會從 column 屬性中指定的列檢索資料。

 <resultMap id="accountUserMap" type="com.example.domain.Account">
        <id property="id" column="aid"/>
        <result property="uid" column="uid"/>
        <result property="money" column="money"/>
        <!--關聯的巢狀的select查詢-->
        <association property="user" javaType="com.example.domain.User" column="uid" select="selectUser"/>
    </resultMap>

    (4)在AccountDao.xml中配置結果對映中的<association property="user" javaType="com.example.domain.User" column="uid" select="selectUser"/>的select="selectUser"的實現以及findAll方法的實現,

<select id="selectUser" resultType="user">
        SELECT * FROM  USER WHERE ID = #{id};
    </select>
<select id="findAll" resultMap="accountUserMap">
       SELECT u.*,a.id AS aid,a.uid,a.money FROM USER u,account a WHERE a.UID= u.id;
    </select>

  這裡我們有兩個 select 查詢語句:一個用來載入賬戶資訊(Account),另外一個用來載入使用者資訊(User),而且accountUserMap的結果對映描述了應該使用 selectUser 語句載入它的 user屬性,其它的列名和屬性名相匹配的屬性將會被自動載入。

    (5)查詢測試

 @Test
    public void findAllTest(){
        List<User> userList = userDao.findAll();
        for (User user: userList){
            System.out.println(user);
        }
    }

測試結果:

  2.關聯的巢狀結果對映實現1。

  (1)(2)步驟是上一方法是相同的。

  (3)主要是修改了上一種方式中第三步中的resultMap中的association關聯屬性,將其替換為:<association property="user" javaType="com.example.domain.User" column="uid" resultMap="userMap"/>,在association 中添加了resultMap="userMap"屬性,userMap為結果對映的 ID,可以將此關聯的巢狀結果集對映到一個合適的物件中,也就是將關聯屬性user的結果對映到對映ID為userMap的resultMap中。userMap的宣告如下:

 <resultMap id="userMap" type="com.example.domain.User">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="birthday" column="birthday" jdbcType="DATE"/>
        <result property="sex" column="sex"/>
        <result property="address" column="address"/>
    </resultMap>

(4)AccountDao.xml的findAll方法的對映則只需要findAll方法,不再需要上一個方式中的selectUser對映的方法

<select id="findAll" resultMap="accountUserMap">
       SELECT u.*,a.id AS aid,a.uid,a.money FROM USER u,account a WHERE a.UID= u.id;
    </select>

(5)(6)查詢程式碼以及測試結果不再貼出

 3.關聯的巢狀結果對映實現2。

   第二種實現方式中使用了外部的結果對映元素來對映關聯。這使得 User的結果對映可以被重用。 然而,如果我們不需要重用它(在上個例子中他是userMap),或者你更喜歡將你所有的結果對映放在一個具有描述性的結果對映元素中。 你可以直接將結果對映作為子元素巢狀在內。

  (1)(2)步驟是上一方法是相同的。

  (3)仍然是修改了上一種方式中第三步中的resultMap結果對映中的association關聯屬性,將其替換如下:

<!--關聯的巢狀的結果對映2-->
        <association property="user" javaType="com.example.domain.User">
            <id property="id" column="id"/>
            <result property="username" column="username"/>
            <result property="birthday" column="birthday" jdbcType="DATE"/>
            <result property="sex" column="sex"/>
            <result property="address" column="address"/>
        </association>

這樣實現與第二種實現大同小異,只是將關聯物件的屬性配置直接在association中進行了配置。

(4)AccountDao.xml的findAll方法的對映的findAll方法

<select id="findAll" resultMap="accountUserMap">
       SELECT u.*,a.id AS aid,a.uid,a.money FROM USER u,account a WHERE a.UID= u.id;
    </select>

(5)(6)查詢程式碼以及測試結果不再貼出

  總結:通過上述例子可以初步窺探了Mybatis中多表聯查(一對一)的使用方式,主要是通過resultMap的高階結果對映來實現的,在本例中最關鍵的屬性是resultMap的關聯屬性association,association也是我們告訴Mybatis物件之間的關係的橋樑,同時也介紹了resultMap的屬性的說明,通過解釋其屬性再加上Demo可以更好的理解結果對映的含義以及使用,這只是最簡單的一種使用方式,以後會詳細介紹一對多、多對多、多對一等複雜情況在Mybatis中的如何查詢對映。

  參考網址:mybatis中文官網  http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.