MyBatis學習總結(三)——多表關聯查詢與動態SQL
阿新 • • 發佈:2018-12-14
一、多表關聯查詢
表與表之間有三種常見的關聯關係,分別是一對一,一對多與多對多關係,MyBatis直接提供一對一與一對多的關聯關係,可以通過間接的方式實現多對多關聯。
1.1、一對一關係
1.1.1、執行環境
假定一個員工(emp)擁有一個登入使用者(user),員工與使用者表之間是一對一關係:
使用者表:
員工表:
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', '湯姆', '[email protected]'); INSERT INTO `emp` VALUES ('2', '2', '梅貴', '[email protected]'); INSERT INTO `emp` VALUES ('3', '3', '馬克', '[email protected]'); INSERT INTO `emp` VALUES ('4', '4', '嶽翰', '[email protected]'); INSERT INTO `emp` VALUES ('5', '5', '馬麗', '[email protected]');
關係:
1.1.2、關聯查詢(1次查詢)
實體:
使用者:
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); } }
結果:
1.1.3、巢狀查詢(2次查詢)
實體:同上
介面:
/**獲得員工通過員工編號,多次查詢*/ 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標籤可用的屬性如下:
- property:物件屬性的名稱
- javaType:物件屬性的型別
- column:所對應的外來鍵欄位名稱
- select:使用另一個查詢封裝的結果
1.2、一對多關係
1.2.1、執行環境
一個使用者帳號可以被多個員工使用,形成一個一對多的關係,表中的資料如下:
員工表emp:
使用者表user:
1.2.2、關聯查詢(1次查詢)
實體:
員工:
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.1.3、巢狀查詢(多次查詢)
實體:同上
介面:
/**獲得使用者通過使用者編號,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
2.0、MySQL環境與前置要求
資料與SQL環境如下:
前置要求:
2.1、什麼是動態SQL
MyBatis的動態SQL是基於OGNL的表示式的。它對SQL語句進行靈活的操作,通過表示式判斷來實現對SQL的靈活拼接、組裝。
mybatis核心對sql語句進行靈活操作,通過表示式進行判斷,對sql進行靈活拼接、組裝。
主要通過以下標籤:if,where,choose(when,otherwise),trim,set,foreach。
2.2、if條件判斷
根據 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); }
結果: