主要內(nèi)容
- Mybatis 延遲加載策略
- Mybatis 緩存
- Mybatis 注解開發(fā)
Mybatis 延遲加載策略
1.1 何為延遲加載?
延遲加載:
就是在需要用到數(shù)據(jù)時才進行加載,不需要用到數(shù)據(jù)時就不加載數(shù)據(jù)出牧。延遲加載也稱懶加載.
好處:
先從單表查詢,需要時再從關(guān)聯(lián)表去關(guān)聯(lián)查詢,大大提高數(shù)據(jù)庫性能奉芦,因為查詢單表要比關(guān)聯(lián)查詢多張表速度要快脉让。
壞處:
因為只有當(dāng)需要用到數(shù)據(jù)時副渴,才會進行數(shù)據(jù)庫查詢,這樣在大批量數(shù)據(jù)查詢時缚够,因為查詢工作也要消耗時間,所以可能造成用戶等待時間變長鹦赎,造成用戶體驗下降谍椅。
1.2 實現(xiàn)需求
需求:
查詢賬戶(Account)信息并且關(guān)聯(lián)查詢用戶(User)信息。如果先查詢賬戶(Account)信息即可滿足要
求古话,當(dāng)我們需要查詢用戶(User)信息時再查詢用戶(User)信息雏吭。把對用戶(User)信息的按需去查詢就是延遲加載。
mybatis第三天實現(xiàn)多表操作時煞额,我們使用了 resultMap來實現(xiàn)一對一思恐,一對多,多對多關(guān)系的操作膊毁。主要
是通過 association胀莹、 collection 實現(xiàn)一對一及一對多映射。
association婚温、 collection 具備延遲加載功能描焰。
1.3 使用 assocation 實現(xiàn)延遲加載
需求:
查詢賬戶信息同時查詢用戶信息。
1.3.1 賬戶的持久層 DAO 接口
package com.ding.dao;
import com.ding.domain.Account;
import java.util.List;
public interface IAccountDao {
/**
* 查詢所有賬戶栅螟,同時獲取賬戶的所屬用戶名稱以及它的地址信息
* @return
*/
List<Account> findAll();
}
1.3.2 賬戶的持久層映射文件
<?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.ding.dao.IAccountDao">
<!-- 建立對應(yīng)關(guān)系 -->
<resultMap type="Account" id="accountMap">
<id column="aid" property="id"/>
<result column="uid" property="uid"/>
<result column="money" property="money"/>
<!-- 它是用于指定從表方的引用實體屬性的 -->
<association property="user" javaType="User"
select="com.ding.dao.IUserDao.findById"
column="uid">
</association>
</resultMap>
<select id="findAll" resultMap="accountMap">
select * from account
</select>
</mapper>
select: 填寫我們要調(diào)用的 select 映射的 id
column : 填寫我們要傳遞給 select 映射的參數(shù)
1.3.3 用戶的持久層接口和映射文件
public interface IUserDao {
/**
* 根據(jù) id 查詢
* @param userId
* @return
*/
User findById(Integer userId);
}
<!-- 根據(jù) id 查詢 -->
<select id="findById" resultType="User" parameterType="int" >
select * from user where id = #{uid}
</select>
1.3.4 開啟 Mybatis 的延遲加載策略
進入 Mybaits 的官方文檔荆秦,找到 settings 的說明信息:
我們需要在 Mybatis 的配置文件 SqlMapConfig.xml 文件中添加延遲加載的配置。
<!-- 開啟延遲加載的支持 -->
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
1.3.5 編寫測試只查賬戶信息不查用戶信息力图。
package com.ding;
import com.ding.dao.IAccountDao;
import com.ding.domain.Account;
import com.ding.domain.AccountUser;
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 org.apache.ibatis.io.Resources;
import java.io.InputStream;
import java.util.List;
public class AccountTest {
private InputStream in ;
private SqlSessionFactory factory;
private SqlSession session;
private IAccountDao accountDao;
@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 的代理對象
accountDao = session.getMapper(IAccountDao.class);
}
@After//在測試方法執(zhí)行完成之后執(zhí)行
public void destroy() throws Exception{
session.commit();
//7.釋放資源
session.close();
in.close();
}
@Test
public void testFindAll1() {
List<Account> accounts = accountDao.findAll();
// for(Account au : accounts) {
// System.out.println(au);
// System.out.println(au.getUser());
// }
}
}
測試結(jié)果如下:
因為本次只是將 Account對象查詢出來放入 List 集合中步绸,并沒有涉及到 User對象,所以就沒有
發(fā)出 SQL 語句查詢賬戶所關(guān)聯(lián)的 User 對象的查詢吃媒。
1.4 使用 Collection 實現(xiàn)延遲加載
同樣我們也可以在一對多關(guān)系配置的<collection>結(jié)點中配置延遲加載策略瓤介。
<collection>結(jié)點中也有 select 屬性, column 屬性赘那。
需求:
完成加載用戶對象時刑桑,查詢該用戶所擁有的賬戶信息。
1.4.1 在 User 實體類中加入 List<Account>屬性
package com.ding.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 Date birthday;
private String sex;
public List<Account> getAccounts() {
return accounts;
}
public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
}
private List<Account> accounts;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getAddress() {
return address;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public void setAddress(String address) {
this.address = address;
}
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;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", address='" + address + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", accounts=" + accounts +
'}';
}
}
1.4.2 編寫用戶和賬戶持久層接口的方法
package com.ding.dao;
import com.ding.domain.Account;
import com.ding.domain.User;
import java.util.List;
public interface IUserDao {
/**
* 查詢所有用戶募舟,同時獲取出每個用戶下的所有賬戶信息
* @return
*/
List<User> findAll();
/**
* 根據(jù)用戶 id 查詢賬戶信息
* @param uid
* @return
*/
List<Account> findByUid(Integer uid);
}
1.4.3 編寫用戶持久層映射配置
<?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.ding.dao.IUserDao">
<resultMap type="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 是用于建立一對多中集合屬性的對應(yīng)關(guān)系
ofType 用于指定集合元素的數(shù)據(jù)類型
select 是用于指定查詢賬戶的唯一標(biāo)識(賬戶的 dao 全限定類名加上方法名稱)
column 是用于指定使用哪個字段的值作為條件查詢
-->
<collection property="accounts" ofType="Account"
select="com.ding.dao.IAccountDao.findByUid"
column="id">
</collection>
</resultMap>
<!-- 配置查詢所有操作 -->
<select id="findAll" resultMap="userMap">
select * from user
</select>
</mapper>
<collection>標(biāo)簽:
主要用于加載關(guān)聯(lián)的集合對象
select 屬性:
用于指定查詢 account 列表的 sql 語句祠斧,所以填寫的是該 sql 映射的 id
column 屬性:
用于指定 select 屬性的 sql 語句的參數(shù)來源,上面的參數(shù)來自于 user 的 id 列拱礁,所以就寫成 id 這一
個字段名了
1.4.4 編寫賬戶持久層映射配置
<!-- 根據(jù)用戶 id 查詢賬戶信息 -->
<select id="findByUid" resultType="Account" parameterType="int">
select * from account where uid = #{uid}
</select>
1.4.5 測試只加載用戶信息
package com.ding;
import com.ding.dao.IUserDao;
import com.ding.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.Date;
import java.util.List;
public class test {
private InputStream in ;
private SqlSessionFactory factory;
private SqlSession session;
private IUserDao userDao;
@Test
public void testFindAll() {
//6.執(zhí)行操作
List<User> users = userDao.findAll();
// for(User user : users) {
// System.out.println("-------每個用戶的內(nèi)容---------");
// System.out.println(user);
// System.out.println(user.getAccounts());
// }
}
@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{
session.commit();
//7.釋放資源
session.close();
in.close();
}
}
測試結(jié)果如下:
我們發(fā)現(xiàn)并沒有加載 Account 賬戶信息琢锋。
Mybatis 緩存
像大多數(shù)的持久化框架一樣辕漂, Mybatis 也提供了緩存策略,通過緩存策略來減少數(shù)據(jù)庫的查詢次數(shù)吴超, 從而提高性能钮热。
Mybatis 中緩存分為一級緩存,二級緩存烛芬。
2.1 Mybatis 一級緩存
2.1.1 證明一級緩存的存在
一級緩存是 SqlSession 級別的緩存隧期,只要 SqlSession 沒有 flush 或 close,它就存在赘娄。
2.1.2 一級緩存的分析
一級緩存是 SqlSession 范圍的緩存仆潮,當(dāng)調(diào)用 SqlSession 的修改,添加遣臼,刪除性置, commit(), close()等方法時揍堰,就會清空一級緩存鹏浅。
第一次發(fā)起查詢用戶 id 為 1 的用戶信息,先去找緩存中是否有 id 為 1 的用戶信息屏歹,如果沒有隐砸,從數(shù)據(jù)庫查
詢用戶信息。
得到用戶信息蝙眶,將用戶信息存儲到一級緩存中季希。
如果 sqlSession 去執(zhí)行 commit 操作(執(zhí)行插入、更新幽纷、刪除)式塌,清空 SqlSession 中的一級緩存,這樣
做的目的為了讓緩存中存儲的是最新的信息友浸,避免臟讀峰尝。
第二次發(fā)起查詢用戶 id 為 1 的用戶信息,先去找緩存中是否有 id 為 1 的用戶信息收恢,緩存中有武学,直接從緩存
中獲取用戶信息。
2.2 Mybatis 二級緩存
二級緩存是 mapper 映射級別的緩存派诬,多個 SqlSession 去操作同一個 Mapper 映射的 sql 語句劳淆,多個
SqlSession 可以共用二級緩存链沼,二級緩存是跨 SqlSession 的默赂。
2.2.1 二級緩存結(jié)構(gòu)圖
首先開啟 mybatis 的二級緩存。
sqlSession1 去查詢用戶信息括勺,查詢到用戶信息會將查詢數(shù)據(jù)存儲到二級緩存中缆八。
如果 SqlSession3 去執(zhí)行相同 mapper 映射下 sql曲掰,執(zhí)行 commit 提交, 將會清空該 mapper 映射下的二
級緩存區(qū)域的數(shù)據(jù)奈辰。
sqlSession2 去查詢與 sqlSession1 相同的用戶信息栏妖, 首先會去緩存中找是否存在數(shù)據(jù),如果存在直接從
緩存中取出數(shù)據(jù)奖恰。
2.2.2 二級緩存的開啟與關(guān)閉
2.2.2.1 第一步:在 SqlMapConfig.xml 文件開啟二級緩存
<settings>
<!-- 開啟二級緩存的支持 -->
<setting name="cacheEnabled" value="true"/>
</settings>
因為 cacheEnabled 的取值默認(rèn)就為 true吊趾,所以這一步可以省略不配置。為 true 代表開啟二級緩存瑟啃;為
false 代表不開啟二級緩存论泛。
2.2.2.2 第二步:配置相關(guān)的 Mapper 映射文件
<cache>標(biāo)簽表示當(dāng)前這個 mapper 映射將使用二級緩存,區(qū)分的標(biāo)準(zhǔn)就看 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>
2.2.2.3 第三步: 配置 statement 上面的 useCache 屬性
<!-- 根據(jù) id 查詢 -->
<select id="findById" resultType="user" parameterType="int" useCache="true">
select * from user where id = #{uid}
</select>
將 UserDao.xml 映射文件中的<select>標(biāo)簽中設(shè)置 useCache=”true”代表當(dāng)前這個 statement 要使用
二級緩存屁奏,如果不使用二級緩存可以設(shè)置為 false。
注意: 針對每次查詢都需要最新的數(shù)據(jù) sql错负,要設(shè)置成 useCache=false坟瓢,禁用二級緩存。
2.2.4 二級緩存注意事項
當(dāng)我們在使用二級緩存時犹撒,所緩存的類一定要實現(xiàn) java.io.Serializable 接口折联,這種就可以使用序列化
方式來保存對象。
Mybatis 注解開發(fā)
3.1 mybatis 的常用注解說明
@Insert:
實現(xiàn)新增
@Update:
實現(xiàn)更新
@Delete:
實現(xiàn)刪除
@Select:
實現(xiàn)查詢
@Result:
實現(xiàn)結(jié)果集封裝
@Results:
可以與@Result 一起使用识颊,封裝多個結(jié)果集
@ResultMap:
實現(xiàn)引用@Results 定義的封裝
@One:
實現(xiàn)一對一結(jié)果集封裝
@Many:
實現(xiàn)一對多結(jié)果集封裝
@SelectProvider:
實現(xiàn)動態(tài) SQL 映射
@CacheNamespace:
實現(xiàn)注解二級緩存的使用
3.2 使用 Mybatis 注解實現(xiàn)基本 CRUD
3.2.1 編寫實體類
注意:
此處我們故意和數(shù)據(jù)庫表的列名不一致崭庸。
package com.ding.domain;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
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 + '\'' +
'}';
}
}
3.2.2 使用注解方式開發(fā)持久層接口
package com.ding.dao;
import com.ding.domain.Account;
import com.ding.domain.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
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();
/**
* 根據(jù) 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);
/**
* 查詢使用聚合函數(shù)
* @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 映射文件了谊囚。
3.2.3 編寫 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>
<!--使用typeAliases配置別名怕享,它只能配置domain中類的別名 -->
<typeAliases>
<package name="com.ding.domain"></package>
</typeAliases>
<!-- 配置 mybatis 的環(huán)境 -->
<environments default="mysql">
<!-- 配置 mysql 的環(huán)境 -->
<environment id="mysql">
<!-- 配置事務(wù)的類型 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置連接數(shù)據(jù)庫的信息:用的是數(shù)據(jù)源(連接池) -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 配置映射信息 -->
<mappers>
<!-- 配置 dao 接口的位置,它有兩種方式
第一種:使用 mapper 標(biāo)簽配置 class 屬性
第二種:使用 package 標(biāo)簽镰踏,直接指定 dao 接口所在的包
-->
<package name="com.ding.dao"/>
</mappers>
</configuration>
3.2.4 編寫測試方法
package com.ding;
import com.ding.dao.IUserDao;
import com.ding.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.Date;
import java.util.List;
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("北京市順義區(qū)");
user.setUserBirthday(new Date());
int res = userDao.saveUser(user);
System.out.println("影響數(shù)據(jù)庫記錄的行數(shù): "+res);
System.out.println("插入的主鍵值: "+user.getUserId());
}
/**
* 測試更新
*/
@Test
public void testUpdate() {
User user = userDao.findById(43);
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);
}
/**
* 測試查詢使用聚合函數(shù)
*/
@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.創(chuàng)建工廠
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
factory = builder.build(in);
//3.創(chuàng)建 session
session = factory.openSession();
//4.創(chuàng)建代理對象
userDao = session.getMapper(IUserDao.class);
}
@After//junit 的注解
public void destroy()throws Exception {
//提交事務(wù)
session.commit();
//釋放資源
session.close();
//關(guān)閉流
in.close();
}
}
3.3 使用注解實現(xiàn)復(fù)雜關(guān)系映射開發(fā)
實現(xiàn)復(fù)雜關(guān)系映射之前我們可以在映射文件中通過配置<resultMap>來實現(xiàn)函筋, 在使用注解開發(fā)時我們需要借
助@Results 注解, @Result 注解奠伪, @One 注解跌帐, @Many 注解。
3.3.1 復(fù)雜關(guān)系映射的注解說明
@Results 注解
代替的是標(biāo)簽<resultMap>
該注解中可以使用單個@Result 注解绊率,也可以使用@Result 集合
@Results( {@Result()谨敛, @Result() })或@Results( @Result())
@Resutl 注解
代替了 <id>標(biāo)簽和<result>標(biāo)簽
@Result 中 屬性介紹:
- id 是否是主鍵字段
- column 數(shù)據(jù)庫的列名
- property 需要裝配的屬性名
- one 需要使用的@One 注解( @Result( one=@One)()))
- many 需要使用的@Many 注解( @Result( many=@many)()))
@One 注解(一對一)
代替了<assocation>標(biāo)簽,是多表查詢的關(guān)鍵滤否,在注解中用來指定子查詢返回單一對象脸狸。
@One 注解屬性介紹:
select 指定用來多表查詢的 sqlmapper
fetchType 會覆蓋全局的配置參數(shù) lazyLoadingEnabled。。
使用格式:
@Result(column=" ",property="",one=@One(select=""))
@Many 注解(多對一)
代替了<Collection>標(biāo)簽,是是多表查詢的關(guān)鍵炊甲,在注解中用來指定子查詢返回對象集合泥彤。
注意:聚集元素用來處理“一對多”的關(guān)系。需要指定映射的 Java 實體類的屬性卿啡,屬性的 javaType (一般為 ArrayList)但是注解中可以不定義吟吝;
使用格式:
@Result(property="",column="",many=@Many(select=""))
3.3.2 使用注解實現(xiàn)一對一復(fù)雜關(guān)系映射及延遲加載
需求:
加載賬戶信息時并且加載該賬戶的用戶信息,根據(jù)情況可實現(xiàn)延遲加載颈娜。(注解方式實現(xiàn))
3.3.2.1 添加 Account 實體類
package com.ding.domain;
import java.io.Serializable;
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 +
'}';
}
}
3.3.2.2 添加賬戶的持久層接口并使用注解配置
package com.ding.dao;
import com.ding.domain.Account;
import com.ding.domain.AccountUser;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.mapping.FetchType;
import java.util.List;
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();
}
3.3.2.3 添加用戶的持久層接口并使用注解配置
package com.ding.dao;
import com.ding.domain.Account;
import com.ding.domain.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
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();
/**
* 根據(jù) id 查詢一個用戶
* @param userId
* @return
*/
@Select("select * from user where id = #{uid} ")
@ResultMap("userMap")
User findById(Integer userId);
}
3.3.2.4 測試一對一關(guān)聯(lián)及延遲加載
package com.ding;
import com.ding.dao.IAccountDao;
import com.ding.domain.Account;
import com.ding.domain.AccountUser;
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 org.apache.ibatis.io.Resources;
import java.io.InputStream;
import java.util.List;
public class AccountTest {
private InputStream in ;
private SqlSessionFactory factory;
private SqlSession session;
private IAccountDao accountDao;
@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 的代理對象
accountDao = session.getMapper(IAccountDao.class);
}
@After//在測試方法執(zhí)行完成之后執(zhí)行
public void destroy() throws Exception{
session.commit();
//7.釋放資源
session.close();
in.close();
}
@Test
public void testFindAll1() {
List<Account> accounts = accountDao.findAll();
// for(Account au : accounts) {
// System.out.println(au);
// System.out.println(au.getUser());
// }
}
}
3.3.3 使用注解實現(xiàn)一對多復(fù)雜關(guān)系映射
需求:
查詢用戶信息時,也要查詢他的賬戶列表官辽。使用注解方式實現(xiàn)炕贵。
分析:
一個用戶具有多個賬戶信息,所以形成了用戶(User)與賬戶(Account)之間的一對多關(guān)系野崇。
3.3.3.1 User 實體類加入 List<Account>
package com.ding.domain;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
public class User implements Serializable {
private Integer userId;
private String userName;
private Date userBirthday;
private String userSex;
private String userAddress;
//一對多關(guān)系映射:主表方法應(yīng)該包含一個從表方的集合引用
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 + '\'' +
", accounts=" + accounts +
'}';
}
}
3.3.3.2 編寫用戶的持久層接口并使用注解配置
package com.ding.dao;
import com.ding.domain.Account;
import com.ding.domain.User;
import org.apache.ibatis.annotations.*;
import org.apache.ibatis.mapping.FetchType;
import java.util.List;
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.ding.dao.IAccountDao.findByUid",
fetchType= FetchType.LAZY
)
)
})
List<User> findAll();
}
@Many:
相當(dāng)于<collection>的配置
select 屬性:代表將要執(zhí)行的 sql 語句
fetchType 屬性:代表加載方式称开,一般如果要延遲加載都設(shè)置為 LAZY 的值
3.3.3.3 編寫賬戶的持久層接口并使用注解配置
package com.ding.dao;
import com.ding.domain.Account;
import com.ding.domain.AccountUser;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.mapping.FetchType;
import java.util.List;
public interface IAccountDao {
@Select("select * from account where uid = #{uid} ")
List<Account> findByUid(Integer userId);
}
3.3.3.4 添加測試方法
package com.ding;
import com.ding.dao.IUserDao;
import com.ding.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.Date;
import java.util.List;
public class test {
private InputStream in ;
private SqlSessionFactory factory;
private SqlSession session;
private IUserDao userDao;
@Test
public void testFindAll() {
//6.執(zhí)行操作
List<User> users = userDao.findAll();
// for(User user : users) {
// System.out.println("-------每個用戶的內(nèi)容---------");
// System.out.println(user);
// System.out.println(user.getAccounts());
// }
}
@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{
session.commit();
//7.釋放資源
session.close();
in.close();
}
}
3.4 mybatis 基于注解的二級緩存
3.4.1 在 SqlMapConfig 中開啟二級緩存支持
<!-- 配置二級緩存 -->
<settings>
<!-- 開啟二級緩存的支持 -->
<setting name="cacheEnabled" value="true"/>
</settings>
3.4.2 在持久層接口中使用注解配置二級緩存
/**
*
* <p>Title: IUserDao</p>
* <p>Description: 用戶的持久層接口</p>
* <p>Company: http://www.itheima.com/ </p>
*/
@CacheNamespace(blocking=true)//mybatis 基于注解方式實現(xiàn)配置二級緩存
public interface IUserDao {}