一、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可以從緩存中命中。二級緩存不是默認開啓的,要開啓二級緩存:
- 在全局配置文件中開啓:
<settings>
<!-- 開啓二級緩存的支持 --> <setting name="cacheEnabled" value="true"/>
</settings>
不過這一步由於cacheEnabled 的取值默認就爲 true,所以這一步可以省略不配置
- 配置相關的 Mapper 映射文件
<mapper namespace="com.MybatisDemo6.Dao.AccountDao">
<!-- 開啓二級緩存的支持 -->
<cache></cache>
</mapper>
- 配置 statement 上面的 useCache 屬性
<!-- 根據 id 查詢 -->
<select id="findById" resultType="user" parameterType="int" useCache="true">
select * from user where id = #{uid}
</select>
域是一個mapper的namespace ,同一個namespace中查詢sql可以從緩存中命中。二級緩存不是默認開啓的,要開啓二級緩存:
- 在全局配置文件中開啓:
<settings>
<!-- 開啓二級緩存的支持 --> <setting name="cacheEnabled" value="true"/>
</settings>
不過這一步由於cacheEnabled 的取值默認就爲 true,所以這一步可以省略不配置
- 配置相關的 Mapper 映射文件
<mapper namespace="com.MybatisDemo6.Dao.AccountDao">
<!-- 開啓二級緩存的支持 -->
<cache></cache>
</mapper>
- 配置 statement 上面的 useCache 屬性
<!-- 根據 id 查詢 -->
<select id="findById" resultType="user" parameterType="int" useCache="true">
select * from user where id = #{uid}
</select>
注意:針對每次查詢都需要最新的數據 sql,要設置成 useCache=false,禁用二級緩存