在上一章中咱們學習了《MyBatis學習總結(二)——MyBatis核心配置文件與輸入輸出映射》,這一章主要是介紹一對一關聯查詢、一對多關聯查詢與動態SQL等內容。html
表與表之間有三種常見的關聯關係,分別是一對一,一對多與多對多關係,MyBatis直接提供一對一與一對多的關聯關係,可能經過間接的方式實現一對多關聯。java
假定一個員工(emp)擁有一個登陸用戶(user),員工與用戶表之間是一對一關係:git
用戶表:sql
員工表:數據庫
SQL:數組
SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for `user` -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '編號', `username` varchar(64) NOT NULL COMMENT '用戶名', `password` varchar(64) NOT NULL COMMENT '密碼', PRIMARY KEY (`id`), UNIQUE KEY `users_username_uindex` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COMMENT='用戶表'; -- ---------------------------- -- Records of user -- ---------------------------- INSERT INTO `user` VALUES ('1', 'tom', '123456'); INSERT INTO `user` VALUES ('2', 'rose', '888888'); INSERT INTO `user` VALUES ('3', 'mark', 'qwerty'); INSERT INTO `user` VALUES ('4', 'jack', 'qaz123'); INSERT INTO `user` VALUES ('5', 'mali', 'uio890'); SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for `emp` -- ---------------------------- DROP TABLE IF EXISTS `emp`; CREATE TABLE `emp` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '編號', `user_id` int(11) DEFAULT NULL COMMENT '用戶編號', `realname` varchar(32) NOT NULL COMMENT '姓名', `email` varchar(64) DEFAULT NULL COMMENT '郵箱', PRIMARY KEY (`id`), KEY `emp_user_id` (`user_id`), CONSTRAINT `emp_user_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COMMENT='員工表'; -- ---------------------------- -- Records of emp -- ---------------------------- INSERT INTO `emp` VALUES ('1', '1', '湯姆', 'tom@gmail.com'); INSERT INTO `emp` VALUES ('2', '2', '梅貴', 'rose@163.com'); INSERT INTO `emp` VALUES ('3', '3', '馬克', 'mark@sina.com'); INSERT INTO `emp` VALUES ('4', '4', '嶽翰', 'jack@gmail.com'); INSERT INTO `emp` VALUES ('5', '5', '馬麗', 'mali@sina.com');
關係:安全
實體:mybatis
用戶:app
package com.zhangguo.mybatis03.entities; /**用戶POJO*/ public class User { private int id; private String username; private String password; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
員工:數據庫設計
package com.zhangguo.mybatis03.entities; /**員工POJO*/ public class Emp { private int id; /**用戶編號*/ private int user_id; private String realname; private String email; /**用戶對象*/ private User user; public int getId() { return id; } public void setId(int id) { this.id = id; } public int getUser_id() { return user_id; } public void setUser_id(int user_id) { this.user_id = user_id; } public String getRealname() { return realname; } public void setRealname(String realname) { this.realname = realname; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public User getUser() { return user; } public Emp setUser(User user) { this.user = user; return this; } }
接口:
package com.zhangguo.mybatis03.dao; import com.zhangguo.mybatis03.entities.Emp; /**員工數據訪口*/ public interface EmpMapper { /**得到員工經過員工編號*/ Emp getEmpById_1(int 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.zhangguo.mybatis03.dao.EmpMapper"> <!--一對一查詢,方法1,經過內聯接--> <select id="getEmpById_1" resultMap="empMap_1" parameterType="int"> SELECT emp.id, emp.user_id, emp.realname, emp.email, `user`.username, `user`.`password` FROM emp INNER JOIN `user` ON emp.user_id = `user`.id where emp.id=#{id} </select> <!--員工關聯查詢結果映射--> <resultMap id="empMap_1" type="Emp"> <id property="id" column="id"></id> <result property="user_id" column="user_id"></result> <result property="realname" column="realname"></result> <result property="email" column="email"></result> <!--映射關係,指定屬性與屬性的類型--> <association property="user" javaType="User"> <id property="id" column="user_id"></id> <result property="username" column="username"></result> <result property="password" column="password"></result> </association> </resultMap> </mapper>
測試:
package com.zhangguo.mybatis03.dao; import com.zhangguo.mybatis03.entities.Emp; import org.junit.Assert; import org.junit.Test; import org.junit.Before; import org.junit.After; /** * EmpDao Tester. * * @author <Authors name> * @version 1.0 * @since <pre>09/30/2018</pre> */ public class EmpDaoTest { EmpMapper empDao; @Before public void before() throws Exception { empDao=new EmpDao(); } @After public void after() throws Exception { } /** * Method: getEmpById_1(int id) * 得到員工經過員工編號 */ @Test public void testGetEmpById_1() throws Exception { Emp entity=empDao.getEmpById_1(1); System.out.println(entity); Assert.assertNotNull(entity); } }
結果:
實體:同上
接口:
/**得到員工經過員工編號,屢次查詢*/ Emp getEmpById_2(int id);
映射:
<!--一對一查詢,方法2,經過屢次查詢(嵌套查詢)--> <select id="getEmpById_2" resultMap="empMap_2"> SELECT emp.id, emp.user_id, emp.realname, emp.email FROM emp where id=#{id} </select> <!--員工屢次查詢結果映射--> <resultMap id="empMap_2" type="Emp"> <id property="id" column="id"></id> <result property="user_id" column="user_id"></result> <result property="realname" column="realname"></result> <result property="email" column="email"></result> <!--經過外鍵user_id再次發起查詢,調用selectUserById得到User對象--> <association property="user" column="user_id" select="selectUserById"></association> </resultMap> <!--根據用戶編號得到用戶對象--> <select id="selectUserById" resultType="User"> SELECT `user`.id, `user`.username, `user`.`password` FROM `user` where id=#{id} </select>
測試:
/** * Method: getEmpById_2(int id) * 得到員工經過員工編號,一對一方法二 */ @Test public void testGetEmpById_2() throws Exception { Emp entity=empDao.getEmpById_2(2); System.out.println(entity); Assert.assertNotNull(entity); }
結果:
MyBatis中使用association標籤來解決一對一的關聯查詢,association標籤可用的屬性以下:
一個用戶賬號能夠被多個員工使用,造成一個一對多的關係,表中的數據以下:
員工表emp:
用戶表user:
實體:
員工:
package com.zhangguo.mybatis03.entities; /**員工POJO*/ public class Emp { private int id; /**用戶編號*/ private int user_id; private String realname; private String email; /**用戶對象*/ private User user; public int getId() { return id; } public void setId(int id) { this.id = id; } public int getUser_id() { return user_id; } public void setUser_id(int user_id) { this.user_id = user_id; } public String getRealname() { return realname; } public void setRealname(String realname) { this.realname = realname; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public User getUser() { return user; } public Emp setUser(User user) { this.user = user; return this; } @Override public String toString() { return "Emp{" + "id=" + id + ", user_id=" + user_id + ", realname='" + realname + '\'' + ", email='" + email + '\'' + ", user=" + user + '}'; } }
用戶:
package com.zhangguo.mybatis03.entities; import java.util.List; /**用戶POJO*/ public class User { private int id; private String username; private String password; /**員工集合,一個用戶對象對應多個員工對象*/ private List<Emp> emps; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public List<Emp> getEmps() { return emps; } public User setEmps(List<Emp> emps) { this.emps = emps; return this; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", emps=" + emps + '}'; } }
接口:
/**得到用戶經過用戶編號,1對多級聯查詢*/ User getUserById_1(int id);
映射:
<!--一對多查詢,方法1,經過內聯接--> <select id="getUserById_1" resultMap="userMap_1" parameterType="int"> SELECT emp.id, emp.user_id, emp.realname, emp.email, `user`.username, `user`.`password` FROM emp INNER JOIN `user` ON emp.user_id = `user`.id where `user`.id=#{id} </select> <resultMap id="userMap_1" type="User"> <id property="id" column="user_id"></id> <result property="username" column="username"></result> <result property="password" column="password"></result> <!--將emps對象映射成一個集合,emps是user類型中的屬性,ofType用於指定集合中存放的對象類型--> <collection property="emps" ofType="Emp"> <id property="id" column="id"></id> <result property="user_id" column="user_id"></result> <result property="realname" column="realname"></result> <result property="email" column="email"></result> </collection> </resultMap>
測試:
/** * Method: getUserById_1(int id) * 得到用戶過用戶編號,級聯查詢 */ @Test public void testGetUserById_1() throws Exception { User entity=empDao.getUserById_1(2); System.out.println(entity); Assert.assertNotNull(entity); }
結果:
上面的示例中會發現User對象中包含多個Emp對象,此時的Emp對象中又引用了User對象,但值是空的,若是想設置值能夠繼續用1對1的辦法賦值:
映射:
<resultMap id="userMap_1" type="User"> <id property="id" column="user_id"></id> <result property="username" column="username"></result> <result property="password" column="password"></result> <!--將emps對象映射成一個集合,emps是user類型中的屬性,ofType用於指定集合中存放的對象類型--> <collection property="emps" ofType="Emp"> <id property="id" column="id"></id> <result property="user_id" column="user_id"></result> <result property="realname" column="realname"></result> <result property="email" column="email"></result> <!--映射關係,指定屬性與屬性的類型--> <association property="user" javaType="User"> <id property="id" column="user_id"></id> <result property="username" column="username"></result> <result property="password" column="password"></result> </association> </collection> </resultMap>
結果:
實體:同上
接口:
/**得到用戶經過用戶編號,1對多嵌套查詢*/ User getUserById_2(int id);
映射:
<!--一對多查詢,方法2,經過嵌套查詢屢次--> <select id="getUserById_2" resultMap="userMap_2" parameterType="int"> SELECT `user`.id, `user`.username, `user`.`password` FROM `user` where id=#{id} </select> <resultMap id="userMap_2" type="User"> <id property="id" column="user_id"></id> <result property="username" column="username"></result> <result property="password" column="password"></result> <!--將emps對象映射成一個集合,emps是user類型中的屬性,ofType用於指定集合中存放的對象類型--> <!--select用於指定再次查詢的SQL編號,column用於指定參數列--> <collection property="emps" ofType="Emp" column="id" select="selectEmpById"></collection> </resultMap> <!--根據員工編號得到員工對象--> <select id="selectEmpById" resultType="Emp"> SELECT emp.id, emp.user_id, emp.realname, emp.email FROM emp where user_id=#{id} </select>
測試:
/** * Method: getUserById_2(int id) * 得到用戶過用戶編號,嵌套查詢 */ @Test public void testGetUserById_2() throws Exception { User entity=empDao.getUserById_2(5); System.out.println(entity); Assert.assertNotNull(entity); }
結果:
MyBatis中使用collection標籤來解決一對多的關聯查詢,ofType屬性指定集合中元素的對象類型。
數據與SQL環境以下:
前置要求:
MyBatis的動態SQL是基於OGNL的表達式的。它對SQL語句進行靈活的操做,經過表達式判斷來實現對SQL的靈活拼接、組裝。
mybatis核心對sql語句進行靈活操做,經過表達式進行判斷,對sql進行靈活拼接、組裝。
主要經過如下標籤:if,where,choose(when,otherwise),trim,set,foreach。
根據 name和 sex 來查詢數據。若是name爲空,那麼將只根據sex來查詢;反之只根據name來查詢
首先不使用 動態SQL 來書寫
接口:
/** * 根據學生姓名和性別得到學生集合 */ List<Student> selectStudentsByNameAndSex(@Param("name") String name,@Param("sex") String sex);
映射:
<select id="selectStudentsByNameAndSex" resultType="student"> SELECT id,name,sex from student where name=#{name} and sex=#{sex}; </select>
測試:
/** * Method: selectStudentsByNameAndSex */ @Test public void testSelectStudentsByNameAndSex() throws Exception { List<Student> students=dao.selectStudentsByNameAndSex("rose",null); System.out.println(students); Assert.assertNotNull(students); }
結果:
上面的查詢語句,咱們發現若是 #{sex} 爲空,那麼查詢結果也是空,如何解決這個問題呢?使用 if 來判斷
<select id="selectStudentsByNameAndSex" resultType="student"> SELECT id,name,sex from student where 1=1 <!--若是test爲真會輸出中間的內容--> <if test="name!=null and name!=''"> and name=#{name} </if> <if test="sex!=null and sex!=''"> and sex=#{sex} </if> </select>
結果:
參考:
<!-- 2 if(判斷參數) - 將實體類不爲空的屬性做爲where條件 --> <select id="getStudentList_if" resultMap="resultMap_studentEntity" parameterType="liming.student.manager.data.model.StudentEntity"> SELECT ST.STUDENT_ID, ST.STUDENT_NAME, ST.STUDENT_SEX, ST.STUDENT_BIRTHDAY, ST.STUDENT_PHOTO, ST.CLASS_ID, ST.PLACE_ID FROM STUDENT_TBL ST WHERE <if test="studentName !=null "> ST.STUDENT_NAME LIKE CONCAT(CONCAT('%', #{studentName, jdbcType=VARCHAR}),'%') </if> <if test="studentSex != null and studentSex != '' "> AND ST.STUDENT_SEX = #{studentSex, jdbcType=INTEGER} </if> <if test="studentBirthday != null "> AND ST.STUDENT_BIRTHDAY = #{studentBirthday, jdbcType=DATE} </if> <if test="classId != null and classId!= '' "> AND ST.CLASS_ID = #{classId, jdbcType=VARCHAR} </if> <if test="classEntity != null and classEntity.classId !=null and classEntity.classId !=' ' "> AND ST.CLASS_ID = #{classEntity.classId, jdbcType=VARCHAR} </if> <if test="placeId != null and placeId != '' "> AND ST.PLACE_ID = #{placeId, jdbcType=VARCHAR} </if> <if test="placeEntity != null and placeEntity.placeId != null and placeEntity.placeId != '' "> AND ST.PLACE_ID = #{placeEntity.placeId, jdbcType=VARCHAR} </if> <if test="studentId != null and studentId != '' "> AND ST.STUDENT_ID = #{studentId, jdbcType=VARCHAR} </if> </select>
雖然1=1這種方法結合if能夠解決咱們的需求,可是1=1明顯是冗餘的,經過where能夠解決。
where 元素知道只有在一個以上的if條件有值的狀況下才去插入「WHERE」子句,若最後的內容是「AND」或「OR」開頭的,where 元素也知道如何將他們去除。
修改後的映射:
<select id="selectStudentsByNameAndSex" resultType="student"> SELECT id,name,sex from student <!--一、若是兩個if只要有一個有輸出就會在sql中添加 where--> <where> <if test="name!=null and name!=''"> <!--二、若是where後以and或or開始則會刪除and或or--> and name like concat(concat('%',#{name}),'%'); </if> <if test="sex!=null and sex!=''"> and sex=#{sex} </if> </where> </select>
測試:
/** * Method: selectStudentsByNameAndSex */ @Test public void testSelectStudentsByNameAndSex() throws Exception { List<Student> students=dao.selectStudentsByNameAndSex("a",null); System.out.println(students); Assert.assertNotNull(students); }
結果:
這個「where」標籤會知道若是它包含的標籤中有返回值的話,它就插入一個‘where’。此外,若是標籤返回的內容是以AND 或OR 開頭的,則它會剔除掉。
where標記的做用相似於動態sql中的set標記,他的做用主要是用來簡化sql語句中where條件判斷的書寫的,以下所示:
<select id="selectByParams" parameterType="map" resultType="user"> select * from user <where> <if test="id != null ">id=#{id}</if> <if test="name != null and name.length()>0" >and name=#{name}</if> <if test="gender != null and gender.length()>0">and gender = #{gender}</if> </where> </select>
在上述SQL中加入ID的值爲null的話,那麼打印出來的SQL爲:select * from user where name="xx" and gender="xx"
where 標記會自動將其後第一個條件的and或者是or給忽略掉
當update語句中沒有使用if標籤時,若是有一個參數爲null,都會致使錯誤。
當在update語句中使用if標籤時,若是前面的if沒有執行,則或致使逗號多餘錯誤。使用set標籤能夠將動態的配置SET 關鍵字,和剔除追加到條件末尾的任何不相關的逗號。若是set包含的內容爲空的話則會出錯。
使用if+set標籤修改後,若是某項爲null則不進行更新,而是保持數據庫原值。
若是經過if判斷表面能夠解決問題,以下所示:
<update id="updateStudent" parameterType="student"> update student set <if test="name!=null and name.lenght()>0"> name=#{name} , </if> <if test="sex!=null and sex.lenght()>0"> sex=#{sex} </if> where id=#{id} </update>
這樣作也會有問題,就是當sex爲空時的sql就變成了 update student set name=#{name} , where id=#{id},這明顯是錯誤的。
同理,上面的對於查詢 SQL 語句包含 where 關鍵字,若是在進行更新操做的時候,含有 set 關鍵詞,咱們怎麼處理呢?
接口:
/** * 更新學生 */ int updateStudent(Student entity);
映射:
<update id="updateStudent" parameterType="student"> update student <!--自動添加set--> <set> <!--智能處理逗號問題--> <if test="name!=null and name.length()>0"> name=#{name} </if> <if test="sex!=null and sex.length()>0"> sex=#{sex} </if> </set> where id=#{id} </update>
注意:某些狀況下逗號必須添加,以下所示:
<update id="updateStudent" parameterType="student"> update student <!--自動添加set--> <set> <!--智能處理逗號問題--> <if test="name!=null and name.length()>0"> name=#{name} , </if> <if test="sex!=null and sex.length()>0"> sex=#{sex} , </if> </set> where id=#{id} </update>
結尾的逗號會被自動刪除。
測試:
/** * Method: updateStudent */ @Test public void testUpdateStudent() throws Exception { //會將實體中的每個字段都更新,很差 // Student entity=dao.selectStudentById(11); // //entity.setName("張麗美"); // entity.setSex("girl"); // // Assert.assertEquals(1,dao.updateStudent(entity)); //不須要先執行查詢 Student student=new Student(); student.setId(9); //只更新了name與sex沒有關係 student.setName("malili"); Assert.assertEquals(1,dao.updateStudent(student)); }
結果:
這樣寫,若是第一個條件 name 爲空,那麼 sql 語句爲:update student set sex=? where id=?
若是第一個條件不爲空,那麼 sql 語句爲:update student u set name= ? , sex = ? where id=?
set主要解決了自動添加標籤與處理逗號的問題,另外這種更新方法比較之前的所有更新方式在開發中性能更高。
若是不想用到全部的查詢條件,只想選擇其中的一個,查詢條件有一個知足便可,使用 choose 標籤能夠解決此類問題,相似於 Java 的 switch 語句。
假定這裏須要優先根據編號搜索,沒有時選擇name,最後考慮sex:
接口:
/** * 根據學生編號、姓名和性別得到學生集合 */ List<Student> selectStudentsByNameAndSex(@Param("id") int id, @Param("name") String name,@Param("sex") String sex);
映射:
<select id="selectStudentsByNameAndSex" resultType="student"> SELECT id,name,sex from student <where> <choose> <when test="id>0"> id=#{id} </when> <when test="name!=null and name!=''"> name=#{name} </when> <otherwise> sex=#{sex} </otherwise> </choose> </where> </select>
測試:
/** * Method: selectStudentsByNameAndSex */ @Test public void testSelectStudentsByNameAndSex() throws Exception { List<Student> students=dao.selectStudentsByNameAndSex(1,"rose","girl"); System.out.println(students); Assert.assertNotNull(students); }
結果:
也就是說,這裏咱們有三個條件,id,name,sex,只能選擇一個做爲查詢條件
若是 id 不爲空,那麼查詢語句爲:select * from student where id=?
若是 id 爲空,那麼看name是否爲空,若是不爲空,那麼語句爲 select * from student where name=?;
若是name爲空,那麼查詢語句爲 select * from student where sex=?
trim標記是一個格式化的標記,能夠完成set或者是where標記的功能
①、用 trim 改寫上面第二點的 if+where 語句
if+where的辦法:
<select id="selectStudentsByNameAndSex" resultType="student"> SELECT id,name,sex from student <!--一、若是兩個if只要有一個有輸出就會在sql中添加 where--> <where> <if test="name!=null and name!=''"> <!--二、若是where後以and或or開始則會刪除and或or--> and name like concat(concat('%',#{name}),'%'); </if> <if test="sex!=null and sex!=''"> and sex=#{sex} </if> </where> </select>
trim的辦法:
<select id="selectStudentsByNameAndSex" resultType="student"> SELECT id,name,sex from student <!--一、prefix表示將前置where,prefixOverrides將刪除打頭內容--> <trim prefix="where" prefixOverrides="and | or"> <if test="name!=null and name!=''"> and name like concat(concat('%',#{name}),'%') </if> <if test="sex!=null and sex!=''"> and sex=#{sex} </if> </trim> </select>
測試結果:
prefix:將加上前綴
prefixoverride:去掉第一個and或者是or
②、用 trim 改寫上面第三點的 if+set 語句
if+set的方法:
<update id="updateStudent" parameterType="student"> update student <!--自動添加set--> <set> <!--智能處理逗號問題--> <if test="name!=null and name.length()>0"> name=#{name} </if> <if test="sex!=null and sex.length()>0"> sex=#{sex} </if> </set> where id=#{id} </update>
trim的方法:
<update id="updateStudent" parameterType="student"> update student <trim prefix="set" suffixOverrides=","> <if test="name!=null and name.length()>0"> name=#{name}, </if> <if test="sex!=null and sex.length()>0"> sex=#{sex}, </if> </trim> where id=#{id} </update>
結果:
suffix:後綴
suffixoverride:去掉最後一個逗號(也能夠是其餘的標記,就像是上面前綴中的and同樣)
能夠自定義添加先後綴,與之對應的屬性是prefix和suffix。同時經過prefixOverrides和suffixOverrides分別來覆蓋首尾部的內容,即忽略沒必要要的先後綴。就是說它能夠充當where標籤,也能夠充當set標籤啦~
充當where標籤:
<trim prefix = "where" prefixOverrides="and|or" > ... </trim>
充當set標籤:
<trim prefix="set" suffixOverrides=","> ... </trim>
例子:動態添加用戶屬性
<insert id="find" resultType="Admin"> insert into admin <trim prefix="(" suffix=")" suffixOverrides=","> <if test = "aname != null and aname !='' "> aname, </if> <if test = "city != null and city !='' "> city, </if> <if test = "age != null and age !='' "> age, </if> </trim> <trim prefix="values(" suffix=")" suffixOverrides=","> <if test = "aname != null and aname !='' "> #{aname}, </if> <if test = "city != null and city !='' "> #{city}, </if> <if test = "age != null and age !='' "> #{age}, </if> </trim> </insert>
上面相應的語句爲:insert into admin (…) values(…);。經過trim標籤用()包裹,以及自動忽略尾部的逗號。
有時候可能某個 sql 語句咱們用的特別多,爲了增長代碼的重用性,簡化代碼,咱們須要將這些代碼抽取出來,而後使用時直接調用。
好比:下面的映射文件中對於id,name,sex出現屢次:
<select id="selectStudentsByNameOrSex" resultType="student"> SELECT id,name,sex from student where name like '%${realname}%' or sex=#{sex}; </select> <select id="selectStudentsByIdOrSex" resultType="student"> SELECT id,name,sex from student where id=#{no} or sex=#{sex}; </select> <select id="selectStudentsByNameAndSex" resultType="student"> SELECT id,name,sex from student <!--一、prefix表示將前置where,prefixOverrides將刪除打頭內容--> <trim prefix="where" prefixOverrides="and | or"> <if test="name!=null and name!=''"> and name like concat(concat('%',#{name}),'%') </if> <if test="sex!=null and sex!=''"> and sex=#{sex} </if> </trim> </select>
加粗的內容是重複的,經過sql片斷複用。
定義sql片斷:
<!--定義sql片斷--> <sql id="col_student"> id,name,sex </sql>
引用 sql 片斷
<select id="selectStudentsByNameOrSex" resultType="student"> <!--引用sql片斷--> SELECT <include refid="col_student"></include> from student where name like '%${realname}%' or sex=#{sex}; </select> <select id="selectStudentsByIdOrSex" resultType="student"> SELECT <include refid="col_student"></include> from student where id=#{no} or sex=#{sex}; </select> <select id="selectStudentsByNameAndSex" resultType="student"> SELECT <include refid="col_student"></include> from student <!--一、prefix表示將前置where,prefixOverrides將刪除打頭內容--> <trim prefix="where" prefixOverrides="and | or"> <if test="name!=null and name!=''"> and name like concat(concat('%',#{name}),'%') </if> <if test="sex!=null and sex!=''"> and sex=#{sex} </if> </trim> </select>
結果:
注意:①、最好基於 單表來定義 sql 片斷,提升片斷的可重用性
②、在 sql 片斷中不要包括 where
sql片斷帶參數:
定義時使用參數:
<!--定義sql片斷--> <sql id="col_student"> ${alias}.id,${alias}.name,${alias}.sex </sql>
引用時指定參數:
<select id="selectStudentsByNameAndSex" resultType="student"> SELECT <include refid="col_student"> <property name="alias" value="s"></property> </include> from student s <!--一、prefix表示將前置where,prefixOverrides將刪除打頭內容--> <trim prefix="where" prefixOverrides="and | or"> <if test="name!=null and name!=''"> and name like concat(concat('%',#{name}),'%') </if> <if test="sex!=null and sex!=''"> and sex=#{sex} </if> </trim> </select>
結果:
foreach元素的功能很是強大,它容許你指定一個集合,聲明能夠在元素體內使用的集合項(item)和索引(index)變量。它也容許你指定開頭與結尾的字符串以及在迭代結果之間放置分隔符。這個元素是很智能的,所以它不會偶然地附加多餘的分隔符。
注意 你能夠將任何可迭代對象(如 List、Set 等)、Map 對象或者數組對象傳遞給 foreach 做爲集合參數。當使用可迭代對象或者數組時,index 是當前迭代的次數,item 的值是本次迭代獲取的元素。當使用 Map 對象(或者 Map.Entry 對象的集合)時,index 是鍵,item 是值。
到此咱們已經完成了涉及 XML 配置文件和 XML 映射文件的討論。下一章將詳細探討 Java API,這樣就能提升已建立的映射文件的利用效率。
foreach的主要用在構建in條件中,他能夠迭代一個集合。foreach元素的屬性主要有:item,index,collection,open,separator,close。
下面對屬性進行簡單的介紹:
item:表示集合中每個元素進行迭代時的別名。
index:指定一個名字,用於表示在迭代過程當中每次迭代的位置。
open:表示以什麼開始。
separator:每次迭代以什麼分割。
close:以什麼關閉。
collection:最重要且必須指定的有三種狀況:
1.若是傳入的是單獨參數的List類型時,collection的屬性值爲list。
2.若是傳入的是單獨參數的數組時,collection的屬性值爲array。
3.若是傳入多個參數時,咱們把多個參數放入map中,單參數也能夠放入map中。map中的key就是參數名,因此collection屬性值就是傳入的List或者array對象在Map裏的key。
1、用 foreach 來改寫 select * from user where id=1 or id=2 or id=3
<select id="selectUserByListId" parameterType="com.ys.vo.UserVo" resultType="com.zhangguo.User"> select * from user <where> <!-- collection:指定輸入對象中的集合屬性 item:每次遍歷生成的對象 open:開始遍歷時的拼接字符串 close:結束時拼接的字符串 separator:遍歷對象之間須要拼接的字符串 select * from user where 1=1 and (id=1 or id=2 or id=3) --> <foreach collection="ids" item="id" open="and (" close=")" separator="or"> id=#{id} </foreach> </where> </select>
2、咱們用 foreach 來改寫 select * from user where id in (1,2,3)
<select id="selectUserByListId" parameterType="com.ys.vo.UserVo" resultType="com.zhangguo.User"> select * from user <where> <!-- collection:指定輸入對象中的集合屬性 item:每次遍歷生成的對象 open:開始遍歷時的拼接字符串 close:結束時拼接的字符串 separator:遍歷對象之間須要拼接的字符串 select * from user where 1=1 and id in (1,2,3) --> <foreach collection="ids" item="id" open="and id in (" close=") " separator=","> #{id} </foreach> </where> </select>
其實動態 sql 語句的編寫每每就是一個拼接的問題,爲了保證拼接準確,咱們最好首先要寫原生的 sql 語句出來,而後在經過 mybatis 動態sql 對照着改。
參考:
<?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"> <!-- namespace的名字須要跟接口的類名一致 --> <mapper namespace="cn.bdqn.dao.UserMapper"> <!-- 一、resultMap屬性:type爲java實體類;id爲此resultMap的標識 二、resultMap的子元素: id – 通常對應到數據庫中該行的ID,設置此項能夠提升Mybatis性能. result – 映射到JavaBean 的某個「簡單類型」屬性,String,int等. association – 映射到JavaBean 的某個「複雜類型」屬性,其餘JavaBean類. collection –複雜類型集合 --> <!--根據roleId獲取用戶列表: 當數據庫中的字段信息與對象的屬性不一致時須要經過resultMap來映射 --> <!-- <resultMap type="User" id="seachUserResult"> <result property="id" column="id"/> <result property="userCode" column="userCode"/> <result property="userName" column="userName"/> <result property="roleId" column="roleId"/> <result property="roleName" column="roleName"/> </resultMap> <select id="getUserListByRoleId" parameterType="Role" resultMap="seachUserResult"> select u.*,r.roleName as roleName from user u,role r where u.roleId = r.id and u.roleId = #{id} </select> --> <!-- 根據roleId獲取用戶列表 association start--> <resultMap type="User" id="seachUserResult"> <result property="id" column="id"/> <result property="userCode" column="userCode" /> <result property="userName" column="userName" /> <result property="roleId" column="roleId" /> <!-- <association property="role" javaType="Role" > <result property="id" column="id"/> <result property="roleCode" column="roleCode"/> <result property="roleName" column="roleName"/> </association> --> <association property="role" javaType="Role" resultMap="roleMap"/> </resultMap> <resultMap type="Role" id="roleMap"> <result property="id" column="id"/> <result property="roleCode" column="roleCode"/> <result property="roleName" column="roleName"/> </resultMap> <select id="getUserListByRoleId" parameterType="Role" resultMap="seachUserResult"> select u.*,r.roleCode as roleCode,r.roleName as roleName from user u,role r where u.roleId = r.id and u.roleId = #{id} </select> <!-- association end--> <!-- 獲取指定用戶的地址列表(user表-address表:1對多關係) collection start--> <resultMap type="User" id="userMap"> <id property="id" column="userId"/> <collection property="addressList" ofType="Address"> <id property="id" column="a_id"/> <result property="postCode" column="postCode"/> <result property="addressContent" column="addressContent"/> </collection> </resultMap> <select id="getAddressListByUserId" parameterType="User" resultMap="userMap"> select *,a.id as a_id from user u,address a where u.id=a.userId and u.id=#{id} </select> <!-- collection end --> <resultMap type="User" id="seachUser"> <result property="id" column="id"/> <result property="userCode" column="userCode"/> <result property="userName" column="userName"/> <result property="roleId" column="roleId"/> <result property="roleName" column="roleName"/> </resultMap> <!-- <select id="searchUserList" parameterType="User" resultMap="seachUser"> select u.*,r.roleName as roleName from user u,role r where u.roleId = r.id and u.roleId = #{roleId} and u.userCode like CONCAT ('%',#{userCode},'%') //防止sql注入 and u.userName like CONCAT ('%',#{userName},'%') </select> --> <!-- 一、有些時候,sql語句where條件中,須要一些安全判斷,例如按性別檢索,若是傳入的參數是空的,此時查詢出的結果極可能是空的,也許咱們須要參數爲空時,是查出所有的信息。這是咱們可使用動態sql,增長一個判斷,當參數不符合要求的時候,咱們能夠不去判斷此查詢條件。 二、mybatis 的動態sql語句是基於OGNL表達式的。能夠方便的在 sql 語句中實現某些邏輯. 整體說來mybatis 動態SQL 語句主要有如下幾類: if 語句 (簡單的條件判斷) choose (when,otherwize) ,至關於java 語言中的 switch ,與 jstl 中的choose 很相似. trim (對包含的內容加上 prefix,或者 suffix 等,前綴,後綴) where (主要是用來簡化sql語句中where條件判斷的,能智能的處理 and or ,沒必要擔憂多餘致使語法錯誤) set (主要用於更新時) foreach (在實現 mybatis in 語句查詢時特別有用) --> <!-- if(判斷參數) - 將實體類不爲空的屬性做爲where條件 --> <select id="searchUserList" parameterType="User" resultMap="seachUser"> select u.*,r.roleName as roleName from user u,role r where u.roleId = r.id <if test="roleId!=null"> and u.roleId = #{roleId} </if> <if test="userCode != null"> and u.userCode like CONCAT ('%',#{userCode},'%') </if> <if test="userName != null"> and u.userName like CONCAT ('%',#{userName},'%') </if> </select> <select id="count" resultType="int"> select count(1) from user </select> <insert id="add" parameterType="User"> insert into user (userCode,userName,userPassword) values (#{userCode},#{userName},#{userPassword}) </insert> <!-- if/set(判斷參數) - 將實體類不爲空的屬性更新 --> <!-- <update id="update" parameterType="User"> update user <set> <if test="userCode != null and userCode != ''">userCode=#{userCode},</if> <if test="userName != null">userName=#{userName},</if> <if test="userPassword != null">userPassword=#{userPassword},</if> <if test="roleId != null">roleId=#{roleId}</if> </set> where id=#{id} </update> --> <!-- if/trim代替set(判斷參數) - 將實體類不爲空的屬性更新 --> <update id="update" parameterType="User"> update user <trim prefix="set" suffixOverrides=","> <if test="userCode != null and userCode != ''">userCode=#{userCode},</if> <if test="userName != null">userName=#{userName},</if> <if test="userPassword != null">userPassword=#{userPassword},</if> <if test="roleId != null">roleId=#{roleId}</if> </trim> where id=#{id} </update> <!--注意: 你能夠傳遞一個List實例或者數組做爲參數對象傳給MyBatis。 當你這麼作的時候,MyBatis會自動將它包裝在一個Map中,用名稱在做爲鍵。 List實例將會以「list」做爲鍵,而數組實例將會以「array」做爲鍵。 配置文件中的parameterType是能夠不配置的--> <resultMap type="User" id="userMapByDep"> <result property="id" column="id"/> <result property="userCode" column="userCode"/> <result property="userName" column="userName"/> </resultMap> <!-- foreach(循環array參數) - 做爲where中in的條件 --> <select id="getUserByDepId_foreach_array" resultMap="userMapByDep"> select * from user where depId in <foreach collection="array" item="depIds" open="(" separator="," close=")"> #{depIds} </foreach> </select> <!-- foreach(循環List<String>參數) - 做爲where中in的條件 --> <select id="getUserByDepId_foreach_list" resultMap="userMapByDep"> select * from user where depId in <foreach collection="list" item="depIdList" open="(" separator="," close=")"> #{depIdList} </foreach> </select> <delete id="delete" parameterType="User"> delete from user where id=#{id} </delete> <select id="getUserList" resultType="User"> select * from user </select> </mapper>
假定咱們要多刪除:
接口:
/** * 刪除多個學生經過編號 */ int deleteStudents(List<Integer> ids);
映射:
<delete id="deleteStudents"> delete from student where id in <foreach collection="list" item="id" open="(" separator="," close=")"> #{id} </foreach> </delete>
注意collection這裏只能是list,不能是ids,由於反射時獲取不到參數名稱。
測試:
/** * Method: deleteStudents */ @Test public void testDeleteStudents() throws Exception { List<Integer> ids=new ArrayList<Integer>(); ids.add(8); ids.add(9); Assert.assertEquals(2,dao.deleteStudents(ids)); }
結果:
bind標籤可使用OGNL表達式建立一個變量並將其綁定到上下文中。
bind標籤的兩個屬性都是不選項,name爲綁定到上下文的變量名,value爲OGNL表達式,建立一個bind標籤後,就能夠在下面直接使用了。 使用bind拼接字符串不只能夠避免因更換數據庫而修改SQL,也能預防SQL注入。
<!-- List<Employee> getEmpsTestInnerParameter(Employee employee); --> <select id="getEmpsTestInnerParameter" resultType="com.hand.mybatis.bean.Employee"> <!-- bind:能夠將OGNL表達式的值綁定到一個變量中,方便後來引用這個變量的值 --> <bind name="bindeName" value="'%'+eName+'%'"/> eName是employee中一個屬性值 SELECT * FROM emp <if test="_parameter!=null"> where ename like #{bindeName} </if> </select>
https://www.bilibili.com/video/av32447485/
https://git.coding.net/zhangguo5/MyBatis03.git
一、我的項目的數據庫設計,我的項目的靜態頁面設計(2個,一個必須是首頁,PC端)
二、重現本章示例
三、任務指導手冊全部mybatis理論題
四、根據以下ER圖建立4個表,完成1-1,1-N,M-N關係的查詢,無需界面,測試經過便可
五、完成圖書管理系統中二表關聯,顯示圖書類型
請實現一個簡易圖書管理系統(LibSystem),實現圖書管理功能,要求以下: 1、管理數據庫中全部圖書(Books),包含圖書編號(isbn)、書名(title)、做者(author)、價格(price)、出版日期(publishDate) 2、Maven多模塊+MySQL+Git+MyBatis+JUnit單元測試 3、表示層能夠是AJAX或JSTL 請實現一個迷你圖書管理系統(LibSystem),實現圖書管理功能,要求以下: 1、管理數據庫中全部圖書分類(Categories),包含圖書編號(id),名稱(name) 2、管理數據庫中全部圖書(Books),包含圖書編號(isbn)、類別(categoryId,外鍵)書名(title)、做者(author)、價格(price)、出版日期(publishDate)、封面(cover)、詳細介紹(details) 3、分頁 4、多條件組件查詢(3個以上的條件) 5、上傳封面 6、富文本編輯器
六、使用任意的原型開發工具設計出我的項目的1-2個界面,工具:Balsamiq Mockups,Axure RP Pro 7.0。
七、將動態sql中的全部知識點所有應用於做業4