文章目錄
一、今日內容
1、mybatis延遲加載
2、mybatis緩存
3、mybatis註解開發
二、mybatis延遲加載
a、什麼是延遲加載
- 也叫懶加載
- 什麼時候需要,什麼時候去獲取 什麼時候需要該數據,什麼時候執行sql語句去查詢
b、一對一延遲加載
今日回顧一對一級聯查詢寫法,出錯點:
粗心錯誤:
jdbc.properties變量名錯誤(模板忘記更新)
AccountDao.xml的namespace忘記改成AccountDao的全名
AccountDao.xml的路勁寫成了cn.ahpu.domain 應該是dao包下
AccountDao.xml的resultMap寫成了resultType 映射時也有一個屬性名忘記改
day04視頻02很好的回顧
模板樣式代碼總結
mybatis-config.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 resource="jdbc.properties"></properties>
<typeAliases>
<!--掃描整個包 包下所有類取別名爲簡單類名(不區分大小寫)-->
<package name="cn.ahpu.domain"></package>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<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>
<!--引入一個包 就引入了該包中所有接口或xml (動態代理前提也要求dao接口和xml包名一致)-->
<package name="cn.ahpu.dao"></package>
</mappers>
</configuration>
jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatisdb_331
jdbc.username=root
jdbc.password=root
log4j.properties
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE, info
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:\axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
回顧昨日一對一
Account和User一對一 查詢account同時得到對應唯一user
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.ahpu</groupId>
<artifactId>mybatis_day04_1_one2one_lazy</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
</dependencies>
</project>
User.java
public class User {
private Integer id;
private String username;
private String password;
private String sex;
private String address;
private Date birthday;
//省略get/set
}
Account.java
public class Account {
private Integer id;
private String name;
private Float money;
private Integer u_id;
//一個賬戶對應一個用戶
private User user;
//省略get/set
}
AccountDao.java
// 查詢所有賬戶 賬戶包含唯一的用戶信息
public List<Account> findAll();
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">
<!--
動態代理 namesapce必須是dao接口全限定名
mybatis-config.xml中配置了typeAliases 引入整個包 因而參數全限定名可以簡寫
-->
<mapper namespace="cn.ahpu.dao.AccountDao">
<resultMap id="accounts" type="account">
<id column="id" property="id"></id>
<result column="name" property="name"></result>
<result column="money" property="money"></result>
<result column="u_id" property="u_id"></result>
<!--配置唯一的user屬性-->
<!--原來的配置-->
<association property="user" javaType="User">
<id column="uid" property="id"></id>
<result column="uname" property="username"></result>
<result column="password" property="password"></result>
<result column="sex" property="sex"></result>
<result column="address" property="address"></result>
<result column="birthday" property="birthday"></result>
</association>
</resultMap>
<select id="findAll" resultMap="accounts">
select * from account,user where u_id=uid
</select>
</mapper>
TestMybatisOne2OneLazy.java
@Test
public void testFindAll(){
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSession sqlSession = new SqlSessionFactoryBuilder().build(inputStream).openSession();
AccountDao accountDao = sqlSession.getMapper(AccountDao.class);
List<Account> list = accountDao.findAll();
for (Account account : list) {
System.out.println(account);
}
sqlSession.close();
}
改進一對一
Account裏只查詢Account表的信息,查詢User表的任務交給UserDao,Account裏引入下方法全名即可
每個xml裏只有本張表的映射配置了 完全隔離開了 不用怕多表查詢兩張表列名相同的衝突了
association 配法有變,加兩個屬性,具體字段便不用配置了
(Account表裏u_id,可以據此來)
UserDao
//根據id查詢用戶
public User findById(Integer id);
UserDao.xml
<mapper namespace="cn.ahpu.dao.UserDao">
<resultMap id="usero" type="user">
<!--沒辦法 屬性名和列名不一致 必須配-->
<id column="uid" property="id"></id>
<result column="uname" property="username"></result>
</resultMap>
<select id="findById" parameterType="int" resultMap="usero">
select * from user where uid=#{id}
</select>
</mapper>
AccountDao.xml 改進配置
<mapper namespace="cn.ahpu.dao.AccountDao">
<resultMap id="accounts" type="account">
<id column="id" property="id"></id>
<result column="name" property="name"></result>
<result column="money" property="money"></result>
<result column="u_id" property="u_id"></result>
<!--配置唯一的user屬性-->
<!--現在改進的配置 新增配置column='外鍵'
column="u_id":指定通過本表哪列查詢對應的用戶對象
select:要執行的方法id 即: mapperId=namespace.id
(看sql可知 user不是在此處查了)
-->
<association property="user" javaType="user" column="u_id" select="cn.ahpu.dao.UserDao.findById"></association>
</resultMap>
<select id="findAll" resultMap="accounts">
select * from account
</select>
</mapper>
一對一延遲加載配置(需要時再獲取 節省資源)
1.必須使用上面改進的association配置法
2.在association裏配置一個fetchType=“lazy”
<mapper namespace="cn.ahpu.dao.AccountDao">
<resultMap id="accounts" type="account">
<id column="id" property="id"></id>
<result column="name" property="name"></result>
<result column="money" property="money"></result>
<result column="u_id" property="u_id"></result>
<!--配置唯一的user屬性-->
<!--fetchType="lazy": lazy:延遲加載 eager:立即加載-->
<association property="user" javaType="user" column="u_id"
select="cn.ahpu.dao.UserDao.findById" fetchType="lazy"></association>
</resultMap>
<select id="findAll" resultMap="accounts">
select * from account
</select>
</mapper>
TestMybatisOne2OneLazy.java
測試方法裏不能直接調用toString了,因爲toString訪問所有屬性,一定會立即加載
@Test
public void testFindAll(){
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSession sqlSession = new SqlSessionFactoryBuilder().build(inputStream).openSession();
AccountDao accountDao = sqlSession.getMapper(AccountDao.class);
List<Account> list = accountDao.findAll();
for (Account account : list) {
System.out.println(account.getName());
System.out.println(account.getUser());
}
sqlSession.close();
}
查看日誌 觀察延遲加載過程
1234564156
2020-01-09 13:00:41,295 1085 [ main] DEBUG cn.ahpu.dao.UserDao.findById - ==> Preparing: select * from user where uid=?
2020-01-09 13:00:41,296 1086 [ main] DEBUG cn.ahpu.dao.UserDao.findById - ==> Parameters: 1(Integer)
2020-01-09 13:00:41,307 1097 [ main] DEBUG cn.ahpu.dao.UserDao.findById - <== Total: 1
User{id=1, username='zhangsan', password='zhangsan', sex='男', address='蜀山', birthday=Wed Jan 29 20:48:42 CST 2020}
242415
2020-01-09 13:00:41,311 1101 [ main] DEBUG cn.ahpu.dao.UserDao.findById - ==> Preparing: select * from user where uid=?
2020-01-09 13:00:41,311 1101 [ main] DEBUG cn.ahpu.dao.UserDao.findById - ==> Parameters: 2(Integer)
2020-01-09 13:00:41,314 1104 [ main] DEBUG cn.ahpu.dao.UserDao.findById - <== Total: 1
User{id=2, username='lisi', password='lisi', sex='男', address='天墉城', birthday=Sat Feb 01 20:48:46 CST 2020}
24235698
2020-01-09 13:00:41,314 1104 [ main] DEBUG cn.ahpu.dao.UserDao.findById - ==> Preparing: select * from user where uid=?
2020-01-09 13:00:41,314 1104 [ main] DEBUG cn.ahpu.dao.UserDao.findById - ==> Parameters: 10(Integer)
2020-01-09 13:00:41,315 1105 [ main] DEBUG cn.ahpu.dao.UserDao.findById - <== Total: 1
User{id=10, username='天河', password='tianhe', sex='男', address='青鸞峯', birthday=Tue Jan 07 16:15:23 CST 2020}
2020-01-09 13:00:41,315 1105 [ main] DEBUG ansaction.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@544a2ea6]
2020-01-09 13:00:41,316 1106 [ main] DEBUG ansaction.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@544a2ea6]
2020-01-09 13:00:41,316 1106 [ main] DEBUG source.pooled.PooledDataSource - Returned connection 1414147750 to pool.
c、一對多延遲加載(也用改進的collection分離配置)
還是用戶對賬戶一對多 一個用戶對應多個賬戶 查詢所有用戶 同時查詢到每個用戶下的多個賬戶
改進的collection分離配置,同上面一對一的改進,account的查詢分離出來交給accountdao自己去查,userdao裏引入一下即可
Account.java 不再有User對象屬性
此處一對多一方寫集合 多方不寫對象
public class Account {
private Integer id;
private String name;
private Float money;
private Integer u_id;
}
User.java 加上Account集合
此處一對多一方寫集合 多方不寫對象
public class User {
private Integer id;
private String username;
private String password;
private String sex;
private String address;
private Date birthday;
//一個用戶對應多個賬戶
private List<Account> accounts;
}
UserDao.java
//查詢所有用戶:包含其下所有賬戶
public List<User> findAll();
AccountDao.java
//根據主人用戶id(u_id=uid) 查詢所有賬戶
public List<Account> findByUid(Integer uid);
AccountDao.xml
<mapper namespace="cn.ahpu.dao.AccountDao">
<select id="findByUid" resultType="account" parameterType="int">
select * from account where u_id=#{uid}
</select>
</mapper>
UserDao.xml ★
<mapper namespace="cn.ahpu.dao.UserDao">
<resultMap id="users" type="user">
<id column="uid" property="id"></id>
<result column="uname" property="username"></result>
<result column="password" property="password"></result>
<result column="sex" property="sex"></result>
<result column="address" property="address"></result>
<result column="birthday" property="birthday"></result>
<!--映射 List<Account> accounts 改進寫法 account的查詢交給accountdao自己
column:寫本表的uid屬性 (拿着本表uid去匹配account表的外鍵u_id)
select:引入accountdao中對應的查詢方法
fetchType="lazy":延遲加載
-->
<collection property="accounts" ofType="account" column="uid"
select="cn.ahpu.dao.AccountDao.findByUid" fetchType="lazy"></collection>
</resultMap>
<select id="findAll" resultMap="users">
select * from user
</select>
</mapper>
TestMybatisOne2ManyLazy 測試多對多改進collection配置與延遲加載
@Test
public void testFindAll(){
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSession sqlSession = new SqlSessionFactoryBuilder().build(inputStream).openSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> list = userDao.findAll();
for (User user : list) {
System.out.println(user.getUsername());
//System.out.println(user.getAccounts());//不寫就不會去查 account表
}
sqlSession.close();
}
d、開啓全局的延遲加載
無需一個個在collection或association中配置fetchType=“lazy”,直接開啓全局懶加載
mybatis-config.xml 里加上全局配置
<!--核心配置文件全局設置-->
<settings>
<!--開啓全局延遲加載-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
注意xml裏屬性的順序(,表示順序 ?表示1個或0個)
<!ELEMENT configuration (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?)>
配置完成後collection裏的fetchType="lazy"可以刪了,測試仍然有延遲加載的效果
配置完畢後默認懶加載 若某個方法需要立即加載可以仍在collection或association中配置:fetchType=“eager”
進行立即加載
三、mybatis緩存
簡單理解:執行過的sql語句不會重複執行,直接到內存裏拿數據
a、一級緩存(不需要額外的配置或代碼 默認存在)
單表查詢所有user對象爲例 驗證一級緩存的存在
UserDao.java
public List<User> findAll();
UserDao.xml
<mapper namespace="cn.ahpu.dao.UserDao">
<resultMap id="usero" type="user">
<id column="uid" property="id"></id>
<result column="uname" property="username"></result>
</resultMap>
<select id="findAll" resultMap="usero">
select * from user
</select>
</mapper>
TestMybatisCache 測試
/**
* 一級緩存
* 在同一sqlSession對象範圍下,兩次執行同一個sql語句,第二次沒有執行sql語句 說明一級緩存的存在
*
* 一級緩存執行流程
* 第一次執行sql語句,查詢到數據,會在一級緩存中存儲sql語句和數據:以sql語句爲key,數據爲map存儲於map中
* 則第二次執行sql時,會先從緩存中查詢,意sql語句爲key查詢,得到數據直接返回。若沒有相應key,則去查數據庫
*/
@Test
public void testFindAll(){
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");
//sqlSessionFactory 單例模式
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao userDao1 = sqlSession.getMapper(UserDao.class);
List<User> list1 = userDao1.findAll();
for (User user : list1) {
System.out.println(user);
}
System.out.println("=====================================================");
//兩次同一個sqlSession
UserDao userdao2 = sqlSession.getMapper(UserDao.class);
List<User> list2 = userdao2.findAll();
for (User user : list2) {
System.out.println(user);
}
sqlSession.close();
}
2020-01-09 16:15:03,599 668 [ main] DEBUG source.pooled.PooledDataSource - Created connection 428910174.
2020-01-09 16:15:03,599 668 [ main] DEBUG ansaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1990a65e]
2020-01-09 16:15:03,601 670 [ main] DEBUG cn.ahpu.dao.UserDao.findAll - ==> Preparing: select * from user
2020-01-09 16:15:03,687 756 [ main] DEBUG cn.ahpu.dao.UserDao.findAll - ==> Parameters:
2020-01-09 16:15:03,719 788 [ main] DEBUG cn.ahpu.dao.UserDao.findAll - <== Total: 5
User{id=1, username='zhangsan', password='zhangsan', sex='男', address='蜀山', birthday=Wed Jan 29 20:48:42 CST 2020}
User{id=2, username='lisi', password='lisi', sex='男', address='天墉城', birthday=Sat Feb 01 20:48:46 CST 2020}
User{id=10, username='天河', password='tianhe', sex='男', address='青鸞峯', birthday=Tue Jan 07 16:15:23 CST 2020}
User{id=17, username='jerry', password='jerry', sex='女', address='北京', birthday=Tue Jan 07 18:17:00 CST 2020}
User{id=18, username='小明', password='jerry', sex='男', address='北京', birthday=Tue Jan 07 23:18:39 CST 2020}
=====================================================
User{id=1, username='zhangsan', password='zhangsan', sex='男', address='蜀山', birthday=Wed Jan 29 20:48:42 CST 2020}
User{id=2, username='lisi', password='lisi', sex='男', address='天墉城', birthday=Sat Feb 01 20:48:46 CST 2020}
User{id=10, username='天河', password='tianhe', sex='男', address='青鸞峯', birthday=Tue Jan 07 16:15:23 CST 2020}
User{id=17, username='jerry', password='jerry', sex='女', address='北京', birthday=Tue Jan 07 18:17:00 CST 2020}
User{id=18, username='小明', password='jerry', sex='男', address='北京', birthday=Tue Jan 07 23:18:39 CST 2020}
2020-01-09 16:15:03,723 792 [ main] DEBUG ansaction.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1990a65e]
2020-01-09 16:15:03,725 794 [ main] DEBUG ansaction.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1990a65e]
2020-01-09 16:15:03,725 794 [ main] DEBUG source.pooled.PooledDataSource - Returned connection 428910174 to pool.
執行增刪改與提交操作都會清空緩存
@Test
public void testFindAll(){
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");
//sqlSessionFactory 單例模式
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao userDao1 = sqlSession.getMapper(UserDao.class);
List<User> list1 = userDao1.findAll();
for (User user : list1) {
System.out.println(user);
}
//兩次同一個sqlSession
UserDao userdao2 = sqlSession.getMapper(UserDao.class);
userdao2.delete(19);
sqlSession.commit();
//主動清空緩存
//sqlSession.clearCache();
System.out.println("=====================================================");
//兩次同一個sqlSession
UserDao userdao3 = sqlSession.getMapper(UserDao.class);
List<User> list3 = userdao3.findAll();
for (User user : list3) {
System.out.println(user);
}
sqlSession.close();
}
2020-01-09 16:10:24,150 1320 [ main] DEBUG ansaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1990a65e]
2020-01-09 16:10:24,170 1340 [ main] DEBUG cn.ahpu.dao.UserDao.findAll - ==> Preparing: select * from user
2020-01-09 16:10:24,193 1363 [ main] DEBUG cn.ahpu.dao.UserDao.findAll - ==> Parameters:
2020-01-09 16:10:24,236 1406 [ main] DEBUG cn.ahpu.dao.UserDao.findAll - <== Total: 6
User{id=1, username='zhangsan', password='zhangsan', sex='男', address='蜀山', birthday=Wed Jan 29 20:48:42 CST 2020}
User{id=2, username='lisi', password='lisi', sex='男', address='天墉城', birthday=Sat Feb 01 20:48:46 CST 2020}
User{id=10, username='天河', password='tianhe', sex='男', address='青鸞峯', birthday=Tue Jan 07 16:15:23 CST 2020}
User{id=17, username='jerry', password='jerry', sex='女', address='北京', birthday=Tue Jan 07 18:17:00 CST 2020}
User{id=18, username='小明', password='jerry', sex='男', address='北京', birthday=Tue Jan 07 23:18:39 CST 2020}
User{id=19, username='小王', password='jerry', sex='男', address='北京', birthday=Tue Jan 07 23:18:58 CST 2020}
2020-01-09 16:10:24,251 1421 [ main] DEBUG cn.ahpu.dao.UserDao.delete - ==> Preparing: delete from user where uid=?
2020-01-09 16:10:24,251 1421 [ main] DEBUG cn.ahpu.dao.UserDao.delete - ==> Parameters: 19(Integer)
2020-01-09 16:10:24,271 1441 [ main] DEBUG cn.ahpu.dao.UserDao.delete - <== Updates: 1
2020-01-09 16:10:24,271 1441 [ main] DEBUG ansaction.jdbc.JdbcTransaction - Committing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1990a65e]
=====================================================
2020-01-09 16:10:24,279 1449 [ main] DEBUG cn.ahpu.dao.UserDao.findAll - ==> Preparing: select * from user
2020-01-09 16:10:24,279 1449 [ main] DEBUG cn.ahpu.dao.UserDao.findAll - ==> Parameters:
2020-01-09 16:10:24,285 1455 [ main] DEBUG cn.ahpu.dao.UserDao.findAll - <== Total: 5
User{id=1, username='zhangsan', password='zhangsan', sex='男', address='蜀山', birthday=Wed Jan 29 20:48:42 CST 2020}
User{id=2, username='lisi', password='lisi', sex='男', address='天墉城', birthday=Sat Feb 01 20:48:46 CST 2020}
User{id=10, username='天河', password='tianhe', sex='男', address='青鸞峯', birthday=Tue Jan 07 16:15:23 CST 2020}
User{id=17, username='jerry', password='jerry', sex='女', address='北京', birthday=Tue Jan 07 18:17:00 CST 2020}
User{id=18, username='小明', password='jerry', sex='男', address='北京', birthday=Tue Jan 07 23:18:39 CST 2020}
2020-01-09 16:10:24,285 1455 [ main] DEBUG ansaction.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1990a65e]
2020-01-09 16:10:24,286 1456 [ main] DEBUG ansaction.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1990a65e]
2020-01-09 16:10:24,286 1456 [ main] DEBUG source.pooled.PooledDataSource - Returned connection 428910174 to pool.
主動清空緩存:
sqlSession.clearCache();
一級緩存
1.在同一sqlSession對象範圍下,兩次執行同一個sql語句,第二次沒有執行sql語句 說明一級緩存的存在
2.執行增刪改,提交操作(sqlSession.commit) 會清空緩存
3.sqlSession.clearCache(); 清空緩存
4.一級緩存是session級別的 兩個sqlSession之間是沒有一級緩存可言的(因而close是更換session,並不是簡單清空緩存)
一級緩存執行流程
第一次執行sql語句,查詢到數據,會在一級緩存中存儲sql語句和數據:以sql語句爲key,數據爲map存儲於map中
則第二次執行sql時,會先從緩存中查詢,意sql語句爲key查詢,得到數據直接返回。若沒有相應key,則去查數據庫
b.二級緩存(需要額外的修改代碼與加配置 基本不用)
二級緩存
1.是sessionFactory級別的 工廠只有一個 也即:它可以在多個sqlSession對象之間共享緩存數據
2.默認是開啓的
3.在需要使用二級緩存的配置映射文件中開啓
4.需要在二級緩存中保存的pojo對象必須實現序列化接口: User implements Serializable (說明二級緩存是寫到外存的)
5.在同一個namespace範圍下,執行提交操作,會清空該namespace的緩存(坑)
eg:多表關聯查詢,第一次查詢A關聯得到B,然後B執行修改清空了緩存(或者說B的數據變了),A一直執行查詢
A一直沒變,那麼namespaceA.sqlAB還在緩存中,B卻早已變了,查詢便出錯了 !巨大的坑!知道坑 用時小心!
好在開發中二級緩存一般不用,都是自己手寫
工作流程
1.在任意一個sqlSession對象中執行了查詢語句,當關閉了sqlSession對象時(說明必須關閉纔有緩存),在二級緩存中保存數據
保存數據:以namespace.sql語句爲key值,以對象爲value進行存儲
2.當其他sqlSession對象執行sql時,根據namespace.sql語句查詢緩存,命中直接取數據,否則纔去讀數據庫
仍然以查詢所有用戶爲例:
相對於上面的修改:
UserDao.xml
User.java
TestMybatisCache2 測試二級緩存的存在
/**
* 二級緩存
* 1.是sessionFactory級別的 工廠只有一個 也即:它可以在多個sqlSession對象之間共享緩存數據
* 2.默認是開啓的
* 3.在需要使用二級緩存的配置映射文件中開啓 <cache/>
* 4.需要在二級緩存中保存的pojo對象必須實現序列化接口: User implements Serializable
*
* 二級緩存的工作流程
* 1.在任意一個sqlSession對象中執行了查詢語句,當關閉了sqlSession對象時(說明必須關閉纔有緩存),在二級緩存中保存數據
* 保存數據:以namespace.sql語句爲key值,以對象爲value進行存儲
* 2.當其他sqlSession對象執行sql時,根據namespace.sql語句查詢緩存,命中直接取數據,否則纔去讀數據庫
*/
@Test
public void testFindAll(){
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");
//sqlSessionFactory 單例模式
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao userDao1 = sqlSession.getMapper(UserDao.class);
List<User> list1 = userDao1.findAll();
for (User user : list1) {
System.out.println(user);
}
sqlSession.close();
System.out.println("=====================================================");
//兩次不同sqlSession
SqlSession sqlSession2 = sqlSessionFactory.openSession();
UserDao userdao2 = sqlSession2.getMapper(UserDao.class);
List<User> list2 = userdao2.findAll();
for (User user : list2) {
System.out.println(user);
}
sqlSession.close();
}
兩個session,一次select語句,還計算出了二級緩存命中率
2020-01-09 16:41:13,880 1788 [ main] DEBUG ansaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@67b467e9]
2020-01-09 16:41:13,882 1790 [ main] DEBUG cn.ahpu.dao.UserDao.findAll - ==> Preparing: select * from user
2020-01-09 16:41:13,916 1824 [ main] DEBUG cn.ahpu.dao.UserDao.findAll - ==> Parameters:
2020-01-09 16:41:13,972 1880 [ main] DEBUG cn.ahpu.dao.UserDao.findAll - <== Total: 5
User{id=1, username='zhangsan', password='zhangsan', sex='男', address='蜀山', birthday=Wed Jan 29 20:48:42 CST 2020}
User{id=2, username='lisi', password='lisi', sex='男', address='天墉城', birthday=Sat Feb 01 20:48:46 CST 2020}
User{id=10, username='天河', password='tianhe', sex='男', address='青鸞峯', birthday=Tue Jan 07 16:15:23 CST 2020}
User{id=17, username='jerry', password='jerry', sex='女', address='北京', birthday=Tue Jan 07 18:17:00 CST 2020}
User{id=18, username='小明', password='jerry', sex='男', address='北京', birthday=Tue Jan 07 23:18:39 CST 2020}
2020-01-09 16:41:14,042 1950 [ main] DEBUG ansaction.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@67b467e9]
2020-01-09 16:41:14,043 1951 [ main] DEBUG ansaction.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@67b467e9]
2020-01-09 16:41:14,043 1951 [ main] DEBUG source.pooled.PooledDataSource - Returned connection 1739876329 to pool.
=====================================================
2020-01-09 16:41:14,112 2020 [ main] DEBUG cn.ahpu.dao.UserDao - Cache Hit Ratio [cn.ahpu.dao.UserDao]: 0.5(緩存命中率0.5)
User{id=1, username='zhangsan', password='zhangsan', sex='男', address='蜀山', birthday=Wed Jan 29 20:48:42 CST 2020}
User{id=2, username='lisi', password='lisi', sex='男', address='天墉城', birthday=Sat Feb 01 20:48:46 CST 2020}
User{id=10, username='天河', password='tianhe', sex='男', address='青鸞峯', birthday=Tue Jan 07 16:15:23 CST 2020}
User{id=17, username='jerry', password='jerry', sex='女', address='北京', birthday=Tue Jan 07 18:17:00 CST 2020}
User{id=18, username='小明', password='jerry', sex='男', address='北京', birthday=Tue Jan 07 23:18:39 CST 2020}
四、mybatis註解開發(★★ 簡單到懷疑人生 今後寫法)
a、實現增刪改查
連xxxDao.xml都沒有了,只有UserDao接口+註解和測試類了
沒有dao對應的配置文件了‘’
根目錄下3個配置文件和User.java 和之前一樣固定不變
只用寫UserDao接口和TestMtbatisCRUD測試類
mybatis-config.xml 核心配置文件中的取別名也可以刪了:
UserDao.java
package cn.ahpu.dao;
import cn.ahpu.domain.User;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
/**
* @author 寒面銀槍
* @create 2020-01-09 17:18
*/
public interface UserDao {
/**
* 查詢所有用戶
* @return
*/
@Select("select * from user")
public List<User> findAll();
/**
* 根據id查詢
* @return
*/
@Select("select * from user where uid=#{id}")
public User findById(Integer id);
/**
* 根據姓名模糊查詢
* @param username
* @return
*/
//此處寫的sql語句內只能是雙引號 不能寫單引號
@Select("select * from user where uname like \"%\"#{username}\"%\"")
public List<User> findByUsername(String username);
/**
* 查詢總記錄數
* @return
*/
@Select("select count(*) from user")
public Integer findTotalCount();
/**
* 添加用戶
* @param user
*/
@Insert("insert into user values(null,#{username},#{password},#{sex},#{address},#{birthday})")
public void insert(User user);
/**
* 更新用戶
* @param user
*/
@Update("update user set uname=#{username},password=#{password},sex=#{sex}," +
"address=#{address},birthday=#{birthday} where uid=#{id}")
public void update(User user);
/**
* 根據id刪除用戶
* @param id
*/
@Delete("delete from user where uid=#{id}")
public void delete(Integer id);
}
TestMtbatisCRUD
package cn.ahpu;
import cn.ahpu.dao.UserDao;
import cn.ahpu.domain.User;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
/**
* @author 寒面銀槍
* @create 2020-01-09 18:19
*/
public class TestMtbatisCRUD {
@Test
public void testFindAll(){
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> list = userDao.findAll();
for (User user : list) {
System.out.println(user);
}
sqlSession.close();
}
@Test
public void testFindById(){
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
User user = userDao.findById(18);
System.out.println(user);
sqlSession.close();
}
@Test
public void testFindByUsername(){
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> list = userDao.findByUsername("s");
for (User user : list) {
System.out.println(user);
}
sqlSession.close();
}
@Test
public void testFindTotalCount(){
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
Integer totalCount = userDao.findTotalCount();
System.out.println(totalCount);
sqlSession.close();
}
@Test
public void testInsert(){
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
User user = new User();
user.setUsername("mayun");user.setAddress("杭州");user.setSex("男");
user.setPassword("123");user.setBirthday(new Date());
userDao.insert(user);
sqlSession.commit();
sqlSession.close();
}
}
註解配置結果集映射
表列名與類屬性名不同,即結果集映射,也用註解完成
/**
* 查詢所有用戶
* @return
*
* @Results:映射結果集
* @Result:映射表列名和屬性名不一樣的列
* id=true:表示id是主鍵
* 註解中只需要寫:列名與屬性名不一致的即可 (不像xml中那樣有時需要全寫)(唯一特殊見四、b、結果集映射特殊情況 用超過1次就必須配置)
*/
@Select("select * from user")
@Results({
@Result(id = true, column = "uid", property = "id"),
@Result(column = "uname", property = "username")
})
public List<User> findAll();
主鍵回顯(執行插入後未提交事務前獲取插入記錄的主鍵)
mysql中獲取主鍵無意義 oracle中有作用
註解方式:
/**
* 添加用戶
* @param user
*/
//保存後獲取主鍵 (執行insert代碼後就可以獲取主鍵)
//keyProperty:主鍵屬性名 keyColumn:主鍵列名 resultType:主鍵類型
// before=false:非添加之前查詢,也即是添加之後可以查詢主鍵
// before=true:添加之前可查詢主鍵 (可惜mysql不支持 oracle支持)
//statement:需要執行的sql語句
//select last_insert_id():最後一次執行添加生成的主鍵 (也即是要查詢的主鍵)
@SelectKey(keyProperty = "id",keyColumn = "uid",resultType = Integer.class,before = false,
statement = "select last_insert_id()")
@Insert("insert into user values(null,#{username},#{password},#{sex},#{address},#{birthday})")
public void insert(User user);
xml方式
<insert id="save" parameterType="user">
<selectKey keyColumn="uid" keyProperty="id" resultType="int" order="AFTER">
select last_insert_id()
</selectKey>
insert into user values(null,#{username},#{password},#{sex},#{address},#{birthday})
</insert>
TestMtbatisCRUD 測試代碼:
@Test
public void testInsert(){
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
User user = new User();
user.setUsername("mayun");user.setAddress("杭州");user.setSex("男");
user.setPassword("123");user.setBirthday(new Date());
userDao.insert(user);
//不加註解此處獲取不了主鍵 (爲何commit之前 注意3大框架整合後不能輕易提交 一提交就不能再用session 萬一延遲加載 有些數據就永遠丟了)
System.out.println(user.getId());
sqlSession.commit();
sqlSession.close();
}
b、實現一對一
賬戶與用戶一對一 查詢賬戶同時查詢其唯一對應的用戶
domain:
User.java
public class User {
private Integer id;
private String username;
private String password;
private String sex;
private String address;
private Date birthday;
}
Account.java
public class Account {
private Integer id;
private String name;
private Float money;
private Integer u_id;
//一個賬戶對應一個用戶
private User user;
}
UserDao.java
public interface UserDao {
//一對一 account那裏傳過來的就是user主鍵
@Select("select * from user where uid=#{id}")
//可憐表列名還是與pojo屬性名不一致
@Results({
@Result(id=true,property = "id",column = "uid"),
@Result(property = "username",column = "uname")
})
public User findById(Integer id);
}
AccountDao.java ★ 註解
public interface AccountDao {
/**
* 查詢所有賬戶 包含對應唯一用戶
* @return
*/
@Select("select * from account")
//屬性與列不一致就得寫結果映射 user肯定沒有匹配列 自然要寫結果映射
//one=@One(select = "",fetchType="") 屬性是單個對應
//many:屬性是多個對應(一對多中的一方寫這個)
//fetchType = FetchType.LAZY:提取方式爲延遲加載 注意位置 (也可以直接在mybatis-config.xml中配置全局的)
@Results({
@Result(property = "user",column = "u_id",javaType = User.class,
one=@One(select = "cn.ahpu.dao.UserDao.findById",fetchType = FetchType.LAZY))
})
public List<Account> findAll();
}
TestMybatisAnnoOne2One 測試類
public class TestMybatisAnnoOne2One {
@Test
public void testFindAll(){
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSession sqlSession = new SqlSessionFactoryBuilder().build(inputStream).openSession();
AccountDao accountDao = sqlSession.getMapper(AccountDao.class);
List<Account> list = accountDao.findAll();
for (Account account : list) {
System.out.println(account.getName()+"|"+account.getMoney()+"元");
System.out.println(account.getUser());
}
sqlSession.close();
}
}
結果集映射特殊情況
之前說過,註解開發只用寫列名和屬性名不同的映射,其他不用寫,但若一個列被引用了兩次或兩次以上,則兩次映射都必須寫出來
eg:上面的一對多關聯查詢,外鍵u_id作爲查詢user的參數引用了一次,那麼再在測試代碼中打印輸出account.u_id屬性值就爲null了,除非再加個配置 (u_id可以傳遞參數了 不能輕易刪除 不過java代碼內確實用不到它)
測試代碼輸入u_id
1.AccountDao.java沒有單獨配置u_id映射
2.AccountDao.java中配置u_id映射
c、實現一對多
一個用戶對應多個賬戶 查詢用戶同時要包含其下所有的賬戶
Account.java
public class Account {
private Integer id;
private String name;
private Float money;
private Integer u_id;
User.java
public class User {
private Integer id;
private String username;
private String password;
private String sex;
private String address;
private Date birthday;
private List<Account> accounts;
}
AccountDao.java
public interface AccountDao {
/**
* 根據u_id查詢賬戶
* @param uid
* @return
* #{}此處也可以隨便寫
*/
@Select("select * from account where u_id=#{uid}")
public List<Account> findByUid(Integer uid);
}
UserDao.java ★
1.List屬性的類型可以省略不配 要配也只能配javaType = List.class, 此處,沒有ofType
2.延遲加載同樣也可以直接配置一個全局的
public interface UserDao {
/**
* 查詢所有用戶 包含每個用戶下的所有多個賬戶
* @return
*/
@Select("select * from user")
@Results({
@Result(id=true,property = "id",column = "uid"),
@Result(property = "username",column = "uname"),
//特殊兩處: 1.xml內寫ofType 此處只有javaType就只能配javaType了
// 2.javaType不能list中單個元素的類型 直接寫集合類型List.class
//但其實此處可以省略不配(2個特殊點就都不用記憶了) 下面的javaType = List.class,可以直接刪掉 ^_^
@Result(property = "accounts",column = "uid",javaType = List.class,
many = @Many(select = "cn.ahpu.dao.AccountDao.findByUid",fetchType = FetchType.LAZY))
})
public List<User> findAll();
}
d、動態sql
單表查詢user 姓名模糊查詢 性別等於查詢
動態sql語句太複雜,不可能直接在註解上寫if for等邏輯了,mybatis的處理是提供新標籤:
@SelectProvider(type=UserSqlProvider.class, method=“findByCondition”)
也即是傳入一個類的方法,返回值爲String字符串,也就是讓你自己在java代碼邏輯內拼接好sql傳回來
好處:多複雜的sql語句都不用怕了,java字符串拼接對程序員來說太簡單了
User.java
public class User {
private Integer id;
private String username;
private String password;
private String sex;
private String address;
private Date birthday;
}
UserDao.java 註解★
public interface UserDao {
/**
* 根據姓名模糊查詢,性別等於查詢
* @return
*/
//@Select("select * from user where uname like \"%\"#{username}\"%\" and sex=#{sex}")
//一個字符串內寫if for語句不大現實 換用新的標籤
// SelectProvider: sql語句提供者
// 屬性; type:sql語句提供者的類字節碼(.class)
// method:提供類中的方法
@SelectProvider(type=UserSqlProvider.class, method="findByCondition")
@Results({
@Result(id = true, column = "uid", property = "id"),
@Result(column = "uname", property = "username")
})
public List<User> findByCondition(User user);
}
UserSqlProvider.java ☆
public class UserSqlProvider {
//方法名和提供對象取得一致吧 好對應 參數user是dao中方法的參數 直接寫 會自動傳進來的
public String findByCondition(User user){
//經常修改用String不合適 要想線程安全也必須用StringBuffer
StringBuffer sb=new StringBuffer();
sb.append("select * from user where 1=1 ");//注意空格
if(user.getUsername()!=null){
sb.append(" and uname like \"%\"#{username}\"%\" ");
}
if(user.getSex()!=null){
sb.append(" and sex=#{sex} ");
}
return sb.toString();
//在這裏拼接sql無論多複雜都能做好了 java代碼字符串拼接太熟悉了
//唯一注意拼接時的空格 最好前後都多加空格
}
}
TestMtbatisCRUD 測試方法類
@Test
public void testFindByCondition(){
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao userDao = sqlSession.getMapper(UserDao.class);
User user = new User();
//user.setUsername("s");
user.setSex("男");
List<User> list = userDao.findByCondition(user);
for (User u : list) {
System.out.println(u);
}
sqlSession.close();
}
mybatis 四天總結
1. mybatis 第一天
自定義框架
mybaits 的入門
2. mybatis 第二天
CRUD : selectList ,selectOne ,insert ,update ,delete
#{ }
${ }
參數類型:簡單類型: ${ value} #{隨便}
pojo類型:${屬性名} #{屬性名}
複雜類型:
map: #{key}
多參數:#{param1} , #{param2}....
傳統模式開發: UserDaoImpl implements UserDao
動態代理開發:UserDao
核心配置文件:properties , typeAliaes , mappers ,settings(延遲加載,二級緩存, 日誌)
返回值類型:
3. mybatis第三天
數據源:type=POOLED(自帶的以後不會用) --- c3p0 ,dbcp ,spring jdbc, druid(阿里出品數據源)
事務:openSession(true | false)
動態的sql語句:if , where , sql片段 , foreach
一對一映射:
一對多映射:
多對多映射:
4. mybatis第四天
延遲加載(以後都用) -- 按需加載
一級緩存: session級別的,
二級緩存:sessionFactory,應用級別的(漏洞 基本不用)
註解開發: (★)