mybatis緩存機制和註解開發4

mybatis延遲加載

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

延遲加載好處:先從單表查詢,需要時再從關聯表去關聯查詢,大大提高數據庫性能,因爲查詢單表要比關聯查詢多張錶速
度要快

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

開啓延遲加載代碼示例
  1. 創建maven工程,把目標標記爲需要的目錄,例如寫代碼的java目標標記爲源碼目錄等
  2. 在pom.xml中添加mybatis所需要的依賴
<dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>

    <!--mybatis依賴包座標-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.5</version>
    </dependency>
    <!--mysql依賴包座標-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.6</version>
      <scope>runtime</scope>
    </dependency>
    <!--log4j依賴包座標-->
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.12</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>

  1. 設置SqlMapConfig.xml,開啓延遲加載的支持在這個文件配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

    <!-- 配置properties第一種方式-->
    <properties>
        <property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
        <property name="jdbc.url" value="jdbc:mysql://localhost:3306/student"/>
        <property name="jdbc.username" value="數據庫用戶名"/>
        <property name="jdbc.password" value="數據庫密碼"/>
    </properties>

    <!-- 開啓延遲加載的支持 -->
    <settings>
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>


    <!-- 配置 mybatis 的環境 -->
    <environments default="mysql">
        <!-- 配置 mybatis 的環境 -->
        <environment id="mysql">
            <!-- 配置事務的類型 -->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置連接數據庫的信息:用的是數據源(連接池) -->
            <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>
        </environment>
    </environments>


    <!-- 告知 mybatis 映射配置的位置 -->
    <mappers>
        <package name="cn.dao"></package>
    </mappers>

</configuration>
  1. 創建實體類對應數據庫字段
    User .java
package cn.domain;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

public class User implements Serializable {
    private Integer id;
    private String username;
    private String address;
    private String sex;
    private Date birthday;
    //一對多關係映射:主表實體應該包含從表實體的集合引用
    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 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;
    }

    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 + '\'' +
                ", address='" + address + '\'' +
                ", sex='" + sex + '\'' +
                ", birthday=" + birthday +
                '}';
    }

}

Account.java

package cn.domain;

import java.io.Serializable;

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

5.編寫持久層接口方法

package cn.dao;

import cn.domain.Account;
import cn.domain.User;

import java.util.List;

public interface IUserDao {
 	/**
     * 查找所以用戶
     * @param uid
     * @return
     */
    List<User> findAll();
}

package cn.dao;

import cn.domain.Account;

import java.util.List;

public interface IAccountDao {

    /**
     * 根據用戶ID查找賬戶
     * @param uid
     * @return
     */
    List<Account> findByUid(Integer uid);
}

6.編寫持久層配置
IAccountDao.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="cn.dao.IAccountDao">
    
    <!-- 根據用戶 id 查詢賬戶信息 -->
    <select id="findByUid" resultType="cn.domain.Account" parameterType="int">
        select * from account where uid = #{uid}
    </select>

</mapper>

IUserDao.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="cn.dao.IUserDao">
    <!-- 根據 id 查詢 -->
    <select id="findById" resultType="cn.domain.User" parameterType="int" >
        select * from user where id = #{uid}
    </select>


    <resultMap type="cn.domain.User" id="userMap">
        <id column="id" property="id"></id>
        <result column="username" property="username"/>
        <result column="address" property="address"/>
        <result column="sex" property="sex"/>
        <result column="birthday" property="birthday"/>
        <!-- collection 是用於建立一對多中集合屬性的對應關係
        ofType 用於指定集合元素的數據類型
        select 是用於指定查詢賬戶的唯一標識(賬戶的 dao 全限定類名加上方法名稱)
        column 是用於指定使用哪個字段的值作爲條件查詢
        -->
        <!--    <collection> 標籤 :-->
        <!--    主要用於加載關聯的集合對象-->
        <!--    select 屬性 :-->
        <!--    用於指定查詢 account 列表的 sql 語句,所以填寫的是該 sql 映射的 id-->
        <!--    column 屬性 :-->
        <!--    用於指定 select 屬性的 sql 語句的參數來源,上面的參數來自於 user 的 id 列,所以就寫成 id 這一v-->
        <collection property="accounts" ofType="cn.domain.Account"
                    select="cn.dao.IAccountDao.findByUid"
                    column="id">
        </collection>
    </resultMap>
    <!-- 配置查詢所有操作 -->
    <select id="findAll" resultMap="userMap">
        select * from user
    </select>

</mapper>

最後測試

package org.example;

import static org.junit.Assert.assertTrue;

import cn.dao.IAccountDao;
import cn.dao.IUserDao;
import cn.domain.Account;
import cn.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.InputStream;
import java.util.List;

/**
 * Unit test for simple App.
 */
public class AppTest {
    private InputStream in ;
    private SqlSessionFactory factory;
    private SqlSession session;
    private IUserDao userDao;

    @Before//在測試方法執行之前執行
    public void init()throws Exception {
        //1.讀取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.創建構建者對象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //3.創建 SqlSession 工廠對象
        factory = builder.build(in);
        //4.創建 SqlSession 對象
        session = factory.openSession();
        //5.創建 Dao 的代理對象
        userDao = session.getMapper(IUserDao.class);
    }

    @After//在測試方法執行完成之後執行
    public void destroy() throws Exception{
        //7.釋放資源
        session.close();
        in.close();
    }


    @Test
    public void testUserFindAll() {
        //6.執行操作
        List<User> users = userDao.findAll();
//        for (User user:users){
//            System.out.println(user);
//            System.out.println(user.getAccounts());
//        }
    }
}

在這裏插入圖片描述
可以看出如果沒有使用Account 並沒有加載 Account 賬戶信息,
如果你使用的話比如把註釋去掉的話結果如下,會把Account 加載進內存
在這裏插入圖片描述

Mybatis緩存

Mybatis 中緩存分爲一級緩存,二級緩存
在這裏插入圖片描述

Mybatis一級緩存

一級緩存是 SqlSession 級別的緩存,只要 SqlSession 沒有 flush 或 close,它就存在,其實使用的時候無論是xml還是註解開發都不需要配置,自己會存在

  • 示例一級緩存的存在
    1.在IUserDao接口中添加查詢方法
 User findById(Integer userId);
  1. 在IUserDao.xml中添加配置
 <!-- 根據 id 查詢 -->
    <select id="findById" resultType="cn.domain.User" parameterType="int" useCache="true">
        select * from user where id = #{uid}
    </select>

測試

@Test
    public void testCach() {
        User user = userDao.findById(41);
        System.out.println("第一次查詢的用戶:"+user);
        User user2 = userDao.findById(41);
        System.out.println("第二次查詢用戶:"+user2);
        System.out.println(user == user2);
    }

在這裏插入圖片描述
查詢了兩次,但最後只執行了一次數據庫操作,這就是 Mybatis 提供給我們的一級緩存在起作用了。因爲一級緩存的存在,導致第二次查詢 id爲41的記錄時,並沒有發出sql語句
從數據庫中查詢數據,而是從一級緩存中查詢

  • 示例一級緩分析
    一級緩存是 SqlSession 範圍的緩存,當調用 SqlSession 的修改,添加,刪除,commit(),close()等方法時,就會清空一級緩存,如下圖
    在這裏插入圖片描述
    測試close()等方法清空緩存
 @Test
    public void testFirstLevelCache(){
        User user1 = userDao.findById(41);
        System.out.println(user1);
//        sqlSession.close();
//        //再次獲取 SqlSession 對象
//        sqlSession = factory.openSession();
        sqlSession.clearCache();//此方法也可以清空緩存
        userDao = sqlSession.getMapper(IUserDao.class);
        User user2 = userDao.findById(41);
        System.out.println(user2);
        System.out.println(user1 == user2);
    }

測試增刪改等方法清空緩存

@Test
    public void testClearlCache(){
        //1.根據 id 查詢用戶
        User user1 = userDao.findById(41);
        System.out.println(user1);
        //2.更新用戶信息會清空sqlSession緩存,再次查找相同的id會從數據庫查找
        user1.setUsername("update user clear cache");
        user1.setAddress("北京市海淀區");
        userDao.updateUser(user1);
        //3.再次查詢 id 爲 41 的用戶
        User user2 = userDao.findById(41);
        System.out.println(user2);
        System.out.println(user1 == user2);
    }

在這裏插入圖片描述

Mybatis二級緩存
  • 二級緩存介紹
    二級緩存:二級緩存是 mapper 映射級別的緩存,多個 SqlSession 去操作同一個 Mapper 映射的 sql 語句,多個
    SqlSession 可以共用二級緩存,二級緩存是跨 SqlSession 的

二級緩存結構圖
在這裏插入圖片描述

  • 二級緩存開啓和關閉
  1. 在 SqlMapConfig.xml 文件開啓二級緩存
<settings>
<!-- 開啓二級緩存的支持 -->
<setting name="cacheEnabled" value="true"/>
</settings>
因爲 cacheEnabled 的取值默認就爲 true,所以這一步可以省略不配置。爲 true 代表開啓二級緩存;爲
false 代表不開啓二級緩存
  1. 第二步:配置相關的 Mapper 映射文 件
<cache>標籤表示當前這個 mapper 映射將使用二級緩存,區分的標準就看 mapper 的 namespace 值。
<?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.itheima.dao.IUserDao">
<!-- 開啓二級緩存的支持 -->
<cache></cache>
</mapper>
  1. 配置 statement 上面的 useCache 屬性
<!-- 根據 id 查詢 -->
<select id="findById" resultType="user" parameterType="int" useCache="true">
select * from user where id = #{uid}
</select>
將 UserDao.xml 映射文件中的<select>標籤中設置 useCache=true”代表當前這個 statement 要使用
二級緩存,如果不使用二級緩存可以設置爲 false。
注意:針對每次查詢都需要最新的數據 sql,要設置成 useCache=false,禁用二級緩存

二級緩存測試

package org.example;

import static org.junit.Assert.assertTrue;

import cn.dao.IAccountDao;
import cn.dao.IUserDao;
import cn.domain.Account;
import cn.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.InputStream;
import java.util.List;

/**
 * Unit test for simple App.
 */
public class AppTest {
    private InputStream in;
    private SqlSessionFactory factory;
    @Before//用於在測試方法執行之前執行
    public void init()throws Exception{
        //1.讀取配置文件,生成字節輸入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.獲取 SqlSessionFactory
        factory = new SqlSessionFactoryBuilder().build(in);
    }
    @After//用於在測試方法執行之後執行
    public void destroy()throws Exception{
        in.close();
    }
    /**
     * 測試二級緩存
     */
    @Test
    public void testCache(){
        SqlSession sqlSession1 = factory.openSession();
        IUserDao dao1 = sqlSession1.getMapper(IUserDao.class);
        User user1 = dao1.findById(41);
        System.out.println(user1);
        sqlSession1.close();//一級緩存消失

        //創建新的SqlSession對象
        SqlSession sqlSession2 = factory.openSession();
        //操作同一個Mapper映射的sql 語句
        IUserDao dao2 = sqlSession2.getMapper(IUserDao.class);
        User user2 = dao2.findById(41);
        System.out.println(user2);
        sqlSession2.close();
        System.out.println(user1 == user2);
    }

}

在這裏插入圖片描述
執行了兩次查詢,並且在執行第一次查詢後,我們關閉了一級緩存,再去執行第二次查詢時,我們發現並沒有對數據庫發出 sql 語句,所以此時的數據就只能是來自於我們所說的二級緩存

二級緩存注意事項:當我們在使用二級緩存時,所緩存的類一定要實現 java.io.Serializable 接口,這種就可以使用序列化
方式來保存對象

緩存執行順序是:二級緩存–>一級緩存–>數據庫

Mybatis 註解開發

使用 Mybatis 註解實現基本 CRUD
  1. 編寫實體類User
public class User implements Serializable {
private Integer userId;
private String userName;
private Date userBirthday;
private String userSex;
private String userAddress;
public Integer getUserId() {
	return userId;
	}
public void setUserId(Integer userId) {
	this.userId = userId;
	}
public String getUserName() {
	return userName;
	}
public void setUserName(String userName) {
	this.userName = userName;
	}
public Date getUserBirthday() {
	return userBirthday;
	}
public void setUserBirthday(Date userBirthday) {
	this.userBirthday = userBirthday;
	}
public String getUserSex() {
	return userSex;
	}
public void setUserSex(String userSex) {
	this.userSex = userSex;
	}

public String getUserAddress() {
	return userAddress;
	}
public void setUserAddress(String userAddress) {
	this.userAddress = userAddress;
	}
@Override
public String toString() {
	return "User [userId=" + userId + ", userName=" + userName + 				", userBirthday="
	+ userBirthday + ", userSex="
	+ userSex + ", userAddress=" + userAddress + "]";
	}
}
//注意:此處我們故意和數據庫表的列名不一致,好知道怎麼使用註解處理映射
  1. 使用註解方式開發 持久層 接口
public interface IUserDao {
	/**
	* 查詢所有用戶
	* @return
	*/
	@Select("select * from user")
	@Results(id="userMap",
	value= {
		@Result(id=true,column="id",property="userId"),
		@Result(column="username",property="userName"),
		@Result(column="sex",property="userSex"),
		@Result(column="address",property="userAddress"),
		@Result(column="birthday",property="userBirthday")
		})
	List<User> findAll();
	/**
	* 根據 id 查詢一個用戶
	* @param userId
	* @return
	*/
	@Select("select * from user where id = #{uid} ")
	@ResultMap("userMap")
	User findById(Integer userId);
	/**
	* 保存操作
	* @param user
	* @return
	*/
	@Insert("insert into
	user(username,sex,birthday,address)values(#{username},#{sex},#{birthday},#{address}
	)")
	@SelectKey(keyColumn="id",keyProperty="id",resultType=Integer.class,before =
	false, statement = { "select last_insert_id()" })
	int saveUser(User user);
	/**
	* 更新操作
	* @param user
	* @return
	*/
	@Update("update user set
	username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id
	=#{id} ")
	int updateUser(User user);
	/**
	* 刪除用戶
	* @param userId
	* @return
	*/
	@Delete("delete from user where id = #{uid} ")
	int deleteUser(Integer userId);
	/**
	* 查詢使用聚合函數
	* @return
	*/
	@Select("select count(*) from user ")
	int findTotal();
	/**
	* 模糊查詢
	* @param name
	* @return
	*/
	@Select("select * from user where username like #{username} ")
	List<User> findByName(String name);
}
	//通過註解方式,我們就不需要再去編寫 UserDao.xml 映射文件了。

  1. 編寫 SqlMapConfig 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<!-- 配置 properties 文件的位置 -->
	<properties resource="jdbcConfig.properties"></properties>
	<!-- 配置別名的註冊 -->
	<typeAliases>
	<package name="cn.domain"/>
	</typeAliases>
	<!-- 配置環境 -->
	<environments default="mysql">
		<!-- 配置 mysql 的環境 -->
		<environment id="mysql">
			<!-- 配置事務的類型是 JDBC -->
			<transactionManager type="JDBC"></transactionManager>
			<!-- 配置數據源 -->
			<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>
		</environment>
	</environments>
	<!-- 配置映射信息 -->
	<mappers>
		<!-- 配置 dao 接口的位置,它有兩種方式
		第一種:使用 mapper 標籤配置 class 屬性
		第二種:使用 package 標籤,直接指定 dao 接口所在的包
		-->
		<package name="cn.dao"/>
	</mappers>
</configuration>

編寫測試方法

public class MybatisAnnotationCRUDTest {
    /**
     * 測試查詢所有
     */
    @Test
    public void testFindAll() {
        List<User> users = userDao.findAll();
        for(User user : users) {
            System.out.println(user);
        }
    }
    /**
     * 測試查詢一個
     */
    @Test
    public void testFindById() {
        User user = userDao.findById(41);
        System.out.println(user);
    }
    /**
     * 測試保存
     */
    @Test
    public void testSave() {
        User user = new User();
        user.setUserName("mybatis annotation");
        user.setUserSex("男");
        user.setUserAddress("北京市順義區");
        user.setUserBirthday(new Date());
        int res = userDao.saveUser(user);
        System.out.println("影響數據庫記錄的行數:"+res);
        System.out.println("插入的主鍵值:"+user.getUserId());
    }
    /**
     * 測試更新
     */
    @Test
    public void testUpdate() {
        User user = userDao.findById(63);
        user.setUserBirthday(new Date());
        user.setUserSex("女");
        int res = userDao.updateUser(user);
        System.out.println(res);
    }
    /**
     * 測試刪除
     */
    @Test
    public void testDelete() {
        int res = userDao.deleteUser(63);
        System.out.println(res);
    }
    /**
     * 測試查詢使用聚合函數
     */
    @Test
    public void testFindTotal() {
        int res = userDao.findTotal();
        System.out.println(res);
    }
    /**
     * 測試模糊查詢
     */
    @Test
    public void testFindByName() {
        List<User> users = userDao.findByName("%m%");
        for(User user : users) {
            System.out.println(user);
        }
    }
    private InputStream in;
    private SqlSessionFactory factory;
    private SqlSession session;
    private IUserDao userDao;
    @Before//junit 的註解
    public void init()throws Exception{
        //1.讀取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.創建工廠
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        factory = builder.build(in);
        //3.創建 session
        session = factory.openSession();
        //4.創建代理對象
        userDao = session.getMapper(IUserDao.class);
    }
    @After//junit 的註解
    public void destroy()throws Exception {
        //提交事務
        session.commit();
        //釋放資源
        session.close();
        //關閉流
        in.close();
    }
}
使用註解實現複雜關係映射開發
  • 複雜關係映射的註解說明
@Results 註解
代替的是標籤<resultMap>
該註解中可以使用單個@Result 註解,也可以使用@Result 集合
@Results{@Result(),@Result()})或@Results@Result())
@Resutl 註解
代替了 <id> 標籤和<result> 標籤
@Result 中 屬性介紹:
id 是否是主鍵字段
column 數據庫的列名
property 需要裝配的屬性名
one 需要使用的@One 註解(@Result(one=@One)()))
many 需要使用的@Many 註解(@Result(many=@many)()))
@One 註解(一對一)
代替了<assocation> 標籤,是多表查詢的關鍵,在註解中用來指定子查詢返回單一對象。
@One 註解屬性介紹:
select 指定用的 來多表查詢的 sqlmapper
fetchType 會覆蓋全局的配置參數 lazyLoadingEnabled。。
使用格式:
@Result(column=" ",property="",one=@One(select=""))
@Many 註解(多對一)
代替了<Collection> 標籤, 是是多表查詢的關鍵,在註解中用來指定子查詢返回對象集合。
注意:聚集元素用來處理“一對多”的關係。需要指定映射的 Java 實體類的屬性,屬性的 javaType
(一般爲 ArrayList)但是註解中可以不定義;
使用格式:
@Result(property="",column="",many=@Many(select=""))
  • 使用 註解 實現一對一 複雜關係映射 及延遲加載
  1. 添加 User 實體類及 Account 實體類
public class User implements Serializable {
    private Integer userId;
    private String userName;
    private Date userBirthday;
    private String userSex;
    private String userAddress;
    public Integer getUserId() {
        return userId;
    }
    public void setUserId(Integer userId) {
        this.userId = userId;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public Date getUserBirthday() {
        return userBirthday;
    }
    public void setUserBirthday(Date userBirthday) {
        this.userBirthday = userBirthday;
    }
    public String getUserSex() {
        return userSex;
    }
    public void setUserSex(String userSex) {
        this.userSex = userSex;
    }
    public String getUserAddress() {
        return userAddress;
    }
    public void setUserAddress(String userAddress) {
        this.userAddress = userAddress;
    }
    @Override
    public String toString() {
        return "User [userId=" + userId + ", userName=" + userName + ", userBirthday="
                + userBirthday + ", userSex="
                + userSex + ", userAddress=" + userAddress + "]";
    }
}

/**
 *
 * 賬戶的實體類Account
 * 
 * 
 */
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 + "]";
    }
}
  1. 添加 賬戶 的持久層 接口 並使用註解配置
public interface IAccountDao {
    /**
     * 查詢所有賬戶,採用延遲加載的方式查詢賬戶的所屬用戶
     * @return
     */
    @Select("select * from account")
    @Results(id="accountMap",
            value= {
                    @Result(id=true,column="id",property="id"),
                    @Result(column="uid",property="uid"),
                    @Result(column="money",property="money"),
                    @Result(column="uid",
                            property="user",
                            one=@One(select="com.itheima.dao.IUserDao.findById",
                                    fetchType=FetchType.LAZY)
                    )
            })
    List<Account> findAll();
}
  1. 添加 用戶的持久層 接口 並使用註解配置
public interface IUserDao {
    /**
     * 查詢所有用戶
     * @return
     */
    @Select("select * from user")
    @Results(id="userMap",
            value= {
                    @Result(id=true,column="id",property="userId"),
                    @Result(column="username",property="userName"),
                    @Result(column="sex",property="userSex"),
                    @Result(column="address",property="userAddress"),
                    @Result(column="birthday",property="userBirthday")
            })
    List<User> findAll();
    /**
     * 根據 id 查詢一個用戶
     * @param userId
     * @return
     */
    @Select("select * from user where id = #{uid} ")
    @ResultMap("userMap")
    User findById(Integer userId);
}

測試一對一關聯及延遲加載

public class AccountTest {
@Test
public void testFindAll() {
List<Account> accounts = accountDao.findAll();
// for(Account account : accounts) {
// System.out.println(account);
// System.out.println(account.getUser());
// }
}
  • 使用 註解 實現一對多
    一個用戶具有多個賬戶信息,所以形成了用戶(User)與賬戶(Account)之間的一對多關係
  1. User 實體類 加入 List
public class User implements Serializable {
    private Integer userId;
    private String userName;
    private Date userBirthday;
    private String userSex;
    private String userAddress;
    //一對多關係映射:主表方法應該包含一個從表方的集合引用
    private List<Account> accounts;
    public List<Account> getAccounts() {
        return accounts;
    }
    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts;
    }
    public Integer getUserId() {
        return userId;
    }
    public void setUserId(Integer userId) {
        this.userId = userId;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public Date getUserBirthday() {
        return userBirthday;
    }
    public void setUserBirthday(Date userBirthday) {
        this.userBirthday = userBirthday;
    }
    public String getUserSex() {
        return userSex;
    }
    public void setUserSex(String userSex) {
        this.userSex = userSex;
    }
    public String getUserAddress() {
        return userAddress;
    }
    public void setUserAddress(String userAddress) {
        this.userAddress = userAddress;
    }
    @Override
    public String toString() {
        return "User [userId=" + userId + ", userName=" + userName + ", userBirthday="
                + userBirthday + ", userSex="
                + userSex + ", userAddress=" + userAddress + "]";
    }
}

  1. 編寫用戶的持久層接口並使用註解配置
public interface IUserDao {
    /**
     * 查詢所有用戶
     * @return
     */
    @Select("select * from user")
    @Results(id="userMap",
            value= {
                    @Result(id=true,column="id",property="userId"),
                    @Result(column="username",property="userName"),
                    @Result(column="sex",property="userSex"),
                    @Result(column="address",property="userAddress"),
                    @Result(column="birthday",property="userBirthday"),
                    @Result(column="id",property="accounts",
                            many=@Many(
                                    select="com.itheima.dao.IAccountDao.findByUid",
                                    fetchType=FetchType.LAZY
                            )
                    )
            })
    List<User> findAll();
}
/*
@Many:
        相當於<collection>的配置
        select 屬性:代表將要執行的 sql 語句
        fetchType 屬性:代表加載方式,一般如果要延遲加載都設置爲 LAZY 的值

 */
 
  1. 編寫賬戶的持久層接口並使用註解配置
public interface IAccountDao {
    /**
     * 根據用戶 id 查詢用戶下的所有賬戶
     * @param userId
     * @return
     */
    @Select("select * from account where uid = #{uid} ")
    List<Account> findByUid(Integer userId);
}
  1. 測試
public class UserTest {
    private InputStream in;
    private SqlSessionFactory factory;
    private SqlSession session;
    private IUserDao userDao;
    @Before//junit 的註解
    public void init()throws Exception{
        //1.讀取配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.創建工廠
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        factory = builder.build(in);
        //3.創建 session
        session = factory.openSession();
        //4.創建代理對象
        userDao = session.getMapper(IUserDao.class);
    }
    @After//junit 的註解
    public void destroy()throws Exception {
        //提交事務
        session.commit();
        //釋放資源
        session.close();
        //關閉流
        in.close();
    }

    /**
     * 測試查詢所有
     */
    @Test
    public void testFindAll() {
        List<User> users = userDao.findAll();
        // for(User user : users) {
        // System.out.println("-----每個用戶的內容-----");
        // System.out.println(user);
        // System.out.println(user.getAccounts());
        // }
    }
}
  • mybatis 基於註解的二級緩存
  1. 在 SqlMapConfig 中開啓二級緩存支持
<!-- 配置二級緩存 -->
<settings>
	<!-- 開啓二級緩存的支持 -->
	<setting name="cacheEnabled" value="true"/>
</settings>
  1. 在持久層接口中使用註解配置二級緩存
@CacheNamespace(blocking=true)//mybatis 基於註解方式實現配置二級緩存
public interface IUserDao {}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章