學習資源來自於嗶哩嗶哩UP遇見狂神說,一個寶藏UP大家快去關注吧!記得三連加分享,不要做白嫖黨.
Mybatis-9.28
環境:
- JDK1.8
- Mysql 5.7
- maven 3.6.1
- IDEA
回顧:
- JDBC
- Mysql
- Java基礎
- Maven
- Junit
SSM框架: 配置文件. 最好的方式: 看官網文檔.Mybatis中文文檔
1.簡介
1.1什么是Mybatis
-
MyBatis 是一款優秀的持久層(Dao層)框架
-
它支持自定義 SQL、存儲過程以及高級映射。
-
MyBatis 免除了幾乎所有的 JDBC 代碼以及設置參數和獲取結果集的工作。
-
MyBatis 可以通過簡單的 XML 或注解來配置和映射原始類型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 對象)為數據庫中的記錄。
-
原名iBatis,所以很多包的名字叫做iBatis
-
2013年11月遷移到Github
如何獲得Mybatis?
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
1.2 持久化
數據持久化
- 持久化就是將程序的數據在持久狀態和瞬時狀態轉化過程
- 內存: 斷電即失
- 數據庫(jdbc),io文件持久化
- 生活: 冷藏,罐頭
為什么需要持久化?
-
有一些對象不能丟掉.
-
內存太貴了
1.3 持久化
Dao層,Service層,Controller層...
- 完成持久化工作的代碼塊
- 層界限十分明顯
1.4 為什么需要Mybatis?
-
幫助程序員將數據存入到數據庫中
-
方便
-
傳統的JDBC代碼太復雜了,簡化.框架.自動化
-
不用Mybatis也可以,更容易上手.技術沒有高低之分
-
優點:(百度百科)
- 簡單易學
- 靈活
- sql和代碼的分離,提高了可維護性。
- 提供映射標簽,支持對象與數據庫的orm字段關系映射
- 提供對象關系映射標簽,支持對象關系組建維護
- 提供xml標簽,支持編寫動態sql。
最重要的一點: 使用的人多!
2. 第一個Mybatis程序
思路: 搭建環境 --> 導入Mybatis --> 編寫代碼 --> 測試!
2.1 搭建環境
1.搭建數據庫
CREATE DATABASE `mybatis`;
USE `mybatis`;
CREATE TABLE `user`(
`id` INT(20) NOT NULL,
`name`VARCHAR(20) NOT NULL DEFAULT 0 COMMENT '名字',
`pwd`VARCHAR(20) NOT NULL DEFAULT 123456 COMMENT '密碼',
PRIMARY KEY (`id`)
)ENGINE = INNODB DEFAULT CHARSET = utf8;
INSERT INTO `user`(`id`,`name`,`pwd`) VALUES
(1,'張三','4354453'),
(2,'李四','434534453'),
(3,'王五','4545343')
2.新建項目
-
新建一個普通的maven項目
-
刪掉src目錄
-
導入maven依賴
<!--導入依賴--> <dependencies> <!--mysql驅動--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> <!--junnit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies>
2.2創建一個模塊
-
編寫mybatis的核心配置文件
<?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核心配置環境--> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> </configuration>
-
編寫mybatis工具類
//sqlSessionFactory --> sqlSession public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; static { try { //使用Mybatis第一步,獲取sqlSessionFactory對象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } //既然有了 SqlSessionFactory,顧名思義,我們可以從中獲得 SqlSession 的實例。 // SqlSession 提供了在數據庫執行 SQL 命令所需的所有方法。 public static SqlSession sqlSession() { return sqlSessionFactory.openSession(); } }
2.3 編寫代碼
-
實體類(pojo)
public class User { private int id; private String name; private String pwd; public User() { } public User(int id, String name, String pwd) { this.id = id; this.name = name; this.pwd = pwd; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", pwd='" + pwd + '\'' + '}'; } }
-
Dao接口
//以后取名會用Mapper,這里先用Dao public interface UserDao { List<User> getUserList(); }
-
接口實現類由原來的UserDaoImpl轉變為一個Mapper配置文件
<?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"> <!--namespace綁定一個對應的Dao/Mapper接口--> <mapper namespace="com.tan.dao.UserDao"> <select id="getUserList" resultType="com.tan.pojo.User"> select * from mybatis.user </select> </mapper>
2.4 測試
2.4.1 junit測試,第一種方法getMapper
public class UserDaoTest {
@Test
public void test() {
//第一步: 獲得SqlSession對象
SqlSession sqlSession = MybatisUtils.sqlSession();
//方式一: getMapper
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> userList = userDao.getUserList();
for (User user : userList) {
System.out.println(user);
}
//關閉SqlSession
sqlSession.close();
}
}
結果: 報錯
運行錯誤,報錯糾錯
注意點:
org.apache.ibatis.binding.BindingException: Type interface com.tan.dao.UserDao is not known to the MapperRegistry.
MapperRegistry(映射注冊表)是什么?
每一個Mapper.xml都需要在Mybatis核心配置文件中注冊!(注意這個錯誤的路徑寫法,這樣寫有問題,被這個問題坑了好久)
- maven過濾設置
配置了Mappers之后仍然出錯
從圖中可以看出maven的生成文件中,dao包下的UserMapper.xml並沒有生成,因為maven約定只會生成在resources下的.xml的配置文件,而UserMapper.xml在java代碼包下.所以需要配置一下maven的資源過濾
pom.xml文件中添加一下代碼:
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
寫到這里我仍然出錯,而且仍然是初始化錯誤,和沒設置maven過濾的報錯一樣,找了半天排除了所有的常規錯誤.
問題解決: mybatis-config.xml
的mappers配置中
之前是這樣寫的:
因為IDEA自動提示路徑,所以我一直沒有懷疑這里會有問題.
改成路徑的格式后就正常了
test運行結果:
2.4.2 junit測試,第二種方法,直接執行sqlSession的方法
//方式二:
List<User> userList = sqlSession.selectList("com.tan.dao.UserDao.getUserList");
2.4.3 兩種方法的區別
首先,不建議使用方法二
官網解釋:
3. 總結
主要的7個步驟:
3. CRUD
1. namespace
namespace中的包名要和mapper接口的包名一致!
2. select
選擇,查詢語句;
- id: 對應namespace中的方法名;
- resultType: sql語句執行的返回值.
- parameterType: 參數類型.
-
編寫接口
java//查詢全部用戶 List<User> getUserList();
-
編寫對應的mapper中的
<!--select查詢語句--> <select id="getUserList" resultType="com.tan.pojo.User"> select * from mybatis.user </select>
-
測試
@Test public void getUserById() { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.getUserById(1); System.out.println(user); sqlSession.close(); }
3. insert
<!--對象中的屬性可以直接取出來-->
<insert id="addUser" parameterType="com.tan.pojo.User">
insert into mybatis.user(id, name, pwd)
values (#{id}, #{name}, #{pwd});
</insert>
4. Update
<update id="updateUser" parameterType="com.tan.pojo.User">
update mybatis.user
set name=(#{name}),
pwd=(#{pwd})
where id = (#{id});
</update>
5. Delete
<delete id="deleteUser" parameterType="com.tan.pojo.User">
delete
from mybatis.user
where id = (#{});
</delete>
Test
@Test
public void addUser() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int res = mapper.addUser(new User(7, "asdawd", "afefew"));
if (res > 0) {
System.out.println("插入成功");
}
//提交事務
sqlSession.commit();
sqlSession.close();
}
@Test
public void updateUser() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.updateUser(new User(5,"hello","135468"));
sqlSession.commit();
sqlSession.close();
}
@Test
public void deleteUser() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.deleteUser(4);
sqlSession.commit();
sqlSession.close();
}
注意點:
- 增刪改需要提交事務
運行錯誤,報錯糾錯
在寫的時候突然報了一個錯誤:
java.lang.ExceptionInInitializerError
at com.tan.dao.UserMapperTest.deleteUser(UserMapperTest.java:77)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
Caused by: org.apache.ibatis.exceptions.PersistenceException:
### Error building SqlSession.
### The error may exist in com/tan/dao/UserMapper.xml
### Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. The XML location is 'com/tan/dao/UserMapper.xml'. Cause: org.apache.ibatis.builder.BuilderException: Parsing error was found in mapping #{}. Check syntax #{property|(expression), var1=value1, var2=value2, ...}
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:80)
at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:64)
at com.tan.utils.MybatisUtils.<clinit>(MybatisUtils.java:26)
... 23 more
Caused by: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. The XML location is 'com/tan/dao/UserMapper.xml'. Cause: org.apache.ibatis.builder.BuilderException: Parsing error was found in mapping #{}. Check syntax #{property|(expression), var1=value1, var2=value2, ...}
at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:121)
at org.apache.ibatis.builder.xml.XMLConfigBuilder.parse(XMLConfigBuilder.java:98)
at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:78)
... 25 more
Caused by: org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. The XML location is 'com/tan/dao/UserMapper.xml'. Cause: org.apache.ibatis.builder.BuilderException: Parsing error was found in mapping #{}. Check syntax #{property|(expression), var1=value1, var2=value2, ...}
at org.apache.ibatis.builder.xml.XMLMapperBuilder.configurationElement(XMLMapperBuilder.java:122)
at org.apache.ibatis.builder.xml.XMLMapperBuilder.parse(XMLMapperBuilder.java:94)
at org.apache.ibatis.builder.xml.XMLConfigBuilder.mapperElement(XMLConfigBuilder.java:374)
at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:119)
... 27 more
Caused by: org.apache.ibatis.builder.BuilderException: Parsing error was found in mapping #{}. Check syntax #{property|(expression), var1=value1, var2=value2, ...}
at org.apache.ibatis.builder.SqlSourceBuilder$ParameterMappingTokenHandler.parseParameterMapping(SqlSourceBuilder.java:132)
at org.apache.ibatis.builder.SqlSourceBuilder$ParameterMappingTokenHandler.buildParameterMapping(SqlSourceBuilder.java:72)
at org.apache.ibatis.builder.SqlSourceBuilder$ParameterMappingTokenHandler.handleToken(SqlSourceBuilder.java:67)
at org.apache.ibatis.parsing.GenericTokenParser.parse(GenericTokenParser.java:77)
at org.apache.ibatis.builder.SqlSourceBuilder.parse(SqlSourceBuilder.java:45)
at org.apache.ibatis.scripting.defaults.RawSqlSource.<init>(RawSqlSource.java:46)
at org.apache.ibatis.scripting.defaults.RawSqlSource.<init>(RawSqlSource.java:40)
at org.apache.ibatis.scripting.xmltags.XMLScriptBuilder.parseScriptNode(XMLScriptBuilder.java:72)
at org.apache.ibatis.scripting.xmltags.XMLLanguageDriver.createSqlSource(XMLLanguageDriver.java:44)
at org.apache.ibatis.builder.xml.XMLStatementBuilder.parseStatementNode(XMLStatementBuilder.java:96)
at org.apache.ibatis.builder.xml.XMLMapperBuilder.buildStatementFromContext(XMLMapperBuilder.java:137)
at org.apache.ibatis.builder.xml.XMLMapperBuilder.buildStatementFromContext(XMLMapperBuilder.java:130)
at org.apache.ibatis.builder.xml.XMLMapperBuilder.configurationElement(XMLMapperBuilder.java:120)
... 30 more
Caused by: java.lang.StringIndexOutOfBoundsException: String index out of range: 0
at java.base/java.lang.StringLatin1.charAt(StringLatin1.java:48)
at java.base/java.lang.String.charAt(String.java:711)
at org.apache.ibatis.builder.ParameterExpression.parse(ParameterExpression.java:44)
at org.apache.ibatis.builder.ParameterExpression.<init>(ParameterExpression.java:39)
at org.apache.ibatis.builder.SqlSourceBuilder$ParameterMappingTokenHandler.parseParameterMapping(SqlSourceBuilder.java:128)
... 42 more
Process finished with exit code -1
這問題很奇怪的地方在於之前之前一直沒有出錯,突然就運行失敗,而且其他運行成功的test也沒有辦法再運行了.
剛開始搜的錯誤是第一個java.lang.ExceptionInInitializerError
,但是這個錯誤太大了,沒有搜到答案,於是又搜索了最后一個錯誤java.lang.StringIndexOutOfBoundsException: String index out of range: 0
,下標從0開始就越界,說明可能是數據庫出了問題,但是檢查一番發現數據庫沒有錯誤,可以運行,於是看了中間的錯誤Parsing error was found in mapping #{}.
,可能是mapper.xml中的 #{}
出了問題,
里面少了id,補上where id = (#{id});
,全部都運行正確.
這么一個小錯誤,居然可以讓整個程序都無法運行......
6. 用Map更新特定字段
使用update更新有一個問題,就是更新的是整個User對象,這樣就必須將這個對象所有的字段都寫出來,但如果我們只更新其中一兩個字段怎么辦?使用Map就不用與表對象字段一一對應.
使用Map來更新:
接口層:
//用Map插入對象
int Map_updateUser(Map<String, Object> map);
Mapper.xml:
<!--Map更新-->
<update id="Map_updateUser" parameterType="com.tan.pojo.User">
update mybatis.user
set name=(#{username})
where id = (#{userid});
</update>
這里只更新了name,沒有更新password,如果用User對象來更新就必須要寫出更新后password的值
Test:
//Map更新
@Test
public void Map_updateUser() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("userid", 6);
map.put("username", "map更新");
mapper.Map_updateUser(map);
sqlSession.commit();
sqlSession.close();
}
7. 模糊查詢
由於mybatis會在sql語句兩邊添加單引號 ``
所以要用 雙引號包裹"%"
, 這樣可以進行模糊查詢,但是idea會提示sql語句錯誤.
<select id="queryBookByName" resultType="Books">
select *
from ssmbuild.books
where bookName like "%"#{bookName}"%"
</select>
所以mybatis提供了CONCAT關鍵字 : 這樣就不會提示錯誤
<select id="queryBookByName" resultType="Books">
select *
from ssmbuild.books
where bookName like CONCAT(CONCAT('%',#{userName},'%'))
</select>
4. 配置解析
1. 核心配置文件
-
mybatis-config.xml
-
Mybatis的配置文件包含了會深深影響MyBatis行為的設置和屬性的信息.
configuration(配置) properties(屬性) settings(設置) typeAliases(類型別名) typeHandlers(類型處理器) objectFactory(對象工廠) plugins(插件) environments(環境配置) environment(環境變量) transactionManager(事務管理器) dataSource(數據源) databaseIdProvider(數據庫廠商標識) mappers(映射器)
2. 環境配置(environments)
MyBatis可以配置多種環境
不過要記住:盡管可以配置多個環境,但每個 SqlSessionFactory 實例只能選擇一種環境。
學會使用配置多套運行環境.
Mybatis默認的事務管理器JDBC , 連接池: POOLED
3. 屬性(properties)
我們可以通過properties屬性來實現引用配置文件
這些屬性可以在外部進行配置,並可以進行動態替換。你既可以在典型的 Java 屬性文件中配置這些屬性,也可以在 properties 元素的子元素中設置 [db.properties]
1.編寫一個配置文件
db.properties
driver = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"
username = root
password = 123456
2.在核心配置文件中引入
配置文件規定的順序:
<!--引入外部配置文件-->
<properties resource="db.properties"/>
還可以這樣寫:
<!--引入外部配置文件-->
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="password" value="123456"/>
</properties>
- 可以直接引入外部文件
- 可以在其中增加一些屬性配置
- 這樣寫如果內部和外部不一樣,會先讀取內部再讀取外部,然后外部會覆蓋內部的property
4.類型別名(typeAliases)
- 類型別名可為 Java 類型設置一個縮寫名字
- 它僅用於 XML 配置,意在降低冗余的全限定類名書寫
<!--給實體類取別名-->
<typeAliases>
<typeAlias type="com.tan.pojo.User" alias="User"/>
</typeAliases>
也可以指定一個包名,MyBatis 會在包名下面搜索需要的 Java Bean
掃描實體類的包,它的默認別名就為這個類的 類名的小寫.
<typeAliases>
<package name="com.tan.pojo"/>
</typeAliases>
<!--pojo下的實體類為User,則縮寫為user(大寫也可以)-->
在實體類比較少的時候,使用第一種.
在實體類十分多,建議使用第二種,若實體類上有注解則使用注解名.
@Alias("自定義別名")
public class User {
}
5. 設置(沒必要記)
這是 MyBatis 中極為重要的調整設置,它們會改變 MyBatis 的運行時行為.
cacheEnabled | 全局性地開啟或關閉所有映射器配置文件中已配置的任何緩存。 |
---|---|
lazyLoadingEnabled | 延遲加載的全局開關。當開啟時,所有關聯對象都會延遲加載。 特定關聯關系中可通過設置 fetchType 屬性來覆蓋該項的開關狀態。 |
logImpl | 指定 MyBatis 所用日志的具體實現,未指定時將自動查找。 |
mapUnderscoreToCamelCase | 是否開啟駝峰命名自動映射,即從經典數據庫列名 A_COLUMN 映射到經典 Java 屬性名 aColumn。 |
6. 其他配置
- plugins插件
- mybatis-generator-core
- mybatis-plus
- 通用mapper
7.映射器(mappers)
MapperRegistry: 注冊綁定我們的Mapper文件.
方式一 :
<!--每一個Mapper.xml都需要在Mybatis核心配置文件中注冊!-->
<mappers>
<mapper resource="com/tan/dao/UserMapper.xml"/>
</mappers>
方式二:使用class文件綁定注冊(不建議使用很容易出錯)
方式三: 使用包掃描注入綁定(不建議使用)
方式二和三的注意點:
- 接口和Mapper配置文件必須同名
- 接口和Mapper配置文件必須在同一個包下
8. 生命周期和作用域
(不知道為什么官網這部分沒有了)
生命周期,和作用域,是至關重要的,因為錯誤地使用回到是非常嚴重的並發問題.
SqlSessionFactoryBuilder :
- 一旦創建SqlSessionFactory , 就不需要它了
- 局部變量
SqlSessionFactory :
- 說白了就是可以想象為 : 數據庫連接池
- 一旦創建就應該在運行期間一直存在,沒有任何理由丟棄它或重新創建一個實例
- 應用作用域
- 最簡單的就是使用單例模式或者靜態單例模式 , 保證全局只有一份變量
**SqlSession : **
- 連接到連接池的一個請求.
- 每個SqlSession連接一個線程 , SqlSession的實例不是線程安全的,因此是不能被共享的,所以他的最佳的作用域是請求或方法作用域.
- 用完之后需要趕緊關閉 , 否則資源被占用 !
這里面的每一個Mapper , 就代表一個具體的業務 ! (增刪改查等)
5. 解決屬性名和字段名不一致的問題
1. 問題
數據庫中的字段
新建一個項目 , 拷貝之前的 , 測試實體類不一致的情況
public class User {
private int id;
private String name;
private String password;
}
輸出: User{id=1, name='張三', password='null'}
// select * from mybatis.user where id = #{id}
// 類型處理器
// select id,name,pwd from mybatis.user where id = #{id}
解決方法:
- sql語句起別名
select id,name,pwd as password from mybatis.user where id = #{id}
2. ResultMap
結果集映射
id name pwd
id name password
<!--結果集映射-->
<resultMap id="UserMap" type="User">
<!--column數據庫中的字段,property實體類中的屬性
把字段映射成屬性-->
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
<select id="getUserById" resultMap="UserMap">
select *
from mybatis.user
where id = #{id}
</select>
resultMap
元素是 MyBatis 中最重要最強大的元素- ResultMap 的設計思想是,對簡單的語句做到零配置,對於復雜一點的語句,只需要描述語句之間的關系就行了
ResultMap
的優秀之處——你完全可以不用顯式地配置它們- 如果這個世界總是這么簡單就好了
6. 日志
6.1 日志工廠
如果一個數據庫操作出現了異常,我們需要排錯.日志就是最好的幫手.
曾經: sout , debug
現在: 日志工廠.
logImpl指定 MyBatis 所用日志的具體實現,未指定時將自動查找。
- SLF4J
- LOG4J [掌握]
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING [掌握 ]
- NO_LOGGING
具體使用哪一個日志實現,在設置中設定,
STDOUT_LOGGING : 標准日志輸出
- 在mybatis核心配置文件中配置日志:
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--大小寫一定要注意,不能有多余的空格-->
6.2 Log4j
什么是Log4j ?
- Log4j是Apache的一個開源項目,通過使用Log4j,我們可以控制日志信息輸送的目的地是控制台)、文件、組件
- 可以控制每一條日志的輸出格式
- 通過定義每一條日志信息的級別,我們能夠更加細致地控制日志的生成過程
- 最令人感興趣的就是,這些可以通過一個配置文件來靈活地進行配置,而不需要修改應用的代碼
-
先導包
<!-- https://mvnrepository.com/artifact/log4j/log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
-
log4j.properties
#將等級為DEBUG的日志信息輸出到console和file這兩個目的地,console和file的定義在下面的代碼 log4j.rootLogger=DEBUG,console,file #控制台輸出的相關設置 log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.Threshold=DEBUG log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=[%c]-%m%n #文件輸出的相關設置 log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/mylog.log log4j.appender.file.MaxFileSize=10mb log4j.appender.file.Threshold=DEBUG log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n #日志輸出級別 log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG
-
配置log4j為日志的實現
<settings> <setting name="logImpl" value="LOG4J"/> </settings>
-
Log4j的使用
簡單使用
-
在要使用Log4j的類中,導入
import org.apache.log4j.Logger;
,注意jdk中有一個自帶的Logger包,不要導錯了. -
日志對象,參數為當前類的class
static Logger logger = Logger.getLogger(UserMapperTest.class);
-
日志級別
@Test public void testLog4j() { logger.info("info: 進入了testLog4j"); logger.debug("debug: 進入了testLog4j"); logger.error("error: 進入了testLog4j"); }
7. 分頁
**思考 **:為什么要分頁?
- 減少數據處理量
7.1 使用Limit分頁
語法: select * from user limit startIndex,pageSize;
select *from user limit 3; #[0,n)
使用Mybatis實現分頁,核心SQL
-
接口
//分頁 List<User> getUserByLimit(Map<String, Integer> map);
-
Mapper.xml
<!--分頁--> <select id="getUserByLimit" parameterType="map" resultMap="UserMap"> select * from mybatis.user limit #{startIndex},#{pageSize} </select>
-
測試
@Test public void getUserByLimit() { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); HashMap<String, Integer> map = new HashMap<String, Integer>(); map.put("startIndex", 0); map.put("pageSize", 2); List<User> userList = mapper.getUserByLimit(map); for (User user : userList) { System.out.println(user); } sqlSession.close(); }
用框架分頁真的簡單了好多😭,當初用servlet傳統分頁,傳五六個參數,sql語句和java語句混在一起,還要拼接sql語句,sql語句寫在String類型里面還沒有任何sql語句的提示,真的要吐了.
7.2 RowBounds分頁
通過java代碼層面實現分頁,強行面向對象,不建議使用,筆記就不做了(其實是我懶).
7.3 分頁插件
看官方文檔就好了,本質都是limit分頁
了解即可,只需要知到是什么就好了
8. 使用注解開發
8.1 面向接口編程
為了解耦
關於接口的理解
-
接口從更深層次的理解,應是定義(規范,約束)與實現(名實分離的原則)的分離。
-
接口的本身反映了系統設計人員對系統的抽象理解。
-
接口應有兩類:
- 第一類是對一個個體的抽象,它可對應為一個抽象體(abstract class);
- 第二類是對一個個體某一方面的抽象,即形成一個抽象面(interface);
-
一個體有可能有多個抽象面。抽象體與抽象面是有區別的。
8.2 使用注解開發
-
接口
@Select("select * from user") List<User> getUsers();
-
mybatis-config.xml (接口第一次時的配置)
<!--綁定接口--> <mappers> <mapper class="com.tan.dao.UserMapper"/> </mappers>
-
@Test public void test() { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.getUsers(); for (User user : users) { System.out.println(user); } sqlSession.close(); }
官方建議: 對於簡單的sql語句可以這么用就,但對於復雜的sql語句就顯得力不從心,所以復雜的sql語句還是建議使用xml配置來運行.
注解開發的實現方法: 通過反射獲取一個類中的所有信息,並幫我們自動化完成一些操作.
- 本質: 反射機制實現
- 底層: 動態代理 !
Mybatis詳細的執行流程
-
SqlSessionFactoryBuilder通過構造器build調用build構造方法
-
build構造方法調用XMLConfigBuilder這個類解析了
(inputStream, environment, properties)
-
解析完后傳給Configuration這個對象(Configuration中包含了所有的配置內容)
-
之后SqlSessionFactory實例化,獲取sqlSession(事務在這一層去做,相當於原來的Connection)
-
sqlSession里面有一個executor執行器,executor執行mapper,mapper通過反射加載出類的所有信息,包括了sqlSession,sqlSession中又有緩存executor執行器(executor把自己套進去了),套進去后開始執行sql語句,sql從配置文件中讀取.
成功執行sql語句提交事務,失敗回滾.
對第5步做一些簡單的解釋: sqlSession中包括了運行一個sql語句所需的條件,包括事務,執行器,加載器等.但就是沒有sql語句,於是通過反射獲取到了接口以及接口下類的所有信息,就有了sql語句,mapper只是放這所有信息的變量 . 於是有條件又有語句就可以執行sql語句了.
9. Lombok
Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.
Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more.
- java library
- plugs
- build tools
- with one annotation your class
使用步驟:
-
在IDEA中安裝Lombok插件.
-
在項目中導包
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency>
-
在實體類上加注解.
用Lombok后的實體類:@Data @AllArgsConstructor @NoArgsConstructor public class User { private int id; private String name; private String password; }
建議: idea的自動生成已經很厲害了 , Lombok只是實現了一點點的簡化,卻要在編譯時通過操作抽象語法樹(AST)改變字節碼的生成 , 變相改變了java語法.這就很不爽了,如果不是公司要求用就盡量別用.
10. 多對一處理
- 多個學生,對應一個老師
- 對於學生這邊而言 , 關聯 ... 多個學生關聯一個老師[多對一]
- 對於老師而言, 集合 ... 一個老師有很多學生 [一對多]
SQL:
CREATE TABLE `teacher` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老師');
CREATE TABLE `student` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
`tid` INT(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fktid` (`tid`),
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小紅', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小張', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');
測試環境搭建
-
導入Lombok
-
新建實體類Teacher , Student
注意: 學生的實體類中有tid
所以實體類實體類應這樣寫 :@Data public class Student { private int id; private String name; //學生需要關聯一個老師 private Teacher teacher; }
-
建立Mapper接口
-
建立Mapper.xml
-
在核心文件中綁定注冊我們的Mapper接口或者文件
-
測試查詢是否能夠成功
按照查詢嵌套處理
<!--
思路:
1. 查詢所有的學生信息
2.根據查詢出來的學生的tid,尋找對應的老師
-->
<select id="getStudent" resultMap="StudentTeacher">
select *
from student;
</select>
<resultMap id="StudentTeacher" type="Student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<!--復雜的屬性,我們需要單獨處理,對象: association,集合: collection
-->
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="Teacher">
select *
from teacher
where id = #{tid};
</select>
簡單解釋:
<association property="tetacher" column="tid" javaType="Teacher" select="getTeacher"/>
用Map將字段對應到屬性,resultMap中前面兩個id
和name
可以直接對應,但是tid
這個復雜的屬性沒辦法直接對應.因為tid
指向的是teacher這個對象,於是用association
將字段tid
指向teacher對象.但是teacher是一個復雜類型,需要再給他一個類型Teacher
.
這樣就將兩個表通過tid
聯系了起來,再在student的association
中再嵌套一個查詢select="getTeacher"
,這樣就完成了一對多(也就是 sql 聯表查詢).
- 一對多的最終解決辦法在springboot中用 json 解決,這個能看懂即可.
按照結果嵌套處理
<!--按照結果嵌套處理-->
<select id="getStudent2" resultMap="StudentTeacher2">
select s.id sid, s.name sname, t.name tname, t.id tid
from student s,
teacher t
where s.tid = t.id;
</select>
<resultMap id="StudentTeacher2" type="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
<result property="id" column="tid"/>
</association>
</resultMap>
上面寫 完整sql 語句,下面用resultMap寫關聯,比第一種好理解一些.
11. 一對多處理
比如: 一個老師擁有多個學生 .
對於老師而言, 就是一對多的關系 .
環境搭建
-
環境搭建,和剛才一樣
實體類:@Data public class Teacher { private int id; private String name; //一個老師擁有多個學生 private List<Student> students; }
按照結果嵌套處理:
<!--按結果查詢-->
<select id="getTeacher" resultMap="TeacherStudent">
select s.id sid, s.name sname, t.name tname, t.id tid
from student s,
teacher t
where s.tid = t.id and t.id = #{tid}
</select>
<resultMap id="TeacherStudent" type="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<!--
復雜的屬性,我們需要單獨處理,對象: association,集合: collection
javaType="" 指定屬性的類型,
但此處類型為 List<Student> ,使用ofType獲取
-->
<collection property="students" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
小結
- 關聯 - association [多對一]
- 集合 - collection [一對多]
- javaType & ofType
- javaType 用來指定實體類中屬性的類型
- ofType 用來指定映射到List或者集合中的 pojo 類型 , 也就是泛型中的約束類型
注意點:
- 盡量通俗易懂
- 注意一對多和多對一中屬性名和字段名的問題
- 如果問題不好排查錯誤,可以使用日志 , 建議使用Log4j
面試高頻 :
- Mysql引擎
- innoDB底層原理
- 索引
- 索引優化
12. 動態SQL
什么是動態SQL: 動態SQL就是根據不同的條件生成不同的SQL語
動態 SQL 是 MyBatis 的強大特性之一。如果你使用過 JDBC 或其它類似的框架,你應該能理解根據不同條件拼接 SQL 語句有多痛苦,例如拼接時要確保不能忘記添加必要的空格,還要注意去掉列表最后一個列名的逗號。利用動態 SQL,可以徹底擺脫這種痛苦。
使用動態 SQL 並非一件易事,但借助可用於任何 SQL 映射語句中的強大的動態 SQL 語言,MyBatis 顯著地提升了這一特性的易用性。
如果你之前用過 JSTL 或任何基於類 XML 語言的文本處理器,你對動態 SQL 元素可能會感覺似曾相識。在 MyBatis 之前的版本中,需要花時間了解大量的元素。借助功能強大的基於 OGNL 的表達式,MyBatis 3 替換了之前的大部分元素,大大精簡了元素種類,現在要學習的元素種類比原來的一半還要少。
if
choose (when, otherwise)
trim (where, set)
foreach
搭建環境
create table `blog`
(
`id` varchar(50) not null comment '博客id',
`title` varchar(100) not null comment '博客標題',
`author` varchar(30) not null comment '博客作者',
`create_time` datetime not null comment '創建時間',
`views` int(30) not null comment '瀏覽量'
) engine = InnoDB
default charset = utf8;
創建一個基礎工程
-
導包
-
編寫配置文件
-
編寫實體類
@Data public class Blog { private String id; private String title; private String author; private Date createTime; private int views; }
-
編寫實體類對應的Mapper接口 和 Mapper.xml文件
if
接口Mapper
//IF 查詢博客
List<Blog> queryBlogIF(Map map);
Mapper.xml
<select id="queryBlogIF" parameterType="map" resultType="blogjava">
select * from mybatis.blog where 1=1
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</select>
select * from mybatis.blog where 1=1 and title = ? and author = ?
,
注意點:
- 如果title和author只傳其中一個,則查詢其中一個
- 如果兩個都傳則取交集
- 如果都不傳則執行
select * from mybatis.blog where 1=1
查詢所有 - 如果不寫
1=1
則在title和author都不傳的時候where關鍵字和and關鍵字會連起來sql語法錯誤.
但是在sql語句中加上1=1
很明顯不規范,於是mybatis中又添加了<where>
標簽.來自動判斷和處理sql拼接時關鍵字會連起來的問題.
where與set
where標簽
接口Mapper
//Choose 查詢博客
List<Blog> queryBlogChoose(Map map);
Mapper.xml
<select id="queryBlogChoose" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<if test="title != null">
and title = #{title} /*此處 and 可有可無*/
</if>
<if test="author != null">
and author = #{author} /*此處 and 可有可無*/
</if>
</where>
</select>
where標簽官網解釋 :
where 元素只會在至少有一個子元素的條件返回SQL子句的情況下才會去插入"WHERE"子句,而且,若語句的開頭為"AND"或"OR",where元素也會將他們去除
也就是說拼接sql語句select * from mybatis.blog where 1=1 and title = ? and author = ?
時where標簽會自動判斷是保留還是刪除and
.
set標簽
與where類似,更新表的時候set關鍵字需要考慮,
是否需要,set標簽提供了只能添加與刪除,
保證不會因為缺少或則多余的,
影響sql語句的執行
接口Mapper
//更新博客
int updateBlog(Map map);
Mapper.xml
<update id="updateBlog" parameterType="map">
update mybatis.blog
<set>
<if test="title != null">
title = #{title}, /*此處 , 可有可無*/
</if>
<if test="views != null">
views = #{views}, /*此處 , 可有可無*/
</if>
</set>
where id = #{id}
</update>
choose (when, otherwise)
接口Mapper
//Choose 查詢博客
List<Blog> queryBlogChoose(Map map);
Mapper.xml
<select id="queryBlogChoose" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<choose>
<when test="title != null">
title = #{title}
</when>
<when test="author != null">
and author = #{author}
</when>
<otherwise>
and 1=1
</otherwise>
</choose>
</where>
</select>
choose (when, otherwise)
類似於java中的switch ,case ,只會執行when 標簽
中最先滿足條件的那個,如果都不滿足條件則執行otherwise
標簽中的內容.
trim(where,set)
/*等價於set標簽*/
<trim prefix="SET" suffixOverrides=",">
...
</trim>
/*等價於where標簽*/
<trim prefix="WHERE" prefixOverrides="AND | OR">
...
</trim>
trim標簽
就是自定義某關鍵詞的前綴后綴是否保留.
所謂的動態SQL , 本質還是SQL語句 , 只是我們可以在SQL 層面去執行一個邏輯代碼
if
where , set , choose , when
SQL片段
抽取部分常用的sql語句,便於復用.
-
使用sql 標簽抽取公共部分
<!--sql片段--> <sql id="choose-title-test"> <choose> <when test="title != null"> title = #{title} </when> <when test="author != null"> and author = #{author} </when> <otherwise> and 1=1 </otherwise> </choose> </sql>
-
在需要的地方使用include標簽引用即可
<!--引用sql片段部分--> <select id="queryBlogChoose" parameterType="map" resultType="blog"> select * from mybatis.blog <where> <include refid="choose-title-test"> </include> </where> </select>
注意事項:
- 最好基於單表來定義SQL片段.保證復用性
- sql 片段中不要有where標簽,盡量只要有 if 判斷就好了
Foreach
sql 語句查詢多個 id :
select * from user where 1=1 and (id=1 or id=2 or id=3)
select * from user where 1=1 and id in (1,2,3)
select * from user where id < 4
官網圖片 :
接口:
//查詢第1-2-3-4號記錄的博客
List<Blog> queryBlogForeach(Map map);
Mapper.xml : (這樣寫Mapper傳入的id在數據庫中找不到不要緊,但一定要傳不然會報錯 . 不傳id會報錯的問題,我試了好久都沒有解決...)
<!--select * from blog where id in (1, 2, 3, 4,5)-->
<select id="queryBlogForeach" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
id in
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</where>
</select>
測試類: (因為我自己寫寫錯了,沒寫出來,所以這里貼一下)
//foreach查詢
@Test
public void queryBlogForeach() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
map.put("ids", list);
List<Blog> blogs = mapper.queryBlogForeach(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
往Map中傳List集合.
小總結
動態SQL就是在拼接SQL語句,我們只要保證SQL的正確性,按照SQL的格式,去排列組合就可以了
建議:
- 先在IDEA的mysql控制台中寫出完整的SQL,再對應地去修改成為我們的動態SQL實現通用即可 .
最后再附上官網對動態SQL的解釋:
動態 SQL 是 MyBatis 的強大特性之一。如果你使用過 JDBC 或其它類似的框架,你應該能理解根據不同條件拼接 SQL 語句有多痛苦,例如拼接時要確保不能忘記添加必要的空格,還要注意去掉列表最后一個列名的逗號。利用動態 SQL,可以徹底擺脫這種痛苦。
13. 緩存(重要,但是掌握概念即可)
具體會學習redis緩存.
13.1 簡介
查詢 : 連接數據庫 , 耗資源
一次查詢的結果, 給他一個可以直接取到的地方 --> 內存 : 緩存
我們再次查詢相同數據的時候,直接就走緩存 ,就不用走數據庫了
- 什么是緩存[Cache]?
- 存在內存中的臨時數據。
- 將用戶經常查詢的數據放在緩存(內存)中,用戶去查詢數據就不用從磁盤上(關系型數據庫數據文件)查詢,從緩存中查詢,從而提高查詢效率,解決了高並發系統的性能問題。
- 為什么使用緩存?
- 減少和數據庫的交互次數,減少系統開銷,提高系統效率。
- 什么樣的數據能使用緩存?
- 經常查詢並且不經常改變的數據。
13.2 Mybatis緩存
- MyBatis包含一個非常強大的查詢緩存特性,它可以非常方便地定制和配置緩存。緩存可以極大的提升查詢效率。
- MyBatis系統中默認定義了兩級緩存:一級緩存和二級緩存
- 默認情況下,只有一級緩存開啟。(SqlSession級別的緩存,也稱為本地緩存)
- 二級緩存需要手動開啟和配置,他是基於namespace級別的緩存。(也就是一個接口或者一個Mapper)
- 為了提高擴展性,MyBatis定義了緩存接口Cache。我們可以通過實現Cache接口來自定義二級緩存
13.3 一級緩存
- 一級緩存也叫本地緩存:SqlSession
- 與數據庫同一次會話期間查詢到的數據會放在本地緩存中。
- 以后如果需要獲取相同的數據,直接從緩存中拿,沒必須再去查詢數據庫;
測試步驟:
-
開啟日志
-
接口(略)
-
Mapper (select *from user where id = #{id})
-
Test類
@Test public void queryUserById() { SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); System.out.println("=============================="); User user2 = mapper.queryUserById(1); System.out.println(user2); System.out.println(user == user2); sqlSession.close(); }
-
查詢的時候當第二次查詢和第一次查詢相同時,直接從一級緩存從獲取 :
-
當兩次查詢不同id時 :
很明顯第二次從數據庫中查詢,沒有緩存
-
緩存失效的情況 :
- 查詢不同的東西 .(上面那個情況)
- 增刪改操作,可能會改變原來的數據,所以必定會刷新緩存 .
- 查詢不同的Mapper.xml (二級緩存都不行)
- 手動清理緩存
sqlSession.clearCache();
小結: 一級緩存是默認開啟的,只有一次SqlSession有效,也就是拿到連接到關閉連接這個區間段 . 一級緩存就是一個Map
13.4 二級緩存
- 二級緩存也叫全局緩存,一級緩存作用域太低了,所以誕生了二級緩存
- 基於namespace級別的緩存,一個名稱空間,對應一個二級緩存;
- 工作機制
- 一個會話查詢一條數據,這個數據就會被放在當前會話的一級緩存中;
- 如果當前會話關閉了,這個會話對應的一級緩存就沒了;但是我們想要的是,會話關閉了,一級緩存中的數據被保存到二級緩存中;
- 新的會話查詢信息,就可以從二級緩存中獲取內容;
- 不同的mapper查出的數據會放在自己對應的緩存(map)中;
步驟:
-
mybatis-config.xml
中開啟全局緩存<settings> <!--顯式開始全局緩存--> <setting name="cacheEnabled" value="true"/> </settings>
-
在使用二級緩存的Mapper.xml中開啟
<!--在當前Mapper.xml中使用二級緩存--> <cache/>
也可以自定義參數 官網緩存參數介紹
<!--在當前Mapper.xml中使用二級緩存--> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
-
測試
<( ̄ c ̄)y▂ξ
沒什么好寫的,就是一級緩存(SqlSession)關閉的時候,會自動提升作用域,其他SqlSession會直接從Mapper中獲取已有的結果.
測試的時候報錯,提醒沒有序列化.
-
開啟二級緩存 存入緩存需要序列化(深拷貝),保證查的是同一個實體類.
pojo 實體類實現Serializable接口即可 :@Data public class User implements Serializable { private int id; private String name; private String pwd; }
小結 :
- 只要開啟了二級緩存,在同一個Mapper下就有效 .
- 所有的數據都會先放在一級緩存中;
- 只有當會話提交,或者關閉的時候,才會提交到二級緩沖中 .
13.5 緩存原理
用戶 -> 二級緩存 -> 一級緩存 -> 數據庫
14 配張思維導圖
完結撒花! 完結撒花! 完結撒花!
開始Spring之旅~ ~ ~