Mybatis(三)--高級查詢

一、mybatis的連接池

我們知道使用連接池技術可以有很多的好處:

  • 資源重用
  • 加快響應速度
  • 利於資源分配

還有等等好處,常見的數據庫連接池技術有c3p0,druid等等。mybatis也爲我們封裝好了它自己的連接池技術,在主配置文件中,配置數據源的時候,

<dataSource type="POOLED">
                <!-- 配置連接數據庫的4個基本信息 -->
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
</dataSource>

指定了type屬性,其中的POOLED的值便是使用了數據庫連接池技術,所以只要指定type屬性爲POOLED,則就是使用了數據庫連接池。

二、mybatis的事務提交

事務的簡短回顧:

  • 事務定義:一個最小的不可再分的工作單元;通常一個事務對應一個完整的業務(例如銀行賬戶轉賬業務,該業務就是一個最小的工作單元)

  • 事務四大特徵(ACID):

    • 原子性(A):事務是最小單位,不可再分
    • 一致性©:事務要求所有的DML語句操作的時候,必須保證同時成功或者同時失敗
    • 隔離性(I):事務A和事務B之間具有隔離性
    • 持久性(D):是事務的保證,事務終結的標誌(內存的數據持久到硬盤文件中)
  • 開啓事務:Start Transaction

  • 事務結束:End Transaction

  • 提交事務:Commit Transaction

  • 回滾事務:Rollback Transaction

在mybatis的CRUD操作的時候,如果不手動提交事務,是無法將數據庫保存在數據庫裏的,提交事務使用Sqlsession的commit方法

 @After
    public void destory() throws IOException {
        //手動提交事務
        session.commit();
        in.close();
        session.close();
    }

設置自動提交事務的辦法是在創建Sqlsession對象的時候傳入一個參數boolean類型的,用於表明是否自動提交事務。

session = factory.openSession(true);

三、mybatis的動態sql語句

1.if標籤

if標籤支持在sql語句中多條件查詢。例如:輸入參數爲user對象,如果有某一個屬性爲空,需要其他屬性同時成立,比如如果用戶名字段爲空,那麼只要地址匹配即可,如果不爲空那麼需要用戶名和地址同時匹配

<select id="findByUser" resultType="com.Domain.User" parameterType="com.Domain.User">
        select * from user where 1=1
        <if test="username != null and username !=''">
            and username like #{username}
        </if>
        <if test="address != null and address != ''">
            and address like #{address}
        </if>
</select>
2.where標籤

在if標籤中使用了where 1= 1,這樣寫不太方便,mybatis提供了一個where標籤

 <select id="findByUser" resultType="com.Domain.User" parameterType="com.Domain.User">
        select * from user
        <where>
            <if test="username != null and username !=''">
                and username like #{username}
            </if>
            <if test="address != null and address != ''">
                and address like #{address}
            </if>
        </where>
</select>
3.foreach

例如sql語句:select * from user where id in(42,43,50,51)

這樣的sql語句可以使用foreach標籤來完成

    <select id="findByIds" resultType="com.Domain.User">
        select * from user where id in
        <foreach collection="ids" item="id" open="(" close=")" separator=",">
            #{id}
        </foreach>
    </select>

foreach標籤內的屬性都是字面意思,很好理解。

 List<User> findByIds(@Param("ids") Integer[] ids);

測試方法

@org.junit.Test
public void testFindByIds(){
    Integer[] ids = {42,43,50,51};
    List<User> users = userDao.findByIds(ids);
    for (User user : users) {
    System.out.println(user);
    }
}

四、多表查詢基於註解

1.一對一(多對一)第一種方法

案例是:賬戶與用戶的關係,一個用戶可以有多個賬戶,一個賬戶只能有一個用戶,所以這個關係從用戶出發就是一對多,從賬戶出發就是一對一。

user表:

在這裏插入圖片描述

account表:

在這裏插入圖片描述

有兩種方法查詢:很顯然,第一種方法就是建立和查詢的sql語句一一對應的實體類,例如:sql語句

SELECT 
	*
FROM
	account,USER
WHERE
	account.`UID` = user.`id`

那麼就需要對應建立一個與sql語句查詢的列名相對應的實體類其中的屬性有id,username,birthday,sex,address,uid,money。這裏有一個技巧,可以讓該實體類直接繼承user類,那麼只需要寫3個屬性的get,set方法

1.建立實體類

user

public class User implements Serializable {

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

    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 String getAddress() {
        return address;
    }

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

    public String getSex() {
        return sex;
    }

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

    public Date getBirthday() {
        return birthday;
    }

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

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

account

public class Account implements Serializable {

    private Integer id;
    private Integer uid;
    private Double money;

    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 +
                '}';
    }
}

AccountUser

public class AccountUser extends User {
    private Integer id;
    private Integer Uid;
    private Double money;

    @Override
    public Integer getId() {
        return id;
    }

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

    public Integer getUid() {
        return Uid;
    }

    public void setUid(Integer uid) {
        Uid = uid;
    }

    public Double getMoney() {
        return money;
    }

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

    @Override
    public String toString() {
        return "AccountUser{" + super.toString() +
                "id=" + id +
                ", Uid=" + Uid +
                ", money=" + money +
                '}';
    }

2.編寫Dao接口方法
public interface AccountDao {
    /**
     * 查詢所有賬戶,同時獲得該賬戶的所屬用戶信息 一對一
     * @return
     */
    List<AccountUser> findAll();
}
3.accountDao.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.MybatisDemo6.Dao.AccountDao">
<select id="findAll" resultType="com.MybatisDemo6.Domain.AccountUser" >
        select * from account, user where account.uid = user.id
    </select>
</mapper>
4.MapperConfig主配置文件
5.測試代碼

直接調用方法,和之前的CRUD操作一樣

6.檢驗結果

在這裏插入圖片描述

2.一對一(多對一)第二種方法

​ 通過上面可以發現這種方法的靈活性不高,而且要編寫很多重複性的代碼,mybatis給我們提供了另外一種方法:使用resulttype的方法

1.修改Account類,加入User類的對象引用
private User user;

public User getUser() {
	return user;
}

public void setUser(User user) {
	this.user = user;
}
2.修改AccountDao的方法
List<Account> findAllByAccount();
3.重新配置AccountDao配置文件
 <resultMap id="accountMap" type="com.MybatisDemo6.Domain.Account">
        <id column="aid" property="id"></id>
        <result column="uid" property="uid"/>
        <result column="money" property="money"/>
        <association property="user" javaType="com.MybatisDemo6.Domain.User">
            <id column="id" property="id"></id>
            <result column="username" property="username"></result>
            <result column="sex" property="sex"></result>
            <result column="birthday" property="birthday"></result>
            <result column="address" property="address"></result>
        </association>
    </resultMap>
    
    <select id="findAllByAccount" resultMap="accountMap" >
        select * from account, user where account.uid = user.id
    </select>
4.測試代碼
    @Test
    public void testFindAllByAccount(){
        AccountDao accountDao = session.getMapper(AccountDao.class);
        List<Account> accounts= accountDao.findAllByAccount();
        for (Account account : accounts) {
            System.out.println(account);
            System.out.println(account.getUser());
        }
    }
5.結果

在這裏插入圖片描述

3.一對多

一個用戶可以對應多個賬戶,這個關係爲一對多

1.修改User類
    public List<Account> getAccount() {
        return account;
    }

    public void setAccount(List<Account> account) {
        this.account = account;
    }

    private List<Account> account;
2.加入方法
 /**
     * 查詢所有的用戶,同時查詢該用戶的所有賬戶 一對多
     * @return
     */
    List<User> findAllByUser();
3.修改Dao配置文件
    <resultMap id="userMap" type="com.MybatisDemo6.Domain.User">
    <id column="id" property="id"></id>
    <result column="username" property="username"></result>
    <result column="sex" property="sex"></result>
    <result column="birthday" property="birthday"></result>
    <result column="address" property="address"></result>
        <collection property="account" ofType="com.MybatisDemo6.Domain.Account">
            <id column="aid" property="id"></id>
            <result column="uid" property="uid"/>
            <result column="money" property="money"/>
        </collection>
    </resultMap>
4.測試代碼
@Test
public void testFindAllByUser(){
    AccountDao accountDao = session.getMapper(AccountDao.class);
    List<User> users = accountDao.findAllByUser();
    for (User user : users) {
        System.out.println(user);
        System.out.println(user.getAccount());
    }
}
5.結果

在這裏插入圖片描述

4.多對多

​ 一個用戶可以有多個角色,一個角色可以有多個用戶,所以用戶與角色之間是多對多的關係。在sql中多對多的關係需要藉助與第三張表來實現,作爲外鍵分別指向兩張表的主鍵

user用戶表

在這裏插入圖片描述
role角色表

在這裏插入圖片描述
user_role第三張表
在這裏插入圖片描述

1.創建role實體類

注意添加

 private List<User> users;

    public List<User> getUsers() {
        return users;
    }
2.加入方法
public interface RoleDao {
    /**
     * 查詢所有角色,並且查詢角色對應的用戶
     * @return
     */
    List<Role> findAll();
}
3.配置Dao配置文件
<mapper namespace="com.MybatisDemo6.Dao.RoleDao">
    <resultMap id="roleMap" type="com.MybatisDemo6.Domain.Role">
        <id column="ID" property="roleID"></id>
        <result column="role_name" property="roleName"></result>
        <result column="role_desc" property="roleDesc"></result>
        <collection property="users" ofType="com.MybatisDemo6.Domain.User">
            <id column="id" property="id"></id>
            <result column="username" property="username"></result>
            <result column="sex" property="sex"></result>
            <result column="birthday" property="birthday"></result>
            <result column="address" property="address"></result>
        </collection>
    </resultMap>
    <select id="findAll" resultMap="roleMap">
 select u.*,r.id as rid,r.role_name,r.role_desc from role r
         left outer join user_role ur  on r.id = ur.rid
         left outer join user u on u.id = ur.uid
    </select>
</mapper>
4.編寫測試類
 @Test
    public void testFindAll(){
        RoleDao roleDao = session.getMapper(RoleDao.class);
        List<Role> roles = roleDao.findAll();
        for (Role role : roles) {
            System.out.println(role);
            System.out.println(role.getUsers());
        }
    }

五、mybatis的延遲加載策略

1.概念

就是在需要用到數據時才進行加載,不需要用到數據時就不加載數據。延遲加載也稱懶加載

優點:在使用關聯對象時,才從數據庫中查詢關聯數據,大大降低數據庫不必要開銷。

缺點:因爲只有當需要用到數據時,纔會進行數據庫查詢,這樣在大批量數據查詢時,因爲查詢工作也需要耗費時間,所以可能造成用戶等待時間變長,造成用戶體驗下降。

2.開啓延遲加載

在全局配置文件中加入:

<settings>
		<setting name="lazyLoadingEnabled"  value="true"/>
		<setting name="aggressiveLazyLoading" value="flase"/>
</settings>

六、mybatis緩存

在Mybatis中,分爲一級緩存和二級緩存

在這裏插入圖片描述

1.一級緩存

一級緩存的作用域是sqlSession,一級緩存是默認開啓的,要想觸發mybatis的一級緩存,要滿足:同一個session中、相同的SQL和參數

2.二級緩存

mybatis 的二級緩存的作用域是一個mapper的namespace ,同一個namespace中查詢sql可以從緩存中命中。二級緩存不是默認開啓的,要開啓二級緩存:

  1. 在全局配置文件中開啓:
<settings>
<!-- 開啓二級緩存的支持 --> <setting name="cacheEnabled" value="true"/>
</settings>

不過這一步由於cacheEnabled 的取值默認就爲 true,所以這一步可以省略不配置

  1. 配置相關的 Mapper 映射文件
<mapper namespace="com.MybatisDemo6.Dao.AccountDao">
	<!-- 開啓二級緩存的支持 -->
	<cache></cache>
</mapper>
  1. 配置 statement 上面的 useCache 屬性
    <!-- 根據 id 查詢 --> 
<select id="findById" resultType="user" parameterType="int" useCache="true">
    select * from user where id = #{uid}
</select>

域是一個mapper的namespace ,同一個namespace中查詢sql可以從緩存中命中。二級緩存不是默認開啓的,要開啓二級緩存:

  1. 在全局配置文件中開啓:
<settings>
<!-- 開啓二級緩存的支持 --> <setting name="cacheEnabled" value="true"/>
</settings>

不過這一步由於cacheEnabled 的取值默認就爲 true,所以這一步可以省略不配置

  1. 配置相關的 Mapper 映射文件
<mapper namespace="com.MybatisDemo6.Dao.AccountDao">
	<!-- 開啓二級緩存的支持 -->
	<cache></cache>
</mapper>
  1. 配置 statement 上面的 useCache 屬性
    <!-- 根據 id 查詢 --> 
<select id="findById" resultType="user" parameterType="int" useCache="true">
    select * from user where id = #{uid}
</select>

注意:針對每次查詢都需要最新的數據 sql,要設置成 useCache=false,禁用二級緩存

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章