💂 個人主頁: Java程式魚
🤟 整個Java 體系的面試題我都會分享,大家可以持續關注
💬 如果文章對你有幫助、歡迎關注、點贊、收藏(一鍵三連)和訂閱專欄哦,
💅 有任何問題歡迎私信,看到會及時回復!
| 序號 | 內容 | 鏈接地址 |
|---|---|---|
| 1 | Java基礎知識面試題 | https://blog.csdn.net/qq_35620342/article/details/119636436 |
| 2 | Java集合容器面試題 | 待分享 |
| 3 | Java并發編程面試題 | 待分享 |
| 4 | Java例外面試題 | 待分享 |
| 5 | JVM面試題 | 待分享 |
| 6 | Java Web面試題 | https://blog.csdn.net/qq_35620342/article/details/119642114 |
| 7 | Spring面試題 | https://blog.csdn.net/qq_35620342/article/details/119956512 |
| 8 | Spring MVC面試題 | https://blog.csdn.net/qq_35620342/article/details/119965560 |
| 9 | Spring Boot面試題 | 待分享 |
| 10 | MyBatis面試題 | https://blog.csdn.net/qq_35620342/article/details/119956541 |
| 11 | Spring Cloud面試題 | 待分享 |
| 12 | Redis面試題 | https://blog.csdn.net/qq_35620342/article/details/119575020 |
| 13 | MySQL資料庫面試題 | https://blog.csdn.net/qq_35620342/article/details/119930887 |
| 14 | RabbitMQ面試題 | 待分享 |
| 15 | Dubbo面試題 | 待分享 |
| 16 | Linux面試題 | 待分享 |
| 17 | Tomcat面試題 | 待分享 |
| 18 | ZooKeeper面試題 | 待分享 |
| 19 | Netty面試題 | 待分享 |
| 20 | 資料結構與演算法面試題 | 待分享 |
文章目錄
- 1.MyBatis是什么?
- 2.ORM是什么
- 3.為什么說Mybatis是半自動ORM映射工具?它與全自動的區別在哪里?
- 4.傳統JDBC開發存在的問題
- 5.JDBC編程有哪些不足之處,MyBatis是如何解決這些問題的?
- 6.MyBatis與Hibernate
- 7.Mybatis優缺點
- 8.MyBatis框架適用場景
- 9.MyBatis編程步驟是什么樣的?
- 10.請說說MyBatis的作業原理
- 11.MyBatis功能架構
- 12.MyBatis的框架架構設計是怎么樣的
- 13.為什么需要預編譯
- 14.Mybatis都有哪些Executor執行器?它們之間的區別是什么?
- 15.Mybatis中如何指定使用哪一種Executor執行器?
- 16.Mybatis延遲加載
- 17.#{}和${}的區別
- 18.模糊查詢like陳述句該怎么寫
- 19.在mapper中如何傳遞多個引數
- 20.Mybatis如何執行批量操作
- 21.如何獲取生成的主鍵
- 22.當物體類中的屬性名和表中的欄位名不一樣 ,怎么辦
- 23.Mapper 撰寫有哪幾種方式?
- 24.MyBatis介面系結的幾種方式
- 25.使用MyBatis的mapper介面呼叫時有哪些要求?
- 26.通常一個mapper.XML對應一個DAO介面,DAO是否可以多載?
- 27.MyBatis不同映射檔案中的id是否可以重復?
- 28.簡述Mybatis的Xml映射檔案和Mybatis內部資料結構之間的映射關系?
- 29.Mybatis是如何將sql執行結果封裝為目標物件并回傳的?都有哪些映射形式?
- 30.Xml映射檔案中,除了常見的select|insert|updae|delete標簽之外,還有哪些標簽?
- 31.MyBatis映射檔案中A標簽參考B標簽,如果B標簽在A的后面定義,可以嗎?
- 32.MyBatis實作一對一,一對多有幾種方式,怎么操作的?
- 33.Mybatis是否可以映射Enum列舉類?
- 34.Mybatis動態sql是做什么的?都有哪些動態sql?能簡述一下動態sql的執行原理不?
- 35.Mybatis是如何進行分頁的?
- 36.簡述Mybatis的插件運行原理,以及如何撰寫一個插件,
- 37.Mybatis快取機制
1.MyBatis是什么?
MyBatis 是一款優秀的持久層框架,它支持定制化 SQL、存盤程序以及高級映射,MyBatis 避免了幾乎所有的 JDBC 代碼和手動設定引數以及獲取結果集,MyBatis 可以使用簡單的 XML 或注解來配置和映射原生型別、介面和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 物件)為資料庫中的記錄,
2.ORM是什么
ORM(Object Relational Mapping),物件關系映射,是一種為了解決關系型資料庫資料與簡單Java物件(POJO)的映射關系的技術,簡單的說,ORM是通過使用描述物件和資料庫之間映射的元資料,將程式中的物件自動持久化到關系型資料庫中,
3.為什么說Mybatis是半自動ORM映射工具?它與全自動的區別在哪里?
Hibernate屬于全自動ORM映射工具,使用Hibernate查詢關聯物件或者關聯集合物件時,可以根據物件關系模型直接獲取,所以它是全自動的,
而Mybatis在查詢關聯物件或關聯集合物件時,需要手動撰寫sql來完成,所以,稱之為半自動ORM映射工具,
4.傳統JDBC開發存在的問題
JDBC是Java連接關系資料庫的底層API,利用JDBC開發持久層存在諸多問題,而MyBatis正是為了解決JDBC的以下問題而生的,
- 資料庫連接創建、釋放頻繁造成系統資源浪費從而影響系統性能,如果使用資料庫鏈接池可解決此問題,
- Sql陳述句在代碼中硬編碼,造成代碼不易維護,實際應用sql變化的可能較大,sql變動需要改變java代碼,
- 使用PreparedStatement向占有位符號傳引數存在硬編碼,因為sql陳述句的where條件不一定,可能多也可能少,修改sql還要修改代碼,系統不易維護,
- 對結果集決議存在硬編碼(查詢列名),sql變化導致決議代碼變化,系統不易維護,如果能將資料庫記錄封裝成pojo物件決議比較方便,
5.JDBC編程有哪些不足之處,MyBatis是如何解決這些問題的?
1、資料庫連接創建、釋放頻繁造成系統資源浪費從而影響系統性能,如果使用資料庫鏈接池可解決此問題,
解決:在mybatis-config.xml中配置資料鏈接池,使用連接池管理資料庫連接,
2、Sql陳述句在代碼中硬編碼,造成代碼不易維護,實際應用sql變化的可能較大,sql變動需要改變java代碼,
解決:將Sql陳述句配置在XXXXmapper.xml檔案中與java代碼分離,
3、使用PreparedStatement向占有位符號傳引數存在硬編碼,因為sql陳述句的where條件不一定,可能多也可能少,修改sql還要修改代碼,系統不易維護,
解決: Mybatis自動將java物件映射至sql陳述句,
4、對結果集決議存在硬編碼(查詢列名),sql變化導致決議代碼變化,系統不易維護,如果能將資料庫記錄封裝成pojo物件決議比較方便,
解決:Mybatis自動將sql執行結果映射至java物件,
6.MyBatis與Hibernate
MyBatis和Hibernate不同,它不完全是一個ORM框架,因為MyBatis需要程式員自己撰寫Sql陳述句,不過Mybatis可以通過XML或注解方式靈活配置要運行的sql陳述句,并將java物件和sql陳述句映射生成最終執行的sql,最后將sql執行的結果再映射生成java物件,
MyBatis學習門檻低,簡單易學,程式員直接撰寫原生態sql,可嚴格控制sql執行性能,靈活度高,非常適合對關系資料模型要求不高的軟體開發,例如互聯網軟體、企業運營類軟體等,因為這類軟體需求變化頻繁,一但需求變化要求成果輸出迅速,但是靈活的前提是mybatis無法做到資料庫無關性,如果需要實作支持多種資料庫的軟體則需要自定義多套sql映射檔案,作業量大,
Hibernate物件/關系映射能力強,對于關系模型要求高的軟體(例如需求固定的定制化軟體)如果用Hibernate開發可以節省很多代碼,提高效率,但是Hibernate的學習門檻高,要精通門檻更高,而且怎么設計O/R映射,在性能和物件模型之間如何權衡,以及怎樣用好Hibernate需要具有很強的經驗和能力才行,
7.Mybatis優缺點
優點
與傳統的資料庫訪問技術相比,ORM有以下優點:
- 基于SQL陳述句編程,相當靈活,不會對應用程式或者資料庫的現有設計造成任何影響,SQL寫在XML里,解除sql與程式代碼的耦合,便于統一管理;提供XML標簽,支持撰寫動態SQL陳述句,并可重用
- 與JDBC相比,減少了50%以上的代碼量,消除了JDBC大量冗余的代碼,不需要手動開關連接
- 很好的與各種資料庫兼容(因為MyBatis使用JDBC來連接資料庫,所以只要JDBC支持的資料庫MyBatis都支持)
- 提供映射標簽,支持物件與資料庫的ORM欄位關系映射;提供物件關系映射標簽,支持物件關系組件維護
- 能夠與Spring很好的集成
缺點
- SQL陳述句的撰寫作業量較大,尤其當欄位多、關聯表多時,對開發人員撰寫SQL陳述句的功底有一定要求
- SQL陳述句依賴于資料庫,導致資料庫移植性差,不能隨意更換資料庫
8.MyBatis框架適用場景
- MyBatis專注于SQL本身,是一個足夠靈活的DAO層解決方案,
- 對性能的要求很高,或者需求變化較多的專案,如互聯網專案,MyBatis將是不錯的選擇,
9.MyBatis編程步驟是什么樣的?
(1)獲取SqlSessionFactory物件
決議檔案的每一個資訊保存在Configuration中,回傳包含Configuration的DefaultSqlSession;
注意:【MappedStatement】:代表一個增刪改查的詳細資訊
(2)獲取SqlSession物件
回傳一個DefaultSqlSession物件,包含Executor和Configuration;
這一步會創建Executor;
(3)獲取介面的代理物件(MapperProxy)
getMapper,使用MapperProxyFactory創建一個MapperProxy的代理物件,代理物件里包含了DefaultSqlSession(Executor)
(4)執行增刪改查方法
(5)關倍訓話
10.請說說MyBatis的作業原理
在學習 MyBatis 程式之前,需要了解一下 MyBatis 作業原理,以便于理解程式,MyBatis 的作業原理如下圖
- SqlMapConfig.xml,此檔案作為MyBatis的全域組態檔,配置了mybatis的運行環境等資訊,Mapper.xml檔案即SQL映射檔案,檔案中配置了操作資料庫的sql陳述句,此檔案需要在SqlMapConfig.xml中加載,
- 通過MyBatis環境等配置資訊構造SqlSessionFactory即會話工廠
- 由會話工廠創建SqlSession即會話,操作資料庫需要通過SqlSession進行,
- Mybatis底層自定義了Executor執行器介面操作資料庫,Executor介面有兩個實作,一個是基本執行器、一個是快取執行器,
- Mapped Statement也是MyBatis一個底層封裝物件,它包裝了MyBatis配置資訊及sql映射資訊等,mapper.xml檔案中一個sql對應一個Mapped Statement物件,sql的id即是Mapped statement的id,
- Mapped Statement對sql執行輸入引數進行定義,包括HashMap、基本型別、pojo,Executor通過Mapped Statement在執行sql前將輸入的java物件映射至sql中,輸入引數映射就是jdbc編程中對PreparedStatement設定引數,
- Mapped Statement對sql執行輸出結果進行定義,包括HashMap、基本型別、Pojo,Executor通過Mapped Statement在執行sql后將輸出結果映射至Java物件中,輸出結果映射程序相當于Jdbc編程中對結果的決議處理程序,
11.MyBatis功能架構
我們把Mybatis的功能架構分為三層:
- API介面層:提供給外部使用的介面API,開發人員通過這些本地API來操縱資料庫,介面層一接收到呼叫請求就會呼叫資料處理層來完成具體的資料處理,
- 資料處理層:負責具體的SQL查找、SQL決議、SQL執行和執行結果映射處理等,它主要的目的是根據呼叫的請求完成一次資料庫操作,
- 基礎支撐層:負責最基礎的功能支撐,包括連接管理、事務管理、配置加載和快取處理,這些都是共用的東西,將他們抽取出來作為最基礎的組件,為上層的資料處理層提供最基礎的支撐,
12.MyBatis的框架架構設計是怎么樣的
這張圖從上往下看,MyBatis的初始化,會從mybatis-config.xml組態檔,決議構造成Configuration這個類,就是圖中的紅框,
(1)加載配置:配置來源于兩個地方,一處是組態檔,一處是Java代碼的注解,將SQL的配置資訊加載成為一個個MappedStatement物件(包括了傳入引數映射配置、執行的SQL陳述句、結果映射配置),存盤在記憶體中,
(2)SQL決議:當API介面層接收到呼叫請求時,會接收到傳入SQL的ID和傳入物件(可以是Map、JavaBean或者基本資料型別),Mybatis會根據SQL的ID找到對應的MappedStatement,然后根據傳入引數物件對MappedStatement進行決議,決議后可以得到最終要執行的SQL陳述句和引數,
(3)SQL執行:將最終得到的SQL和引數拿到資料庫進行執行,得到操作資料庫的結果,
(4)結果映射:將操作資料庫的結果按照映射的配置進行轉換,可以轉換成HashMap、JavaBean或者基本資料型別,并將最終結果回傳,
13.為什么需要預編譯
1.定義:
SQL 預編譯指的是資料庫驅動在發送 SQL 陳述句和引數給 DBMS 之前對 SQL 陳述句進行編譯,這樣 DBMS 執行 SQL 時,就不需要重新編譯,
2.為什么需要預編譯
JDBC 中使用物件 PreparedStatement 來抽象預編譯陳述句,使用預編譯,預編譯階段可以優化 SQL 的執行,預編譯之后的 SQL 多數情況下可以直接執行,DBMS 不需要再次編譯,越復雜的SQL,編譯的復雜度將越大,預編譯階段可以合并多次操作為一個操作,同時預編譯陳述句物件可以重復利用,把一個 SQL 預編譯后產生的 PreparedStatement 物件快取下來,下次對于同一個SQL,可以直接使用這個快取的 PreparedState 物件,Mybatis默認情況下,將對所有的 SQL 進行預編譯,
14.Mybatis都有哪些Executor執行器?它們之間的區別是什么?
- SimpleExecutor是最簡單的執行器,根據對應的sql直接執行即可,不會做一些額外的操作;
- BatchExecutor執行器,顧名思義,通過批量操作來優化性能,通常需要注意的是批量更新操作,由于內部有快取的實作,使用完成后記得呼叫flushStatements來清除快取,
- ReuseExecutor 可重用的執行器,重用的物件是Statement,也就是說該執行器會快取同一個sql的Statement,省去Statement的重新創建,優化性能,內部的實作是通過一個HashMap來維護Statement物件的,由于當前Map只在該session中有效,所以使用完成后記得呼叫flushStatements來清除Map,
15.Mybatis中如何指定使用哪一種Executor執行器?
在Mybatis組態檔中,在設定(settings)可以指定默認的ExecutorType執行器型別,也可以手動給DefaultSqlSessionFactory的創建SqlSession的方法傳遞ExecutorType型別引數,如SqlSession openSession(ExecutorType execType),
配置默認的執行器,SIMPLE 就是普通的執行器;REUSE 執行器會重用預處理陳述句(prepared statements); BATCH 執行器將重用陳述句并執行批量更新,
16.Mybatis延遲加載
在實際使用中,我們會經常性的涉及到多表聯合查詢,但是有時候,并不會立即用到所有的查詢結果,我來舉兩個例子:
- 例如,查詢一批筆記本電腦的進貨明細,而不直接展示每列明細對應電腦配置或者價格等的詳細資訊,等到用戶需要取出某筆記本相關的詳細資訊的時候,再進行單表查詢
- 再例如 ,銀行中,某個用戶擁有50個賬戶(打比方),再我們查詢這個而用戶的資訊,這個用戶下所有賬戶的詳細資訊很顯然,在使用的時候再查詢才是比較合理的
針對這樣一種情況,延遲加載這一種機制就出現了,延遲加載(懶加載)顧名思義,就是對某種資訊推遲加載,這樣的技術也就幫助我們實作了 “按需查詢” 的機制,在一對多,或者多對多的情況下
既然提到了延遲加載,當然順便提一句立即加載,它的含義就是不管是否用戶需要,一呼叫,則馬上查詢,這種方式,適合與多對一,或者一對一的情況下,
首先,配置基本的環境,然后我們首先在資料庫準備兩張表
User表
CREATE TABLE USER (
`id` INT(11)NOT NULL AUTO_INCREMENT,
`username` VARCHAR(32) NOT NULL COMMENT '用戶名',
`telephone` VARCHAR(11) NOT NULL COMMENT '手機',
`birthday` DATETIME DEFAULT NULL COMMENT '生日',
`gender` CHAR(1) DEFAULT NULL COMMENT '性別',
`address` VARCHAR(256) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
Account表
CREATE TABLE `account` (
`ID` int(11) NOT NULL COMMENT '編號',
`UID` int(11) default NULL COMMENT '用戶編號',
`MONEY` double default NULL COMMENT '金額',
PRIMARY KEY (`ID`),
KEY `FK_Reference_8` (`UID`),
CONSTRAINT `FK_Reference_8` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
然后分別創建出其對應的物體類
User類
public class User implements Serializable {
private Integer id;
private String username;
private String telephone;
private Date birthday;
private String gender;
private String address;
//一對多關系映射,主表物體應該包含從表物體的集合參考
private List<Account> accounts;
...... 請補充 get set 和 toString 方法
}
Account類
public class Account implements Serializable {
private Integer id;
private Integer uid;
private Double money;
//從表物體應該包含一個主表物體的物件參考
private User user;
...... 請補充 get set 和 toString 方法
}
UserMapper.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="cn.ideal.mapper.UserMapper">
<!-- 定義User的resultMap-->
<resultMap id="userAccountMap" type="User">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="telephone" column="telephone"></result>
<result property="birthday" column="birthday"></result>
<result property="gender" column="gender"></result>
<result property="address" column="address"></result>
<collection property="accounts" ofType="account">
<id property="id" column="aid"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
</collection>
</resultMap>
<!-- 查詢所有用戶 并且顯示對應賬戶資訊 -->
<select id="findAll" resultMap="userAccountMap">
SELECT u.*,a.id as aid,a.uid,a.money FROM user u LEFT OUTER JOIN account a on u.id = a.uid;
</select>
<!-- 根據id查詢用戶 -->
<select id="findById" parameterType="INT" resultType="User">
select * from user where id = #{uid}
</select>
</mapper>
兩個介面中創建對應方法
public interface AccountMapper {
/**
* 查詢所有賬戶
* @return
*/
List<Account> findAll();
}
public interface UserMapper {
/**
* 查詢所有用戶資訊,同時顯示出該用戶下的所有賬戶
*
* @return
*/
List<User> findAll();
/**
* 根據id查詢用戶資訊
* @param userId
* @return
*/
User findById(Integer userId);
}
延遲加載代碼實作
首先,給大家演示一下,我們之前一對一查詢用戶的方式,同時會將用戶對應所有的賬戶資訊,也查詢出來
/**
* 測驗查詢所有
*/
@Test
public void testFindAll() {
List<User> users= userMapper.findAll();
for (User user : users) {
System.out.println("---------------------");
System.out.println(user);
System.out.println(user.getAccounts());
}
}
效果:

這種方式是通過 SQL 陳述句,以及resultMap將 用戶和賬戶的資訊同時查詢出來
那么如何實作我們上面所說的延遲加載呢?
這次我們選擇 查詢賬戶,然后延遲加載用戶的資訊
修改AccountMapper.xml
首先需要修改的就是賬戶的映射組態檔,可以看到我們在查詢時,依舊定義了一個 resultMap 先封裝了 Account ,然后通過association 進行關聯 User,其中使用的就是 select 和 column 實作了延遲加載用戶資訊
- select 用來指定延遲加載所需要執行的 SQL 陳述句,也就是指定 某個SQL映射檔案中的某個select標簽對的 id,在這里我們指定了用戶中通過id查詢資訊的方法
- column 是指關聯的用戶資訊查詢的列,在這里也就是關聯的用戶的主鍵即,id
<mapper namespace="cn.ideal.mapper.AccountMapper">
<!-- 定義封裝 Account和User 的resultMap -->
<resultMap id="userAccountMap" type="Account">
<id property="id" column="id"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<!-- 配置封裝 User 的內容
select:查詢用戶的唯一標識
column:用戶根據id查詢的時候,需要的引數值
-->
<association property="user" column="uid" javaType="User" select="cn.ideal.mapper.UserMapper.findById"></association>
</resultMap>
<!-- 根據查詢所有賬戶 -->
<select id="findAll" resultMap="userAccountMap">
SELECT * FROM account
</select>
</mapper>
第一次測驗代碼
我們只執行一下賬戶的查詢所有方法,看一下,是否能夠實作我們的效果
@Test
public void testFindAll(){
List<Account> accounts = accountMapper.findAll();
}
執行效果

可以看到,三條 SQL 陳述句都執行了,這是為什么呢?
這是因為,我們在測驗方法之前,需要開啟延遲加載功能
延遲加載功能
經過查閱檔案,我們知道了,如果想要開始延遲加載功能,就需要在總組態檔 SqlMapConfig.xml 中配置 setting 屬性,也就是將延遲加載 lazyLoadingEnable 的開關設定成 teue ,由于是按需加載,所以還需要將積極加載修改為消極加載,也就是將 aggressiveLazyLoading 改為 false
當然,由于我這里匯入的 MyBatis 版本為 3.4.5 所以這個值默認就是 false 實際上不用設定也可以,不過我們還是寫出來
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"></setting>
</settings>
注意:如果有使用typeAliases配置別名的話一定要將 typeAliases 標簽放在后面
再次測驗
仍然只執行查詢方法
@Test
public void testFindAll(){
List<Account> accounts = accountMapper.findAll();
}
執行效果

這一次果然只執行了一條查詢 account 的命令
那么當用戶想要查看到,每個賬戶對應下的用戶的時候呢?這也就是按需查詢,只需要在測驗時,加入對應獲取方法就可以了
@Test
public void testFindAll(){
List<Account> accounts = accountMapper.findAll();
for (Account account : accounts){
System.out.println("----------------------------");
System.out.println(account);
System.out.println(account.getUser());
}
}
執行一下

可以看到,我們延遲加載的目的達到了
17.#{}和${}的區別
#{} 是預編譯處理,像傳進來的資料會加個" "(#將傳入的資料都當成一個字串,會對自動傳入的資料加一個雙引號)
就 是 字 符 串 替 換 , 直 接 替 換 掉 占 位 符 , {} 就是字串替換,直接替換掉占位符, 就是字符串替換,直接替換掉占位符,方式一般用于傳入資料庫物件,例如傳入表名.
使用 ${} 的話會導致 sql 注入,什么是 SQL 注入呢?比如 select * from user where id = ${value}
value 應該是一個數值吧,然后如果對方傳過來的是 001 and name = tom,這樣不就相當于多加了一個條件嘛?把SQL陳述句直接寫進來了,如果是攻擊性的陳述句呢?001;drop table user,直接把表給刪了
所以為了防止 SQL 注入,能用 #{} 的不要去用 ${}
如果非要用 ${} 的話,那要注意防止 SQL 注入問題,可以手動判定傳入的變數,進行過濾,一般 SQL 注入會輸入很長的一條 SQL 陳述句
18.模糊查詢like陳述句該怎么寫
方式1:$ 這種方式,簡單,但是無法防止SQL注入,所以不推薦使用
LIKE '%${name}%'
方式2:#
LIKE "%"#{name}"%"
方式3:字串拼接
AND name LIKE CONCAT(CONCAT('%',#{name},'%'))
方式4:bind標簽
<select id="searchStudents" resultType="com.example.entity.StudentEntity"
parameterType="com.example.entity.StudentEntity">
<bind name="pattern1" value="'%' + _parameter.name + '%'" />
<bind name="pattern2" value="'%' + _parameter.address + '%'" />
SELECT * FROM test_student
<where>
<if test="age != null and age != '' and compare != null and compare != ''">
age
${compare}
#{age}
</if>
<if test="name != null and name != ''">
AND name LIKE #{pattern1}
</if>
<if test="address != null and address != ''">
AND address LIKE #{pattern2}
</if>
</where>
ORDER BY id
</select>
方式5:java代碼里寫
param.setUsername("%CD%"); 在 java 代碼中傳參的時候直接寫上
<if test="username!=null"> AND username LIKE #{username}</if>
然后 mapper 里面直接寫 #{} 就可以了
19.在mapper中如何傳遞多個引數
方法一:使用map介面傳遞引數
嚴格來說,map適用幾乎所有場景,但是我們用得不多,原因有兩個:首先,map是一個鍵值對應的集合,使用者要通過閱讀它的鍵,才能明了其作用;其次,使用map不能限定其傳遞的資料型別,因此業務性質不強,可讀性差,使用者要讀懂代碼才能知道需要傳遞什么引數給它,所以不推薦用這種方式傳遞多個引數,
public List<Role> findRolesByMap(Map<String, Object> parameterMap);
<select id="findRolesByMap" parameterType="map" resultType="role">
select id, role_name as roleName, note from t_role where role_name like concat('%', #{roleName}, '%') and note like concat('%', #{note}, '%')
</select>
方法二:使用注解傳遞多個引數
MyBatis為開發者提供了一個注解@Param(org.apache.ibatis.annotations.Param),可以通過它去定義映射器的引數名稱,使用它可以得到更好的可讀性 這個時候需要修改映射檔案的代碼,此時并不需要給出parameterType屬性,讓MyBatis自動探索便可以了 使可讀性大大提高,使用者也方便了,但是這會帶來一個麻煩,如果SQL很復雜,擁有大于10個引數,那么介面方法的引數個數就多了,使用起來就很不容易,不過不必擔心,MyBatis還提供傳遞Java Bean的形式,
public List<Role> findRolesByAnnotation(@Param("roleName") String rolename, @Param("note") String note);
<select id="findRolesByAnnotation" resultType="role">
select id, role_name as roleName, note from t_role where role_name like concat('%', #{roleName}, '%') and note like concat('%', #{note}, '%')
</select>
方法三:通過Java Bean傳遞多個引數
public List<Role> findRolesByBean(RoleParams roleParam);
<select id="findRolesByBean" parameterType="com.xc.pojo.RoleParams" resultType="role">
select id, role_name as roleName, note from t_role where role_name like concat('%', #{roleName}, '%') and note like concat('%', #{note}, '%')
</select>
方法四:混合使用
在某些情況下可能需要混合使用幾種方法來傳遞引數,舉個例子,查詢一個角色,可以通過角色名稱和備注進行查詢,與此同時還需要支持分頁
public List<Role> findByMix(@Param("params") RoleParams roleParams, @Param("page") PageParam PageParam);
<select id="findByMix" resultType="role">
select id, role_name as roleName, note from t_role
where role_name like concat('%', #{params.roleName}, '%') and note like concat('%', #{params.note}, '%') limit #{page.start}, #{page.limit}
</select>
20.Mybatis如何執行批量操作
Mybatis常會出現批量操作,如批量查詢,批量插入,批量修改(replace into),批量操作要比回圈執行效率提升很多,這里對mybatis的批量操作做一個總結講解,
(1)Foreach
foreach:foreach的主要用在構建in條件中,它可以在SQL陳述句中進行迭代一個集合,foreach元素的屬性主要有 item,index,collection,open,separator,close,它是批量操作的核心標簽,下面都是foreach在不同場景的應用和寫法,
List<String>
select id, accid, key1, key1createtime key1CreateTime, key2, key2createtime key2CreateTime
from m_acc_keys where accid in
<foreach item="item" index="index" collection="paramIds" open="(" separator="," close=")">
#{item}
</foreach>
注意:你可以傳遞一個 List 實體或者陣列作為引數物件傳給 MyBatis,當你這么做的時候,MyBatis 會自動將它包裝在一個 Map 中,用名稱在作為鍵,List 實體將會以“list”作為鍵,而陣列實體將會以“array”作為鍵,
List<Object>
這是一個批量保存(插入或修改)的例子
<insert id="insertList" parameterType="list" >
replace into xx (id, c1,c2) values
<foreach collection="list" item="it" separator=",">
(#{it.id},#{it.c1},#{it.c2})
</foreach>
</insert>
replace:會根據主鍵和唯一索引判斷該記錄是否存在,存在就洗掉在插入(修改),不存在就插入(insert),
(2)陣列
還是一個查詢的例子
public List<Column> selectByIds(String[] ids);
<select id="selectByIds" parameterType="java.lang.String"
resultType="columnMap">
<include refid="select_clause"></include>
where c.id in
<foreach collection="array" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
(3)多重回圈
colids和proids是兩個平級的資料,
<delete id="move" parameterType="java.util.Map">
insert into b_column_programme(columnId, programmeId)
values
<foreach collection="colids" item="colid" separator="," >
<foreach collection="proids" item="proid" separator="," >
(#{colid}, #{proid})
</foreach>
</foreach>
</delete>
(4)批量插入id自增長
Mybatis在版本3.4.x以上支持批量插入系結自增長id,常用版本3.4.1,
對應的maven支持
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
Mapper.xml寫法
<insert id="insertList" parameterType="list" useGeneratedKeys="true" keyProperty="id" >
insert into xx
(c1,c2)
values
<foreach collection="list" item="it" separator=",">
(#{it.c1},#{it.c2})
</foreach>
</insert>
21.如何獲取生成的主鍵
MySQL:Mapper 檔案 insert 陳述句設定
useGeneratedKeys="true" keyProperty="id"
Oracle:Mapper 檔案 insert 陳述句增加
<selectKey keyProperty="id" order="BEFORE" resultType="Integer">
select xxx_SEQ.nextval from dual
</selectKey>
22.當物體類中的屬性名和表中的欄位名不一樣 ,怎么辦
第1種: 通過在查詢的SQL陳述句中定義欄位名的別名,讓欄位名的別名和物體類的屬性名一致,
<select id="getOrder" parameterType="int" resultType="com.jourwon.pojo.Order">
select order_id id, order_no orderno ,order_price price form orders where order_id=#{id};
</select>
第2種: 通過<resultMap>來映射欄位名和物體類屬性名的一一對應的關系,
<select id="getOrder" parameterType="int" resultMap="orderResultMap">
select * from orders where order_id=#{id}
</select>
<resultMap type="com.jourwon.pojo.Order" id="orderResultMap">
<!–用id屬性來映射主鍵欄位–>
<id property="id" column="order_id">
<!–用result屬性來映射非主鍵欄位,property為物體類屬性名,column為資料庫表中的屬性–>
<result property ="orderno" column ="order_no"/>
<result property="price" column="order_price" />
</reslutMap>
23.Mapper 撰寫有哪幾種方式?
第一種:介面實作類繼承 SqlSessionDaoSupport:使用此種方法需要撰寫mapper 介面,mapper 介面實作類、mapper.xml 檔案,
(1)在 sqlMapConfig.xml 中配置 mapper.xml 的位置
<mappers>
<mapper resource="mapper.xml 檔案的地址" />
<mapper resource="mapper.xml 檔案的地址" />
</mappers>
(2)定義 mapper 介面
(3)實作類集成 SqlSessionDaoSupport
mapper 方法中可以 this.getSqlSession()進行資料增刪改查,
(4)spring 配置
<bean id=" " class="mapper 介面的實作">
<property name="sqlSessionFactory"
ref="sqlSessionFactory"></property>
</bean>
第二種:使用 org.mybatis.spring.mapper.MapperFactoryBean:
(1)在 sqlMapConfig.xml 中配置 mapper.xml 的位置,如果 mapper.xml 和mappre 介面的名稱相同且在同一個目錄,這里可以不用配置
<mappers>
<mapper resource="mapper.xml 檔案的地址" />
<mapper resource="mapper.xml 檔案的地址" />
</mappers>
(2)定義 mapper 介面:
(3)mapper.xml 中的 namespace 為 mapper 介面的地址
(4)mapper 介面中的方法名和 mapper.xml 中的定義的 statement 的 id 保持一致
(5)Spring 中定義
<bean id="" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="mapper 介面地址" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
第三種:使用 mapper 掃描器:
(1)mapper.xml 檔案撰寫:
mapper.xml 中的 namespace 為 mapper 介面的地址;
mapper 介面中的方法名和 mapper.xml 中的定義的 statement 的 id 保持一致;
如果將 mapper.xml 和 mapper 介面的名稱保持一致則不用在 sqlMapConfig.xml中進行配置,
(2)定義 mapper 介面:
注意 mapper.xml 的檔案名和 mapper 的介面名稱保持一致,且放在同一個目錄
(3)配置 mapper 掃描器:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="mapper 介面包地址
"></property>
<property name="sqlSessionFactoryBeanName"
value="sqlSessionFactory"/>
</bean>
(4)使用掃描器后從 spring 容器中獲取 mapper 的實作物件,
24.MyBatis介面系結的幾種方式
介面系結有兩種方式
(1)使用注解,在介面的方法上面添加@Select@Update等注解,里面寫上對應的SQL陳述句進行SQL陳述句的系結,
(2)通過映射檔案xml方式進行系結,指定xml映射檔案中的namespace對應的介面的全路徑名
什么時候用注解系結?什么時候用xml系結?
當SQL陳述句比較簡單的時候,使用注解系結就可以了,當SQL陳述句比較復雜的話,使用xml方式系結,一般用xml方式系結比較多
25.使用MyBatis的mapper介面呼叫時有哪些要求?
-
Mapper介面方法名和mapper.xml中定義的每個sql的id相同,
-
Mapper介面方法的輸入引數型別和mapper.xml中定義的每個sql 的parameterType的型別相同,
-
Mapper介面方法的輸出引數型別和mapper.xml中定義的每個sql的resultType的型別相同,
-
Mapper.xml檔案中的namespace即是mapper介面的類路徑,
26.通常一個mapper.XML對應一個DAO介面,DAO是否可以多載?
答:不能多載,方法名對應的 mapper.xml 檔案里的一個 id,這個與方法名對應,系統會根據 namespace+id 找到對應的方法對應,
Dao 介面即 Mapper 介面,介面的全限名,就是映射檔案中的 namespace 的值;介面的方法名,就是映射檔案中 Mapper 的 Statement 的 id 值;介面方法內的引數,就是傳遞給 sql 的引數,Mapper 介面是沒有實作類的,當呼叫介面方法時,介面全限名+方法名拼接字串作為 key 值,可唯一定位一個 MapperStatement,在 Mybatis 中,每一個標簽,都會被決議為一個MapperStatement 物件,
舉例:com.mybatis3.mappers.StudentDao.findStudentById,可以唯一找到 namespace 為 com.mybatis3.mappers.StudentDao 下面 id 為findStudentById 的 MapperStatement,
Mapper 介面里的方法,是不能多載的,因為是使用 全限名+方法名 的保存和尋找策略,Mapper 介面的作業原理是 JDK 動態代理,Mybatis 運行時會使用 JDK動態代理為 Mapper 介面生成代理物件 proxy,代理物件會攔截介面方法,轉而執行 MapperStatement 所代表的 sql,然后將 sql 執行結果回傳,
27.MyBatis不同映射檔案中的id是否可以重復?
可以重復,但是需要映射檔案的namespace不同
不同的 Xml 映射檔案,如果配置了 namespace,那么 id 可以重復;如果沒有配置 namespace,那么 id 不能重復,
原因就是 namespace+id 是作為 Map<String, MapperStatement>的 key使用的,如果沒有 namespace,就剩下 id,那么,id 重復會導致資料互相覆寫,
有了 namespace,自然 id 就可以重復,namespace 不同,namespace+id 自然也就不同,
28.簡述Mybatis的Xml映射檔案和Mybatis內部資料結構之間的映射關系?
答:Mybatis將所有Xml配置資訊都封裝到All-In-One重量級物件Configuration內部,在Xml映射檔案中,<parameterMap>標簽會被決議為ParameterMap物件,其每個子元素會被決議為ParameterMapping物件,<resultMap>標簽會被決議為ResultMap物件,其每個子元素會被決議為ResultMapping物件,每一個<select>、<insert>、<update>、<delete>標簽均會被決議為MappedStatement物件,標簽內的sql會被決議為BoundSql物件,
29.Mybatis是如何將sql執行結果封裝為目標物件并回傳的?都有哪些映射形式?
第一種是使用<resultMap>標簽,逐一定義列名和物件屬性名之間的映射關系,
第二種是使用sql列的別名功能,將列別名書寫為物件屬性名,比如T_NAME AS NAME,物件屬性名一般是name,小寫,但是列名不區分大小寫,Mybatis會忽略列名大小寫,智能找到與之對應物件屬性名,你甚至可以寫成T_NAME AS NaMe,Mybatis一樣可以正常作業,
有了列名與屬性名的映射關系后,Mybatis通過反射創建物件,同時使用反射給物件的屬性逐一賦值并回傳,那些找不到映射關系的屬性,是無法完成賦值的,
30.Xml映射檔案中,除了常見的select|insert|updae|delete標簽之外,還有哪些標簽?
還有很多其他的標簽,<resultMap>、<parameterMap>、<sql>、<include>、<selectKey>,加上動態sql的9個標簽,trim|where|set|foreach|if|choose|when|otherwise|bind等,其中<sql>為sql片段標簽,通過<include>標簽引入sql片段,<selectKey>為不支持自增的主鍵生成策略標簽,
31.MyBatis映射檔案中A標簽參考B標簽,如果B標簽在A的后面定義,可以嗎?
雖然 Mybatis 決議 Xml 映射檔案是按照順序決議的,但是,被參考的 B 標簽依然可以定義在任何地方,Mybatis 都可以正確識別,
原理:
Mybatis 決議 A 標簽時,發現參考了 B 標簽,未決議到 B 標簽,此時會把 A 標簽標記為未決議狀態;
繼續決議下面內容,把剩下決議完之后,再決議標記為未決議的標簽;
此時已決議到 B 標簽,此時再決議A標簽時,B標簽已經存在,A 標簽也就順利決議完成,
32.MyBatis實作一對一,一對多有幾種方式,怎么操作的?
有聯合查詢和嵌套查詢,聯合查詢是幾個表聯合查詢,只查詢一次,通過在resultMap里面的association,collection節點配置一對一,一對多的類就可以完成
嵌套查詢是先查一個表,根據這個表里面的結果的外鍵id,去再另外一個表里面查詢資料,也是通過配置association,collection,但另外一個表的查詢通過select節點配置,
33.Mybatis是否可以映射Enum列舉類?
Mybatis可以映射列舉類,不單可以映射列舉類,Mybatis可以映射任何物件到表的一列上,映射方式為自定義一個TypeHandler,實作TypeHandler的setParameter()和getResult()介面方法,
TypeHandler有兩個作用,一是完成從javaType至jdbcType的轉換,二是完成jdbcType至javaType的轉換,體現為setParameter()和getResult()兩個方法,分別代表設定sql問號占位符引數和獲取列查詢結果,
34.Mybatis動態sql是做什么的?都有哪些動態sql?能簡述一下動態sql的執行原理不?
Mybatis動態sql可以讓我們在Xml映射檔案內,以標簽的形式撰寫動態sql,完成邏輯判斷和動態拼接sql的功能,Mybatis提供了9種動態sql標簽trim|where|set|foreach|if|choose|when|otherwise|bind,
其執行原理為,使用OGNL從sql引數物件中計算運算式的值,根據運算式的值動態拼接sql,以此來完成動態sql的功能,
35.Mybatis是如何進行分頁的?
(1)原始方法,使用 limit,需要自己處理分頁邏輯:
對于 mysql 資料庫可以使用 limit ,如:
select * from table limit 5; --回傳前5行
select * from table limit 0,5; --同上,回傳前5行
select * from table limit 5,10; --回傳6-15行
對于 oracle 資料庫可以使用 rownum ,如:從表Sys_option(主鍵為sys_id)中從第10條記錄開始檢索20條記錄,陳述句如下:
SELECT * FROM (SELECT ROWNUM R,t1.* From Sys_option where rownum < 30 ) t2
Where t2.R >= 10
(2)攔截StatementHandler,其實質還是在最后生成limit陳述句
(3)使用PageHelper插件,這是目前比較常見的方法
36.簡述Mybatis的插件運行原理,以及如何撰寫一個插件,
Mybatis僅可以撰寫針對ParameterHandler、ResultSetHandler、StatementHandler、Executor這4種介面的插件,Mybatis使用JDK的動態代理,為需要攔截的介面生成代理物件以實作介面方法攔截功能,每當執行這4種介面物件的方法時,就會進入攔截方法,具體就是InvocationHandler的invoke()方法,當然,只會攔截那些你指定需要攔截的方法,
實作Mybatis的Interceptor介面并復寫intercept()方法,然后在給插件撰寫注解,指定要攔截哪一個介面的哪些方法即可,記住,別忘了在組態檔中配置你撰寫的插件,
37.Mybatis快取機制
快取機制減輕資料庫壓力,提高資料庫性能
mybatis的快取分為兩級:一級快取、二級快取
(1)一級快取:
一級快取為 ?sqlsesson? 快取,快取的資料只在 SqlSession 內有效,在操作資料庫的時候需要先創建 SqlSession 會話物件,在物件中有一個 HashMap 用于存盤快取資料,此 HashMap 是當前會話物件私有的,別的 SqlSession 會話物件無法訪問,
具體流程:
第一次執行 select 完畢會將查到的資料寫入 SqlSession 內的 HashMap 中快取起來
第二次執行 select 會從快取中查資料,如果 select 同傳引數一樣,那么就能從快取中回傳資料,不用去資料庫了,從而提高了效率
注意:
1、如果 SqlSession 執行了 DML 操作(insert、update、delete),并 commit 了,那么 mybatis 就會清空當前 SqlSession 快取中的所有快取資料,這樣可以保證快取中的存的資料永遠和資料庫中一致,避免出現差異
2、當一個 SqlSession 結束后那么他里面的一級快取也就不存在了, mybatis 默認是開啟一級快取,不需要配置
3、 mybatis 的快取是基于 [namespace:sql陳述句:引數] 來進行快取的,意思就是, SqlSession 的 HashMap 存盤快取資料時,是使用 [namespace:sql:引數] 作為 key ,查詢回傳的陳述句作為 value 保存的
(2)二級快取:
二級快取是? mapper? 級別的快取,也就是同一個 namespace 的 mapper.xml ,當多個 SqlSession 使用同一個 Mapper 操作資料庫的時候,得到的資料會快取在同一個二級快取區域
二級快取默認是沒有開啟的,需要在 setting 全域引數中配置開啟二級快取
開啟二級快取步驟:
1、?conf.xml? 配置全域變數開啟二級快取
<settings>
<setting name="cacheEnabled" value="true"/>默認是false:關閉二級快取
<settings>
2、在? userMapper.xml ?中配置
當前mapper下所有陳述句開啟二級快取
這里配置了一個 LRU 快取,并每隔60秒重繪,最大存盤512個物件,而回傳的物件是只讀的
若想禁用當前?select?陳述句的二級快取,添加 ?useCache="false"?修改如下:
<select id="getCountByName" parameterType="java.util.Map" resultType="INTEGER" statementType="CALLABLE" useCache="false">
具體流程:
1.當一個? sqlseesion ?執行了一次? select? 后,在關閉此? session? 的時候,會將查詢結果快取到二級快取
2.當另一個? sqlsession ?執行? select? 時,首先會在他自己的一級快取中找,如果沒找到,就回去二級快取中找,找到了就回傳,就不用去資料庫了,從而減少了資料庫壓力提高了性能
注意:
1、如果 ?SqlSession? 執行了 DML 操作?(insert、update、delete)?,并 ?commit? 了,那么 ?mybatis? 就會清空當前? mapper? 快取中的所有快取資料,這樣可以保證快取中的存的資料永遠和資料庫中一致,避免出現差異
2、? mybatis? 的快取是基于? [namespace:sql陳述句:引數] ?來進行快取的,意思就是,?SqlSession? 的 ?HashMap? 存盤快取資料時,是使用 ?[namespace:sql:引數] ?作為 ?key? ,查詢回傳的陳述句作為 ?value? 保存的,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/297108.html
標籤:其他
上一篇:redis分布式鎖詳解(優化redis分布式鎖的程序及Redisson使用)
下一篇:RabbitMQ(二)實踐篇 動手理解RabbitMQ五種模式 兩小時搞定Rabbit應用 摸清路由器的的路由、廣播、Topic 型別 了解Comfirm Return的程序
