Mybatis回顧總結
Mybatis是一個基於Java的持久層框架,內部封裝了JDBC,使開發者只需要關注SQL語句本身,而不需要去處理載入驅動、建立連線、建立statement等複雜過程。
Mybatis通過xml或者註解,將要執行的statement配置起來,通過Java物件和statement中SQL的動態引數進行對映,生成最終執行的SQL語句。
1、開發步驟
- 匯入Mybatis依賴的jar包
- [建立庫表對應實體類]
- 編寫對映檔案xxxMapper.xml
- 編寫核心配置檔案xxx.xml
<dependencies> <!--Mybatis依賴的jar--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.8</version> </dependency> <!--MySQL驅動--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> </dependency> <!--Oracle驅動--> <dependency> <groupId>com.oracle.database.jdbc</groupId> <artifactId>ojdbc8</artifactId> <version>21.3.0.0</version> </dependency> <!--測試jar--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies>
對映檔案:empMapper.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"> <!--namespace:名稱空間,值可以任意,但是,若使用面向介面的方式開發時,必須是介面的全限定名--> <mapper namespace="org.silence.dao.EmpMapper"> <!--SQL語句, id:唯一標識,值可任意,但當使用面向介面的方式開發時,必須是介面中的方法名 resultType:返回值型別,若返回實體物件,則使用物件的全限定名--> <select id="queryAll" resultType="org.silence.entity.Emp"> select * from emp </select> </mapper>
核心配置檔案:Mybatis-conf.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> <!--用於配置資料庫環境--> <environments default="oracle"> <!--Oracle資料庫配置--> <environment id="oracle"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="oracle.jdbc.driver.OracleDriver"/> <property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:orcl"/> <property name="username" value="scott"/> <property name="password" value="scott"/> </dataSource> </environment> <!--MySQL資料庫配置--> <environment id="mysql"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/root/"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <!--引入mapper對映檔案--> <mappers> <mapper resource="mapper/EmpMapper.xml"/> </mappers> </configuration>
對映介面:
public interface EmpMapper { List<Emp> queryAll(); }
測試類:
@Test public void test1() throws IOException { String resource = "Mybatis-conf.xml"; // 載入配置檔案 InputStream inputStream = Resources.getResourceAsStream(resource); // 獲取SqlSession物件 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession session = sqlSessionFactory.openSession(); // 方式①:原始方式,直接通過session物件執行SQL語句,引數為mapper.xml中的namespace值+SQL語句的id List<Emp> list = session.selectList("org.silence.dao.EmpMapper.queryAll"); System.out.println(list); System.out.println("面向介面開發===================="); // 方式②:面向介面,通過session物件獲取介面的實現類mapper物件(Mybatis自動實現) EmpMapper mapper = session.getMapper(EmpMapper.class); // 呼叫mapper中的方法即可 List<Emp> empList = mapper.queryAll(); System.out.println(empList); }
2、工作流程
-
載入配置並初始化
載入配置檔案,將SQL的配置資訊載入成一個個MappedStatement物件(包括了傳入引數對映配置、執行的SQL語句、結果對映配置),儲存在記憶體中。
-
接受呼叫請求
呼叫MyBatis提供的API,傳入SQL的id和傳入引數物件,將請求傳遞給下層的請求處理層進行處理。
-
處理操作請求
- 根據SQL的id查詢對應的MappedStatement物件
- 根據傳入引數物件解析MappedStatement物件,得到最重要執行的SQL和執行傳入引數。
- 獲取資料庫連線,執行SQL。
- 根據MappedStatement物件中的結果對映配置對得到的執行結果進行轉換處理,得到最終的處理結果。
- 釋放資料庫連線。
- 返回處理結果。
3、核心配置檔案
MyBatis 的配置檔案包含了會深深影響 MyBatis 行為的設定和屬性資訊。 配置文件的結構如下:
-
configuration(配置)
-
這些屬性可以在外部進行配置,並可以進行動態替換。你既可以在典型的 Java 屬性檔案中配置這些屬性,也可以在 properties 元素的子元素中設定。設定好的屬性可以在整個配置檔案中用來替換需要動態配置的屬性值:
<!--配置資料庫連線屬性--> <properties resource="org/silence/db/jdbc.properties"> <property name="username" value="test"/> <property name="password" value="123456"/> </properties> <!--使用已經配置的資料庫連線屬性--> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource>
還可以在 SqlSessionFactoryBuilder.build() 方法中傳入屬性值:
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, props); // 或者 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, props);
通過方法引數傳遞的屬性具有最高優先順序,resource/url 屬性中指定的配置檔案次之,最低優先順序的則是 properties 元素中指定的屬性。
-
這是 MyBatis 中極為重要的調整設定,它們會改變 MyBatis 的執行時行為。
-
environments(環境配置)
- environment(環境變數)
- transactionManager(事務管理器)
- dataSource(資料來源)
- environment(環境變數)
-
4、快取
本地快取(一級快取)
預設開啟。基於PerpetualCache的HashMap本地快取,其儲存作用域為session。
每當一個新 session 被建立,MyBatis 就會建立一個與之相關聯的本地快取。任何在 session 執行過的查詢結果都會被儲存在本地快取中,所以,當再次執行引數相同的相同查詢時,就不需要實際查詢資料庫了。本地快取將會在做出修改、事務提交或回滾,以及關閉 session 時清空。
預設情況下,本地快取資料的生命週期等同於整個 session 的週期。由於快取會被用來解決迴圈引用問題和加快重複巢狀查詢的速度,所以無法將其完全禁用。但是可以通過設定 localCacheScope=STATEMENT
來只在語句執行時使用快取。
注意,如果 localCacheScope 被設定為 SESSION,對於某個物件,MyBatis 將返回在本地快取中唯一物件的引用。對返回的物件(例如 list)做出的任何修改將會影響本地快取的內容,進而將會影響到在本次 session 中從快取返回的值。因此,不要對 MyBatis 所返回的物件作出更改,以防後患。
可以隨時呼叫SqlSession 中的clearCache()
方法來清空本地快取:
String resource = "Mybatis-conf.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); // 手動清空快取 sqlSession.clearCache(); ...
判斷兩次查詢完全相同:
mybatis認為,對於兩次查詢,如果以下條件都完全一樣,那麼就認為它們是完全相同的兩次查詢。
-
傳入的statementId。
-
查詢時要求的結果集中的結果範圍。
-
這次查詢所產生的最終要傳遞給JDBC java.sql.Preparedstatement的Sql語句字串(boundSql.getSql() )。
-
傳遞給java.sql.Statement要設定的引數值。
二級快取
MyBatis 內建了一個強大的事務性查詢快取機制,它可以非常方便地配置和定製。
預設情況下,只啟用了本地的會話快取(一級快取),它僅僅對一個會話中的資料進行快取。
要啟用全域性的二級快取,只需要在你的 SQL 對映檔案中新增一行:
<cache/>
這個簡單語句的效果如下:
- 對映語句檔案中的所有 select 語句的結果將會被快取。
- 對映語句檔案中的所有 insert、update 和 delete 語句會重新整理快取。
- 快取會使用最近最少使用演算法(LRU, Least Recently Used)演算法來清除不需要的快取。
- 快取不會定時進行重新整理(也就是說,沒有重新整理間隔)。
- 快取會儲存列表或物件(無論查詢方法返回哪種)的 1024 個引用。
- 快取會被視為讀/寫快取,這意味著獲取到的物件並不是共享的,可以安全地被呼叫者修改,而不干擾其他呼叫者或執行緒所做的潛在修改。
同時,resultType的類必須實現Serializable介面。
提示 :快取只作用於 cache 標籤所在的對映檔案中的語句。如果混合使用 Java API 和 XML 對映檔案,在共用介面中的語句將不會被預設快取。你需要使用 @CacheNamespaceRef
註解指定快取作用域。
cache 元素的屬性:
<!--
這個配置建立了一個 FIFO 快取,每隔 60 秒重新整理,最多可以儲存結果物件或列表的 512 個引用,而且返回的物件被認為是隻讀的,因此對它們進行修改可能會在不同執行緒中的呼叫者產生衝突。
-->
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
eviction(快取清除策略):
可用的清除策略有:
-
LRU
– 最近最少使用:移除最長時間不被使用的物件。 -
FIFO
– 先進先出:按物件進入快取的順序來移除它們。 -
SOFT
– 軟引用:基於垃圾回收器狀態和軟引用規則移除物件。 -
WEAK
– 弱引用:更積極地基於垃圾收集器狀態和弱引用規則移除物件。
預設的清除策略是 LRU。
flushInterval(重新整理間隔):可以被設定為任意的正整數,設定的值應該是一個以毫秒為單位的合理時間量。 預設情況是不設定,也就是沒有重新整理間隔,快取僅僅會在呼叫語句時重新整理。
size(引用數目):可以被設定為任意正整數,要注意欲快取物件的大小和執行環境中可用的記憶體資源。預設值是 1024。
readOnly(只讀):可以被設定為 true 或 false。只讀的快取會給所有呼叫者返回快取物件的相同例項。 因此這些物件不能被修改。這就提供了可觀的效能提升。而可讀寫的快取會(通過序列化)返回快取物件的拷貝。 速度上會慢一些,但是更安全,因此預設值是 false。
注!:二級快取是事務性的。
**只有當 SqlSession **
- 完成並提交時
- 完成並回滾,但沒有執行 flushCache=true 的 insert/delete/update 語句時
- 關閉時
二級快取才會生效,對查詢進行快取。
兩次查詢之間進行了任意的資料修改操作,會使一級快取和二級快取同時失效。
在Mapper.xml的具體SQL語句中通過 useCahe="false"
可以關閉當前語句的快取:
<select id="findById" useCahe="false" ... /></select>
cache-ref
對某一名稱空間的語句,只會使用該名稱空間的快取進行快取或重新整理。 可以使用 cache-ref
元素來引用另一個快取來實現在多個名稱空間中共享相同的快取配置和例項。
<cache-ref namespace="com.someone.application.data.SomeMapper"/>