從net到java:MyBatis快速入門
第一:這不是net與java的對比,只是我學習java相關知識梳理的筆記。
第二:這也沒有否認net,只是現在的工作需要自己會java.
第三:這不深入。只是我看了些官網和網上的影片,算是入門的總結。
1.Mybatis
MyBatis 原本是apache的一個開源項目iBatis,2010年這個項目由Apache Software Foundation遷移到了Google Code,並且改名為MyBatis,2013年11月遷移到GitHub。它是一款優秀的持久層框架,它支援自定義 SQL、存儲過程以及高級映射(ORM),支援XML或者註解來配置和映射原生類型、介面和java的POJO(Plain Old Java Objects,普通老式的Java對象)資料庫中的記錄。。
2.下載MyBatis
2.1.github
//github.com/mybatis/mybatis-3/releases
2.2Maven倉庫
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
3.入門
3.1驅動與依賴
採用maven//search.maven.org/ 查詢相關的驅動。
<dependencies>
<!--dependency>
<groupId>org.wyl</groupId>
<artifactId>[the artifact id of the block to be mounted]</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency-->
<!-- mysql驅動 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<!-- 整合log4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.4</version>
</dependency>
<!-- 測試junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
右擊pom.xml->meave 重新載入項目。包下載完即可。
將 mybatis-x.x.x.jar 文件置於 classpath 中即可。
3.2.MyBatis的功能架構
3.2.1.架構
API介面層:提供給外部使用的介面API,開發人員通過這些本地API來操縱資料庫。介面層一接收到調用請求就會調用數據處理層來完成具體的數據處理。
數據處理層:負責具體的SQL查找、SQL解析、SQL執行和執行結果映射處理等。它主要的目的是根據調用的請求完成一次資料庫操作。
- 參數映射(參數解析和參數綁定):獲取並解析映射文件中的 Statement標籤及其屬性(parameterType)。也就是解析SQL語句並為 SQL語句準備需要進行綁定的參數
- SQL解析:對 Statement【
<select>
、<update>
、<delete>
、<insert>
】標籤中的內容進行解析、拼接、封裝,最後得到一個完整的帶有佔位符的 SQL語句。也就是 JDBC中的準備 SQL語句的過程 - 結果映射(結果集解析和結果集處理):獲取配置文件中的結果集類型,並進行類型轉換,將ResultSet進行結果映射。也就是 JDBC中處理結果集的步驟。
基礎支撐層:負責最基礎的功能支撐,包括連接管理、事務管理、配置載入和快取處理,這些都是共用的東西,將他們抽取出來作為最基礎的組件。為上層的數據處理層提供最基礎的支撐。
-
管理 Mybatis與資料庫的連接方式
-
管理 Mybatis的事務
-
載入 配置文件
-
Mybatis 查詢快取
3.2.2.全局配置文件 mybatis-config
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"//mybatis.org/dtd/mybatis-3-config.dtd">
<!-- configuration標籤 => 聲明MyBatis核心配置 -->
<configuration>
<!-- environments標籤 => 設置MyBatis選用的環境資訊 -->
<environments default="development">
<environment id="development">
<!-- transactionManager標籤 => 事務管理 -->
<transactionManager type="JDBC"/>
<!-- dataSource標籤 => 配置數據源屬性 -->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!--將mapper映射文件註冊到全局配置文件中-->
<mappers>
<mapper resource="org/mybatis/example/WylMapper.xml"/>
</mappers>
</configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"//mybatis.org/dtd/mybatis-3-config.dtd">
<!-- configuration標籤 => 聲明MyBatis核心配置 -->
<configuration>
<!-- environments標籤 => 設置MyBatis選用的環境資訊 -->
<environments default="development">
<environment id="development">
<!-- transactionManager標籤 => 事務管理 -->
<transactionManager type="JDBC"/>
<!-- dataSource標籤 => 配置數據源屬性 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/wyl?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=CST"/>
<property name="username" value="nps"/>
<property name="password" value="123.nps@zst"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mappers/userMapper.xml"/>
</mappers>
</configuration>
3.2.3.SqlSessionFactory 會話工廠
-
通過Mybatis的配置資訊,使用
SqlSessionFactoryBuilder
構建器,來構建會話工廠對象 -
SqlSessionFactory 創建了 Configuration對象,使用 Configuration對象來構建SqlSession會話工廠
-
SqlSessionFactoryBuilder構建器使用了 Builder構建者設計模式
-
SqlSessionFactory 會話工廠,使用了工廠設計模式
public class MyBatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
// 定義XML核心配置文件路徑資訊
String resource = "mybatis-config.xml";
// 讀取XML核心配置文件路徑資訊
InputStream inputStream = Resources.getResourceAsStream(resource);
// 獲得實例化SQLSessionFactory
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//調用SqlSessionFactory.openSession()方法,返回SqlSession對象
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
3.2.4.SqlSession介面方法
程式通過SqlSession來操作資料庫
- SqlSession對外提供了一整套的增刪改查的api,通過api來操作資料庫
- SqlSession還能夠獲取動態的Mapper介面
- SqlSession的作用域是方法級別的,也就是從創建到銷毀必須保證在方法內完成,注意一定要在方法內部銷毀sqlSession,千萬不要忘記
- sqlSession在使用完後一定要及時關閉,尤其是在Service層,會在一個方法中同時使用多個sqlSession,每個sqlSession在用完後最好在下一行程式碼就關閉,避免影響其他sqlSession。
//從 SqlSessionFactory 中獲取 SqlSession
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UsersDao mapper = sqlSession.getMapper(UsersDao.class);
List<Users> usersInfo = mapper.getUsersInfo();
也可以
try (SqlSession session = sqlSessionFactory.openSession()) {
User user = (User) session.selectOne("org.mybatis.example.WylMapper.getUsersById",12);
}
3.2.5.pojo層
對應的資料庫表的實體類
package com.wyl.mybatis.pojo;
/**
* @創建人 王延領
* @創建時間 2021/8/16
* 描述
**/
public class Users {
private int id;
private String username;
private String password;
private String email;
private int gender;
public Users() {
}
public Users(int id, String username, String password, String email, int gender) {
this.id = id;
this.username = username;
this.password = password;
this.email = email;
this.gender = gender;
}
@Override
public String toString() {
return "Users{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
", gender=" + gender +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public int getGender() {
return gender;
}
public void setGender(int gender) {
this.gender = gender;
}
}
快速生成構造函數get set 等的方法,空白處右擊生成即可
3.2.6. dao層
與資料庫交互相關程式碼,對應Mybatis的mapper介面
package com.wyl.mybatis.dao;
import com.wyl.mybatis.pojo.Users;
import java.util.List;
import java.util.Map;
/**
* @創建人 王延領
* @創建時間 2021/8/18
* 描述
**/
public interface UserDao {
// 【select】所有用戶資訊
List<Users> getUsersInfo();
// 【select】指定用戶資訊
Users getUserInfoById(int id);
// 【update】指定用戶資訊
int updateUseInfoById(Users user);
// 【insert】指定用戶資訊
int insertUser(Users user);
// 【delete】指定用戶資訊
int deleteUserById(int id);
// 【insert】 批量用戶資訊
int insertManyUseList(List<Users> users);
// 【select】 模糊查詢
List<Users> getUsersInfoByPhantomSelect(String username);
}
3.2.7.mapper
userMapper.xml配置對應的資料庫操作映射,在mybatis-config進行註冊。
<!--如圖所示配置-->
<mappers>
<mapper resource="mappers/usersMapper.xml"/>
</mappers>
mapper如下。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"//mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- mapper標籤: 【namespace】: 指定dao層,綁定Dao -->
<mapper namespace="com.wyl.mybatis.dao.UserDao">
<!-- select sql: 綁定getUsersInfo方法,返回所有用戶資訊【id】: 綁定Dao中的方法名
【resultType】: 指定對應【類的形式】返回結果集的類型 -->
<select id="getUsersInfo" resultType="com.wyl.mybatis.pojo.Users">
select * from users
</select>
</mapper>
測試:
@Test
public void getUsersInfo() {
// 調用MyBatisUtils.getSqlSession()方法,獲取SqlSession對象
SqlSession sqlSession = MyBatisUtils.getSqlSession();
// 調用獲取到的SQLSession對象中的getMapper對象
// 反射Dao介面,動態代理Dao介面中的方法,並將這些方法存在對象【mapper】中
UserDao mapper = sqlSession.getMapper(UserDao.class);
// 調用mapper中對應方法,並設置對應的對象來接收其返回結果
// 以下為測試方法getUsersInfo() => 獲取所有Users表中資訊,並用對應類接收
List<Users> usersInfo = mapper.getUsersInfo();
// for循環遍歷輸出List集合
for (Users users : usersInfo) {
System.out.println(users);
}
// 關閉sqlSession
sqlSession.close();
}
3.3.作用域與生命周期
3.3.1.SqlSessionFactoryBuilder
這個類可以被實例化、使用和丟棄,一旦創建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 實例的最佳作用域是方法作用域(也就是局部方法變數)。 你可以重用 SqlSessionFactoryBuilder 來創建多個 SqlSessionFactory 實例,但最好還是不要一直保留著它,以保證所有的 XML 解析資源可以被釋放給更重要的事情。
3.3.2.SqlSessionFactory
SqlSessionFactory 一旦被創建就應該在應用的運行期間一直存在,沒有任何理由丟棄它或重新創建另一個實例。類似於資料庫連接池。 使用 SqlSessionFactory 的最佳實踐是在應用運行期間不要重複創建多次,多次重建 SqlSessionFactory 被視為一種程式碼「壞習慣」。因此 SqlSessionFactory 的最佳作用域是應用作用域。 有很多方法可以做到,最簡單的就是使用單例模式或者靜態單例模式。
3.3.3.SqlSession
每個執行緒都應該有它自己的 SqlSession 實例。SqlSession 的實例不是執行緒安全的,因此是不能被共享的,所以它的最佳的作用域是請求或方法作用域。 絕對不能將 SqlSession 實例的引用放在一個類的靜態域,甚至一個類的實例變數也不行。 也絕不能將 SqlSession 實例的引用放在任何類型的託管作用域中,比如 Servlet 框架中的 HttpSession。 如果你現在正在使用一種 Web 框架,考慮將 SqlSession 放在一個和 HTTP 請求相似的作用域中。 換句話說,每次收到 HTTP 請求,就可以打開一個 SqlSession,返回一個響應後,就關閉它。 這個關閉操作很重要,為了確保每次都能執行關閉操作,你應該把這個關閉操作放到 finally 塊中。
3.3.4.Mapper
是一種創建的用於綁定映射語句的介面,Mapper 介面的實例是用 SqlSession 來獲得的。同樣,從技術上來說,最廣泛的 Mapper 實例作用域像 SqlSession 一樣,使用請求作用域。確切地說,在方法被調用的時候調用 Mapper 實例,然後使用後,就自動銷毀掉。不需要使用明確的註銷。當一個請求執行正確無誤的時候,像 SqlSession 一樣,你可以輕而易舉地操控這一切。保持簡單性,保持 Mapper 在方法體作用域內。
4.xml 配置解析
MyBatis 的配置文件包含了會深深影響 MyBatis 行為的設置和屬性資訊。 配置文檔的頂層結構如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"//mybatis.org/dtd/mybatis-3-config.dtd">
<!-- 配置文件的根元素 -->
<configuration>
<!-- 屬性:定義配置外在化 -->
<properties></properties>
<!-- 設置:定義mybatis的一些全局性設置 -->
<settings>
<!-- 具體的參數名和參數值 -->
<setting name="" value=""/>
</settings>
<!-- 類型名稱:為一些類定義別名 -->
<typeAliases></typeAliases>
<!-- 類型處理器:定義Java類型與資料庫中的數據類型之間的轉換關係 -->
<typeHandlers></typeHandlers>
<!-- 對象工廠 -->
<objectFactory type=""></objectFactory>
<!-- 插件:mybatis的插件,插件可以修改mybatis的內部運行規則 -->
<plugins>
<plugin interceptor=""></plugin>
</plugins>
<!-- 環境:配置mybatis的環境 -->
<environments default="">
<!-- 環境變數:可以配置多個環境變數,比如使用多數據源時,就需要配置多個環境變數 -->
<environment id="">
<!-- 事務管理器 -->
<transactionManager type=""></transactionManager>
<!-- 數據源 -->
<dataSource type=""></dataSource>
</environment>
</environments>
<!-- 資料庫廠商標識 -->
<databaseIdProvider type=""></databaseIdProvider>
<!-- 映射器:指定映射文件或者映射類 -->
<mappers></mappers>
</configuration>
圖片來自官網
4.1.屬性(properties)
這些屬性可以在外部進行配置,並可以進行動態替換。你既可以在典型的 Java 屬性文件中配置這些屬性,也可以在 properties 元素的子元素中設置.
比如第三章的配置文件我們可以如下寫法
<!-- properties標籤 => 讀取外部properties文件 -->
<properties resource="dataSource.properties">
<property name="username" value="root"/>
<property name="password" value="123"/>
</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>
這個例子中的username和password將會由properties元素中設置的相應值來替換。driver和url屬性將會由dataSource.properties文件中對應的值來替換。這樣就為配置提供了諸多靈活選擇。
屬性也可以被傳遞到SqlSessionBuilder.build()方法中。
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, props);
// ... or ...
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment, props);
但是,這也就涉及到了優先順序的問題,如果屬性不只在一個地方配置,那麼mybatis將會按照下面的順序來載入:
1.在properties元素體內指定的屬性首先被讀取。
- 然後根據properties元素中的resource屬性讀取類路徑下屬性文件或根據url屬性指定的路徑讀取屬性文件,並覆蓋已讀取的同名屬性。
- 最後讀取作為方法參數傳遞的屬性,並覆蓋已讀取的同名屬性。
通過方法參數傳遞的屬性具有最高優先順序,resource/url 屬性中指定的配置文件次之,最低優先順序的則是 properties 元素中指定的屬性
從 MyBatis 3.4.2 開始,你可以為佔位符指定一個默認值。例如:
<dataSource type="POOLED">
<!-- ... -->
<property name="username" value="${username:ut_user}"/> <!-- 如果屬性 'username' 沒有被配置,'username' 屬性的值將為 'ut_user' -->
</dataSource>
這個特性默認是關閉的。要啟用這個特性,需要添加一個特定的屬性來開啟這個特性。例如:
<properties resource="org/mybatis/example/config.properties">
<!-- ... -->
<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/> <!-- 啟用默認值特性 -->
</properties>
4.2.設置(settings)
一個配置完整的 settings 元素的示例如下:
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
這是 MyBatis 中極為重要的調整設置,它們會改變 MyBatis 的運行時行為。 下表描述了設置中各項設置的含義、默認值等。
設置名 | 描述 | 有效值 | 默認值 |
---|---|---|---|
cacheEnabled | 全局性地開啟或關閉所有映射器配置文件中已配置的任何快取。 | true | false | true |
lazyLoadingEnabled | 延遲載入的全局開關。當開啟時,所有關聯對象都會延遲載入。 特定關聯關係中可通過設置 fetchType 屬性來覆蓋該項的開關狀態。 |
true | false | false |
aggressiveLazyLoading | 開啟時,任一方法的調用都會載入該對象的所有延遲載入屬性。 否則,每個延遲載入屬性會按需載入(參考 lazyLoadTriggerMethods )。 |
true | false | false (在 3.4.1 及之前的版本中默認為 true) |
multipleResultSetsEnabled | 是否允許單個語句返回多結果集(需要資料庫驅動支援)。 | true | false | true |
useColumnLabel | 使用列標籤代替列名。實際表現依賴於資料庫驅動,具體可參考資料庫驅動的相關文檔,或通過對比測試來觀察。 | true | false | true |
useGeneratedKeys | 允許 JDBC 支援自動生成主鍵,需要資料庫驅動支援。如果設置為 true,將強制使用自動生成主鍵。儘管一些資料庫驅動不支援此特性,但仍可正常工作(如 Derby)。 | true | false | False |
autoMappingBehavior | 指定 MyBatis 應如何自動映射列到欄位或屬性。 NONE 表示關閉自動映射;PARTIAL 只會自動映射沒有定義嵌套結果映射的欄位。 FULL 會自動映射任何複雜的結果集(無論是否嵌套)。 | NONE, PARTIAL, FULL | PARTIAL |
autoMappingUnknownColumnBehavior | 指定發現自動映射目標未知列(或未知屬性類型)的行為。NONE : 不做任何反應WARNING : 輸出警告日誌('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日誌等級必須設置為 WARN )FAILING : 映射失敗 (拋出 SqlSessionException ) |
NONE, WARNING, FAILING | NONE |
defaultExecutorType | 配置默認的執行器。SIMPLE 就是普通的執行器;REUSE 執行器會重用預處理語句(PreparedStatement); BATCH 執行器不僅重用語句還會執行批量更新。 | SIMPLE REUSE BATCH | SIMPLE |
defaultStatementTimeout | 設置超時時間,它決定資料庫驅動等待資料庫響應的秒數。 | 任意正整數 | 未設置 (null) |
defaultFetchSize | 為驅動的結果集獲取數量(fetchSize)設置一個建議值。此參數只可以在查詢設置中被覆蓋。 | 任意正整數 | 未設置 (null) |
defaultResultSetType | 指定語句默認的滾動策略。(新增於 3.5.2) | FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(等同於未設置) | 未設置 (null) |
safeRowBoundsEnabled | 是否允許在嵌套語句中使用分頁(RowBounds)。如果允許使用則設置為 false。 | true | false | False |
safeResultHandlerEnabled | 是否允許在嵌套語句中使用結果處理器(ResultHandler)。如果允許使用則設置為 false。 | true | false | True |
mapUnderscoreToCamelCase | 是否開啟駝峰命名自動映射,即從經典資料庫列名 A_COLUMN 映射到經典 Java 屬性名 aColumn。 | true | false | False |
localCacheScope | MyBatis 利用本地快取機制(Local Cache)防止循環引用和加速重複的嵌套查詢。 默認值為 SESSION,會快取一個會話中執行的所有查詢。 若設置值為 STATEMENT,本地快取將僅用於執行語句,對相同 SqlSession 的不同查詢將不會進行快取。 | SESSION | STATEMENT | SESSION |
jdbcTypeForNull | 當沒有為參數指定特定的 JDBC 類型時,空值的默認 JDBC 類型。 某些資料庫驅動需要指定列的 JDBC 類型,多數情況直接用一般類型即可,比如 NULL、VARCHAR 或 OTHER。 | JdbcType 常量,常用值:NULL、VARCHAR 或 OTHER。 | OTHER |
lazyLoadTriggerMethods | 指定對象的哪些方法觸發一次延遲載入。 | 用逗號分隔的方法列表。 | equals,clone,hashCode,toString |
defaultScriptingLanguage | 指定動態 SQL 生成使用的默認腳本語言。 | 一個類型別名或全限定類名。 | org.apache.ibatis.scripting.xmltags.XMLLanguageDriver |
defaultEnumTypeHandler | 指定 Enum 使用的默認 TypeHandler 。(新增於 3.4.5) |
一個類型別名或全限定類名。 | org.apache.ibatis.type.EnumTypeHandler |
callSettersOnNulls | 指定當結果集中值為 null 的時候是否調用映射對象的 setter(map 對象時為 put)方法,這在依賴於 Map.keySet() 或 null 值進行初始化時比較有用。注意基本類型(int、boolean 等)是不能設置成 null 的。 | true | false | false |
returnInstanceForEmptyRow | 當返回行的所有列都是空時,MyBatis默認返回 null 。 當開啟這個設置時,MyBatis會返回一個空實例。 請注意,它也適用於嵌套的結果集(如集合或關聯)。(新增於 3.4.2) |
true | false | false |
logPrefix | 指定 MyBatis 增加到日誌名稱的前綴。 | 任何字元串 | 未設置 |
logImpl | 指定 MyBatis 所用日誌的具體實現,未指定時將自動查找。 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 未設置 |
proxyFactory | 指定 Mybatis 創建可延遲載入對象所用到的代理工具。 | CGLIB | JAVASSIST | JAVASSIST (MyBatis 3.3 以上) |
vfsImpl | 指定 VFS 的實現 | 自定義 VFS 的實現的類全限定名,以逗號分隔。 | 未設置 |
useActualParamName | 允許使用方法簽名中的名稱作為語句參數名稱。 為了使用該特性,你的項目必須採用 Java 8 編譯,並且加上 -parameters 選項。(新增於 3.4.1) |
true | false | true |
configurationFactory | 指定一個提供 Configuration 實例的類。 這個被返回的 Configuration 實例用來載入被反序列化對象的延遲載入屬性值。 這個類必須包含一個簽名為static Configuration getConfiguration() 的方法。(新增於 3.2.3) |
一個類型別名或完全限定類名。 | 未設置 |
shrinkWhitespacesInSql | 從SQL中刪除多餘的空格字元。請注意,這也會影響SQL中的文字字元串。 (新增於 3.5.5) | true | false | false |
defaultSqlProviderType | Specifies an sql provider class that holds provider method (Since 3.5.6). This class apply to the type (or value ) attribute on sql provider annotation(e.g. @SelectProvider ), when these attribute was omitted. |
A type alias or fully qualified class name | Not set |
4.3.類型別名(typeAliases)
類型別名可為 Java 類型設置一個縮寫名字。 它僅用於 XML 配置,意在降低冗餘的全限定類名書寫。
<typeAlias alias="user" type="com.wyl.mybatis.pojo.Users"></typeAlias>
當這樣配置時,user
可以用在任何使用 com.wyl.mybatis.pojo.Users
的地方。
在使用註解的時候也可以指定一個包名,再使用其類的時候直接小寫類名即可。.比如:
<!--除非使用註解,否則不支援自定義別名-->
<package name="com.wyl.mybatis.dao"/>
4.4.映射器(mappers)
定義SQL映射語句,指定MyBatis尋找SQL語句。
-
- 使用相對於類路徑的資源引用 【推薦】:
<mappers>
<mapper resource="mappers/userMapper.xml"/>
</mappers>
- 使用映射器介面實現類的完全限定類名
<mappers>
<mapper class="com.wyl.mybatis.dao.UsersDao"/>
</mappers>
- 將包內的映射器介面實現全部註冊為映射器(注意相對位置)
<mappers>
<package name="com.wyl.mybatis.dao"/>
</mappers>
- 使用完全限定資源定位符(URL) 【不推薦使用】
<mappers>
<mapper url="file:///var/mappers/usersMapper.xml"/>
</mappers>
4.5.環境配置(environments)
MyBatis 可以配置成適應多種環境,這種機制有助於將 SQL 映射應用於多種資料庫之中, 現實情況下有多種理由需要這麼做。例如,開發、測試和生產環境需要有不同的配置;或者想在具有相同 Schema 的多個生產資料庫中使用相同的 SQL 映射。還有許多類似的使用場景。
不過要記住:儘管可以配置多個環境,但每個 SqlSessionFactory 實例只能選擇一種環境。
所以,如果你想連接兩個資料庫,就需要創建兩個 SqlSessionFactory 實例,每個資料庫對應一個。
<environments default="oracle">
<environment id="mysql">
<!--mysql 配置-->
</environment>
<environment id="oracle">
<!--oracle 配置-->
</environment>
</environments>
4.6.事務管理器(transactionManager)
在 MyBatis 中有兩種類型的事務管理器(也就是 type=”[JDBC|MANAGED]”):
在 MyBatis 中有兩種類型的事務管理器(也就是 type=”[JDBC|MANAGED]”):
-
JDBC – 這個配置直接使用了 JDBC 的提交和回滾設施,它依賴從數據源獲得的連接來管理事務作用域。
-
MANAGED – 這個配置幾乎沒做什麼。它從不提交或回滾一個連接,而是讓容器來管理事務的整個生命周期(比如 JEE 應用伺服器的上下文)。 默認情況下它會關閉連接。然而一些容器並不希望連接被關閉,因此需要將 closeConnection 屬性設置為 false 來阻止默認的關閉行為。例如:
<transactionManager type="MANAGED">
<property name="closeConnection" value="false"/>
</transactionManager>
提示 如果你正在使用 Spring + MyBatis,則沒有必要配置事務管理器,因為 Spring 模組會使用自帶的管理器來覆蓋前面的配置。
這兩種事務管理器類型都不需要設置任何屬性。它們其實是類型別名,換句話說,你可以用 TransactionFactory 介面實現類的全限定名或類型別名代替它們。
public interface TransactionFactory {
default void setProperties(Properties props) { // 從 3.5.2 開始,該方法為默認方法
// 空實現
}
Transaction newTransaction(Connection conn);
Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}
在事務管理器實例化後,所有在 XML 中配置的屬性將會被傳遞給 setProperties() 方法。你的實現還需要創建一個 Transaction 介面的實現類,這個介面也很簡單:
public interface Transaction {
Connection getConnection() throws SQLException;
void commit() throws SQLException;
void rollback() throws SQLException;
void close() throws SQLException;
Integer getTimeout() throws SQLException;
}
使用這兩個介面,你可以完全自定義 MyBatis 對事務的處理。
4.7.數據源(dataSource)
-
【官方聲明】:dataSource 元素使用標準的 JDBC 數據源介面來配置 JDBC 連接對象的資源。
-
【官方聲明】:大多數 MyBatis 應用程式會按示例中的例子來配置數據源。雖然數據源配置是可選的,但如果要啟用延遲載入特性,就必須配置數據源。
-
【官方聲明】:有三種內建的數據源類型(也就是 type=”[UNPOOLED|POOLED|JNDI]”)
-
UNPOOLED– 這個數據源的實現會每次請求時打開和關閉連接。雖然有點慢,但對那些資料庫連接可用性要求不高的簡單應用程式來說,是一個很好的選擇。 性能表現則依賴於使用的資料庫,對某些資料庫來說,使用連接池並不重要,這個配置就很適合這種情形。
-
POOLED– 這種數據源的實現利用「池」的概念將 JDBC 連接對象組織起來,避免了創建新的連接實例時所必需的初始化和認證時間。 這種處理方式很流行,能使並發 Web 應用快速響應請求。
-
JNDI – 這個數據源實現是為了能在如 EJB 或應用伺服器這類容器中使用,容器可以集中或在外部配置數據源,然後放置一個 JNDI 上下文的數據源引用。
-
MyBatis默認數據源類型 => 【POOLED】
-
數據源類型: dbcp c3p0 druid hikari
4.8.對象工廠(objectFactory)
MyBatis 每次創建結果對象的新實例時,它都會使用一個對象工廠(ObjectFactory)實例來完成。 默認的對象工廠需要做的僅僅是實例化目標類,要麼通過默認構造方法,要麼在參數映射存在的時候通過參數構造方法來實例化。 如果想覆蓋對象工廠的默認行為,則可以通過創建自己的對象工廠來實現.
自定義對象工廠
ObjectFactory是個介面類,其默認實現類是DefaultObjectFactory。在 MyBatis 中,默認的DefaultObjectFactory要做的就是實例化查詢結果對應的目標類,有兩種方式可以將查詢結果的值映射到對應的目標類:一種是通過目標類的默認構造方法,另外一種就是通過目標類的有參構造方法。
4.8.1.自定義對象工廠
MyBatis允許註冊自定義的ObjectFactory,只需要實現介面 org.apache.ibatis.reflection.factory.ObjectFactory即可。但是在大部分的情況下,我們都不需要自定義ObjectFactory對象工廠,只需要繼承系統已經實現好的 DefaultObjectFactory ,通過一定的改寫來完成我們所需要的工作。
有時候在新建一個新對象(構造方法或者有參構造方法),在得到對象之前需要處理一些邏輯,或者在執行該類的有參構造方法時,在傳入參數之前,要對參數進行一些處理,這時就可以創建自己的 ObjectFactory 來載入該類型的對象。如下所示,增加了日誌列印功能:
public class MyObjectFactory extends DefaultObjectFactory
{
private static final long serialVersionUID = 1L;
Logger log = Logger.getLogger(MyObjectFactory.class);
private Object temp = null;
@Override
public void setProperties(Properties properties)
{
super.setProperties(properties);
}
@Override
public <T> T create(Class<T> type)
{
T result = super.create(type);
log.info("創建對象:" + result.toString());
return result;
}
@Override
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs)
{
T result = super.create(type, constructorArgTypes, constructorArgs);
log.info("創建對象:" + result.toString());
return result;
}
@Override
public <T> boolean isCollection(Class<T> type)
{
return super.isCollection(type);
}
然後,需要在 SqlMapConfig.xml 全局配置文件中配置該自定義對象工廠即可,程式碼如下:
<objectFactory type="cn.mybatis.mydemo.MyObjectFactory">
<property name="key" value="value" />
</objectFactory>
這樣 MyBatis 就會採用配置的 MyObjectFactory 來生成結果集對象,採用下面的程式碼進行測試。
public class MyBatisDemo
{
public static void main(String[] args) throws IOException
{
Logger log = Logger.getLogger(MyBatisDemo.class);
InputStream config = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(config);
SqlSession session = factory .openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = userMapper.getUser(1L);
}
}
4.9.插件
4.9.1.插件的分類
第一類:插件是對系統的一種補充,例如在分散式系統中,可以使用插件的方式,實現記憶體插件、磁碟插件、線性網路插件、Paxos插件等。此類插件等同於組件。
第二類:插件是對系統默認功能的自定義修改,例如mybatis裡面自定義插件,它實現的攔截器的功能。此類插件等同於攔截器。
4.9.2.MyBatis攔截器插件
MyBatis允許用戶在已映射語句執行過程中的某一點進行攔截調用。MyBatis使用插件來攔截的方法調用,故此MyBatis插件通常稱為:Mybatis攔截器。默認情況下,MyBatis允許使用插件來攔截的對象包括下面的四大金剛:
- Executor:MyBatis的執行器,用於執行增刪改查操作
- ParameterHandler:處理SQL的參數對象
- ResultSetHandler:處理SQL的返回結果集
- StatementHandler:資料庫的處理對象,用於執行SQL語句
在Java裡面,我們想攔截某個對象,只需要把這個對象包裝一下,用程式碼行話來說,就是重新生成一個代理對象。
4.9.2.註解實例
@Intercepts({
@Signature(
type=Executor.class,
method="query",
args={MappedStatement.class,Object.class,RowBounds.class,ResultHandler.class}
)
})
public class MyInterceptor implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
return invocation.proceed();
}
public Object plugin(Object target) {
return Plugin.wrap(target,this);
}
public void setProperties(Properties arg0) {}
@Intercepts 攔截器註解,此註解聲明此類是一個插件類。其中可以聲明多個 @Signature 簽名資訊註解,type 為攔截的方法所屬的介面類型,method 為攔截的方法名稱,args 是參數資訊。
intercept 方法是一個對目標方法進行攔截的抽象方法,而 plugin 方法的作用是將攔截器插入目標對象。
setProperties 方法的作用是將全局配置文件中的參數注入插件類中。
MyBatis 全局配置文件中配置該插件即可
<plugins>
<plugin interceptor="com.wyl.mybatis.unit.MyInterceptor"></plugin>
</plugins>
4.10.typeHandlers(類型處理器)
在JDBC中,需要在PreparedStatement對象中設置那些已經預編譯過的SQlL語句的參數。執行SQL後,會通過ResultSet對象獲取得到資料庫的數據,而這些在Mybatis是根據數據的類型通過typeHandler來實現的。
在typeHandler中,分為jdbcType和javatype,其中jdbcType用於定義資料庫類型,而javaType用於定義Java類型,那麼typeHandler的作用就是承擔jdbcTypr和javaType之間的相互轉換。
和別名一樣,在 MyBatis 中存在系統定義 typeHandler 和自定義 typeHandler。MyBatis 會根據 javaType 和資料庫的 jdbcType 來決定採用哪個 typeHandler 處理這些轉換規則。系統提供的 typeHandler 能覆蓋大部分場景的要求,但是有些情況下是不夠的,比如我們有特殊的轉換規則,枚舉類就是這樣。
4.10.1.系統定義的typeHandler
這些就是MyBatis 系統已經創建好的typeHandler。在大部分的情況下無須顯式地聲明jdbcType 和javaType ,或者用typeHandler 去指定對應的typeHandler 來實現數據類型轉換,因為MyBatis 系統會自己探測。有時候需要修改一些轉換規則,比如枚舉類往往需要自己去編寫規則。
public interface TypeHandler<T> {
// 使用typeHandler通過PreparedStatement對象進行設置SQL參數的時候使用的具體方法。其中:ps是PreparedStatement對象;i是參數在SQL語句中的下標;jdbcType是資料庫類型
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
//JDBC結果集中獲取數據進行轉換,使用列名(columnName)或下標(columeIdex)獲取資料庫的數據
T getResult(ResultSet rs, String columnName) throws SQLException;
T getResult(ResultSet rs, int columnIndex) throws SQLException;
//存儲過程專用
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
4.10.2.自定義TypeHandler
從系統定義的typeHandler中可以知道,要實現typeHandler就需要去實現介面Typehandler,或者繼承BaseTypeHandler(實際上,BaseTypehandler實現了typeHandler介面)
實現TypeHandler
public class MyTypeHandler implements TypeHandler<String>{
//定義一個日誌
Logger log = Logger.getLogger(MyTypeHandler.class);
@Override
public String getResult(ResultSet rs, String columnName) throws SQLException {
String result = rs.getString(columnName);
log.info("讀取string參數1【"+result+"】");
return result;
}
@Override
public String getResult(ResultSet rs, int columnIdex) throws SQLException {
String result = rs.getString(columnIdex);
log.info("讀取string參數2【"+result+"】");
return result;
}
@Override
public String getResult(CallableStatement cs, int columnIdex) throws SQLException {
String result = cs.getString(columnIdex);
log.info("讀取string參數3【"+result+"】");
return result;
}
@Override
public void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
log.info("設置string參數【"+parameter+"】");
ps.setString(i, parameter);
}
}
在程式碼中使用自定義typeHandler
<!-- 類型轉換器 -->
<typeHandlers>
<typeHandler handler="com.wyl.mybatis.utils.MyTypeHandler" jdbcType="VARCHAR" javaType="string"/>
</typeHandlers>
or
<resultMap id="roleMapper" type="role">
<result property="id" column="id"/>
<result property="roleName" column="role_name" jdbcType="VARCHAR" javaType="string"/>
<result property="note" column="note" typeHandler="com.wyl.mybatis.utils.MyTypeHandler"/>
</resultMap>
<select id="getRole" parameterType="long" resultMap="roleMapper">
select id,role_name,note from t_role where id = #{id}
</select>
有時候配置的typeHandler太多,也可以使用包掃描的方式
<typeHandlers>
<package name="com.wyl.mybatis.unit.typeHandler"/>
</typeHandlers>
@MappedJdbcTypes(JdbcType.VARCHAR) // 表示把資料庫中的varchar類型轉成java的String類型時使用該轉換器
@MappedTypes(String.class)
public class MyTypeHandler implements TypeHandler<String>{
Logger logger = Logger.getLogger(MyTypeHandler.class);
@Override
public String getResult(ResultSet rs, String columnName) throws SQLException {
String result = rs.getString(columnName);
logger.info("讀取string參數1【" + result +"】");
return result;
}
@Override
public String getResult(ResultSet rs, int columnIndex) throws SQLException {
String result = rs.getString(columnIndex);
logger.info("讀取string參數2【" + result +"】");
return result;
}
@Override
public String getResult(CallableStatement cs, int columnIndex) throws SQLException {
String result = cs.getString(columnIndex);
logger.info("讀取string參數3【" + result +"】");
return result;
}
@Override
public void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
logger.info("設置string參數【" + parameter + "】");
ps.setString(i, parameter);
}
}
4.10.3.枚舉TypeHandler
在絕大多數情況下,typeHandler 因為枚舉而使用,MyBatis 已經定義了兩個類作為枚舉類型的支援,這兩個類分別是:
- EnumOrdinalTypeHandler。使用整數下標作為參數傳遞的(枚舉類型的默認轉換類)
<resultMap id="userMapper" type="user">
<result property="id" column="id" />
<result property="userName" column="user_name" />
<result property="password" column="passsword" />
<result property="sex" column="sex"
typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler" />
</resultMap>
<select id="getUser" resultMap="userMapper" parameterType="long">
select id,user_name,password,sex,mobile,tel,email,note from myUser
where id=#{id}
</select>
- EnumTypeHandler。使用枚舉字元串名稱作為參數傳遞的
<resultMap id="userMapper" type="com.mybatis.po.User">
<result property="id" column="id" />
<result property="userName" column="user_name" />
<result property="password" column="passsword" />
<result property="sex" column="sex"
typeHandler="org.apache.ibatis.type.EnumTypeHandler" />
</resultMap>
<select id="getUser" resultMap="userMapper" parameterType="long">
select id,user_name,password,sex,mobile,tel,email,note from myUser
where id=#{id}
</select>
4.10.4.BlobTypeHandler讀取Blob欄位
MyBatis 對資料庫的 Blob 欄位也進行了支援,它提供了一個 BlobTypeHandler,為了應付更多的場景,它還提供了 ByteArrayTypeHandler,只是它不太常用.
create table file(
id int(12) not null auto_increment,
content blob not null,
primary key(id)
);
//pojo
public class TestFile{
long id;
byte[] content;
/** setter and getter **/
}
<resultMap type="com.ssm.chapter5.pojo.TestFile" id="file">
<id column="id" property="id"/>
<id column="content" property="content" typeHandler="org.apache.ibatis.type.BlobTypeHandler"/>
</resultMap>
4.11.資料庫廠商標識(databaseIdProvider)
MyBatis 可以根據不同的資料庫廠商執行不同的語句,這種多廠商的支援是基於映射語句中的
databaseId
屬性。 MyBatis 會載入帶有匹配當前資料庫databaseId
屬性和所有不帶databaseId
屬性的語句。 如果同時找到帶有databaseId
和不帶databaseId
的相同語句,則後者會被捨棄。 為支援多廠商特性,只要像下面這樣在 mybatis-config.xml 文件中加入databaseIdProvider
即可:
<databaseIdProvider type="DB_VENDOR" />
databaseIdProvider 對應的 DB_VENDOR 實現會將 databaseId 設置為 DatabaseMetaData#getDatabaseProductName()
返回的字元串。 由於通常情況下這些字元串都非常長,而且相同產品的不同版本會返回不同的值,你可能想通過設置屬性別名來使其變短:
<databaseIdProvider type="DB_VENDOR">
<property name="SQL Server" value="sqlserver"/>
<property name="DB2" value="db2"/>
<property name="Oracle" value="oracle" />
</databaseIdProvider>
在提供了屬性別名時,databaseIdProvider 的 DB_VENDOR 實現會將 databaseId 設置為資料庫產品名與屬性中的名稱第一個相匹配的值,如果沒有匹配的屬性,將會設置為 「null」。 在這個例子中,如果 getDatabaseProductName()
返回「Oracle (DataDirect)」,databaseId 將被設置為「oracle」。
你可以通過實現介面 org.apache.ibatis.mapping.DatabaseIdProvider
並在 mybatis-config.xml 中註冊來構建自己的 DatabaseIdProvider:
public interface DatabaseIdProvider {
default void setProperties(Properties p) { // 從 3.5.2 開始,該方法為默認方法
// 空實現
}
String getDatabaseId(DataSource dataSource) throws SQLException;
}
5.映射器
5.1.Select
編寫介面dao
User getUserById(int id);
編寫對應的mapper中的sql語句,注意要寫在對應的mapper下
<!--
id:對應的dao介面
resultType:sql語句執行的返回值
parameterType : 參數類型
User:為別名
-->
<select id="getUserById" parameterType="int" resultType="user">
select * from mybatis.user where id=#{id}
</select>
@Test
public void getUserInfoById(){
// 調用MyBatisUtils.getSqlSession()方法,獲取SqlSession對象
SqlSession sqlSession = MyBatisUtils.getSqlSession();
// 調用獲取到的SQLSession對象中的getMapper對象
// 反射Dao介面,動態代理Dao介面中的方法,並將這些方法存在對象【mapper】中
UsersDao mapper = sqlSession.getMapper(UsersDao.class);
Users user = mapper.getUserInfoById(2);
System.out.println(user);
// 關閉sqlSession
sqlSession.close();
}
查詢所有
// 【select】所有用戶資訊
List<Users> getUsersInfo();
<!-- select sql: 綁定getUsersInfo方法,返回所有用戶資訊 -->
<select id="getUsersInfo" resultType="com.camemax.com.camemax.pojo.Users">
select * from school.users
</select>
@Test
public void getUsersInfo(){
// 調用MyBatisUtils.getSqlSession()方法,獲取SqlSession對象
SqlSession sqlSession = MyBatisUtils.getSqlSession();
// 調用獲取到的SQLSession對象中的getMapper對象
// 反射Dao介面,動態代理Dao介面中的方法,並將這些方法存在對象【mapper】中
UsersDao mapper = sqlSession.getMapper(UsersDao.class);
// 調用mapper中對應方法,並設置對應的對象來接收其返回結果
// 以下為測試方法getUsersInfo() => 獲取所有Users表中資訊,並用對應類接收
List<Users> usersInfo = mapper.getUsersInfo();
// for循環遍歷輸出List集合
for (Users users : usersInfo) {
System.out.println(users);
}
// 關閉sqlSession
sqlSession.close();
}
5.2.insert
插入單條細膩
// 【insert】指定用戶資訊
int insertUser(Users user);
<!-- insert sql: 綁定insertUser方法,插入單個用戶資訊-->
<insert id="insertUser" parameterType="user" >
insert into users
values (#{id},#{username},#{password},#{email},#{gender})
</insert>
@Test
public void insertUsers(){
// 調用MyBatisUtils.getSqlSession()方法,獲取SqlSession對象
SqlSession sqlSession = MyBatisUtils.getSqlSession();
// 調用獲取到的SQLSession對象中的getMapper對象
// 反射Dao介面,動態代理Dao介面中的方法,並將這些方法存在對象【mapper】中
UsersDao mapper = sqlSession.getMapper(UsersDao.class);
int i = mapper.insertUser(
new Users(2, "wyl", "123456", "[email protected]", 0)
);
//提交事務
sqlSession.commit();
if ( i > 0 ){
System.out.println("Insert 成功!");
}
// 關閉sqlSession
sqlSession.close();
}
插入多條數據
// 【insert】 批量用戶資訊
int insertManyUseList(List<Users> users);
<!-- insert sql: 綁定insertManyUseMap,批量插入 -->
<insert id="insertManyUseList" >
insert into users values
/* foreach 標籤:
-【item】屬性: 表示集合中每一個元素進行迭代時的別名
- 【collection】屬性: 參數類型是一個List的時候,collection屬性值為list
- 【separator】屬性: 表示在每次進行迭代之間以什麼符號作為分隔符。
*/
<foreach item="user" collection="list" separator=",">
(#{user.id},#{user.username},#{user.password},#{user.email},#{user.gender})
</foreach>
</insert>
@Test
public void insertManyUseList(){
List<Users> users = new ArrayList<Users>();
users.add(new Users(2, "wyl", "123", "[email protected]", 20));
users.add(new Users(3, "wjm", "123456", "[email protected]", 30));
users.add(new Users(4, "王延領", "1123456", "[email protected]", 41));
users.add(new Users(5, "王經墨", "223", "[email protected]", 51));
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UsersDao mapper = sqlSession.getMapper(UsersDao.class);
int i = mapper.insertManyUseList(users);
if ( i > 0 ){
System.out.println("插入成功過!");
}
sqlSession.commit();
sqlSession.close();
}
5.3.1.主鍵回填
如果你的資料庫支援自動生成主鍵的欄位(比如 MySQL 和 SQL Server),那麼你可以設置 useGeneratedKeys=」true」,然後再把 keyProperty 設置為目標屬性就 OK 了
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
insert into users
values (#{id},#{username},#{password},#{email},#{gender})
</insert>
如果你的資料庫還支援多行插入, 你也可以傳入一個 Author
數組或集合,並返回自動生成的主鍵。
5.3.2.自定義主鍵
首先會運行 selectKey 元素中的語句,並設置 Users的 id,然後才會調用插入語句。這樣就實現了資料庫自動生成主鍵類似的行為,同時保持了 Java 程式碼的簡潔。
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
select if(max(id)==null,1,max(id)+2) from users
</selectKey>
insert into users
values (#{id},#{username},#{password},#{email},#{gender})
</insert>
5.3.update
// 【update】指定用戶資訊
int updateUseInfoById(Users user);
<!-- update sql: 綁定updateUser方法,更新指定用戶資訊 -->
<update id="updateUseInfoById" parameterType="users">
update users
set username = #{username},
password = #{password},
email = #{email},
gender = #{gender}
where id = #{id}
</update>
@Test
public void updateUseInfoById(){
SqlSession session = MyBatisUtils.getSqlSession();
UsersDao mapper = session.getMapper(UsersDao.class);
int i = mapper.updateUseInfoById(new Users(1, "王延領", "123456", "[email protected]", 1));
if ( i > 0 ){
System.out.println(mapper.getUserInfoById(1).getUsername() + " 修改成了!");
}
session.commit();
session.close();
}
5.4.delete
// 【delete】指定用戶資訊
int deleteUserById(int id);
<!-- delete sql: 綁定deleteUserById方法,刪除指定用戶資訊 -->
<delete id="deleteUserById" parameterType="int">
delete from users
where id = #{id}
</delete>
@Test
public void deleteUserInfoById(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UsersDao mapper = sqlSession.getMapper(UsersDao.class);
String willDeleteUsername = mapper.getUserInfoById(2).getUsername();
int i = mapper.deleteUserById(2);
if (i > 0){
System.out.println(willDeleteUsername + " 已刪除!");
}
sqlSession.commit();
sqlSession.close();
}
5.5.模糊查詢like
// 【select】 模糊查詢
List<Users> getUsersInfoByPhantomSelect(String username);
<!-- select sql: 綁定getUsersInfoByPhantomSelect,模糊查詢 -->
<select id="getUsersInfoByPhantomSelect" resultType="Users">
select * from users where username like #{username}
</select>
@Test
public void getUsersInfoByPhantomSelect(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UsersDao mapper = sqlSession.getMapper(UsersDao.class);
List<Users> users = mapper.getUsersInfoByPhantomSelect("%e%");
for (Users user : users) {
System.out.println(user);
}
sqlSession.close();
}
5.6.sql
這個元素可以用來定義可重用的 SQL 程式碼片段,以便在其它語句中使用。
<sql id='userCols'> user_name,pwd</sql>
<select id="getUsersInfoByPhantomSelect" resultType="Users">
select <include refid='userCols' from school.users where username like #{username}
</select>
5.7.映射結果(resultMap)
resultMap
元素是 MyBatis 中最重要最強大的元素。它可以讓你從 90% 的 JDBC ResultSets
數據提取程式碼中解放出來,並在一些情形下允許你進行一些 JDBC 不支援的操作。實際上,在為一些比如連接的複雜語句編寫映射程式碼的時候,一份 resultMap
能夠代替實現同等功能的數千行程式碼。ResultMap的設計思想是,對簡單的語句做到零配置,對於複雜一點的語句,只需要描述語句之間的關係就行了。
5.7.1結構
<!--元素的 type 屬性表示需要的 POJO,id 屬性是 resultMap 的唯一標識-->
<resultMap id="" type="">
<constructor><!-- 類再實例化時用來注入結果到構造方法 -->
<idArg/><!-- ID參數,結果為ID -->
<arg/><!-- 注入到構造方法的一個普通結果 -->
</constructor>
<id/><!-- 用於表示哪個列是主鍵 -->
<result/><!-- 注入到欄位或JavaBean屬性的普通結果 -->
<association property=""/><!-- 用於一對一關聯 -->
<collection property=""/><!-- 用於一對多、多對多關聯 -->
<discriminator javaType=""><!-- 使用結果值來決定使用哪個結果映射 -->
<case value=""/><!-- 基於某些值的結果映射 -->
</discriminator>
</resultMap>
5.7.2.使用 Map 存儲結果集
任何 select 語句都可以使用 Map 存儲結果,示例程式碼如下:
<!-- 查詢所有用戶資訊存到Map中 -->
<select id="selectAllUserMap" resultType="map">
select * from user
</select>
@Test
// 查詢所有用戶資訊存到Map中
List<Map<String, Object>> lmp = userDao.selectAllUserMap();
for (Map<String, Object> map : lmp) {
System.out.println(map);
}
上述 Map 的 key 是 select 語句查詢的欄位名(必須完全一樣),而 Map 的 value 是查詢返回結果中欄位對應的值
5.7.3.使用POJO存儲結果集
Map 用起來很方便,但可讀性稍差,有的開發者不太喜歡使用 Map,更多時候喜歡使用 POJO 的方式。
package com.wyl.mybatis.pojo;
public class User {
private Integer m_uid;
private String m_uname;
private String m_usex;
// 此處省略setter和getter方法
@Override
public String toString() {
return "User[uid=" + m_uid + ",uname=" + m_uname + ",usex=" + m_usex
+ "]";
}
}
<!-- 查詢指定用戶資訊 -->
<resultMap id="com.wyl.mybatis.pojo.User" type="users">
<!-- 類屬性【userId】映射為資料庫中的【id】欄位 -->
<id property="userId" column="id"/>
<!-- 類屬性【userName】映射為資料庫中的【name】欄位 -->
<result property="userName" column="name" />
<!-- 類屬性【userPasswd】映射為資料庫中的【password】欄位 -->
<result property="userPasswd" column="password" />
</resultMap>
<!-- 【resultMap】屬性指向<resultMap>標籤 -->
<select id="getUsersInfo" resultType="MyBatisAliasUsers" >
select * from users
</select>
@Test
// 使用resultMap映射結果集
List<User> listResultMap = userDao.selectResultMap();
for (User myUser : listResultMap) {
System.out.println(myUser);
}
5.8.分頁
5.8.1.使用limit分頁
//分頁
List<User> getUserByLimit(Map<String,Integer> map);
<!--分頁
語法:
SELECT * from user limit startIndex,pageSize;
SELECT * from user limit 3; #[0,3)
-->
<select id="getUserByLimit" parameterType="map" resultMap="User">
select * from user limit #{startIndex},#{pageSize}
</select>
//分頁
@Test
public void getUserByLimit(){
SqlSession sqlSession=MybatisUtils.getsqlSession();
UserMapper mapper =sqlSession.getMapper(UserMapper.class);
Map<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();
}
5.8.2.RowBounds分頁
//分頁2
List<User> getUserByRowBounds();
<select id="getUserByRowBounds" resultMap="User">
select * from user
</select>
//分頁2
@Test
public void getUserByRowBounds(){
SqlSession sqlSession=MybatisUtils.getsqlSession();
//RowBounds
RowBounds rowBounds = new RowBounds(1, 2);
List<User> userList = sqlSession.selectList("com.wyl.mybatis.dao.UserMapper.getUserByRowBounds",null,rowBounds);
for (User user: userList) {
System.out.println(user);
}
sqlSession.close();
}
5.8.3.使用分頁插件
5.9.級聯查詢
級聯關係是一個資料庫實體的概念,有 3 種級聯關係,分別是一對一級聯、一對多級聯以及多對多級聯
5.9.1.一對一關聯查詢
在 MyBatis 中,通過
在
- property:指定映射到實體類的對象屬性。
- column:指定表中對應的欄位(即查詢返回的列名)。
- javaType:指定映射到實體對象屬性的類型。
- select:指定引入嵌套查詢的子 SQL 語句,該屬性用於關聯映射中的嵌套查詢。
public class Teacher {
private int tid;
private String tname;
private Classes classes;
public int getTid() {
return tid;
}
public void setTid(int tid) {
this.tid = tid;
}
public String getTname() {
return tname;
}
public void setTname(String tname) {
this.tname = tname;
}
public Classes getClasses() {
return classes;
}
public void setClasses(Classes classes) {
this.classes = classes;
}
@Override
public String toString() {
return "Teacher [tid=" + tid + ", tname=" + tname + ", classes=" + classes + "]";
}
}
public class Classes {
private int cid;
private String cname;
private Teacher teacher;
public int getCid() {
return cid;
}
public void setCid(int cid) {
this.cid = cid;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
@Override
public String toString() {
return "Classes [cid=" + cid + ", cname=" + cname + ", teacher=" + teacher + "]";
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"//mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="one.to.one.classesMapper">
<!--
方式一:嵌套結果:使用嵌套結果映射來處理重複的聯合結果的子集
封裝聯表查詢的數據(去除重複的數據)
select * from classes c, teacher t where c.tid=t.tid and c.tid=#{tid}
-->
<select id="getClasses" resultMap="getClassesMap" parameterType="int">
select * from classes c ,teacher t
where c.tid=t.tid and c.tid=#{tid}
</select>
<resultMap type="one.to.one.Classes" id="getClassesMap">
<id column="cid" property="cid"/>
<result column="cname" property="cname"/>
<association property="teacher" javaType="one.to.one.Teacher">
<id column="tid" property="tid"></id>
<result column="tname" property="tname"/>
</association>
</resultMap>
<!--
方式一:嵌套結果:使用嵌套結果映射來處理重複的聯合結果的子集
封裝聯表查詢的數據(去除重複的數據)
select * from teacher t,classes c where t.cid = c.cid and t.cid=#{cid}
-->
<select id="getTeacher" resultMap="getTeacherMap" parameterType="int">
select * from teacher t,classes c
where t.cid = c.cid and t.cid=#{cid}
</select>
<resultMap type="one.to.one.Teacher" id="getTeacherMap">
<id column="tid" property="tid"/>
<result column="tname" property="tname"/>
<association property="classes" javaType="one.to.one.Classes">
<id column="cid" property="cid"/>
<result column="cname" property="cname"/>
</association>
</resultMap>
<!--
方式二:嵌套查詢:通過執行另外一個SQL映射語句來返回預期的複雜類型
SELECT * FROM classes WHERE cid=1;
SELECT * FROM teacher WHERE tid=1 //1 是上一個查詢得到的tid的值
property:別名(屬性名) column:列名 -->
<!-- 把teacher的欄位設置進去 -->
<select id="getClasses2" resultMap="getClassesMap2">
select * from classes c where c.cid = #{cid}
</select>
<resultMap type="one.to.one.Classes" id="getClassesMap2">
<id column="cid" property="cid"/>
<result column="cname" property="cname"/>
<collection property="teacher" column="tid" select="getTeacherCollection">
</collection>
</resultMap>
<select id="getTeacherCollection" resultType="one.to.one.Teacher">
select tid tid,tname tname from teacher where tid=#{tid}
</select>
</mapper>
我們這裡一對一的關聯操作,有兩種方式:
1、使用嵌套結果映射來處理重複的聯合結果的子集
2、通過執行另外一個SQL映射語句來返回預期的複雜類型
//一對一嵌套結果方式:根據教師id查詢班級資訊
@Test
public void testGetClasses(){
String statement = "one.to.one.classesMapper.getClasses";
Classes c = session.selectOne(statement, 1);
System.out.println(c);
}
//一對一嵌套結果方式:根據班級id查詢教師資訊
@Test
public void testGetTeacher(){
String statement = "one.to.one.classesMapper.getTeacher";
Teacher t = session.selectOne(statement, 1);
System.out.println(t);
}
//一對一嵌套查詢方式:根據教師id查詢班級資訊
@Test
public void testGetClasses2(){
String statement = "one.to.one.classesMapper.getClasses2";
Classes c = session.selectOne(statement, 1);
System.out.println(c);
}
5.9.2. 多對一查詢
-
SQL返回的值需要使用到類時的處理方式
-
模擬測試:多個學生對應一個老師
- MySQL測試表【Teachers】、【Students】
- 測試實體類【Teachers】、【Students】
- dao層【TeachersMapper】、【StudentsMapper】
- XML映射文件【teachersMapper.xml】、【studentsMapper.xml】
- 核心配置文件=>【mybatis-config.xml】綁定dao介面、註冊XML映射文件
- 輸出測試
- 整體目錄結構
5.9.2.1 環境搭建
MySQL創建測試數據
use school;
#教師表
DROP TABLE IF exists teachers;
create table teachers(
`tid` int(10),
`tname` varchar(20) DEFAULT NULL,
PRIMARY KEY (`tid`)
)ENGINE=INNODB DEFAULT CHARSET=utf8;
#學生表
DROP TABLE IF exists students;
create table students(
`id` int(10) ,
`name` varchar(20) DEFAULT NULL,
`tid` int(10) DEFAULT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teachers` (`tid`)
)ENGINE=INNODB DEFAULT CHARSET=utf8;
insert into teachers (`tid`,`tname`) values (1,'卡梅克斯');
insert into students (`id`,`name`,`tid`) values (1,'小紅',1);
insert into students (`id`,`name`,`tid`) values (2,'小黃',1);
insert into students (`id`,`name`,`tid`) values (3,'小黑',1);
insert into students (`id`,`name`,`tid`) values (4,'小白',1);
insert into students (`id`,`name`,`tid`) values (5,'小紫',1);
5.9.2.2 實體類與介面
-
學生相關
-
【Students】實體類
package com.camemax.pojo;
import org.apache.ibatis.type.Alias;
@Alias("students")
public class Students {
private int sid;
private String sname;
// 添加【Teachers】類屬性
private Teachers teacher;
public Students() {};
public Students(int sid, String sname, Teachers teacher) {
this.sid = sid;
this.sname = sname;
this.teacher = teacher;
}
public int getSid() {
return sid;
}
public void setSid(int sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public Teachers getTeacher() {
return teacher;
}
public void setTeacher(Teachers teacher) {
this.teacher = teacher;
}
@Override
public String toString() {
return "Students{" +
"sid=" + sid +
", sname='" + sname + '\'' +
", teacher=" + teacher +
'}';
}
}
- 【StudentsMapper】介面
package com.camemax.dao;
import com.camemax.pojo.Students;
import java.util.List;
public interface StudentsMapper {
//查詢所有學生資訊,同時輸出教師資訊
List<Students> getStudentsInfo();
}
-
教師相關
-
【Teachers】實體類
package com.camemax.pojo;
import org.apache.ibatis.type.Alias;
@Alias("teachers")
public class Teachers {
private int tid;
private String tname;
public Teachers() {};
public Teachers(int tid, String tname) {
this.tid = tid;
this.tname = tname;
}
public int getTid() {
return tid;
}
public void setTid(int tid) {
this.tid = tid;
}
public String getTname() {
return tname;
}
public void setTname(String tname) {
this.tname = tname;
}
@Override
public String toString() {
return "Teachers{" +
"tid=" + tid +
", tname='" + tname + '\'' +
'}';
}
}
- 【TeachersMapper】介面
package com.camemax.dao;
public interface TeachersMapper {
}
5.9.2.3 Mapper映射器
- mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"//mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="db.properties"/>
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
<typeAliases>
<package name="com.camemax.pojo"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${propDriver}"/>
<property name="url" value="${propUrl}"/>
<property name="username" value="${propUsername}"/>
<property name="password" value="${propPassword}"/>
</dataSource>
</environment>
</environments>
<!-- 註冊Mapper-->
<mappers>
<mapper resource="mapper/studentsMapper.xml"/>
<mapper resource="mapper/teachersMapper.xml"/>
</mappers>
</configuration>
5.9.2.4. 按查詢嵌套處理【子查詢】
- studentsMapper.xml
<!-- 按查詢嵌套處理 -->
<select resultMap="StudentsInfoMapBySelect" id="getStudentsInfo">
select * from school.students
</select>
<resultMap id="StudentsInfoMapBySelect" type="students">
<id property="sid" column="id"/>
<result property="sname" column="name"/>
<!-- 複雜類型: Teachers類
【association】: 對象
- 【property】: 設置獲取到的結果集欄位 => private Teachers teacher
- 【column】: 設置映射對應的資料庫欄位 => tid
- 【javaType】: 設置返回類型 => Teachers
- 【select】: 子查詢綁定。通過其他<select>標籤中的值,指向其他select語句 => <select id="TeachersInfo">
【collection】: 集合
-->
<association property="teacher" column="tid" javaType="Teachers" select="TeachersInfo"/>
</resultMap>
<!-- 查詢指定教師資訊 -->
<select id="TeachersInfo" resultType="teachers">
select * from school.teachers where tid = #{tid}
</select>
- teachersMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"//mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wyl.mybatis.dao.TeachersMapper">
</mapper>
5.9.2.5 按結果嵌套處理【關聯】
- studentsMapper.xml
<!-- 按結果嵌套處理 -->
<select id="getStudentsInfo" resultMap="getStudentsInfoByResult">
select s.id studentId,
s.name studentName,
t.tname teacherName
from students s,teachers t
where s.tid = t.tid;
</select>
<resultMap id="getStudentsInfoByResult" type="students">
<id property="sid" column="studentId"/>
<result property="sname" column="studentName"/>
<association property="teacher" javaType="Teachers">
<result property="tname" column="teacherName"/>
</association>
</resultMap>
- teachersMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"//mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wyl.mybatis.dao.TeachersMapper">
</mapper>
5.9.3. 一對多查詢
- 模擬測試:一名老師有多名學生 => 【面向教師】
- 本質:使用<collection>標籤完成一對多的輸出
5.9.3.1. 基於[12.1環境搭建](#12.1 環境搭建)做出的修改
- dao層 => 【TeachersDao】
package com.camemax.dao;
import com.camemax.pojo.Students;
import com.camemax.pojo.Teachers;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface TeachersMapper {
// 傳入指定教師編號,返回其下學生資訊
List<Students> getTeacherByIdHasStudents(@Param("tid") int tid);
}
- 實現類 => 【Teachers】
package com.camemax.pojo;
import org.apache.ibatis.type.Alias;
import java.util.List;
@Alias("teachers")
public class Teachers {
private int tid;
private String tname;
// 新增屬性 : 教師擁有的學生
private List<Students> teacherHasStudents;
public List<Students> getTeacherHasStudents() {
return teacherHasStudents;
}
public void setTeacherHasStudents(List<Students> teacherHasStudents) {
this.teacherHasStudents = teacherHasStudents;
}
public Teachers(int tid, String tname, List<Students> teacherHasStudents) {
this.tid = tid;
this.tname = tname;
this.teacherHasStudents = teacherHasStudents;
}
public Teachers() {};
public int getTid() {
return tid;
}
public void setTid(int tid) {
this.tid = tid;
}
public String getTname() {
return tname;
}
public void setTname(String tname) {
this.tname = tname;
}
@Override
public String toString() {
return "Teachers{" +
"tid=" + tid +
", tname='" + tname + '\'' +
", teacherHasStudents=" + teacherHasStudents +
'}';
}
}
- 實體類 => 【Students】
package com.camemax.pojo;
import org.apache.ibatis.type.Alias;
@Alias("students")
public class Students {
private int sid;
private String sname;
private int tid;
public Students(){};
public Students(int sid, String sname, int tid) {
this.sid = sid;
this.sname = sname;
this.tid = tid;
}
@Override
public String toString() {
return "Students{" +
"sid=" + sid +
", sname='" + sname + '\'' +
", tid=" + tid +
'}';
}
public int getSid() {
return sid;
}
public void setSid(int sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public int getTid() {
return tid;
}
public void setTid(int tid) {
this.tid = tid;
}
}
- 測試實現類 => 【DaoTest】
@Test
public void getStudentsByTid(){
MyBatisUtils mybatis = new MyBatisUtils();
SqlSession sqlSession = mybatis.getSqlSession();
TeachersMapper mapper = sqlSession.getMapper(TeachersDao.class);
System.out.println(mapper.getStudentsByTid(1));
sqlSession.close();
}
5.9.3.2. 按查詢嵌套處理 【子查詢】
- XML映射文件 => teachersMapper.xml
<select id="getStudentsByTid" resultMap="getStudentsByTidMapUseSelect">
select * from school.teachers where tid = #{tid}
</select>
<!-- 創建【getStudentsByTidMapUseSelect】映射結果集,實現一對多結果返回。
注意:Teachers類 使用了 @Alias("teachers")
-->
<resultMap id="getStudentsByTidMapUseSelect" type="teachers">
<id property="tid" column="tid" />
<result property="tname" column="name" />
<!-- Teachers類中新增List<Students> teacherHasStudents屬性欄位
javaType: 指定在java中的欄位類型屬性
ofType: 指定類型所屬類
select: 使resultMap綁定指定<select>標籤
column: 使resultMap傳遞指定的屬性欄位
-->
<collection property="teacherHasStudents" javaType="ArrayList" ofType="students" select="getStudentsByTid" column="tid"/>
</resultMap>
<!-- 子查詢:學生資訊 -->
<select id="getStudentsByTid" resultMap="studentsMap">
select * from school.students where tid = #{tid}
</select>
<!-- 創建【studentsMap】,映射Students類中,與Teachers表欄位不一致的屬性欄位 -->
<resultMap id="studentsMap" type="students">
<id property="sid" column="id" />
<result property="sname" column="name"/>
<!-- 不加會導致欄位【tid】結果為0 -->
<result property="tid" column="tid" />
</resultMap>
- 輸出結果
// 按查詢嵌套處理 => 子查詢 結果:
[Teachers{tid=1, tname='卡梅克斯', teacherHasStudents=[Students{sid=1, sname='小紅', tid=1}, Students{sid=2, sname='小黃', tid=1}, Students{sid=3, sname='小黑', tid=1}, Students{sid=4, sname='小白', tid=1}, Students{sid=5, sname='小紫', tid=1}]}]
5.9.3.3. 按結果嵌套處理 【關聯查詢】
- XML映射文件 => teachersMapper.xml
<select id="getTeacherByIdHasStudents" resultMap="teacherGetStudentsByResult">
select s.id studentId,s.name studentName,s.tid,t.tname teacherName,t.tid
from students s,teachers t
where s.tid = t.tid
and t.tid = #{tid}
</select>
<resultMap id="teacherGetStudentsByResult" type="teachers">
<id property="tid" column="tid"/>
<result property="tname" column="teacherName"/>
<collection property="teacherHasStudents" ofType="students">
<id property="sid" column="studentId"/>
<result property="sname" column="studentName"/>
<result property="tid" column="tid" />
</collection>
</resultMap>
- 測試結果
// 按結果嵌套處理 => 關聯查詢 結果:
[Teachers{tid=1, tname='卡梅克斯', teacherHasStudents=[Students{sid=1, sname='小紅', tid=1}, Students{sid=2, sname='小黃', tid=1}, Students{sid=3, sname='小黑', tid=1}, Students{sid=4, sname='小白', tid=1}, Students{sid=5, sname='小紫', tid=1}]}]
5.10.快取
如果每次查詢都連接資料庫 ,耗資源!一次查詢的結果,給他暫存在一個可以直接取到的地方!–> 記憶體 : 快取
我們再次查詢相同數據的時候,直接走快取,就不用走資料庫了
所以經常查詢又不常改變的數據可以使用快取,減少和資料庫的交互次數,減少系統開銷,提高系統效率.
-
MyBatis包含一個非常強大的查詢快取特性,它可以非常方便地訂製和配置快取。快取可以極大的提升查詢效率。
-
MyBatis系統中默認定義了兩級快取:一級快取和二級快取.
-
默認情況下,只有一級快取開啟。(SqlSession級別的快取,也稱為本地快取)
-
二級快取需要手動開啟和配置,他是基於namespace級別的快取。
-
為了提高擴展性,MyBatis定義了快取介面Cache。我們可以通過實現Cache介面來自定義二級快取
5.10.1.一級快取
也叫本地快取: SqlSession.
-
一級快取默認是開啟的,只在一次SqlSession中有效,也就是拿到連接到關閉連接這個區間段!
-
一級快取就是一個Map
-
在同一個 SqlSession 中, Mybatis 會把執行的方法和參數通過演算法生成快取的鍵值, 將鍵值和結果存放在一個 Map 中, 如果後續的鍵值一樣, 則直接從 Map 中獲取數據;
-
不同的 SqlSession 之間的快取是相互隔離的;
-
用一個 SqlSession, 可以通過配置使得在查詢前清空快取;
-
任何的 UPDATE, INSERT, DELETE 語句都會清空快取。
5.10.2.二級快取
- 級快取也叫全局快取,一級快取作用域太低了,所以誕生了二級快取
- 基於namespace級別的快取,一個名稱空間,對應一個二級快取;
- 工作機制
3.1.一個會話查詢一條數據,這個數據就會被放在當前會話的一級快取中;
3.2.如果當前會話關閉了,這個會話對應的一級快取就沒了;但是我們想要的是,會話關閉了,一級快取中的數據被保存到二級快取中;
3.3.新的會話查詢資訊,就可以從二級快取中獲取內容;
3.4.不同的mapper查出的數據會放在自己對應的快取(map)中;
步驟:
開啟全局快取
<!--顯示的開啟全局快取-->
<setting name="cacheEnabled" value="true"/>
在要使用二級快取的Mapper中開啟
<!--在當前Mapper.xml中使用二級快取-->
<cache/>
也可以自定義參數
<!--在當前Mapper.xml中使用二級快取-->
<cache eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
小結:
- 只要開啟了二級快取,在同一個Mapper下就有效
- 所有的數據都會先放在一級快取中;
- 只有當會話提交,或者關閉的時候,才會提交到二級緩衝中
6.動態sql
動態 SQL 是 MyBatis 的強大特性之一。
MyBatis 3 替換了之前的大部分元素,大大精簡了元素種類,現在要學習的元素種類比原來的一半還要少。
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
6.1.if
使用動態 SQL 最常見情景是根據條件包含 where 子句的一部分。
<select id="findActiveBlogWithTitleLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE state = 『ACTIVE』
<if test="title != null">
AND title like #{title}
</if>
</select>
6.2.choose、when、otherwise
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = 『ACTIVE』
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
6.3.trim、where、set
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
6.3.foreach
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
6.4.script
@Update({"<script>",
"update Author",
" <set>",
" <if test='username != null'>username=#{username},</if>",
" <if test='password != null'>password=#{password},</if>",
" <if test='email != null'>email=#{email},</if>",
" <if test='bio != null'>bio=#{bio}</if>",
" </set>",
"where id=#{id}",
"</script>"})
void updateAuthorValues(Author author);
6.5.bind
<select id="selectBlogsLike" resultType="Blog">
<bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
SELECT * FROM BLOG
WHERE title LIKE #{pattern}
</select>
6.6.trim
<select id="queryBlogsByTrim" parameterType="blogs">
select * from test.blog
<trim prefix="WHERE" prefixOverride="AND |OR ">
<if test="titleMap != null"> AND title = #{titleMap}</if>
<if test="authorMap != null"> OR author = #{authorMap}</if>
</trim>
</select>
<update id="updateBlogInfoByTrim" parameterType="map">
update test.blog
<trim prefix="SET" suffixOverride=",">
<if test="titleMap != null"> title = #{titleMap},</if>
<if test="authorMap != null"> author = #{authorMap},</if>
</trim>
where id = #{idMap}
</update>
6.7.多資料庫支援
如果配置了 databaseIdProvider,你就可以在動態程式碼中使用名為 「_databaseId」 的變數來為不同的資料庫構建特定的語句。比如下面的例子:
<insert id="insert">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
<if test="_databaseId == 'oracle'">
select seq_users.nextval from dual
</if>
<if test="_databaseId == 'db2'">
select nextval for seq_users from sysibm.sysdummy1"
</if>
</selectKey>
insert into users values (#{id}, #{name})
</insert>
6.8.動態 SQL 中的插入腳本語言
MyBatis 從 3.2 版本開始支援插入腳本語言,這允許你插入一種語言驅動,並基於這種語言來編寫動態 SQL 查詢語句。
可以通過實現以下介面來插入一種語言:
public interface LanguageDriver {
ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql);
SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType);
SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType);
}
實現自定義語言驅動後,你就可以在 mybatis-config.xml 文件中將它設置為默認語言:
<typeAliases>
<typeAlias type="org.sample.MyLanguageDriver" alias="myLanguage"/>
</typeAliases>
<settings>
<setting name="defaultScriptingLanguage" value="myLanguage"/>
</settings>
或者,你也可以使用 lang
屬性為特定的語句指定語言:
<select id="selectBlog" lang="myLanguage">
SELECT * FROM BLOG
</select>
或者,在你的 mapper 介面上添加 @Lang
註解:
public interface Mapper {
@Lang(MyLanguageDriver.class)
@Select("SELECT * FROM BLOG")
List<Blog> selectBlog();
}
7.註解
優缺點:
- 優點:省去複雜的mapper映射器中的sql程式碼相關配置
- 缺點:無法執行複雜的SQL,例如:存在欄位異常不匹配時,使用註解執行SQL容易出現找不到值的情況(查詢結果為’null’)
@Select("select * from school.users where id = #{userId}")
Users getUserInfoById(@Param("userId") int id);
@Insert("insert into school.users value(#{id},#{username},#{password},#{email},#{gender})")
int insertUserInfo(@Param("userId") int id
,@Param("userName") String username
,@Param("userPassword") String password
,@Param("userEmail") String email
,@Param("userGender") int gender
);
@Delete("delete from school.users where id = #{userId}")
int deleteUserInfoById(@Param("userId") int id);
@Update("update school.users set username = #{userName} , password = #{userPassword} , email = #{userEmail} , gender = #{userGender} where id = #{userId}")
int updateUserInfoById(
@Param("userId") int id
,@Param("userName") String username
,@Param("userPassword") String password
,@Param("userEmail") String email
,@Param("userGender") int gender
);
8.日誌
Mybatis 通過使用內置的日誌工廠提供日誌功能。內置日誌工廠將會把日誌工作委託給下面的實現之一:
- SLF4J
- Apache Commons Logging
- Log4j 2
- Log4j
- JDK logging
在mybatis-config.xml中我們已經設置了日誌的默認值為STDOUT_LOGGING標準日誌輸出:
<!--配日誌-SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
測試輸出
8.1.Log4J
- Log4j是Apache的一個開源項目,通過使用Log4j,可以控制日誌資訊輸送的目的地是控制台、文件、GUI組件,甚至是套介面伺服器、NT的事件記錄器、UNIX Syslog守護進程等;
- 控制每一條日誌的輸出格式;通過定義每一條日誌資訊的級別,能夠更加細緻地控制日誌的生成過程。
- 通過一個配置文件來靈活地進行配置,而不需要修改應用的程式碼。
- 導入Maven依賴
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
- 映射器開啟日誌功能
<configuration>
<settings>
...
<setting name="logImpl" value="LOG4J"/>
...
</settings>
</configuration>
- 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/MyBatis.txt
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
8.2.日誌類使用
導入Apache-Log4J包
import org.apache.log4j.Logger;
使用反射當前對象來創建當前Logger對象
// 創建靜態變數Logger對象 => logger
// 使用當前類.class反射創建logger對象
static Logger logger = logger.getLogger(UsersDaoTest.class)
@Test
public void log4jTest(){
logger.info("info: 日誌輸出等級【Info】");
logger.debug("debug: 日誌輸出等級【DEBUG】");
logger.error("error: 日誌輸出等級【ERROR】");
}