初步了解
總體架構設計
Mybatis 整體框架如下:
介面層
MyBatis 和資料庫的互動有兩種方式:
- 使用傳統的 MyBatis 提供的 API;
- 使用 Mapper 介面;
使用傳統的 MyBatis 提供的 API
這是傳統的傳遞 Statement Id 和查詢引數給 SqlSession 物件,使用 SqlSession 物件完成和資料庫的互動;MyBatis 提供了非常方便和簡單的 API,供用戶實作對資料庫的增刪改查資料操作,以及對資料庫連接資訊和 MyBatis 自身配置資訊的維護操作,
使用 Mapper 介面
MyBatis 將組態檔中的每一個<mapper>
節點抽象為一個 Mapper 介面,而這個介面中宣告的方法和跟<mapper>
節點中的<select|update|delete|insert>
節點項對應,即<select|update|delete|insert>
節點的 id 值為 Mapper 介面中的方法名稱,parameterType 值表示 Mapper 對應方法的入參型別,而 resultMap 值則對應了 Mapper 介面表示的回傳值型別或者回傳結果集的元素型別,
根據 MyBatis 的配置規范配置好后,通過 SqlSession.getMapper(XXXMapper.class)方法,MyBatis 會根據相應的介面宣告的方法資訊,通過動態代理機制生成一個 Mapper 實體,我們使用 Mapper 介面的某一個方法時,MyBatis 會根據這個方法的方法名和引數型別,確定 Statement Id,底層還是通過 SqlSession.select("statementId",parameterObject);或者 SqlSession.update("statementId",parameterObject); 等等來實作對資料庫的操作, MyBatis 參考 Mapper 介面這種呼叫方式,純粹是為了滿足面向介面編程的需要,(其實還有一個原因是在于,面向介面的編程,使得用戶在介面上可以使用注解來配置 SQL 陳述句,這樣就可以脫離 XML 組態檔,實作“0 配置”),
資料處理層
資料處理層可以說是 MyBatis 的核心,從大的方面上講,它要完成兩個功能:
- 通過傳入引數構建動態 SQL 陳述句;
- SQL 陳述句的執行以及封裝查詢結果集成
List<E>
通過傳入引數構建動態 SQL 陳述句;
動態陳述句生成可以說是 MyBatis 框架非常優雅的一個設計,MyBatis 通過傳入的引數值,使用 Ognl 來動態地構造 SQL 陳述句,使得 MyBatis 有很強的靈活性和擴展性,
引數映射指的是對于 java 資料型別和 jdbc 資料型別之間的轉換:這里有包括兩個程序:查詢階段,我們要將 java 型別的資料,轉換成 jdbc 型別的資料,通過 preparedStatement.setXXX() 來設值;另一個就是對 resultset 查詢結果集的 jdbcType 資料轉換成 java 資料型別,
SQL 陳述句的執行以及封裝查詢結果集成List<E>
動態 SQL 陳述句生成之后,MyBatis 將執行 SQL 陳述句,并將可能回傳的結果集轉換成List<E>
串列,MyBatis 在對結果集的處理中,支持結果集關系一對多和多對一的轉換,并且有兩種支持方式,一種為嵌套查詢陳述句的查詢,還有一種是嵌套結果集的查詢,
框架支撐層
- 事務管理機制
事務管理機制對于 ORM 框架而言是不可缺少的一部分,事務管理機制的質量也是考量一個 ORM 框架是否優秀的一個標準,
- 連接池管理機制
由于創建一個資料庫連接所占用的資源比較大, 對于資料吞吐量大和訪問量非常大的應用而言,連接池的設計就顯得非常重要,
- 快取機制
為了提高資料利用率和減小服務器和資料庫的壓力,MyBatis 會對于一些查詢提供會話級別的資料快取,會將對某一次查詢,放置到 SqlSession 中,在允許的時間間隔內,對于完全相同的查詢,MyBatis 會直接將快取結果回傳給用戶,而不用再到資料庫中查找,
- SQL 陳述句的配置方式
傳統的 MyBatis 配置 SQL 陳述句方式就是使用 XML 檔案進行配置的,但是這種方式不能很好地支持面向介面編程的理念,為了支持面向介面的編程,MyBatis 引入了 Mapper 介面的概念,面向介面的引入,對使用注解來配置 SQL 陳述句成為可能,用戶只需要在介面上添加必要的注解即可,不用再去配置 XML 檔案了,但是,目前的 MyBatis 只是對注解配置 SQL 陳述句提供了有限的支持,某些高級功能還是要依賴 XML 組態檔配置 SQL 陳述句,
引導層
引導層是配置和啟動 MyBatis 配置資訊的方式,MyBatis 提供兩種方式來引導 MyBatis :基于 XML 組態檔的方式和基于 Java API 的方式,
主要構件及其相互關系
主要的核心部件解釋如下:
SqlSession
作為 MyBatis 作業的主要頂層 API,表示和資料庫互動的會話,完成必要資料庫增刪改查功能Executor
MyBatis 執行器,是 MyBatis 調度的核心,負責 SQL 陳述句的生成和查詢快取的維護StatementHandler
封裝了 JDBC Statement 操作,負責對 JDBC statement 的操作,如設定引數、將 Statement 結果集轉換成 List 集合,ParameterHandler
負責對用戶傳遞的引數轉換成 JDBC Statement 所需要的引數,ResultSetHandler
負責將 JDBC 回傳的 ResultSet 結果集物件轉換成 List 型別的集合;TypeHandler
負責 java 資料型別和 jdbc 資料型別之間的映射和轉換MappedStatement
MappedStatement 維護了一條<select|update|delete|insert>
節點的封裝,SqlSource
負責根據用戶傳遞的 parameterObject,動態地生成 SQL 陳述句,將資訊封裝到 BoundSql 物件中,并回傳BoundSql
表示動態生成的 SQL 陳述句以及相應的引數資訊Configuration
MyBatis 所有的配置資訊都維持在 Configuration 物件之中,
流程簡解
準備
/src/main/resources/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>
<property name="driver" value="https://www.cnblogs.com/jiuxialb/archive/2023/05/22/com.mysql.cj.jdbc.Driver"/>
<property name="url"
value="https://www.cnblogs.com/jiuxialb/archive/2023/05/22/jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF8&useSSL=false&autoReconnect=true"/>
<property name="username" value="https://www.cnblogs.com/jiuxialb/archive/2023/05/22/root"/>
<property name="password" value="https://www.cnblogs.com/jiuxialb/archive/2023/05/22/123456"/>
</properties>
<!-- 環境,可以配置多個,default:指定采用哪個環境 -->
<environments default="test">
<environment id="test">
<!-- 事務管理器,JDBC型別的事務管理器 -->
<transactionManager type="JDBC"/>
<!-- 資料源,池型別的資料源 -->
<dataSource type="POOLED">
<property name="driver" value="https://www.cnblogs.com/jiuxialb/archive/2023/05/22/${driver}"/> <!-- 配置了properties,所以可以直接參考 -->
<property name="url" value="https://www.cnblogs.com/jiuxialb/archive/2023/05/22/${url}"/>
<property name="username" value="https://www.cnblogs.com/jiuxialb/archive/2023/05/22/${username}"/>
<property name="password" value="https://www.cnblogs.com/jiuxialb/archive/2023/05/22/${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="TeacherMapper.xml"/>
</mappers>
</configuration>
-
/src/main/resources/TeacherMapper.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:命名空間,隨便寫,一般保證命名空間唯一 --> <mapper namespace="TeacherMapper"> <!-- statement,內容:sql陳述句,id:唯一標識,隨便寫,在同一個命名空間下保持唯一 resultType:sql陳述句查詢結果集的封裝型別,tb_user即為資料庫中的表 --> <select id="selectTest" resultType="org.apache.ibatis.test.Teacher"> select * from teacher where id = #{id} </select> </mapper>
-
/src/main/resources/log4j.properties
log4j.rootLogger=DEBUG, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
-
/src/main/java/org/apache/ibatis/test/Teacher.java
package org.apache.ibatis.test; public class Teacher { private Long id; private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Teacher{" + "id=" + id + ", name='" + name + '\'' + '}'; } }
-
/src/main/java/org/apache/ibatis/test/Test.java
package org.apache.ibatis.test; 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 java.io.IOException; import java.io.InputStream; public class Test { public static void main(String[] args) throws IOException { // 指定全域組態檔 String resource = "mybatis-config.xml"; // 讀取組態檔 InputStream inputStream = Resources.getResourceAsStream(resource); // 構建sqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 獲取sqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); // 操作CRUD,第一個引數:指定statement,規則:命名空間+“.”+statementId // 第二個引數:指定傳入sql的引數:這里是用戶id Teacher test = sqlSession.selectOne("TeacherMapper.selectTest", 1); System.out.println(test.getName()); } }
整體流程
<iframe id="embed_dom" name="embed_dom" frameborder="0" style="width: 100%; height: 300px" src="https://www.processon.com/embed/6462fcc6bb56a110e3ddb091"></iframe>- 通過
mybatis-config.xml
進行初始化創建出SqlSessionFactory
,其內部是通過創建XMLConfigBuilder
物件,然后自己進行決議XML
檔案,把檔案內容決議封裝為Configuration
物件,最后由SqlSessionFactory
進行封裝為SqlSessionFactory
物件來完成SqlSessionFactory
的創建, - 通過
SqlSessionFactory
物件進行開啟一個SqlSession
,其內部是通過獲取Configuration
中Environment
資料進行構建TransactionFactory
–>Executor
,最后封裝為SqlSession
回傳進行使用 - 通過
SqlSession
執行XML
中對應的方法,其中是先通過statement
即XML 指定的類#方法名稱
獲取MappedStatement
物件,然后提交給Executor
進行執行,
目錄詳解
exception
包
下圖是Mybatis
中例外的關系圖:
背景知識
- 工廠模式
- 例外的封裝
講解
工廠模式
public class ExceptionFactory {
private ExceptionFactory() {
// Prevent Instantiation
}
public static RuntimeException wrapException(String message, Exception e) {
return new PersistenceException(ErrorContext.instance().message(message).cause(e).toString(), e);
}
}
- 私有建構式:導致該工廠無法創建出對應的物件
- 靜態
wrapException
方法,用于通過例外資訊和例外型別進行封裝例外為Mybatis
中的例外型別,全域通過ExceptionFactory.wrapException()
進行生產出對應的例外物件
例外型別
-
IbatisException:
Mybatis
中最高的例外,但是直接繼承該類的子類只有PersistenceException
,而且該類也添加了@Deprecated
說明以后可能去除, -
PersistenceException: 譯為持久化例外,
Mybatis
對應是持久化框架,后期可能該例外型別為Mybatis
所有例外的父類, -
TooManyResultsException:譯為多潭訓傳結果例外,用處為
selectOne
卻回傳多條記錄時所拋出的例外, -
TypeException: 譯為型別例外,當
Mybatis
中需要型別轉化時,若轉換失敗則會拋出該例外, -
CacheException: 譯為快取例外,當
Mybatis
讀取快取中資料出現問題時則會拋出該例外, -
ParsingException: 譯為決議例外,當前代碼未看到使用,
-
ScriptingException: 譯為腳本例外,
-
ResultMapException: 譯為結果映射例外,在結果轉換為對應型別的物件時,若轉換失敗則會拋出例外,
-
DataSourceException: 譯為資料源例外,在初始化資料源時若出現錯誤則會拋出該例外,
-
TransactionException: 譯為事務例外,在給
connection
開啟事務時若失敗則會拋出該例外, -
BuilderException: 譯為建造例外,在建造物件失敗時會拋出該例外,
-
SqlSessionException: 譯為
SqlSession
的例外,基本只會在SqlSessionManager
中使用,主要是SqlSession
使用程序中的例外, -
ReflectionException: 譯為反射例外,基本只會在反射使用時會拋出該例外,
-
ExecutorException: 譯為執行器例外,會在執行緒操作資料庫的時候拋出該例外,
-
BatchExecutorException:譯為批量執行器例外,會在執行緒批量操作資料庫的時候拋出該例外,
-
BindingException: 譯為系結例外,主要是
mapper
映射的時候會拋出該例外, -
LogException: 譯為日志例外,目前只在
LogFactory
構建日志相關的時候才會拋出該例外, -
PluginException: 譯為插件例外,目前只在
Plugin
中使用,在獲取插件資訊時候會拋出該例外,
Mybatis
型別主要是根據業務相關包放在一起,所以命名絕大多數都能夠直觀的看到原因所在,
jdbc
包
背景知識
- 模版模式
- 易用性
講解
模版模式
某些類通用的一些處理方法一致,但是處理物件可能存在不同,此時可以使用模版方法,抽取父類撰寫通用處理方法,子類只需實作獲取物件的方法即可,
public class SQL extends AbstractSQL<SQL> {
@Override
public SQL getSelf() {
return this;
}
}
public abstract class AbstractSQL<T> {
private static final String AND = ") \nAND (";
private static final String OR = ") \nOR (";
private final SQLStatement sql = new SQLStatement();
public abstract T getSelf();
public T UPDATE(String table) {
sql().statementType = SQLStatement.StatementType.UPDATE;
sql().tables.add(table);
return getSelf();
}
public T SET(String sets) {
sql().sets.add(sets);
return getSelf();
}
......
}
以上代碼可知:
- 若用戶需要自定
SQL
如ExplainSQL
,從而進行性能調優,此時只需要繼承AbstractSQL
即可,而無需撰寫原方法,
易用性
為了用戶使用方便和構建 SQL
的直觀性,AbstractSQL
命名采用了全大寫的模式,以此讓用戶更加易用,
SqlRunner
類
public int insert(String sql, Object... args) throws SQLException {
PreparedStatement ps;
if (useGeneratedKeySupport) {
ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
} else {
ps = connection.prepareStatement(sql);
}
try {
setParameters(ps, args);
ps.executeUpdate();
if (useGeneratedKeySupport) {
List<Map<String, Object>> keys = getResults(ps.getGeneratedKeys());
if (keys.size() == 1) {
Map<String, Object> key = keys.get(0);
Iterator<Object> i = key.values().iterator();
if (i.hasNext()) {
Object genkey = i.next();
if (genkey != null) {
try {
return Integer.parseInt(genkey.toString());
} catch (NumberFormatException e) {
//ignore, no numeric key support
}
}
}
}
}
return NO_GENERATED_KEY;
} finally {
try {
ps.close();
} catch (SQLException e) {
//ignore
}
}
}
public int update(String sql, Object... args) throws SQLException {
PreparedStatement ps = connection.prepareStatement(sql);
try {
setParameters(ps, args);
return ps.executeUpdate();
} finally {
try {
ps.close();
} catch (SQLException e) {
//ignore
}
}
}
此類在Mybatis
中沒有任何的使用,此類應該只是為了提供給用戶,讓用戶可以自定義執行相關 SQL
,分析其方法本質為原始JDBC
相關操作流程,
ScriptRunner
類
private void executeFullScript(Reader reader) {
StringBuilder script = new StringBuilder();
try {
BufferedReader lineReader = new BufferedReader(reader);
String line;
while ((line = lineReader.readLine()) != null) {
script.append(line);
script.append(LINE_SEPARATOR);
}
String command = script.toString();
println(command);
executeStatement(command);
commitConnection();
} catch (Exception e) {
String message = "Error executing: " + script + ". Cause: " + e;
printlnError(message);
throw new RuntimeSqlException(message, e);
}
}
private void executeLineByLine(Reader reader) {
StringBuilder command = new StringBuilder();
try {
BufferedReader lineReader = new BufferedReader(reader);
String line;
while ((line = lineReader.readLine()) != null) {
handleLine(command, line);
}
commitConnection();
checkForMissingLineTerminator(command);
} catch (Exception e) {
String message = "Error executing: " + command + ". Cause: " + e;
printlnError(message);
throw new RuntimeSqlException(message, e);
}
}
private void handleLine(StringBuilder command, String line) throws SQLException {
String trimmedLine = line.trim();
if (lineIsComment(trimmedLine)) {
Matcher matcher = DELIMITER_PATTERN.matcher(trimmedLine);
if (matcher.find()) {
delimiter = matcher.group(5);
}
println(trimmedLine);
} else if (commandReadyToExecute(trimmedLine)) {
command.append(line.substring(0, line.lastIndexOf(delimiter)));
command.append(LINE_SEPARATOR);
println(command);
executeStatement(command.toString());
command.setLength(0);
} else if (trimmedLine.length() > 0) {
command.append(line);
command.append(LINE_SEPARATOR);
}
}
此類的核心方法如上,可看出其本質和SqlRunner
類一致,
datasource
包
背景知識
講解
transaction
包
背景知識
講解
cursor
包
背景知識
講解
io
包
背景知識
講解
reflection
包
背景知識
講解
logging
包
背景知識
講解
scripting
包
背景知識
講解
type
包
背景知識
講解
mapping
包
背景知識
講解
builder
包
背景知識
講解
parsing
包
背景知識
講解
binding
包
背景知識
講解
annotations
和 lang
包
背景知識
講解
cache
包
背景知識
講解
plugin
包
背景知識
講解
session
包
背景知識
講解
流程詳解
總結
生活在失去所有的希望和期盼,才能一直做出納什均衡解轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/553067.html
標籤:其他
上一篇:Java基礎學習:尚硅谷 面向物件進階 客戶資訊管理軟體
下一篇:返回列表