第三天:mybatis連接池與事務(wù)深入 ,動態(tài) SQL 和多表

第三天:mybatis的深入和多表

mybatis框架 學習計劃

共四天
第一天:mybatis入門
mybatis的概述
mybatis的環(huán)境搭建
mybatis入門案例
自定義mybatis框架(主要的目的是為了讓大家了解mybatis中執(zhí)行細節(jié))
第二天:mybatis基本使用
mybatis的單表crud操作
mybatis的參數(shù)和返回值
mybatis的dao編寫
mybatis配置的細節(jié)
幾個標簽的使用
第三天:mybatis的深入和多表
mybatis的連接池
mybatis的事務(wù)控制及設(shè)計的方法
mybatis的多表查詢
一對多(多對一)
多對多
第四天:mybatis的緩存和注解開發(fā)
mybatis中的加載時機(查詢的時機)
mybatis中的一級緩存和二級緩存
mybatis的注解開發(fā)
單表CRUD
多表查詢

3.1 Mybatis 連接池與事務(wù)深入

3.1.1 Mybatis 的連接池技術(shù)

? 我們在前面的 WEB 課程中也學習過類似的連接池技術(shù),而在 Mybatis 中也有連接池技術(shù),但是它采用的是自己的連接池技術(shù)而柑。在 Mybatis 的 SqlMapConfig.xml 配置文件中,通過<dataSource type=”pooled”>來實現(xiàn) Mybatis 中連接池的配置文捶。
Mybatis 連接池的分類

3.1.1.1 Mybatis 連接池的分類

在 Mybatis 中我們將它的數(shù)據(jù)源 dataSource 分為以下幾類:

UNPOOLED 不使用連接池的數(shù)據(jù)源
POOLED 使用連接池的數(shù)據(jù)源
JNDI 使用 JNDI 實現(xiàn)的數(shù)據(jù)源

? 相應地,MyBatis 內(nèi)部分別定義了實現(xiàn)了 java.sql.DataSource 接口的 UnpooledDataSource,
PooledDataSource 類來表示 UNPOOLED荷逞、POOLED 類型的數(shù)據(jù)源粹排。

? 在這三種數(shù)據(jù)源中,我們一般采用的是 POOLED 數(shù)據(jù)源(很多時候我們所說的數(shù)據(jù)源就是為了更好的管理數(shù)據(jù)庫連接,也就是我們所說的連接池技術(shù))种远。

Mybatis 中數(shù)據(jù)源的配置

3.1.1.2 Mybatis 中數(shù)據(jù)源的配置

? 我們的數(shù)據(jù)源配置就是在 SqlMapConfig.xml文件中,具體配置如下:


<!-- 配置數(shù)據(jù)源(連接池)信息 -->
<dataSource type="POOLED">
    <property name="driver" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</dataSource>

? MyBatis 在初始化時,根據(jù)<dataSource>的 type 屬性來創(chuàng)建相應類型的的數(shù)據(jù)源 DataSource,即:
type="POOLED": MyBatis 會創(chuàng)建 PooledDataSource 實例
type="UNPOOLED" : MyBatis 會創(chuàng)建 UnpooledDataSource 實例

type="JNDI": MyBatis 會從 JNDI 服務(wù)上查找 DataSource 實例,然后返回使用

3.1.1.3 Mybatis 中 DataSource的存取

? MyBatis 是 通 過 工 廠 模 式 來 創(chuàng) 建 數(shù) 據(jù) 源 DataSource 對 象 的 , MyBatis 定 義 了 抽 象 的 工 廠 接口 :org.apache.ibatis.datasource.DataSourceFactory,通過其 getDataSource()方法返回數(shù)據(jù)源DataSource
下面是 DataSourceFactory 源碼,具體如下:

package org.apache.ibatis.datasource;
import java.util.Properties;
import javax.sql.DataSource;
    /**
     * @author Clinton Begin
     */
    public interface DataSourceFactory {
        void setProperties(Properties props);
        DataSource getDataSource();
    }

? MyBatis 創(chuàng)建了 DataSource 實例后,會將其放到 Configuration 對象內(nèi)的 Environment對象中, 供以后使用顽耳。

具體分析過程如下:

1.先進入 XMLConfigBuilder類中,可以找到如下代碼:

image.png

2.分析 configuration對象的 environment屬性,結(jié)果如下:

image.png

3.1.1.4 Mybatis 中連接的獲取過程分析

? 當我們需要創(chuàng)建 SqlSession 對象并需要執(zhí)行 SQL 語句時,這時候 MyBatis 才會去調(diào)用 dataSource 對象來創(chuàng)建 java.sql.Connection對象坠敷。也就是說,java.sql.Connection對象的創(chuàng)建一直延遲到執(zhí)行 SQL 語句的時候。

@Test
public void testSql() throws Exception {
    InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
    SqlSession sqlSession   = factory.openSession();
    List<User> list = sqlSession.selectList("findUserById",41);
    System.out.println(list.size());
}

? 只有當?shù)?4 句 sqlSession.selectList("findUserById"),才會觸發(fā) MyBatis 在底層執(zhí)行下面這個方
法來創(chuàng)建 java.sql.Connection對象射富。
如何證明它的加載過程呢?
我們可以通過斷點調(diào)試,在 PooledDataSource 中找到如下 popConnection()方法,如下所示:

image.png

分析源代碼,得出 PooledDataSource 工作原理如下:

image.png

下面是連接獲取的源代碼:

image.png

? 最后我們可以發(fā)現(xiàn),真正連接打開的時間點,只是在我們執(zhí)行 SQL 語句時,才會進行膝迎。其實這樣做我們也可以進一步發(fā)現(xiàn),數(shù)據(jù)庫連接是我們最為寶貴的資源,只有在要用到的時候,才去獲取并打開連接,當我們用完了就再立即將數(shù)據(jù)庫連接歸還到連接池中。

3.1.2 Mybatis 的事務(wù)控制

3.1.2.1 Mybatis 中手動提交事務(wù)

? Mybatis 中事務(wù)的提交方式,本質(zhì)上就是調(diào)用 JDBC 的 setAutoCommit()來實現(xiàn)事務(wù)控制胰耗。
我們運行之前所寫的代碼:

@Test
public void testSaveUser() throws Exception {
    User user = new User();
    user.setUsername("mybatis user09");
    //6.執(zhí)行操作
    int res = userDao.saveUser(user);
    System.out.println(res);
    System.out.println(user.getId());
}
@Before//在測試方法執(zhí)行之前執(zhí)行
public void init()throws Exception {
    //1.讀取配置文件
    in = Resources.getResourceAsStream("SqlMapConfig.xml");
    //2.創(chuàng)建構(gòu)建者對象
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    //3.創(chuàng)建 SqlSession 工廠對象
    factory = builder.build(in);
    //4.創(chuàng)建 SqlSession 對象
    session = factory.openSession();
    //5.創(chuàng)建 Dao 的代理對象
    userDao = session.getMapper(IUserDao.class);
}
@After//在測試方法執(zhí)行完成之后執(zhí)行
public void destroy() throws Exception{
    //7.提交事務(wù)
    session.commit();
    //8.釋放資源
    session.close();
    in.close();
}

? 這是我們的Connection 的整個變化過程,通過分析我們能夠發(fā)現(xiàn)之前的 CUD 操作過程中,我們都要手動進行事務(wù)的提交,原因是 setAutoCommit()方法,在執(zhí)行時它的值被設(shè)置為 false 了,所以我們在 CUD 操作中,必須通過 sqlSession.commit()方法來執(zhí)行提交操作

3.1.2.2 Mybatis 自動提交事務(wù)的設(shè)置

? 通過上面的研究和分析,現(xiàn)在我們一起思考,為什么 CUD 過程中必須使用 sqlSession.commit()提交事務(wù)?主要原因就是在連接池中取出的連接,都會將調(diào)用 connection.setAutoCommit(false)方法,這樣我們就必須使用 sqlSession.commit()方法,相當于使用了 JDBC 中的 connection.commit()方法實現(xiàn)事務(wù)提交限次。
明白這一點后,我們現(xiàn)在一起嘗試不進行手動提交,一樣實現(xiàn) CUD 操作。

@Before//在測試方法執(zhí)行之前執(zhí)行
public void init()throws Exception {
    //1.讀取配置文件
    in = Resources.getResourceAsStream("SqlMapConfig.xml");
    //2.創(chuàng)建構(gòu)建者對象
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    //3.創(chuàng)建 SqlSession 工廠對象
    factory = builder.build(in);
    //4.創(chuàng)建 SqlSession 對象
    session = factory.openSession(true);
    //5.創(chuàng)建 Dao 的代理對象
    userDao = session.getMapper(IUserDao.class);
}
@After//在測試方法執(zhí)行完成之后執(zhí)行
public void destroy() throws Exception{
    //7.釋放資源
    session.close();
    in.close();
}

? 我們發(fā)現(xiàn),此時事務(wù)就設(shè)置為自動提交了,同樣可以實現(xiàn) CUD 操作時記錄的保存宪郊。雖然這也是一種方式,但就編程而言,設(shè)置為自動提交方式為 false 再根據(jù)情況決定是否進行提交,這種方式更常用掂恕。因為我們可以根據(jù)業(yè)務(wù)情況來決定提交是否進行提交。

3.2 Mybatis 的動態(tài) SQL 語句

3.2.1 動態(tài) SQL 之<if>標簽

? 我們根據(jù)實體類的不同取值,使用不同的 SQL 語句來進行查詢弛槐。比如在 id 如果不為空時可以根據(jù) id 查詢,如果 username 不同空時還要加入用戶名作為條件懊亡。這種情況在我們的多條件組合查詢中經(jīng)常會碰到。

  1. 持久層 Dao 接口
/**
 * 根據(jù)傳入?yún)?shù)條件
 * @param user 查詢的條件,有可能有用戶名,有可能有性別,也有可能有地址,還有可能是都有
 * @return
 */
List<User> findUserByCondition(User user);
  1. 持久層 Dao 映射配置
<!--根據(jù)條件查詢-->
<select id="findUserByCondition" resultType="org.example.domain.User" parameterType="org.example.domain.User">
    select * from user
    where 1=1
    <if test="username != null and username != '' ">
        and username like concat('%',#{username},'%')
    </if>
    <if test="address != null and address != '' ">
        and address like concat('%',#{address},'%')
    </if>
</select>

? 注意:<if>標簽的 test 屬性中寫的是對象的屬性名,如果是包裝類的對象要使用 OGNL 表達式的寫法乎串。
另外要注意 where 1=1 的作用~!

  1. 測試

    @Test
    public void  testFindUserByCondition(){
        User user = new User();
        user.setUsername("王");
        List<User> list = userDao.findUserByCondition(user);
        for (User u: list) {
            System.out.println(u);
        }
    }
    

3.2.2 動態(tài) SQL 之<where>標簽

為了簡化上面 where 1=1 的條件拼裝,我們可以采用<where>標簽來簡化開發(fā)店枣。

持久層 Dao 映射配置

<!--根據(jù)條件查詢-->
<select id="findUserByCondition" resultType="org.example.domain.User" parameterType="org.example.domain.User">
    select * from user
    <where>
        <if test="username != null and username != '' ">
            and username like concat('%',#{username},'%')
        </if>
        <if test="address != null and address != '' ">
            and address like concat('%',#{address},'%')
        </if>
    </where>
</select>

3.2.3 動態(tài)標簽之<foreach>標簽

? 傳入多個 id 查詢用戶信息,用下邊兩個 sql 實現(xiàn):
SELECT * FROM USERS WHERE username LIKE '%張%' AND (id =10 OR id =89 OR id=16)
SELECT * FROM USERS WHERE username LIKE '%張%' AND id IN (10,89,16)

? 這樣我們在進行范圍查詢時,就要將一個集合中的值,作為參數(shù)動態(tài)添加進來。
這樣我們將如何進行參數(shù)的傳遞?

  1. 在 QueryVo 中加入一個 List 集合用于封裝參數(shù)
public class QueryVo implements Serializable {

    private User user;

    private List<Integer> ids;

    public User getUser() {
        return user;
    }

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

    public List<Integer> getIds() {
        return ids;
    }

    public void setIds(List<Integer> ids) {
        this.ids = ids;
    }
}
  1. 持久層 Dao 接口

    /**
     * 根據(jù)queryVo提供的id集合,查詢用戶信息
     * @param vo
     * @return
     */
    List<User> findUserInIds(QueryVo vo);
    
  2. 持久層 Dao 映射配置

<!--根據(jù)queryVo提供的id集合,查詢用戶信息-->
<select id="findUserInIds" resultType="org.example.domain.User" parameterType="org.example.domain.QueryVo">
    select * from user
    <where>
        <if test="ids != null and ids.size() > 0">
            <foreach collection="ids" open="id in (" close=")" item="id" separator=",">
                #{id}
            </foreach>
        </if>
    </where>

</select>

SQL 語句:
select 字段 from user where id in (?)
<foreach> 標簽用于遍歷集合,它的屬性:
collection: 代表要遍歷的集合元素,注意編寫時不要寫#{}
open: 代表語句的開始部分
close: 代表結(jié)束部分
item="id" item: 代表遍歷集合的每個元素,生成的變量名
sperator: 代表分隔符

  1. 編寫測試方法

    /**
     * foreach 標簽的使用
     */
    @Test
    public void  testFindUserInIds(){
        QueryVo vo = new QueryVo();
        List<Integer> ids = new ArrayList<Integer>();
        ids.add(41);
        ids.add(42);
        ids.add(51);
        vo.setIds(ids);
    
        List<User> list = userDao.findUserInIds(vo);
        for (User u: list) {
            System.out.println(u);
        }
    }
    

3.2.4 Mybatis 中簡化編寫的 SQL 片段

? Sql 中可將重復的 sql提取出來,使用時用 include引用即可,最終達到 sql 重用的目的叹誉。

  1. 定義代碼片段

    ?

    <sql id="defaultUser">
        select * from user
    </sql>
    
  1. 引用代碼片段

        <!-- 配置查詢所有操作 -->
        <select id="findAll" resultType="org.example.domain.User">
            <include refid="defaultUser"></include>
    <!--        select * from user-->
        </select>
    

3.3 Mybatis 多表查詢之一對多

3.3.1 一對一查詢(多對一)

? 使用 resultMap,定義專門的 resultMap 用于映射一對一查詢結(jié)果鸯两。
通過面向?qū)ο蟮?has a)關(guān)系可以得知,我們可以在 Account 類中加入一個 User 類的對象來代表這個賬戶是哪個用戶的。

1. 定義賬戶信息的實體類

/**
 * 賬號實體類
 */
public class Account implements Serializable {

    private  Integer id;

    private Integer uid;

    private Double money;

    //從表實體應該包含一個主體實體的對象引用
    private User user;


    public User getUser() {
        return user;
    }

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

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }

    public Double getMoney() {
        return money;
    }

    public void setMoney(Double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", uid=" + uid +
                ", money=" + money +
                ", user=" + user +
                '}';
    }
}

2. 編寫 Sql 語句

select b.*,a.id as aid,a.uid,a.money from  account a, user b where a.uid = b.id;

3. 定義賬戶的持久層 Dao 接口

/**
 * 查詢所有賬號,同時還要獲取到當前賬號的所屬用戶信息
 * @return
 */
List<Account> findAll();

4. 定義 AccountDao.xml 文件中的查詢配置信息

<!-- 定義account和user的resultMap-->
    <resultMap id="accountUserMap" type="org.example.domain.Account">
        <id property="id" column="aid"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>

        <!-- 一對一的關(guān)系映射: 配置封裝user的內(nèi)容 -->
        <association property="user" column="uid" javaType="org.example.domain.User">
            <id property="id" column="id"></id>
            <result property="username" column="username"></result>
            <result property="address" column="address"></result>
            <result property="sex" column="sex"></result>
            <result property="birthday" column="birthday"></result>
        </association>
    </resultMap>

    <!-- 查詢所有賬號-->
    <select id="findAll" resultMap="accountUserMap">
         select b.*,a.id as aid,a.uid,a.money from  account a, user b where a.uid = b.id;

    </select>

5. 測試方法

/**
 * 查詢所有賬號
 */
@Test
public void testFindAll()  {
    //6.使用代理對象執(zhí)行查詢所有方法
    List<Account> accounts = accountDao.findAll();
    for(Account account : accounts) {
        System.out.println(account);
        //System.out.println(account.getUser());
    }
}

3.3.2 一對多查詢

? 需求:
查詢所有用戶信息及用戶關(guān)聯(lián)的賬戶信息长豁。
分析:
用戶信息和他的賬戶信息為一對多關(guān)系,并且查詢過程中如果用戶沒有賬戶信息,此時也要將用戶信息
查詢出來,我們想到了左外連接查詢比較合適钧唐。

1 . 編寫 SQL 語句

SELECT
 u.*,
 a.ID as aid,
 a.MONEY
FROM
 user u
LEFT JOIN account a on
 u.id = a.UID

2 . User 類加入 List<Account>

/**
 * 用戶的實體類
 */
public class User implements Serializable {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    // 一對多關(guān)系映射; 主表實體應該包含從表實體的集合引用
    private List<Account> accounts;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public List<Account> getAccounts() {
        return accounts;
    }

    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

3. 用戶持久層 Dao 接口中加入查詢方法

/**
 * 查詢所有用戶操作
 * @return
 */
List<User> findAll();

4. 用戶持久層 Dao 映射文件配置

<!-- 定義User的resultMap -->
    <resultMap id="userAccountMap" type="org.example.domain.User">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="sex" column="sex"></result>
        <result property="birthday" column="birthday"></result>
        <result property="address" column="address"></result>
        <!--配置user對象中國accounts集合映射-->
        <collection property="accounts" ofType="org.example.domain.Account">
            <id property="id" column="aid"></id>
            <result property="uid" column="uid"></result>
            <result property="money" column="money"></result>
        </collection>

    </resultMap>

    <!-- 配置查詢所有操作 -->
    <select id="findAll" resultMap="userAccountMap">
        SELECT
            u.*,
            a.ID as aid,
            a.MONEY
        FROM
            user u
        LEFT JOIN account a on
            u.id = a.UID    
    </select>

5. 測試方法

@Test
    public void testFindAll()  {
        //6.使用代理對象執(zhí)行查詢所有方法
        List<User> users = userDao.findAll();
        for(User user : users) {
            System.out.println("---------------------------------");
            System.out.println(user);
            for (Account account : user.getAccounts()){
                System.out.println(account);
            }
        }
    }

3.4 Mybatis 多表查詢之多對多

3.4.1 實現(xiàn) Role 到 User 多對多

通過前面的學習,我們使用 Mybatis 實現(xiàn)一對多關(guān)系的維護。多對多關(guān)系其實我們看成是雙向的一對多關(guān)系匠襟。

1 業(yè)務(wù)要求及實現(xiàn) SQL需求:

實現(xiàn)查詢所有對象并且加載它所分配的用戶信息钝侠。
    分析:
    查詢角色我們需要用到 Role 表,但角色分配的用戶的信息我們并不能直接找到用戶信息,而是要通過中間表(USER_ROLE 表)才能關(guān)聯(lián)到用戶信息。
   下面是實現(xiàn)的 SQL 語句:
SELECT
 r.id as rid,
 r.role_name,
 r.role_desc,
 u.id ,
 u.username,
 u.birthday,
 u.sex,
 u.address
FROM
`role` r
LEFT JOIN user_role ur ON
r.ID = ur.RID
LEFT JOIN `user` u ON u.id = ur.UID

2 編寫角色實體類

public class Role  implements Serializable {

    private Integer roleId;

    private String roleName;

    private String roleDesc;

    // 多對多的關(guān)系映射: 一個角色可以賦予多個用戶
    private List<User> users;

    public List<User> getUsers() {
        return users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }

    public Integer getRoleId() {
        return roleId;
    }

    public void setRoleId(Integer roleId) {
        this.roleId = roleId;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    public String getRoleDesc() {
        return roleDesc;
    }

    public void setRoleDesc(String roleDesc) {
        this.roleDesc = roleDesc;
    }

    @Override
    public String toString() {
        return "Role{" +
                "roleId=" + roleId +
                ", roleName='" + roleName + '\'' +
                ", roleDesc='" + roleDesc + '\'' +
                '}';
    }
}

3 編寫 Role 持久層接口

    /**
     * 查詢所有角色
     * @return
     */
    List<Role> findAll();
}

4 編寫映射文件

<!--定義Role表的ResultMap-->
<resultMap id="roleMap" type="org.example.domain.Role">
    <id property="roleId" column="rid"></id>
    <result property="roleName" column="ROLE_NAME"></result>
    <result property="roleDesc" column="ROLE_DESC"></result>
    <collection property="users" ofType="org.example.domain.User">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>

    </collection>
</resultMap>

<!-- 查詢所有角色-->
<select id="findAll" resultMap="roleMap">
    SELECT
        r.id as rid,
        r.role_name,
        r.role_desc,
        u.id ,
        u.username,
        u.birthday,
        u.sex,
        u.address
    FROM
    `role` r
    LEFT JOIN user_role ur ON
    r.ID = ur.RID
    LEFT JOIN `user` u ON u.id = ur.UID
</select>

5 編寫測試類

public class RoleTest {

    private  InputStream in;
    private  SqlSession sqlSession;
    private RoleDao roleDao;

    @Before  // test方法執(zhí)行之前執(zhí)行
    public void init() throws IOException {
            //1.讀取配置文件
             in = Resources.getResourceAsStream("SqlMapConfig.xml");
            //2.創(chuàng)建 SqlSessionFactory 的構(gòu)建者對象
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

            //3.使用構(gòu)建者創(chuàng)建工廠對象 SqlSessionFactory
            SqlSessionFactory factory = builder.build(in);

            //4.使用 SqlSessionFactory 生產(chǎn) SqlSession 對象
            sqlSession = factory.openSession();

            //5.使用 SqlSession 創(chuàng)建 dao 接口的代理對象
        roleDao = sqlSession.getMapper(RoleDao.class);

    }

    @After // test方法執(zhí)行之后執(zhí)行
    public void destroy() throws IOException {
        //提交事務(wù)
        sqlSession.commit();
        //7.釋放資源
        sqlSession.close();
        in.close();
    }

    /**
     * 查詢所有賬號
     */
    @Test
    public void testFindAll()  {
        //6.使用代理對象執(zhí)行查詢所有方法
        List<Role> roles = roleDao.findAll();
        for(Role role : roles) {
            System.out.println(role);
            System.out.println(role.getUsers());
        }
    }
}

12

3.4.2 實現(xiàn) User 到 Role 的多對多

? 從 User 出發(fā),我們也可以發(fā)現(xiàn)一個用戶可以具有多個角色,這樣用戶到角色的關(guān)系也還是一對多關(guān)系酸舍。這樣我們就可以認為 User 與 Role 的多對多關(guān)系,可以被拆解成兩個一對多關(guān)系來實現(xiàn)帅韧。

1 業(yè)務(wù)要求及實現(xiàn) SQL需求:

SELECT
    u.id ,
    u.username,
    u.birthday,
    u.sex,
    u.address,
    r.id as rid,
    r.role_name,
    r.role_desc 
FROM
`user` u
LEFT JOIN user_role ur ON
u.ID = ur.UID
LEFT JOIN `role` r ON r.id = ur.RID

2 編寫角色實體類

/**
 * 用戶的實體類
 */
public class User implements Serializable {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    // 一對多關(guān)系映射; 主表實體應該包含從表實體的集合引用
    private List<Account> accounts;
    // 多對多的關(guān)系映射: 一個用戶可以具有多個角色
    private List<Role> roles;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public List<Account> getAccounts() {
        return accounts;
    }

    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts;
    }

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

3 編寫 Role 持久層接口

/**
 * 查詢所有用戶及權(quán)限信息
 * @return
 */
List<User> findAllRole();

4 編寫映射文件

<!-- 定義User權(quán)限的resultMap -->
<resultMap id="userRoleMap" type="org.example.domain.User">
    <id property="id" column="id"></id>
    <result property="username" column="username"></result>
    <result property="sex" column="sex"></result>
    <result property="birthday" column="birthday"></result>
    <result property="address" column="address"></result>
    <!--配置user對象中國accounts集合映射-->
    <collection property="roles" ofType="org.example.domain.Role">
        <id property="roleId" column="rid"></id>
        <result property="roleName" column="role_name"></result>
        <result property="roleDesc" column="role_desc"></result>
    </collection>

</resultMap>

<select id="findAllRole" resultMap="userRoleMap">
        SELECT
            u.id ,
            u.username,
            u.birthday,
            u.sex,
            u.address,
            r.id as rid,
            r.role_name,
            r.role_desc
        FROM
        `user` u
        LEFT JOIN user_role ur ON
        u.ID = ur.UID
        LEFT JOIN `role` r ON r.id = ur.RID
    </select>

5 編寫測試類

@Test
public void testFindAllRole(){
    List<User> users = userDao.findAllRole();
    for (User user : users){
        System.out.println(user);
        System.out.println(user.getRoles());
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市啃勉,隨后出現(xiàn)的幾起案子忽舟,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件叮阅,死亡現(xiàn)場離奇詭異刁品,居然都是意外死亡,警方通過查閱死者的電腦和手機帘饶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門哑诊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人及刻,你說我怎么就攤上這事镀裤。” “怎么了缴饭?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵暑劝,是天一觀的道長。 經(jīng)常有香客問我颗搂,道長担猛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任丢氢,我火速辦了婚禮傅联,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘疚察。我一直安慰自己蒸走,他們只是感情好,可當我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布貌嫡。 她就那樣靜靜地躺著比驻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪岛抄。 梳的紋絲不亂的頭發(fā)上别惦,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天,我揣著相機與錄音夫椭,去河邊找鬼掸掸。 笑死,一個胖子當著我的面吹牛蹭秋,可吹牛的內(nèi)容都是我干的扰付。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼感凤,長吁一口氣:“原來是場噩夢啊……” “哼悯周!你這毒婦竟也來了粒督?” 一聲冷哼從身側(cè)響起陪竿,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后族跛,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體闰挡,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年礁哄,在試婚紗的時候發(fā)現(xiàn)自己被綠了长酗。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡桐绒,死狀恐怖夺脾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情茉继,我是刑警寧澤咧叭,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站烁竭,受9級特大地震影響菲茬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜派撕,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一婉弹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧终吼,春花似錦镀赌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至垫卤,卻和暖如春威彰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背穴肘。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工歇盼, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人评抚。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓豹缀,卻偏偏與公主長得像,于是被迫代替她去往敵國和親慨代。 傳聞我的和親對象是個殘疾皇子邢笙,可洞房花燭夜當晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容