MyBatis 真正強大之處就在這些映射語句,也就是它的魔力所在。對于它的強大功能,SQL 映射文件的配置卻非常簡單。
如果您比較SQL 映射文件配置與JDBC 代碼,您很快可以發現,使用SQL 映射文件配置可以節省95%的代碼量。MyBatis 被創建來專注于SQL,但又給您自己的實現極大的空間。
?
需要配置的基本元素
1. cache – 配置給定模式的緩存
2. cache-ref – 從別的模式中引用一個緩存
3. resultMap – 這是最復雜而卻強大的一個元素了,它描述如何從結果集中加載對象
4. sql – 一個可以被其他語句復用的SQL 塊
5. insert – 映射INSERT 語句
6. update – 映射UPDATE 語句
7. delete – 映射DELEETE 語句
8. select - 映射SELECT語句
?
?
配置注意
- <!--mappers是告訴MyBatis?去哪尋找映射SQL?的語句。可以使用類路徑中的資源引用,或者使用字符,輸入確切的URL?引用。-->??
- <mappers>??
- ????<mapper?resource="com/accp/mybatis/data/BlogMapper.xml"?/>??
- </mappers>??
?
當Java接口與XML文件在一個相對路徑下時,可以不在myBatis配置文件的mappers中聲明。
?
?
?
SQL?
這個元素可以被用來定義可重用的SQL代碼段,可以包含在其他語句中。?
例子請看下面select中例子
?
?
SELECT
mybatis select是mybatis 中最常用的元素之一。- <?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="com.accp.mybatis.model.Blog">??
- ??????
- ????<!--可重用的SQL代碼段-->??
- ????<sql?id="blog_column">id,title,author_id?as?authorId</sql>??
- ??????
- ????<select?id="selectBlog_as_map"?parameterType="int"?resultType="hashmap">??
- ????????select?<include?refid="blog_column"/>?from?Blog?where?id?=?#{id}??
- ????</select>??
- ??????
- </mapper>??
?這個語句被稱作selectBlog_as_map,使用一個int (或Integer)類型的參數,并返回一個HashMap類型的對象
?
#{id}告訴mybatis創建了一個PreparedStatement(預處理語句)參數。
在JDBC中,類似的代碼如下:
?
- String?selectBlog_as_map?=?“select?*?from?Blog?where?id?=?”;???
- PreparedStatement?ps?=?conn.prepareStatement(selectBlog_as_map);???
- ps.setInt(1,id);???
- public?static?void?selectBlogAsMap(int?id)?{??
- ????SqlSession?session?=?sqlMapper.openSession();??
- ????Map<String,?Object>?map?=?session.selectOne("selectBlog_as_map",id);??
- ????System.out.println(map);??
- ????session.close();??
- }??
?
屬性 | 描述 | 取值 | 默認 |
---|---|---|---|
id | 在這個模式下唯一的標識符,可被其它語句引用 | ? | ? |
parameterType | 傳給此語句的參數的完整類名或別名 | ? | ? |
resultType | 語句返回值類型的整類名或別名。注意,如果是集合, 那么這里填寫的是集合的項的整類名或別名,而不是集合本身的類名。 (resultType 與resultMap 不能并用) | ? | ? |
resultMap | 引用的外部resultMap 名。結果集映射是MyBatis 中最強大的特性。 許多復雜的映射都可以輕松解決。(resultType 與resultMap 不能并用) | ? | ? |
flushCache | 如果設為true,則會在每次語句調用的時候就會清空緩存。select 語句默認設為false | true|false | false |
useCache | 如果設為true,則語句的結果集將被緩存。select 語句默認設為false true|false false timeout 設置驅動器在拋出異常前等待回應的最長時間,默認為不設值,由驅動器自己決定 | true|false | false |
timeout | 設置驅動器在拋出異常前等待回應的最長時間,默認為不設值,由驅動器自己決定 | 正整數 | 未設置 |
fetchSize | 設置一個值后,驅動器會在結果集數目達到此數值后, 激發返回,默認為不設值,由驅動器自己決定 | 正整數 | 驅動器決定 |
statementType | statement,preparedstatement,callablestatement。 預準備語句、可調用語句 | STATEMENT PREPARED CALLABLE | PREPARED |
resultSetType | forward_only,scroll_sensitive,scroll_insensitive 只轉發,滾動敏感,不區分大小寫的滾動 | FORWARD_ONLY SCROLL_SENSITIVE SCROLL_INSENSITIVE | 驅動器決定 |
?
- <?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="com.accp.mybatis.model.Blog">??
- ??
- ????<insert?id="insertBlog"?parameterType="Blog">??
- ????????insert?into?Blog??
- ????????????(id,title,author_id)??
- ????????values??
- ????????????(#{id},#{title},#{authorId})??
- ????</insert>??
- ??
- </mapper>??
對于insert如果你的數據庫支持自動生成主鍵的字段(比如 MySQL 和 SQL Server),那么你可以設置 useGeneratedKeys=”true”,然后把keyProperty 設成對應的列,就搞定了。
例如Blog表已經對 id 使用了自動生成的列類型,那么語句如下:
?
- <?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="com.accp.mybatis.model.Blog">??
- ??
- ????<insert?id="insertBlog"?parameterType="Blog"?useGeneratedKeys=”true”?keyProperty=”id”>??
- ????????insert?into?Blog??
- ????????????(title,author_id)??
- ????????values??
- ????????????(#{title},#{authorId})??
- ????</insert>??
- ??????
- </mapper>??
還可以使用selectKey元素。下面例子,使用mysql數據庫nextval('student')為自定義函數,用來生成一個key。
?
例如:
?
- <?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="com.accp.mybatis.model.Blog">??
- ????<insert?id="insertBlog"?parameterType="Blog"?>??
- ????????<selectKey?keyProperty="studentID"?resultType="String"?order="BEFORE">????
- ???????????select?nextval('id')????
- ????????</selectKey>????
- ????????insert?into?Blog??
- ????????????(id,title,author_id)??
- ????????values??
- ????????????(#{id},#{title},#{authorId})??
- ????</insert>???
- </mapper>??
?
insert語句屬性配置細節:
屬性 | 描述 | 取值 | 默認 |
id | 在這個模式下唯一的標識符,可被其它語句引用 | ? | ? |
parameterType | 傳給此語句的參數的完整類名或別名 | ? | ? |
flushCache | 如果設為true,則會在每次語句調用的時候就會清空緩存。select 語句默認設為false | true|false | false |
useCache | 如果設為true,則語句的結果集將被緩存。select 語句默認設為false true|false false timeout 設置驅動器在拋出異常前等待回應的最長時間,默認為不設值,由驅動器自己決定 | true|false | false |
timeout | 設置驅動器在拋出異常前等待回應的最長時間,默認為不設值,由驅動器自己決定 | 正整數 | 未設置 |
fetchSize | 設置一個值后,驅動器會在結果集數目達到此數值后,激發返回,默認為不設值,由驅動器自己決定 | 正整數 | 驅動器決定 |
statementType | statement,preparedstatement,callablestatement。 預準備語句、可調用語句 | STATEMENT PREPARED CALLABLE | PREPARED |
useGeneratedKeys | 告訴MyBatis 使用JDBC 的getGeneratedKeys 方法來獲取數據庫自己生成的主鍵(MySQL、SQLSERVER 等 關系型數據庫會有自動生成的字段)。默認:false | true|false | false |
keyProperty | 標識一個將要被MyBatis 設置進getGeneratedKeys 的key 所返回的值,或者為insert 語句使用一個selectKey 子元素。 | ? | ? |
?
?
selectKey語句屬性配置細節:
?
屬性 | 描述 | 取值 |
keyProperty | selectKey 語句生成結果需要設置的屬性。 | ? |
resultType | 生成結果類型,MyBatis 允許使用基本的數據類型,包括String 、int類型。 | ? |
order | 可以設成BEFORE 或者AFTER,如果設為BEFORE,那它會先選擇主鍵, 然后設置keyProperty,再執行insert語句;如果設為AFTER,它就先運行insert 語句再運行selectKey 語句, 通常是insert 語句中內部調用數據庫(像Oracle)內嵌的序列機制。 | BEFORE AFTER |
statementType | 像上面的那樣, MyBatis 支持STATEMENT,PREPARED和CALLABLE 的語句形式, ?對應Statement ,PreparedStatement 和CallableStatement 響應 | STATEMENT PREPARED CALLABLE |
?
UPDATE,DELETE
- <?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="com.accp.mybatis.model.Blog">??
- ????<update?id="updateBlog"?parameterType="Blog">????
- ???????UPDATE?Blog????
- ????????????SET?title?=?#{title},?????
- ????????????????author_id?=?#{author.id},????
- ?????????WHERE?id?=?#{id};??
- ????</update>????
- </mapper>??
delete 簡單例子:
- <delete?id="deleteBlog"?parameterType="Blog">????
- ??????DELETE?FROM?BLOG?WHERE?ID?=?#{id}????
- </delete>??
update、delete語句屬性配置細節:
?
屬性 | 描述 | 取值 | 默認 |
id | 在這個模式下唯一的標識符,可被其它語句引用 | ? | ? |
parameterType | 傳給此語句的參數的完整類名或別名 | ? | ? |
flushCache | 如果設為true,則會在每次語句調用的時候就會清空緩存。select 語句默認設為false | true|false | false |
useCache | 如果設為true,則語句的結果集將被緩存。select 語句默認設為false true|false false timeout 設置驅動器在拋出異常前等待回應的最長時間,默認為不設值,由驅動器自己決定 | true|false | false |
timeout | 設置驅動器在拋出異常前等待回應的最長時間,默認為不設值,由驅動器自己決定 | 正整數 | 未設置 |
fetchSize | 設置一個值后,驅動器會在結果集數目達到此數值后,激發返回,默認為不設值,由驅動器自己決定 | 正整數 | 驅動器決定 |
statementType | statement,preparedstatement,callablestatement。 預準備語句、可調用語句 | STATEMENT PREPARED CALLABLE | PREPARED |
- <?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="com.accp.mybatis.model.Blog">??
- ??????
- ????<insert?id="insertBlog"?parameterType="Blog">??
- ????????insert?into?Blog??
- ????????????(id,title,author_id)??
- ????????values??
- ????????????(#{id},#{title},#{author.id})??
- ????</insert>??
- </mapper>??
首先,像MyBatis的其他部分,參數可以指定一個確定的數據類型。
- #{property,javaType=int,jdbcType=NUMERIC}??
注意:如果null被當作值來傳遞,對于所有可能為空的列,JDBC Type是需要的。也可以通過閱讀PreparedStatement. setNull()方法的JavaDocs文檔來研究它。
為了以后自定義類型處理器,你可以指定一個確定的類型處理器類(或別名),比如:
- #{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler}??
對于數值類型,對于決定有多少數字是相關的,有一個數值范圍。
- #{height,javaType=double,jdbcType=NUMERIC,numericScale=2}??
- #{department,??
- ????mode=OUT,??
- ????jdbcType=CURSOR,??
- ????javaType=ResultSet,??
- ????resultMap=departmentResultMap}??
默認情況下,使用#{}格式的語法會導致MyBatis創建預處理語句屬性并以它為背景設置安全的值(比如?)。這樣做很安全,很迅速,也是首選的做法,有時你只是想直接在SQL語句中插入一個不改變的字符串。比如,像ORDER BY,你可以這樣來使用:
ORDER BY ${columnName}
這里MyBatis不會修改或轉義字符串。
重要:接受從用戶輸出的內容并提供給語句中不變的字符串,這樣做是不安全的。這會導致潛在的SQL注入攻擊,因此你不應該允許用戶輸入這些字段,或者通常自行轉義并檢查。
resultMap
- package?com.accp.mybatis.model;??
- ??
- public?class?Blog?{??
- ????private?Integer?id;??
- ????private?String?title;??
- ????private?Integer?authorId;??
- ??
- //省略get和set方法??
- }??
基于JavaBean的規范,上面這個類有3個屬性:id,title和authorId。這些在select語句中會精確匹配到列名。
這樣的一個JavaBean可以被映射到結果集,就像映射到HashMap一樣簡單。
- <?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="com.accp.mybatis.model.Blog">????
- ??????
- ????<select?id="selectBlog_by_id"?parameterType="int"?resultType="Blog">??
- ????????select?*?from?Blog?where?id?=?#{id}??
- ????</select>??
- ??
- </mapper>??
這些情況下,MyBatis會在幕后自動創建一個ResultMap,基于屬性名來映射列到JavaBean的屬性上。如果列名沒有精確匹配,你可以在列名上使用select字句的別名(一個標準的SQL特性)來匹配標簽。
ResultMap最優秀的地方你已經了解了很多了,但是你還沒有真正的看到一個。只是出于示例的原因,讓我們來看看最后一個示例中外部的resultMap是什么樣子的,這也是解決列名不匹配的另外一種方式。
- <resultMap?id="Blog_result"?type="Blog"?>??
- ????<id?column="id"?property="id"?/>??
- ????<result?column="title"?property="title"/>??
- ????<result?column="author_id"?property="authorId"/>??
- </resultMap>??
引用它的語句使用resultMap屬性就行了(注意我們去掉了resultType屬性)。比如:
- <?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="com.accp.mybatis.model.Blog">??
- ??
- ??
- ????<resultMap?id="Blog_result"?type="Blog"?>??
- ????????<id?column="id"?property="id"?/>??
- ????????<result?column="title"?property="title"/>??
- ????????<result?column="author_id"?property="authorId"/>??
- ????</resultMap>????
- ??
- ????<!--?resultType與resultMap不能同時使用?-->?????
- ????<select?id="selectBlog_by_id"?parameterType="int"?resultMap="Blog_result">??
- ????????select?*?from?Blog?where?id?=?#{id}??
- ????</select>??
- ??
- </mapper>??