本篇篇幅較長,請善用 Ctrl + F 搜索功能。html
結尾補充了 MyBatis 中 resultMap 的映射級別。java
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- git
MyBatis學完也有幾天了,總結一下學習到的內容,也算是複習了。github
使用MyBatis以前,咱們要知道,什麼是MyBatis?sql
MyBatis是apache一個開源的,基於Java的持久層框架。數據庫
MyBatis的優勢有哪些?apache
學習簡單,提供半自動的關係映射,SQL語句與代碼分離。api
MyBatis的缺點又有哪些?數組
要會寫SQL語句;每一個數據庫的SQL語句都多少會有誤差,因此不方便更換數據庫。mybatis
MyBatis適合什麼樣的項目?
適合性能要求很高,或者須要變化較多的項目。
要使用MyBatis,得有MyBatis的jar包。
在https://github.com/mybatis/mybatis-3/releases下載mybatis-x.x.x.zip,我用的版本是3.3.2。
壓縮包裏的mybatis-x.x.x.jar就是咱們要的jar包了,.pdf的是幫助文檔,lib文件夾裏的是MyBatis的依賴包,具體做用自行百度,一塊兒丟到項目的lib的文件夾裏所有add build一下就好了。
哦對了,jdbc的jar包請自行準備。
只有jar包還不夠,咱們還須要xml配置文件。
新建一個Source Folder,將配置文件統一放在裏面。爲了方便識別,MyBatis的配置文件咱們能夠命名爲「mybatis-config.xml」,配置文件的內容大體是這樣的,改改就能用:
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE configuration 3 PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-config.dtd"> 5 <configuration> 6 <properties resource="mybatis.properties" /><!-- jdbc配置文件 --> 7 <typeAliases><!-- 類型別名 --> 8 <typeAlias type="cn.bwm.pojo.User" alias="User"/><!-- 給指定的類起一個別名 --> 9 <package name="cn.bwm.pojo" /><!-- 給包裏的全部類起一個和類名同樣的別名 --> 10 </typeAliases> 11 <environments default="test"><!-- 配置環境 ,default選擇默認配置--> 12 <environment id="test"><!-- 環境元素 --> 13 <transactionManager type="JDBC" /> <!-- 配置事務管理器 --> 14 <dataSource type="POOLED"><!-- 數據源 --> 15 <property name="driver" value="${driver}" /><!-- jdbc配置文件對應屬性 --> 16 <property name="url" value="${url}" /> 17 <property name="username" value="${username}" /> 18 <property name="password" value="${password}" /> 19 </dataSource> 20 </environment> 21 </environments> 22 <mappers><!-- 映射器 --> 23 <mapper resource="cn/bwm/dao/IUserMapper.xml" /><!-- xml映射文件 --> 24 </mappers> 25 </configuration>
每一個標籤的具體做用請參考官方文檔,中文的,http://www.mybatis.org/mybatis-3/zh/getting-started.html。
最後一步也是舉足輕重的一步,配置MyBatis的兩個dtd文件。
這兩個文件,一個叫「mybatis-3-config.dtd」,另外一個叫「mybatis-3-mapper.dtd」,咱們能夠經過解壓縮mybatis-x.x.x.jar,在\org\apache\ibatis\builder\xml 這個目錄下找到這兩個文件。
我用的是MyEclipse10.6,配置的步驟是:
Windows → Preferences ; 選擇XML Catalog ,選擇User Specified Entries,單擊 Add ; 單擊 File System 後選擇dtd文件, Key填寫 -//mybatis.org//DTD Config 3.0//EN (與MyBatis-config.xml文件投中的 -//mybatis.org//DTD Config 3.0//EN 相同)。
兩個dtd文件配置步驟同樣,配置完成之後,在MyBatis-config.xml文件和xml映射文件中就可使用 alt + / 自動聯想了。
完成以上步驟之後,MyBatis就算是部署到項目中了,至於如何使用MyBatis,咱們還須要準備一個數據庫。這個數據庫至少要有兩張結構簡單有主外鍵關係的表,以及少許數據。在項目中建立與數據庫的表對應的實體類,在數據訪問層建立實體類對應的接口,並聲明抽象方法,例如:
/** * 老師類 * @author Administrator * */ public class Teacher { private int id; private String name; private Student student; private List<Student> studentList; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Student getStudent() { return student; } public void setStudent(Student student) { this.student = student; } public List<Student> getStudentList() { return studentList; } public void setStudentList(List<Student> studentList) { this.studentList = studentList; } } /** * 學生類 * @author Administrator * */ public class Student { private int id; private String name; private int tid; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getTid() { return tid; } public void setTid(int tid) { this.tid = tid; } } /** * Teacher實體類的對應接口 * @author Administrator * */ public interface ITeacherMapper { /** * 根據id查詢老師 * @param id * @return Teacher對象 */ public Teacher queryTeacherById(int id); }
在接口的同目錄下建立同名xml文件:
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE mapper 3 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 5 <mapper namespace="cn.bwm.dao.ITeacherMapper"> 6 <select id="queryTeacherById" parameterType="int" resultType="Teacher"> 7 SELECT `Id`,`Name` FROM `teacher` WHERE `Id` = #{id} 8 </select> 9 </mapper>
寫完這些代碼和配置文件,如今,咱們再來簡單瞭解一下MyBatis的核心接口和類:
深刻了解請查看官方文檔,中文的, http://www.mybatis.org/mybatis-3/zh/java-api.html 。
根據三個核心接口和類,咱們再寫一個用來獲取SqlSession對象的工具類:
1 public class SqlSessionUtil { 2 private static SqlSessionFactory sqlSessionFactory; 3 4 static{ 5 SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); 6 try { 7 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); 8 sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream); 9 } catch (IOException e) { 10 e.printStackTrace(); 11 } 12 } 13 14 /** 15 * 獲取SqlSession對象 16 * @return 17 */ 18 public static SqlSession getSqlSession(){ 19 return sqlSessionFactory.openSession(); 20 } 21 22 /** 23 * 關閉SqlSession對象 24 * @param sqlSession 25 */ 26 public static void closeSqlSession(SqlSession sqlSession){ 27 if (sqlSession != null) { 28 sqlSession.close(); 29 } 30 } 31 }
如今,咱們能夠寫測試代碼了:
1 public class Test { 2 public static void main(String[] args) { 3 SqlSession sqlSession = SqlSessionUtil.getSqlSession(); 4 Teacher teacher = sqlSession.getMapper(ITeacherMapper.class).queryTeacherById(1); 5 System.out.println("編號:" + teacher.getId() + "\t姓名:" + teacher.getName()); 6 SqlSessionUtil.closeSqlSession(sqlSession); 7 } 8 }
相信你們也看出來了,咱們使用MyBatis執行了簡單的查詢操做,並將結果封裝成了一個對象,而這其中的關鍵就在於與接口同名的xml映射文件。
咱們來看一下映射文件中用到了哪些元素:
咱們在查詢的時候,不可能只有單參數查詢,而parameterType只能寫一個類型,這個時候,有三種方法:
先說第一種,使用對象做爲參數。這種方法應該比較適合插入操做,咱們這裏強行使用一波。
先在接口裏寫上方法:
1 /** 2 * 根據編號和姓名查詢老師 3 * @param t Teacher對象 4 * @return Teacher對象 5 */ 6 public Teacher queryTeacherByIdName(Teacher t);
在xml映射文件裏寫對應的SQL語句,parameterType寫的是參數類型 Teacher,#{id}和#{name}都是 Teacher 類的屬性,CONCAT()是MySQL的函數,用來拼接字符串:
1 <select id="queryTeacherByIdName" parameterType="Teacher" resultType="Teacher"> 2 SELECT `Id`,`Name` FROM `teacher` WHERE `Id` = #{id} AND `Name` LIKE CONCAT('%' , #{name} , '%') 3 </select>
測試代碼是這樣的:
1 public static void main(String[] args) { 2 SqlSession sqlSession = SqlSessionUtil.getSqlSession(); 3 Teacher t = new Teacher(); 4 t.setId(1); 5 t.setName("小"); 6 Teacher teacher = sqlSession.getMapper(ITeacherMapper.class).queryTeacherByIdName(t); 7 System.out.println("編號:" + teacher.getId() + "\t姓名:" + teacher.getName()); 8 SqlSessionUtil.closeSqlSession(sqlSession); 9 }
這樣便完成了使用對象做爲參數來進行查詢的操做。
而後是第二種,將參數封裝成Map集合,咱們修改一下剛纔的方法,把參數改爲 Map<String , Object>集合:
1 /** 2 * 根據編號和姓名查詢老師 3 * @param map Map<String,Object>集合 4 * @return Teacher對象 5 */ 6 public Teacher queryTeacherByIdName(Map<String,Object> map);
xml映射文件裏,parameterType改爲了 map,是MyBatis提供的別名:
1 <select id="queryTeacherByIdName" parameterType="map" resultType="Teacher"> 2 SELECT `Id`,`Name` FROM `teacher` WHERE `Id` = #{id} AND `Name` LIKE CONCAT('%' , #{name} , '%') 3 </select>
測試代碼把剛纔做爲參數的 Teacher 對象改爲 Map<String , Object>集合:
1 public static void main(String[] args) { 2 SqlSession sqlSession = SqlSessionUtil.getSqlSession(); 3 Map<String,Object> map = new HashMap<String,Object>(); 4 map.put("id", 1); 5 map.put("name", "小"); 6 Teacher teacher = sqlSession.getMapper(ITeacherMapper.class).queryTeacherByIdName(map); 7 System.out.println("編號:" + teacher.getId() + "\t姓名:" + teacher.getName()); 8 SqlSessionUtil.closeSqlSession(sqlSession); 9 }
這樣就完成了使用 Map 傳遞多個參數。
最後是第三種,使用@Param註解,仍是直接修改剛纔的方法,把 Map<String , Object> 換成 @Param 註解的參數:
1 /** 2 * 根據編號和姓名查詢老師 3 * @param id 編號 4 * @param name 姓名 5 * @return Teacher 對象 6 */ 7 public Teacher queryTeacherByIdName(@Param("tid")int id , @Param("tname")String name);
xml文件裏,由於@Param("")裏寫的是 tid 和 tname,因此 #{} 也要寫的同樣:
1 <select id="queryTeacherByIdName" parameterType="map" resultType="Teacher"> 2 SELECT `Id`,`Name` FROM `teacher` WHERE `Id` = #{tid} AND `Name` LIKE CONCAT('%' , #{tname} , '%') 3 </select>
測試代碼裏,參數也不用放在 Map 集合裏了:
1 public static void main(String[] args) { 2 SqlSession sqlSession = SqlSessionUtil.getSqlSession(); 3 Teacher teacher = sqlSession.getMapper(ITeacherMapper.class).queryTeacherByIdName(1 , "小"); 4 System.out.println("編號:" + teacher.getId() + "\t姓名:" + teacher.getName()); 5 SqlSessionUtil.closeSqlSession(sqlSession); 6 }
這就是 @Param 的使用方法。
三種方法說完了,但無論參數有多少,咱們如今查詢的結果都只是一個簡單對象,若是結果的類型包含另外一個類型,或者包含一個集合,這裏咱們就要了解一下 <resultMap> 元素及其子元素 <result>、<association> 和 <conllection> 。
咱們經過代碼來說解這幾個個元素的使用方法,個人 Teacher 類中已經有一個 Student 屬性和一個 List<Student> 屬性:
1 /** 2 * 老師類 3 * @author Administrator 4 * 5 */ 6 public class Teacher { 7 private int id; 8 private String name; 9 private Student student; //學生對象 10 private List<Student> studentList; //學生集合 11 //省略 getset 方法 12 }
接口裏的方法能夠不用修改,xml映射文件須要大改一下:
1 <select id="queryTeacherByIdName" resultMap="query"> 2 select t.`Id` as tid , t.`Name`as tname , s.`Id` as sid , s.`Name` as sname from `teacher` as t 3 inner join `student` as s on s.`Tid` = t.`Id` where t.`Id` = #{tid} 4 </select> 5 <resultMap type="Teacher" id="query"> 6 <id column="tid" property="id"/> 7 <result column="tname" property="name"/> 8 <association property="student" javaType="Student"> 9 <id column="sid" property="id"/> 10 <result column="sname" property="name"/> 11 <result column="tid" property="tid"/> 12 </association> 13 <collection property="studentList" ofType="Student"> 14 <id column="sid" property="id"/> 15 <result column="sname" property="name"/> 16 <result column="tid" property="tid"/> 17 </collection> 18 </resultMap>
select 標籤的 resultType 屬性換成了 resultMap屬性,值與 resultMap 標籤的 id 同樣。
另外,resultMap 結果映射能夠複用,咱們改一下xml映射文件的代碼:
1 <select id="queryTeacherByIdName" resultMap="query1"> 2 select t.`Id` as tid , t.`Name`as tname , s.`Id` as sid , s.`Name` as sname from `teacher` as t 3 inner join `student` as s on s.`Tid` = t.`Id` where t.`Id` = #{tid} 4 </select> 5 6 <resultMap type="Student" id="student"> 7 <id column="sid" property="id"/> 8 <result column="sname" property="name"/> 9 <result column="tid" property="tid"/> 10 </resultMap> 11 12 <resultMap type="Teacher" id="query1"> 13 <id column="tid" property="id"/> 14 <result column="tname" property="name"/> 15 <association resultMap="student" property="student" javaType="Student"/> 16 </resultMap> 17 18 <resultMap type="Teacher" id="query2"> 19 <id column="tid" property="id"/> 20 <result column="tname" property="name"/> 21 <collection resultMap="student" property="studentList" ofType="Student"/> 22 </resultMap>
在代碼裏,兩個 resultMap 裏的 association 和 collection 都重用了 query 這個 resultMap ,能夠節省很多代碼。順便一提,我在測試時,association 和 collection 沒法在一個 resultMap 裏重用同一個 resultMap ,collection 會失效,最終查詢出來的結果,List的元素數量是0,具體緣由暫時還沒找到。
查詢部分算是結束了,接下是比較簡單的增刪改操做,先在接口裏寫上對應的方法:
1 /** 2 * 增長老師 3 * @param teacher Teacher對象 4 * @return 5 */ 6 public int addTeacher(Teacher teacher); 7 8 /** 9 * 修改老師 10 * @param teacher Teacher對象 11 * @return 12 */ 13 public int updateTeacher(Teacher teacher); 14 15 /** 16 * 根據id刪除老師 17 * @param id 老師的編號 18 * @return 19 */ 20 public int deleteTeacher(int id);
xml映射文件裏也要使用對應的標籤,分別是 <insert>、<update>和<delete>:
1 <insert id="addTeacher" parameterType="Teacher"> 2 INSERT INTO `teacher`(`Name`) VALUES(#{name}) 3 </insert> 4 5 <update id="updateTeacher" parameterType="Teacher"> 6 UPDATE `teacher` SET `Name` = #{name} WHERE id = #{id} 7 </update> 8 9 <delete id="deleteTeacher" parameterType="int"> 10 DELETE FROM `teacher` WHERE `Id` = #{id} 11 </delete>
由於增刪改的操做返回的是數據庫受影響的行數,因此這個三個標籤是沒有 resultType 和 resultMap 兩個屬性的。
測試部分須要增長一點點代碼:
1 /** 2 * 添加老師 3 */ 4 private static void addTeacher() { 5 SqlSession sqlSession = SqlSessionUtil.getSqlSession(); 6 Teacher teacher = new Teacher(); 7 teacher.setName("小黑白"); 8 int result = sqlSession.getMapper(ITeacherMapper.class).addTeacher(teacher); 9 if (result > 0) { 10 sqlSession.commit(); //提交事務 11 System.out.println("添加成功!"); 12 }else{ 13 System.out.println("添加失敗!"); 14 } 15 SqlSessionUtil.closeSqlSession(sqlSession); 16 } 17 18 /** 19 * 修改老師 20 */ 21 private static void updateTeacher() { 22 SqlSession sqlSession = SqlSessionUtil.getSqlSession(); 23 Teacher teacher = new Teacher(); 24 teacher.setId(2); 25 teacher.setName("小黑"); 26 int result = sqlSession.getMapper(ITeacherMapper.class).updateTeacher(teacher); 27 if (result > 0) { 28 sqlSession.commit(); //提交事務 29 System.out.println("修改爲功!"); 30 }else{ 31 System.out.println("修改失敗!"); 32 } 33 SqlSessionUtil.closeSqlSession(sqlSession); 34 } 35 36 /** 37 * 根據id刪除老師 38 */ 39 private static void deleteTeacher() { 40 SqlSession sqlSession = SqlSessionUtil.getSqlSession(); 41 int result = sqlSession.getMapper(ITeacherMapper.class).deleteTeacher(2); 42 if (result > 0) { 43 sqlSession.commit(); //提交事務 44 System.out.println("刪除成功!"); 45 }else{ 46 System.out.println("刪除失敗!"); 47 } 48 SqlSessionUtil.closeSqlSession(sqlSession); 49 }
這三個操做都有一個共同的特色,就是在判斷數據庫受影響的行數大於0之後,都會調用 SqlSession 的 commit() 方法。
這是由於 MyBatis 在執行增刪改操做的時候並無直接操做數據庫,咱們能夠理解成是在操做一個虛擬的數據庫,當咱們調用 commit() 方法後,咱們所作的操做纔會對真正的數據庫產生影響。
以上就是使用 MyBatis 對數據庫進行增刪改查操做的示例,咱們不難發現,把 SQL語句寫在 xml映射文件裏會致使咱們沒法用代碼改變 SQL 語句,顯得不夠靈活,對此,MyBatis爲咱們提供了動態SQL。
動態SQL是MyBatis的一個強大的特性,基於 OGNL 的表達式,使咱們能夠方便的動態改變 SQL 語句。
用於動態SQL的元素有:
假設,咱們使用 id 和 name 來查詢老師,若是 name 沒有傳入參數,就只用 id 查詢老師,這時候就能夠使用 if 來進行判斷:
1 <select id="queryTeacher" parameterType="map" resultType="Teacher"> 2 SELECT * FROM `teacher` 3 WHERE `Id` = #{id} 4 <if test="name != null and name != ''"> 5 AND `Name` LIKE CONCAT('%' , #{name} , '%') 6 </if> 7 </select>
test 屬性其餘標籤也有,用來判斷條件。
再假設,id 和 name 只要有其中一個就能夠了,按照有條件進行查詢,若是沒有就查詢所有,這時可使用 choose 和 它的子元素 when、otherwise來實現:
1 <select id="queryTeacher" parameterType="map" resultType="Teacher"> 2 SELECT * FROM `teacher` 3 WHERE 4 <choose> 5 <when test="id != null and id != 0"> 6 `Id` = #{id} 7 </when> 8 <when test="name != null and name != ''"> 9 `Name` = #{name} 10 </when> 11 <otherwise> 12 1 = 1 13 </otherwise> 14 </choose> 15 </select>
choose 至關於 Java 中的 switch 語句,從上向下開始判斷,只要有 when 的 test 成立,就返回該 when 裏的 SQL 語句並跳出 choose,若是全部的 when 都不成立,就返回 otherwise 裏的SQL語句。由於是從上向下判斷的,因此要注意 SQL 語句的優先順序。
還有最後的 otherwise 裏的 1 = 1,若是沒有這個的話,頗有可能由於 id 和 name 兩個都沒有參數而形成 where 後面什麼都沒有,形成 SQL 語句報錯。這時,就須要使用 trim元素,它能夠靈活的去除多餘的關鍵字:
1 <select id="queryTeacher" parameterType="map" resultType="Teacher"> 2 SELECT * FROM `theacher` 3 <trim prefix="WHERE" prefixOverrides="and | or"> 4 <if test="id != null and id != 0"> 5 AND `id` = #{id} 6 </if> 7 <if test="name != null and name != ''"> 8 AND `name` = #{name} 9 </if> 10 </trim> 11 </select>
trim 元素會自動識別標籤內是否有返回值,有的話就在內容的前面加上 prefix 屬性的值 where,並忽略 prefixOverrides 裏所包含的內容 and 和 or 。(這部分代碼只爲了展現 trim 的做用,請忽略代碼的意義)
這部分的 trim 的使用等價於 where 標籤:
1 <select id="queryTeacher" parameterType="map" resultType="Teacher"> 2 SELECT * FROM `theacher` 3 <where> 4 <if test="id != null and id != 0"> 5 AND `id` = #{id} 6 </if> 7 <if test="name != null and name != ''"> 8 AND `name` = #{name} 9 </if> 10 </where> 11 </select>
trim 不只能夠在 SQL 語句前添加內容和忽略內容,也能夠在 SQL 語句後追加內容和忽略內容,例如剛纔的修改操做:
1 <update id="updateTeacher" parameterType="Teacher"> 2 UPDATE `teacher` SET `Name` = #{name} WHERE id = #{id} 3 <trim prefix="Set" suffixOverrides="," suffix="WHERE id = #{id}"> 4 <if test="id != null"> 5 `id` = #{id} , 6 </if> 7 <if test="name != null"> 8 `name` = #{name} , 9 </if> 10 </trim> 11 </update>
使用 prefix 在 SQL 語句前加上 prefix 的值 Set, 在 SQL 語句的最後加上 suffix 的值 WHERE id = #{id},忽略 SQL 語句最後的 suffixOverrides 的值 , 。(這段代碼只展現了 trim 的做用,請無視不合理的部分)
這部分 trim 的使用等價於 set 標籤:
1 <update id="updateTeacher" parameterType="Teacher"> 2 UPDATE `teacher` SET `Name` = #{name} WHERE id = #{id} 3 <set> 4 <if test="id != null"> 5 `id` = #{id} , 6 </if> 7 <if test="name != null"> 8 `name` = #{name} , 9 </if> 10 </set> 11 WHERE `id` = #{id} 12 </update>
不過 WHERE 須要本身寫。
另外,prefix、perfixOverriders、suffix 和 suffixOverriders四個屬性是能夠同時使用的,根據時間狀況選擇。
foreach 元素,用於循環集合,主要用於構建 IN 條件語句的時候使用:
1 <select id="queryTeacher" resultType="Teacher"> 2 SELECT * FROM `theacher` 3 WHERE `id` IN 4 <foreach collection="list" item="id" index="index" open="(" separator="," close=")"> 5 #{id} 6 </foreach> 7 </select>
這裏咱們迭代了 list 集合,collection 屬性表示迭代時每一個元素的別名;index 表示每次迭代到的位置;open 表示 SQL 語句以什麼開始;separator 表示每次迭代直接用什麼進行分隔;close 表示 SQL 語句以什麼結束;collection 必須指定,對應要遍歷的集合。
在遍歷 對象 和 數組時,index 表示當前迭代的次數,item 的值是表示當前迭代獲取的元素。
遍歷 Map 時,index 是鍵, item 是值。
補充一下 resultMap 的映射級別。
我在使用 resultMap 自定義映射結果的時候發現,即便我只在 resultMap 裏定義一個 id ,其餘的屬性也仍是會自動映射上去。
這是由於 MyBatis 的配置文件中,autoMappingBehavior 的默認級別是 PARTIAL,只會自動映射沒有定義嵌套結果集映射的結果集,改爲 NONE 就能夠取消自動映射,改爲FULL的話,會自動映射覆雜的結果集,不管是否嵌套。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
到這裏,關於MyBatis我所學習到的內容就所有結束了。
第一次寫博客沒有經驗,寫的很長,也很爛。
感謝耐心看到這裏的你,若是對你有所幫助就再好不過了。