Power Apps 中修改 SharePoint Online 資料
1、#{}和${}的區別是什麼?
注:面試官真題。
⭐參考閱讀一篇寫得非常好的文章:#{}與${}的區別
#{}
是預編譯處理,${}
是字串替換。
#{}
- mybatis在處理
#{}
時,會將sql中的#{}
替換為?
號,呼叫PreparedStatement
的setString(int parameterIndex, String x)
方法來賦值,這個方法會將使用者輸入的單引號'
加上轉義字元\
,以起到阻止惡意sql注入的問題; - 同時對
#{}
會有預編譯的機制。- 預編譯是提前對SQL語句進行預編譯,而其後注入的引數將不會再進行SQL編譯。我們知道,SQL注入是發生在編譯的過程中,因為惡意注入了某些特殊字元,最後被編譯成了惡意的執行操作。而預編譯機制則可以很好的防止SQL注入。
- 也就是說,編譯之後SQL語句的結構不會再發生變化,
#{}
指代的位置也僅僅作為字串填入,因此惡意SQL字串不會再改變SQL語句的結構。
- mybatis在處理
⭐關於
PreparedStatement
推薦閱讀:java中PreparedStatement和Statement詳細講解
其中講到一個例子:
sql語句:**SELECT * FROM admin WHERE username = '韋小寶' AND password = ?**
如此呼叫**PreparedStatement.setString**
方法時:
**parepaerStatement.setString(2, "222' OR '8'='8'"**
也就是想將第二個引數password
變為**222' OR '8'='8'**
,想使得整句是
**SELECT * FROM admin WHERE username = '韋小寶' AND password = 222' OR '8'='8'**
,注入**'8'='8'**
這個恆為真的條件判斷,起到繞過密碼認證的過程。這就是sql注入。
而**PreparedStatement.setString**
會起到作用,使得實際上的sql語句變成:
**SELECT * FROM admin WHERE username = '韋小寶' AND password = '222\' OR \'8\'=\'8'**
,將'
給轉義成了普通字元,而沒有了斷開字元的功能,從而阻止了sql注入。
${}
- mybatis在處理
${}
時,就是把${}
替換成變數的值。 - 也就是說,惡意SQL語句還是可以改變原本SQL語句的結構,無法阻止SQL注入。
- mybatis在處理
使用 #{} 可以有效的防止SQL注入,提高系統安全性。
2、XML對映檔案中,除了常見的 select|insert|update|delete
標籤之外,還有哪些標籤?
注:京東面試。
答:還有很多其他的標籤,
<resultMap>
:
resultMap是Mybatis最強大的元素,它可以將查詢到的複雜資料(比如查詢到幾個表中資料)對映到一個結果集當中。
<resultMap id="唯一的標識" type="對映的pojo物件">
<id column="表的主鍵欄位,或者可以為查詢語句中的別名欄位" jdbcType="欄位型別" property="對映pojo物件的主鍵屬性" />
<result column="表的一個欄位(可以為任意表的一個欄位)" jdbcType="欄位型別" property="對映到pojo物件的一個屬性(須為type定義的pojo物件中的一個屬性)"/>
<result column=... .../>
<result column=... .../>
...
<!--POJO的物件屬性-->
<association property="pojo的一個物件屬性" javaType="pojo關聯的pojo物件">
<id column="關聯pojo物件對應表的主鍵欄位" jdbcType="欄位型別" property="關聯pojo物件的主席屬性"/>
<result column="任意表的欄位" jdbcType="欄位型別" property="關聯pojo物件的屬性"/>
</association>
<association property=.../>
<association property=.../>
...
<!-- 集合中的property須為oftype定義的pojo物件的屬性-->
<collection property="pojo的集合屬性" ofType="集合中的pojo物件">
<id column="集合中pojo物件對應的表的主鍵欄位" jdbcType="欄位型別" property="集合中pojo物件的主鍵屬性" />
<result column="可以為任意表的欄位" jdbcType="欄位型別" property="集合中的pojo物件的屬性" />
</collection>
<collection .../>
<collection .../>
...
</resultMap>
~~<parameterMap>~~
(已棄用, 改用內聯引數對映和parameterType
屬性)<sql>
- 使用sql標籤減少重複的sql語句,不僅可以減少我們的程式碼量,還更加方便我們檢視!
⭐參考:
<include>
- include標籤引用,可以複用SQL片段
- mybatis中
標籤的作用
<selectKey>
<selectKey>
在Mybatis中是為了解決Insert資料時不支援主鍵自動生成的問題, 將<selectKey>
放在<insert>
之後,通過LAST_INSERT_ID()
獲得剛插入的自動增長的id的值。- Mybatis 示例之 SelectKey
- 加上動態
的 9 個標籤, trim|where|set|foreach|if|choose|when|otherwise|bind
等,其中<sql>
為 sql 片段標籤,通過<include>
標籤引入 sql 片段,<selectKey>
為不支援自增的主鍵生成策略標籤
3、最佳實踐中,通常一個 Xml 對映檔案,都會寫一個 Dao 介面與之對應,請問,這個 Dao 介面的工作原理是什麼?Dao 接口裡的方法,引數不同時,方法能過載嗎?
注:京東面試。
答:Dao
介面,就是人們常說的 Mapper 介面
- 介面的全限名,就是對映檔案中的
namespace
的值
<mapper namespace="com.project.module.dao.PojoDao">
...
- 介面的方法名,就是對映檔案中
MappedStatement
的 id 值
<select id="getXXXX" resultType="java.util.Map">
...
</select>
- 介面方法內的引數,就是傳遞給 sql 的引數。 Mapper 介面是沒有實現類的,當呼叫介面方法時,介面全限名+方法名拼接字串作為 key 值,可唯一定位一個
MappedStatement
,舉例:com.mybatis3.mappers.StudentDao.findStudentById
,可以唯一找到namespace
為com.mybatis3.mappers.StudentDao
下面id = findStudentById
的MappedStatement
。 - 在 MyBatis 中,每一個
<select>
、<insert>
、<update>
、<delete>
標籤,都會被解析為一個MappedStatement
物件。
Dao 接口裡的方法可以過載,但是 Mybatis 的 XML 裡面的 ID 不允許重複, 但是可以用動態sql的方式實現
比如:
Mapper
:
/**
* Mapper接口裡面方法過載
*/
public interface StuMapper {
List<Student> getAllStu();
List<Student> getAllStu(@Param("id") Integer id);
}
xml
檔案:
<select id="getAllStu" resultType="com.pojo.Student">
select * from student
<where>
<if test="id != null">
id = #{id}
</if>
</where>
</select>
能正常執行,並能得到相應的結果,這樣就實現了在 Dao 介面中寫過載方法。
Mybatis 的 Dao 介面可以有多個過載方法,但是多個介面對應的對映必須只有一個,否則啟動會報錯。
Dao 介面方法可以過載,但是需要滿足以下條件:
- 僅有一個無參方法和一個有參方法
- 多個有參方法時,引數數量必須一致。且使用相同的
@Param
,或者使用param1
這種, 不一致時取最長的引數數量,其他數量不符合的方法報錯。
測試如下:
PersonDao.java
Person queryById();
Person queryById(@Param("id") Long id);
Person queryById(@Param("id") Long id, @Param("name") String name);
PersonMapper.xml
<select id="queryById" resultMap="PersonMap">
select
id, name, age, address
from person
<where>
<if test="id != null">
id = #{id}
</if>
<if test="name != null and name != ''">
name = #{name}
</if>
</where>
limit 1
</select>
queryById()
方法執行時,無引數,動態 sql 可以正常執行。queryById(1L,"1")
方法執行時,引數id和name屬性都可以獲取到,動態 sql 正常執行。queryById(1L)
方法執行時,只有一個引數,報錯。
4、MyBatis 是如何進行分頁的?分頁外掛的原理是什麼?
答:
- MyBatis 使用 RowBounds 物件進行分頁,它是針對 ResultSet 結果集執行的記憶體分頁,而非物理分頁;
- 可以在 sql 內直接書寫帶有物理分頁的引數來完成物理分頁功能(Limit)
- 也可以使用分頁外掛來完成物理分頁。
分頁外掛的基本原理是使用 MyBatis 提供的外掛介面,實現自定義外掛,在外掛的攔截方法內攔截待執行的 sql,然後重寫 sql,根據 dialect 方言,新增對應的物理分頁語句和物理分頁引數。
舉例: select _ from student
,攔截 sql 後重寫為: select t._ from (select \* from student)t limit 0,10
10、MyBatis 是否支援延遲載入?如果支援,它的實現原理是什麼?
答:MyBatis 僅支援 association 關聯物件和 collection 關聯集合物件的延遲載入,association
指的就是一對一
,collection
指的就是一對多
查詢。在 MyBatis 配置檔案中,可以配置是否啟用延遲載入 lazyLoadingEnabled=true|false
。
⭐推薦閱讀:Mybatis延遲載入的實現以及使用場景
它的原理是,使用 CGLIB
建立目標物件的代理物件,當呼叫目標方法時,進入攔截器方法,
比如呼叫 a.getB().getName()
,攔截器 invoke()
方法發現 a.getB()
是 null
值,那麼就會單獨傳送事先儲存好的查詢關聯 B 物件的 sql,把 B 查詢上來,然後呼叫 a.setB(b)
,於是 a 的物件 b 屬性就有值了,接著完成 a.getB().getName()
方法的呼叫。這就是延遲載入的基本原理。
當然了,不光是 MyBatis,幾乎所有的包括 Hibernate,支援延遲載入的原理都是一樣的。