1. 程式人生 > >MyBatis學習總結(三)——多表關聯查詢與動態SQL

MyBatis學習總結(三)——多表關聯查詢與動態SQL

一、多表關聯查詢

表與表之間有三種常見的關聯關係,分別是一對一,一對多與多對多關係,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);
    }

結果: