MyBatis框架核心之(三)註解使用介面對映
三、Mybatis介面對映(註解)
傳統的mapper.xml+介面使用介面對映相對較麻煩
所以我們可以使用註解來簡化開發
支援的註解有以下:
MyBatis可以利用SQL對映檔案來配置,也可以利用Annotation來設定。MyBatis提供的一些基本註解如下表所示。
註解 |
目標 |
相對應的 XML |
描述 |
@CacheNamespace |
類 |
<cache> |
為給定的名稱空間 (比如類)配置快取。屬性:implemetation,eviction, flushInterval,size,readWrite,blocking和 properties。 |
@Property |
N/A |
<property> |
Specifies the property value or placeholder(can replace by configuration properties that defined at themybatis-config.xml). Attributes: name, value. (Available on MyBatis 3.4.2+) |
@CacheNamespaceRef |
類 |
<cacheRef> |
參照另外一個名稱空間的快取來使用。屬性:value, name。 If you use this annotation, you should be specified either |
@ConstructorArgs |
方法 |
<constructor> |
收集一組結果傳遞給一個劫奪物件的構造方法。屬性:value,是形式引數的陣列。 |
@Arg |
N/A |
· <arg> · <idArg> |
單獨的構造方法參數 , 是 ConstructorArgs集合的一部分。屬性: id,column,javaType,typeHandler。 id屬性是布林值, 來標識用於比較的屬性,和<idArg>XML元素相似。 |
@TypeDiscriminator |
方法 |
<discriminator> |
一組例項值被用來決定結果對映的表現。屬性: column, javaType, jdbcType, typeHandler,cases。cases屬性就是實例的陣列。 |
@Case |
N/A |
<case> |
單獨例項的值和它對應的對映。屬性: value,type,results。Results屬性是結果陣列,因此這個註解和實際的 ResultMap很相似,由下面的 Results註解指定。 |
@Results |
方法 |
<resultMap> |
結果對映的列表, 包含了一個特別結果列如何被對映到屬性或欄位的詳情。屬性:value, id。value屬性是 Result 註解的陣列。這個id的屬性是結果對映的名稱。 |
@Result |
N/A |
· <result> · <id> |
在列和屬性或欄位之間的單獨結果映射。屬性:id,column, property, javaType ,jdbcType ,type Handler, one,many。id屬性是一個布林值,表示了應該被用於比較(和在 XML 對映中的<id>相似)的屬性。one屬性是單獨的聯系,和 <association>相似 ,而 many 屬性是對集合而言的 , 和 <collection>相似。它們這樣命名是為了避免名稱衝突。 |
@One |
N/A |
<association> |
複雜型別的單獨屬性值對映。屬性: select,已對映語句(也就是對映器方法)的完全限定名,它可以載入合適類型的例項。注意:聯合對映在註解 API 中是不支援的。這是因為 Java註解的限制,不允許迴圈引用。fetchType會覆蓋全域性的配置引數lazyLoadingEnabled。 |
@Many |
N/A |
<collection> |
對映到複雜型別的集合屬性。屬性:select,已對映語句(也就是對映器方法)的全限定名,它可以載入合適型別的例項的集合,fetchType會覆蓋全域性的配置引數lazyLoadingEnabled。注意聯合對映在註解 API中是不支援的。這是因為 Java註解的限制,不允許迴圈引用 |
@MapKey |
方法 |
復雜類型的集合屬性對映。屬性 : select,是對映語句(也就是對映器方法)的完全限定名,它可以載入合適類型的一組例項。注意:聯合對映在 Java 註解中是不支援的。這是因為 Java注解的限制,不允許迴圈引用。 |
|
@Options |
方法 |
對映語句的屬性 |
這個註解提供訪問交換和配置選項的寬廣範圍,它們通常在對映語句上作為屬性出現。而不是將每條語句註解變復雜,Options註解提供連貫清晰的方式來訪問它們。屬性:useCache=true , flushCache=FlushCachePolicy.DEFAULT , resultSetType=FORWARD_ONLY , statementType=PREPARED , fetchSize=-1 , , timeout=-1 useGeneratedKeys=false , keyProperty=”id” , keyColumn=”” , resultSets=””。理解 Java註解是很重要的,因為沒有辦法來指定“null”作為值。因此,一旦你使用了 Options註解,語句就受所有預設值的支配。要注意什麼樣的預設值來避免不期望的行為。 |
· @Insert · @Update · @Delete · @Select |
方法 |
· <insert> · <update> · <delete> · <select> |
這些註解中的每一個代表了執行的真實 SQL。它們每一個都使用字串陣列 (或單獨的字串)。如果傳遞的是字符串陣列,它們由每個分隔它們的單獨空間串聯起來。這就當用 Java程式碼構建 SQL時避免了“丟失空間”的問題。然而,如果你喜歡,也歡迎你串聯單獨的字串。屬性:value,這是字串陣列用來組成單獨的 SQL 語句。 |
· @InsertProvider · @UpdateProvider · @DeleteProvider · @SelectProvider |
方法 |
· <insert> · <update> · <delete> · <select> |
這些可選的 SQL 註解允許你指定一個類名和一個方法在執行時來返回執行允許建立動態的 SQL。基於執行的對映語句, MyBatis 會例項化這個類,然後執行由 provider指定的方法. You can pass objects that passed to arguments of a mapper method, "Mapper interface type" and "Mapper method" via theProviderContext(available since MyBatis 3.4.5 or later) as method argument. (In MyBatis 3.4 or later, it's allow multiple parameters)屬性: type,method。type屬性是類。method屬性是方法名。注意:這節之後是對類的討論,它可以幫助你以乾淨,容於閱讀的方式來構建動態 SQL。 |
@Param |
Parameter |
N/A |
如果你的對映器的方法需要多個引數,這個註解可以被應用於對映器的方法引數來給每個引數一個名字。否則,多引數將會以它們的順序位置來被命名 (不包括任何 RowBounds引數) 比如。 #{param1} , #{param2}等 , 這是默認的。使用 @Param(“person”),引數應該被命名為 #{person}。 |
@SelectKey |
方法 |
<selectKey> |
該註解複製了<selectKey>的功能,用在註解了@Insert, @InsertProvider, @Updateor@UpdateProvider的方法上。在其他方法上將被忽略。如果你指定了一個@SelectKey註解,然後Mybatis將忽略任何生成的key屬性通過設定@Options,或者配置屬性。屬性:statement是要執行的sql語句的字串陣列,keyProperty是需要更新為新值的引數物件屬性,before可以是true或者false分別代表sql語句應該在執行insert之前或者之後,resultType是keyProperty的Java型別,statementType是語句的型別,取Statement, PreparedStatement和CallableStatement對應的STATEMENT, PREPARED或者CALLABLE其中一個,預設是PREPARED。 |
@ResultMap |
方法 |
N/A |
這個註解給@Select或者@SelectProvider提供在XML對映中的<resultMap>的id。這使得註解的select可以複用那些定義在XML中的ResultMap。如果同一select註解中還存在@Results或者@ConstructorArgs,那麼這兩個註解將被此註解覆蓋。 |
@ResultType |
Method |
N/A |
當使用結果處理器時啟用此註解。這種情況下,返回型別為void,所以Mybatis必須有一種方式決定物件的型別,用於構造每行資料。如果有XML的結果對映,使用@ResultMap註解。如果結果型別在XML的<select>節點中指定了,就不需要其他的註解了。其他情況下則使用此註解。比如,如果@Select註解在一個方法上將使用結果處理器,返回型別必須是void並且這個註解(或者@ResultMap)是必須的。這個註解將被忽略除非返回型別是void。 |
@Flush |
方法 |
N/A |
如果這個註解使用了,它將呼叫定義在Mapper介面中的SqlSession#flushStatements()方法。(Mybatis 3.3或者以上) |
這些註解的用法有很多,這裡只是簡單介紹如何簡單的使用註解代替mapper.xml檔案,下幾篇文章會詳細解釋其他幾種註解的使用
本文主要的註解有 @Select、@Delete、@Insert、@update、和@Param
1. Mybatis.xml配置檔案(Mybatis的大腦)
1).指定jdbc.properties檔案的路徑
使用<propertiesresource=" "></properties>標籤
2).配置jdbc環境
<!-- 配置jdbc環境 --> <environmentsdefault="development"> <environmentid="development"> <transactionManagertype="JDBC"/> <!-- 配置資料庫連線資訊根據el表示式取值--> <dataSourcetype="POOLED"> <propertyname="driver"value="${driverClass}"/> <propertyname="url"value="${url}"/> <propertyname="username"value="${user}"/> <propertyname="password"value="${password}"/> </dataSource> </environment> </environments> |
3).配置介面對映
因為是註解使用介面對映,所以這裡不需要配置mapper.xml檔案,直接使用<mapperclass=" "/>標籤,直接配置介面的全類名(包名+介面名)
<mappers> <!-- 註冊介面如果使用的是註解則不需要mappers檔案--> <!-- mapper 介面的全類名 --> <mapperclass="cn.et.fuqiang.interfaceMap.xml.InterfaceMyUser"/> </mappers> |
詳細配置
Mybatis.xml |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- 指定jdbc的配置檔案位置 --> <propertiesresource="cn/et/fuqiang/interfaceMap/xml/jdbc.properties"></properties> <!-- 配置jdbc環境 --> <environmentsdefault="development"> <environmentid="development"> <transactionManagertype="JDBC"/> <!-- 配置資料庫連線資訊 --> <dataSourcetype="POOLED"> <propertyname="driver"value="${driverClass}"/> <propertyname="url"value="${url}"/> <propertyname="username"value="${user}"/> <propertyname="password"value="${password}"/> </dataSource> </environment> </environments> <!-- 使用介面對映配置查詢語句 --> <mappers> <!-- 註冊介面如果使用的是註解則不需要mappers檔案--> <mapperclass="cn.et.fuqiang.interfaceMap.xml.InterfaceMyUser"/> </mappers> </configuration> |
2. 定義介面和添加註解
因為我們要用註解替換mapper.xml檔案,所以所有配置的重點都在定義的mapper接中。
註解的使用大大的簡化了mybatis的使用,我們只需要在方法上宣告一個註解,然後寫上sql語句,需要用什麼型別接受返回的結果,就將方法的返回值寫為什麼型別。
引數的設定與通常的方法一樣。
以查詢方法為例:
該註解中寫查詢語句
@Select(“select * from myuser where userid=#{0}”)
public Map<String,Object>queryMyUserById(Integeruserid);
方法的返回型別
使用@Select註解需要什麼型別的返回值,直接將方法的返回型別設定為什麼
引數
直接將sql語句中需要的引數設定在形參中,如果sql語句中要使用到形參的引數名,則可以在形參前加@Param(“引數名”)註解,然後在方法中直接使用
Sql語句中獲取引數的值:
1. 根據索引獲取
從0開始(比如形參中有兩個引數,索引就是0,1)
2. 根據預設的引數名獲取
從param1開始(比如形參中有兩個引數,預設的屬性名就是param1,param2)
3. 根據自定義名獲取
在形參的引數前使用 function name(@Param(“name”)Stringname)
則可以直接使用name
兩種表示式:
el表示式取值(${})
只能用在形參中指定的名稱 和 預設的引數名 param1,2,3…. 名字來取值,不能根據索引取值因為el表示式會把索引當運算的數字
注意:
el表示式取值的方法相當於字串的拼接如果是字串的話需要自己新增單引號 如:name=’${param1}’)
所以使用el表示式無法防止sql注入
ognl表示式取值(#{})
可以根據上面的三種的任意一種取值,並且取值的方法是以? 號的方式,相當與使用jdbc中 預編譯的sql PreparedStatement 方法傳入引數的,所以可以防止sql注入。
下面對@Update、@Insert、@Delete和@SelectKey演示
package cn.et.fuqiang.interfaceMap.annotation; import java.util.Map; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.SelectKey; import org.apache.ibatis.annotations.Update; public interface InterfaceMyUser { @Select("select * from myuser where userid=#{0}") public Map<String,Object> queryMyUserById(Integer userid); @Update("update myuser set username='${username}',userage=#{userage} where userid=#{0}") public void updateUserByMap(Integer userid,@Param("username")String username,@Param("userage") Integer userage); @SelectKey(before=true,keyProperty="userid",resultType=int.class,statement="select nvl(max(userid),0)+1 from myuser") @Insert("insert into myuser(userid,username,userage) values(#{userid},#{username},#{userage})") public void saveUserByMap(Map<String,Object> map); @Delete("delete from myuser where userid=#{param1}") public void deleteUserById(Integer userid); } |
在update中使用到了@SelectKey註解,該方法可以用來在插入和修改的時候,查詢序列,或者是做一個簡單的子查詢返回一個需要的值
注意:@Update、@Insert和@Delete沒有返回值所以方法的返回型別需要寫為
Void
所有程式碼實現
Jdbc.properties檔案的配置
url=jdbc:oracle:thin:@localhost:1521:orcl user=mybatis password=mybatis driverClass=oracle.jdbc.OracleDriver |
Mybatis.xml配置檔案的配置
mybatis.xml |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- 指定jdbc的配置檔案位置 --> <propertiesresource="cn/et/fuqiang/interfaceMap/annotation/jdbc.properties"></properties> <!-- 配置jdbc環境 --> <environmentsdefault="development"> <environmentid="development"> <transactionManagertype="JDBC"/> <!-- 配置資料庫連線資訊 --> <dataSourcetype="POOLED"> <propertyname="driver"value="${driverClass}"/> <propertyname="url"value="${url}"/> <propertyname="username"value="${user}"/> <propertyname="password"value="${password}"/> </dataSource> </environment> </environments> <!-- 使用介面對映配置查詢語句 --> <mappers> <!-- 註冊介面如果使用的是註解則不需要mappers檔案--> <mapperclass="cn.et.fuqiang.interfaceMap.annotation.InterfaceMyUser"/> </mappers> </configuration> |
介面程式碼
InterfaceMyUser.java |
package cn.et.fuqiang.interfaceMap.annotation; import java.util.Map; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.SelectKey; import org.apache.ibatis.annotations.Update; public interface InterfaceMyUser { @Select("select * from myuser where userid=#{0}") public Map<String,Object> queryMyUserById(Integer userid); /** * 從sql語句中 獲取引數 有多中方式 * 不能根據索引取值因為el表示式會把索引當運算的數字 * el表示式 ${} 只能用在形參中指定的名稱 來獲取 和 param1 名字 (獲取值得方法相當於字串的拼接 如果是字串的話要用引號) * ognl表示式 #{} 可以根據傳入的變數名來獲取值,也可以根據上面兩種方式 (獲取值得方法是防注入的,?會用問號代替) * 預設是根據 形參的索引 從0開始 * * 如果要使用形參的引數名作為鍵 則需要在該形參前指定 @param("對應的名字") * * @param userid * @param username * @param userage */ @Update("update myuser set username='${username}',userage=#{userage} where userid=#{0}") public void updateUserByMap(Integer userid,@Param("username")String username,@Param("userage") Integer userage); @SelectKey(before=true,keyProperty="userid",resultType=int.class,statement="select nvl(max(userid),0)+1 from myuser") @Insert("insert into myuser(userid,username,userage) values(#{userid},#{username},#{userage})") public void saveUserByMap(Map<String,Object> map); @Delete("delete from myuser where userid=#{param1}") public void deleteUserById(Integer userid); } |
測試類程式碼
InterfaceMapAnnotationTest.java |
package cn.et.fuqiang.interfaceMap.annotation; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; public class InterfaceMapAnnotationTest { private static SqlSession session; private static InterfaceMyUser mapper=null; static{ //mybatis的配置檔案 String resource = "mybatis.xml"; //使用類載入器載入mybatis的配置檔案(它也載入關聯的對映檔案) InputStream is = InterfaceMapAnnotationTest.class.getResourceAsStream(resource); //構建sqlSession的工廠 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is); //使用MyBatis提供的Resources類載入mybatis的配置檔案(它也載入關聯的對映檔案) //Reader reader = Resources.getResourceAsReader(resource); //構建sqlSession的工廠 //SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader); //建立能執行對映檔案中sql的sqlSession session = sessionFactory.openSession(); mapper=session.getMapper(InterfaceMyUser.class); } //@Test public void quesrUserById(){ System.out.println(mapper.queryMyUserById(2)); } //@Test public void deleteUserByID(){ mapper.deleteUserById(3); session.commit(); } //@Test public void insertUserByMap(){ Map<String,Object> map = new HashMap<String,Object>(); map.put("username", "wfq"); map.put("userage", 120); mapper.saveUserByMap(map); session.commit(); } @Test public void updateUserByMap(){ mapper.updateUserByMap(2,"wava",100); session.commit();
|