什么是ORM?
- ORM(Object/Relational Mapping)即對象關系映射,是一種數據持久化技術。它在對象模型和關系型數據庫直接建立起對應關系,并且提供一種機制,通過JavaBean對象去操作數據庫表的數據。 MyBatis通過簡單的XML或者注解的方式進行配置和原始映射,將實體類和SQL語句之間建立映射關系,是一種半自動(之所以說是半自動,因為我們要自己寫SQL)的ORM實現。
MyBatis框架的優缺點及其適用的場合
優點
- 與JDBC相比,減少了50%以上的代碼量。
- MyBatis是易學的持久層框架,小巧并且簡單易學。
- MyBatis相當靈活,不會對應用程序或者數據庫的現有設計強加任何影響,SQL寫在XML文件里,從程序代碼中徹底分離,降低耦合度,便于統一的管理和優化,并可重用。
- 提供XML標簽,支持編寫動態的SQL,滿足不同的業務需求。
- 提供映射標簽,支持對象與數據庫的ORM字段關系映射。
缺點
- SQL語句的編寫工作量較大,對開發人員編寫SQL的能力有一定的要求。
- SQL語句依賴于數據庫,導致數據庫不具有好的移植性,不可以隨便更換數據庫。
適用場景
- MyBatis專注于SQL自身,是一個足夠靈活的DAO層解決方案。對性能的要求很高,或者需求變化較多的項目,例如Web項目,那么MyBatis是不二的選擇。
MyBatis與Hibernate有哪些不同?
- Mybatis和hibernate不同,它不完全是一個ORM框架,因為MyBatis需要程序員自己編寫Sql語句。
- Mybatis直接編寫原生態sql,可以嚴格控制sql執行性能,靈活度高,非常適合對關系數據模型要求不高的軟件開發,因為這類軟件需求變化頻繁,一但需求變化要求迅速輸出成果。但是靈活的前提是mybatis無法做到數據庫無關性,如果需要實現支持多種數據庫的軟件,則需要自定義多套sql映射文件,工作量大。
- Hibernate對象/關系映射能力強,數據庫無關性好,對于關系模型要求高的軟件,如果用hibernate開發可以節省很多代碼,提高效率。
#{}和${}的區別是什么?
#{}
?是預編譯處理,${}
是字符串替換。- Mybatis在處理
#{}
時,會將sql中的#{}
替換為?號,調用PreparedStatement的set方法來賦值; - Mybatis在處理
${}
時,就是把${}
替換成變量的值。 - 使用
#{}
可以有效的防止SQL注入,提高系統安全性。
當實體類中的屬性名和表中的字段名不一樣,怎么辦?
- 第1種: 通過在查詢的sql語句中定義字段名的別名,讓字段名的別名和實體類的屬性名一致。
- 第2種: 通過 `` 來映射字段名和實體類屬性名的一一對應的關系。
模糊查詢like語句該怎么寫?
- 第1種:在Java代碼中添加sql通配符。
- 第2種:在sql語句中拼接通配符,會引起sql注入
Dao接口的工作原理是什么?Dao接口里的方法,參數不同時,方法能重載嗎?
- Dao接口即Mapper接口。接口的全限名,就是映射文件中的namespace的值;接口的方法名,就是映射文件中Mapper的Statement的id值;接口方法內的參數,就是傳遞給sql的參數。 Mapper接口是沒有實現類的,當調用接口方法時,接口全限名+方法名拼接字符串作為key值,可唯一定位一個MapperStatement。在Mybatis中每
、、、
標簽,都會被解析為一個MapperStatement對象。 Mapper接口里的方法,是不能重載的,因為是使用 全限名+方法名 的保存和尋找策略。Mapper 接口的工作原理是JDK動態代理,Mybatis運行時會使用JDK動態代理為Mapper接口生成代理對象proxy,代理對象會攔截接口方法,轉而執行MapperStatement所代表的sql,然后將sql執行結果返回。
Mybatis是如何進行分頁的?分頁插件的原理是什么?
- Mybatis使用RowBounds對象進行分頁,它是針對ResultSet結果集執行的內存分頁,而非物理分頁。可以在sql內直接書寫帶有物理分頁的參數來完成物理分頁功能,也可以使用分頁插件來完成物理分頁。 分頁插件的基本原理是使用Mybatis提供的插件接口,實現自定義插件,在插件的攔截方法內攔截待執行的sql,然后重寫sql,根據dialect方言,添加對應的物理分頁語句和物理分頁參數。
Mybatis是如何將sql執行結果封裝為目標對象并返回的?都有哪些映射形式?
- 第一種是使用``標簽,逐一定義數據庫列名和對象屬性名之間的映射關系。
- 第二種是使用sql列的別名功能,將列的別名書寫為對象屬性名。
- 有了列名與屬性名的映射關系后,Mybatis通過反射創建對象,同時使用反射給對象的屬性逐一賦值并返回,那些找不到映射關系的屬性,是無法完成賦值的。
Mybatis動態sql有什么用?執行原理?有哪些動態sql?
- Mybatis動態sql可以在Xml映射文件內,以標簽的形式編寫動態sql,執行原理是根據表達式的值完成邏輯判斷并動態拼接sql的功能。
Mybatis的Xml映射文件中,不同的Xml映射文件,id是否可以重復?
- 不同的Xml映射文件,如果配置了namespace,那么id可以重復;如果沒有配置namespace,那么id不能重復; 原因就是namespace+id是作為Map <String,MapperStatement> 的key使用的,如果沒有namespace,就剩下id,那么,id重復會導致數據互相覆蓋。有了namespace,自然id就可以重復,namespace不同,namespace+id自然也就不同。
為什么說Mybatis是半自動ORM映射工具?它與全自動的區別在哪里?
- Hibernate屬于全自動ORM映射工具,使用Hibernate查詢關聯對象或者關聯集合對象時,可以根據對象關系模型直接獲取,所以它是全自動的。而Mybatis在查詢關聯對象或關聯集合對象時,需要手動編寫sql來完成,所以,稱之為半自動ORM映射工具。
MyBatis實現一對一有幾種方式?具體怎么操作的?
- 有聯合查詢和嵌套查詢,聯合查詢是幾個表聯合查詢,只查詢一次, 通過在resultMap里面配置association節點配置一對一的類就可以完成; 嵌套查詢是先查一個表,根據這個表里面的結果的 外鍵id,去再另外一個表里面查詢數據,也是通過association配置,但另外一個表的查詢通過select屬性配置。
MyBatis實現一對多有幾種方式,怎么操作的?
- 有聯合查詢和嵌套查詢。聯合查詢是幾個表聯合查詢,只查詢一次,通過在resultMap里面的collection節點配置一對多的類就可以完成;嵌套查詢是先查一個表,根據這個表里面的 結果的外鍵id,去再另外一個表里面查詢數據,也是通過配置collection,但另外一個表的查詢通過select節點配置。
Mybatis是否支持延遲加載?如果支持,它的實現原理是什么?
- Mybatis僅支持association關聯對象和collection關聯集合對象的延遲加載,association指的就是一對一,collection指的就是一對多查詢。在Mybatis配置文件中,可以配置是否啟用延遲加載lazyLoadingEnabled=true|false。 它的原理是,使用CGLIB創建目標對象的代理對象,當調用目標方法時,進入攔截器方法,比如調用a.getB().getName(),攔截器invoke()方法發現a.getB()是null值,那么就會單獨發送事先保存好的查詢關聯B對象的sql,把B查詢上來,然后調用a.setB(b),于是a的對象b屬性就有值了,接著完成a.getB().getName()方法的調用。
Mybatis的一級、二級緩存
緩存就是內存中的數據,常常來自對數據庫查詢結果的保存。使用緩存,我們可以避免頻繁與數據庫進行交互,從而提高響應速度。
MyBatis 也提供了對緩存的支持,分為一級緩存和二級緩存,來看下下面這張圖:
1)一級緩存 Mybatis的一級緩存是指SQLSession,一級緩存的作用域是SQlSession, Myabits默認開啟一級緩存。
在同一個SqlSession中,執行相同的SQL查詢時;第一次會去查詢數據庫,并寫在緩存中,第二次會直接從緩存中取。 當執行SQL時候兩次查詢中間發生了增刪改的操作,則SQLSession的緩存會被清空。
每次查詢會先去緩存中找,如果找不到,再去數據庫查詢,然后把結果寫到緩存中。 Mybatis的內部緩存使用一個HashMap,key為hashcode+statementId+sql語句。Value為查詢出來的結果集映射成的java對象。 SqlSession執行insert、update、delete等操作commit后會清空該SQLSession緩存。
2) Mybatis二級緩存是默認不開啟的,作用于一個Application,是Mapper級別的,多個SqlSession使用同一個Mapper的sql能夠使用二級緩存。
什么是MyBatis的接口綁定?有哪些實現方式?
- 接口綁定,就是在MyBatis中任意定義接口,然后把接口里面的方法和SQL語句綁定, 我們直接調用接口方法就可以,這樣比起原來了SqlSession提供的方法我們可以有更加靈活的選擇和設置。 接口綁定有兩種實現方式:
- 注解綁定,就是在接口的方法上面加上 @Select、@Update等注解,里面包含Sql語句來綁定;
- 外一種就是通過xml里面寫SQL來綁定, 在這種情況下,要指定xml映射文件里面的namespace必須為接口的全路徑名。當Sql語句比較簡單時候,用注解綁定, 當SQL語句比較復雜時候,用xml綁定,一般用xml綁定的比較多。
使用MyBatis的mapper接口調用時有哪些要求?
- Mapper接口方法名和mapper.xml中定義的每個sql的id相同;
- Mapper接口方法的輸入參數類型和mapper.xml中定義的每個sql 的parameterType的類型相同;
- Mapper接口方法的輸出參數類型和mapper.xml中定義的每個sql的resultType的類型相同;
- Mapper.xml文件中的namespace即是mapper接口的類路徑。
mybatis是如何防止SQL注入的?
- 首先看一下下面兩個sql語句的區別:
<select id="selectByNameAndPassword" parameterType="java.util.Map" resultMap="BaseResultMap">select id, username, passwordfrom userwhere username = #{username,jdbcType=VARCHAR} andpassword = #{password,jdbcType=VARCHAR}
</select>
<select id="selectByNameAndPassword" parameterType="java.util.Map" resultMap="BaseResultMap">select id, username, password,from userwhere username = ${username,jdbcType=VARCHAR} andpassword = ${password,jdbcType=VARCHAR}
</select>
mybatis中的#和$的區別:
#
將傳入的數據都當成一個字符串,會對自動傳入的數據加一個雙引號。 如:where username=#{username}
,如果傳入的值是111,那么解析成sql時的值為where username="111"
, 如果傳入的值是id,則解析成的sql為where username="id"
.$
將傳入的數據直接顯示生成在sql中。如:where username=${username}
,如果傳入的值是111,那么解析成sql時的值為where username=111
;如果傳入的值是:drop table user;
,則解析成的sql為:select id, username, password, role from user where username=;drop table user
;#
方式能夠很大程度防止sql注入,$
方式無法防止Sql注入。$
方式一般用于傳入數據庫對象,例如傳入表名.- 一般能用
#
的就別用$
,若不得不使用“${xxx}”
這樣的參數,要手工地做好過濾工作,來防止sql注入攻擊。 - 在MyBatis中,
“${xxx}”
這樣格式的參數會直接參與SQL編譯,從而不能避免注入攻擊。但涉及到動態表名和列名時,只能使用“${xxx}”
這樣的參數格式。所以,這樣的參數需要我們在代碼中手工進行處理來防止注入。
sql注入:
- SQL注入,大家都不陌生,是一種常見的攻擊方式。攻擊者在界面的表單信息或URL上輸入一些奇怪的SQL片段(例如“or ‘1’=’1’”這樣的語句),有可能入侵參數檢驗不足的應用程序。所以,在我們的應用中需要做一些工作,來防備這樣的攻擊方式。在一些安全性要求很高的應用中(比如銀行軟件),經常使用將SQL語句全部替換為存儲過程這樣的方式,來防止SQL注入。這當然是一種很安全的方式,但我們平時開發中,可能不需要這種死板的方式。
mybatis是如何做到防止sql注入的
- MyBatis框架作為一款半自動化的持久層框架,其SQL語句都要我們自己手動編寫,這個時候當然需要防止SQL注入。其實,MyBatis的SQL是一個具有“輸入+輸出”的功能,類似于函數的結構,參考上面的兩個例子。其中,parameterType表示了輸入的參數類型,resultType表示了輸出的參數類型。回應上文,如果我們想防止SQL注入,理所當然地要在輸入參數上下功夫。上面代碼中使用#的即輸入參數在SQL中拼接的部分,傳入參數后,打印出執行的SQL語句,會看到SQL是這樣的:
select id, username, password from user where username=? and password=?
- 不管輸入什么參數,打印出的SQL都是這樣的。這是因為MyBatis啟用了預編譯功能,在SQL執行前,會先將上面的SQL發送給數據庫進行編譯;執行時,直接使用編譯好的SQL,替換占位符“?”就可以了。因為SQL注入只能對編譯過程起作用,所以這樣的方式就很好地避免了SQL注入的問題。
底層實現原理
- MyBatis是如何做到SQL預編譯的呢?其實在框架底層,是JDBC中的PreparedStatement類在起作用,PreparedStatement是我們很熟悉的Statement的子類,它的對象包含了編譯好的SQL語句。這種“準備好”的方式不僅能提高安全性,而且在多次執行同一個SQL時,能夠提高效率。原因是SQL已編譯好,再次執行時無需再編譯。
結論:
#{}:相當于JDBC中的PreparedStatement
${}:是輸出變量的值
- 簡單說,#{}是經過預編譯的,是安全的;${}是未經過預編譯的,僅僅是取變量的值,是非安全的,存在SQL注入。