1. 程式人生 > 其它 >java之學習記錄 6 - 1 - Mybatis多表查詢、巢狀查詢

java之學習記錄 6 - 1 - Mybatis多表查詢、巢狀查詢

技術標籤:java

資料庫表關係介紹

關係型資料庫表關係分為

  • * 一對一
  • * 一對多
  • * 多對多

案例環境準備

DROP TABLE IF EXISTS `orders`;
CREATE TABLE `orders` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`ordertime` VARCHAR(255) DEFAULT NULL,
`total` DOUBLE DEFAULT NULL,
`uid` INT(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `uid` (`uid`),
CONSTRAINT `orders_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `user` (`id`)
) ENGINE=INNODB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of orders
-- ----------------------------
INSERT INTO `orders` VALUES ('1', '2020-12-12', '3000', '1');
INSERT INTO `orders` VALUES ('2', '2020-12-12', '4000', '1');
INSERT INTO `orders` VALUES ('3', '2020-12-12', '5000', '2');
-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`rolename` VARCHAR(255) DEFAULT NULL,
`roleDesc` VARCHAR(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES ('1', 'CTO', 'CTO');
INSERT INTO `sys_role` VALUES ('2', 'CEO', 'CEO');
-- ----------------------------
-- Table structure for sys_user_role 
-- ---------------------------- 
DROP TABLE IF EXISTS `sys_user_role`; 
CREATE TABLE `sys_user_role` ( 
`userid` INT(11) NOT NULL, 
`roleid` INT(11) NOT NULL, 
PRIMARY KEY (`userid`,`roleid`), 
KEY `roleid` (`roleid`), 
CONSTRAINT `sys_user_role_ibfk_1` FOREIGN KEY (`userid`) REFERENCES `sys_role` (`id`), 
CONSTRAINT `sys_user_role_ibfk_2` FOREIGN KEY (`roleid`) REFERENCES `user` (`id`) 
) ENGINE=INNODB DEFAULT CHARSET=utf8; 
-- ---------------------------- 
-- Records of sys_user_role 
-- ---------------------------- 
INSERT INTO `sys_user_role` VALUES ('1', '1'); 
INSERT INTO `sys_user_role` VALUES ('2', '1'); 
INSERT INTO `sys_user_role` VALUES ('1', '2'); 
INSERT INTO `sys_user_role` VALUES ('2', '2');

一對一(多對一)

1 介紹 一對一查詢模型
  • 使用者表和訂單表的關係為,一個使用者有多個訂單,一個訂單隻從屬於一個使用者
  • 一對一查詢的需求:查詢所有訂單,與此同時查詢出每個訂單所屬的使用者

一對一查詢語句
SELECT * FROM orders o LEFT JOIN USER u ON o.`uid`=u.`id`;
2 程式碼實現 1 Orders 實體
public class Orders {
    private int id;
    private String ordertime;
    private Double total;
    private int uid;
    // 表示當前訂單屬於哪個使用者
    private User user;
}
2 OrderMapper 介面
    /*
    * 一對一關聯查詢:查詢所有訂單,同時還要查詢出每個訂單所屬的使用者資訊
    * */
    public List<Orders> findAllWithUser();
3 OrderMapper.xml 對映
    <!-- 一對一查詢:查詢所有訂單,同時還要查詢出每個訂單所屬的使用者資訊 -->
    <resultMap id="orderMap" type="orders">
        <id property="id" column="id"/>
        <result property="ordertime" column="ordertime"/>
        <result property="total" column="total"/>
        <result property="uid" column="uid"/>
        <!--
            association:在進行一對一關聯查詢配置時,使用association標籤進行關聯
                property="user":要封裝實體的屬性名
                javaType="com.lagou.domain.User" 要封裝實體的屬性型別
        -->
        <association property="user" javaType="com.lagou.domain.User">
            <id property="id" column="uid"/>
            <result property="username" column="username"/>
            <result property="birthday" column="birthday"/>
            <result property="sex" column="sex"/>
            <result property="address" column="address"/>
        </association>
    </resultMap>
    <select id="findAllWithUser" resultMap="orderMap">
        SELECT * FROM orders o LEFT JOIN USER u ON o.uid = u.id
    </select>

4 )測試程式碼

    /*
    * 一對一查詢:查詢所有訂單,同時還要查詢出每個訂單所屬的使用者資訊
    * */
    @Test
    public void test1() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
        List<Orders> ordersList = mapper.findAllWithUser();
        for (Orders orders : ordersList) {
            System.out.println(orders);
        }
        sqlSession.close();
    }

一對多

1 介紹 一對多查詢模型
  • 使用者表和訂單表的關係為,一個使用者有多個訂單,一個訂單隻從屬於一個使用者
  • 一對多查詢的需求:查詢所有使用者,與此同時查詢出該使用者具有的訂單

一對多查詢語句
SELECT *,o.id oid FROM USER u LEFT JOIN orders o ON u.`id` = o.`uid`;
2 程式碼實現 1 User 實體
public class User {
    private int id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    // 表示多方關係:集合 代表了當前使用者所具有的訂單列表
    private List<Orders> ordersList;
}

2 UserMapper介面

    /*
    * 一對多關聯查詢:查詢所有的使用者,同時還要查詢出每個使用者所關聯的訂單資訊
    * */
    public List<User> findAllWithOrder();

3 UserMapper.xml對映

 <!-- 一對多關聯查詢:查詢所有的使用者,同時還要查詢出每個使用者所關聯的訂單資訊 -->
    <resultMap id="userMap" type="com.lagou.domain.User">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>
        <!--
            collection:一對多使用collection標籤進行關聯
         -->
        <collection property="ordersList" ofType="com.lagou.domain.Orders">
            <id property="id" column="oid"></id>
            <result property="ordertime" column="ordertime"></result>
            <result property="total" column="total"></result>
            <result property="uid" column="uid"></result>
        </collection>
    </resultMap>
    <select id="findAllWithOrder" resultMap="userMap">
        SELECT u.*,o.id oid,o.ordertime,o.uid,o.total FROM orders o RIGHT JOIN USER u ON o.uid = u.id
    </select>

4 )測試程式碼

    /*
     * 一對多關聯查詢:查詢所有的使用者,同時還要查詢出每個使用者所關聯的訂單資訊
     * */
    @Test
    public void test2() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> allWithOrder = mapper.findAllWithOrder();
        for (User user : allWithOrder) {
            System.out.println(user);
        }
        sqlSession.close();
    }

多對多

1 介紹 多對多查詢的模型
  • 使用者表和角色表的關係為,一個使用者有多個角色,一個角色被多個使用者使用
  • 多對多查詢的需求:查詢所有使用者同時查詢出該使用者的所有角色

多對多查詢語句
SELECT * FROM
    USER u -- 使用者表 
    LEFT JOIN user_role ur -- 左外連線中間表 
        ON u.`id` = ur.`uid` 
    LEFT JOIN role r -- 左外連線中間表 
        ON ur.`rid` = r.`id` ;
2 程式碼實現 1 User Role 實體
public class User { 
    private Integer id; 
    private String username; 
    private Date birthday; 
    private String sex;
    private String address; 
    // 代表當前使用者關聯的角色列表 
    private List<Role> roleList; 
}

public class Role { 
    private Integer id; 
    private String roleName; 
    private String roleDesc; 
}
2 UserMapper 介面
    /*
     * 多對多關聯查詢:查詢所有的使用者,同時還要查詢出每個使用者所關聯的角色資訊
     * */
    public List<User> findAllWithRole();
3 UserMapper.xml 對映
    <!-- 多對多關聯查詢:查詢所有的使用者,同時還要查詢出每個使用者所關聯的角色資訊 -->
    <resultMap id="userRoleMap" type="user">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>
        <collection property="roleList" ofType="role">
            <id property="id" column="rid"></id>
            <result property="rolename" column="rolename"></result>
            <result property="roleDesc" column="roleDesc"></result>
        </collection>
    </resultMap>
    <select id="findAllWithRole" resultMap="userRoleMap">
        SELECT u.*,r.id rid,r.rolename,r.roleDesc FROM USER u
            LEFT JOIN sys_user_role ur ON ur.userid = u.id
		    LEFT JOIN sys_role r ON ur.roleid = r.id
    </select>
4 )測試程式碼
    /*
     * 多對多關聯查詢:查詢所有的使用者,同時還要查詢出每個使用者所關聯的角色資訊
     * */
    @Test
    public void test3() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> allWithRole = mapper.findAllWithRole();
        for (User user : allWithRole) {
            System.out.println(user);
        }
        sqlSession.close();
    }

小結

MyBatis 多表配置方式
  • * 多對一(一對一)配置:使用<resultMap>+<association>做配置
  • * 一對多配置:使用<resultMap>+<collection>做配置
  • * 多對多配置:使用<resultMap>+<collection>做配置
  • * 多對多的配置跟一對多很相似,難度在於SQL語句的編寫。

MyBatis巢狀查詢

1 什麼是巢狀查詢

  • 巢狀查詢就是將原來多表查詢中的聯合查詢語句拆成單個表的查詢,再使用mybatis的語法巢狀在一起。
舉個例子
* 需求:查詢一個訂單,與此同時查詢出該訂單所屬的使用者 
1. 聯合查詢
    SELECT * FROM orders o LEFT JOIN USER u ON o.`uid`=u.`id`; 
2. 巢狀查詢 
    2.1 先查詢訂單 
        SELECT * FROM orders 
    2.2 再根據訂單uid外來鍵,查詢使用者
        SELECT * FROM `user` WHERE id = #{根據訂單查詢的uid} 
    2.3 最後使用mybatis,將以上二步巢狀起來 ...

2 一對一巢狀查詢

2.1 介紹
  • 需求:查詢一個訂單,與此同時查詢出該訂單所屬的使用者
一對一查詢語句
-- 先查詢訂單 
SELECT * FROM orders; 
-- 再根據訂單uid外來鍵,查詢使用者 
SELECT * FROM `user` WHERE id = #{訂單的uid};
2.2 程式碼實現 1 OrderMapper 介面
    /*
     * 一對一巢狀查詢:查詢所有訂單,同時還要查詢出每個訂單所屬的使用者資訊
     * */
    public List<Orders> findAllWithUser2();
2 OrderMapper.xml 對映
     <resultMap id="orderMap2" type="com.lagou.domain.Orders">
        <id property="id" column="id"/>
        <result property="ordertime" column="ordertime"/>
        <result property="total" column="total"/>
        <result property="uid" column="uid"/>

        <!-- 問題1:怎麼去執行第二條sql .問題二:如何執行第二條sql的時候,把uid作為引數進行傳遞 -->
        <association property="user" fetchType="eager" javaType="user" select="com.lagou.com.lagou.mapper.UserMapper.findById" column="uid">

        </association>
    </resultMap>
    <!-- 一對一巢狀查詢 -->
    <select id="findAllWithUser2" resultMap="orderMap2">
        select * from orders
    </select>
3 UserMapper 介面
    /*
    * 根據id查詢使用者
    * */
    public User findById(int id);
4 UserMapper.xml 對映
    <!-- 根據id查詢使用者
        useCache="true":代表當前這個statement是使用二級快取
    -->
    <select id="findById" useCache="true" flushCache="true" resultType="user" parameterType="int">
        select * from user where id = #{id}
    </select>
5 )測試程式碼
     /*
     * 一對一巢狀查詢:查詢所有訂單,同時還要查詢出每個訂單所屬的使用者資訊
     * */
    @Test
    public void test4() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
        List<Orders> ordersList = mapper.findAllWithUser2();
        for (Orders orders : ordersList) {
            System.out.println(orders);
        }
        sqlSession.close();
    }

3 一對多巢狀查詢

3.1 介紹
  • 需求:查詢所有使用者,與此同時查詢出該使用者具有的訂單
一對多查詢語句
-- 先查詢使用者 
SELECT * FROM `user`; 
-- 再根據使用者id主鍵,查詢訂單列表 
SELECT * FROM orders where uid = #{使用者id};
3.2 程式碼實現 a UserMapper 介面
    /*
     * 一對多巢狀查詢:詢所有的使用者,同時還要查詢出每個使用者所關聯的訂單資訊
     * */
    public List<User> findAllWithOrder2();
b UserMapper.xml 對映
    <!-- 一對多巢狀查詢:詢所有的使用者,同時還要查詢出每個使用者所關聯的訂單資訊 -->
    <resultMap id="userOrderMap" type="user">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>

        <collection property="ordersList" ofType="orders" select="com.lagou.mapper.OrderMapper.findByUid" column="id"></collection>
    </resultMap>
    <select id="findAllWithOrder2" resultMap="userOrderMap">
        select * from user
    </select>
c OrderMapper 介面
    /*
     * 根據uid查詢對應訂單
     * */
    public List<Orders> findByUid(int uid);
d OrderMapper.xml 對映
    <select id="findByUid" parameterType="int" resultType="orders">
        select * from orders where uid = #{uid}
    </select>
e )測試程式碼
     /*
     * 一對多巢狀查詢:查詢所有的使用者,同時還要查詢出每個使用者所關聯的訂單資訊
     * */
    @Test
    public void test5() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> allWithOrder2 = userMapper.findAllWithOrder2();
        for (User user : allWithOrder2) {
            System.out.println(user);
            // 要用到該使用者的訂單資訊
            System.out.println(user.getOrdersList());
        }
        sqlSession.close();
    }

4 多對多巢狀查詢

4.1 介紹
  • 需求:查詢使用者 同時查詢出該使用者的所有角色
多對多查詢語句
-- 先查詢使用者 
SELECT * FROM `user`; 
-- 再根據使用者id主鍵,查詢角色列表 
SELECT * FROM role r INNER JOIN user_role ur ON r.`id` = ur.`rid` WHERE ur.`uid` = #{使用者id};
4.2 程式碼實現 a UserMapper 介面
     /*
     * 多對多巢狀查詢:查詢所有的使用者,同時還要查詢出每個使用者所關聯的角色資訊
     * */
    public List<User> findAllWithRole2();

bUserMapper.xml對映

    <!-- 多對多巢狀查詢:查詢所有的使用者,同時還要查詢出每個使用者所關聯的角色資訊 -->
    <resultMap id="userRoleMap2" type="user">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>

        <collection property="roleList" ofType="role" column="id" select="com.lagou.mapper.RoleMapper.findByUid"></collection>
    </resultMap>
    <select id="findAllWithRole2" resultMap="userRoleMap2">
        select * from user
    </select>

cRoleMapper介面

    /*
    * 根據使用者id 查詢對應角色
    * */
    public List<Role> findByUid(int uid);    

dRoleMapper.xml對映

    <select id="findByUid" resultType="role" parameterType="int">
        SELECT * FROM sys_role r INNER JOIN sys_user_role ur ON r.id = ur.roleid
	        WHERE ur.userid = #{uid}
    </select>

e)測試程式碼

    /*
     * 多對多巢狀查詢:查詢所有的使用者,同時還要查詢出每個使用者所關聯的角色資訊
     * */
    @Test
    public void test6() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> allWithRole = mapper.findAllWithRole2();
        for (User user : allWithRole) {
            System.out.println(user);
        }
        sqlSession.close();
    }

5 小結

  • 一對一配置:使用<resultMap>+<association>做配置,通過column條件,執行select查詢
  • 一對多配置:使用<resultMap>+<collection>做配置,通過column條件,執行select查詢
  • 多對多配置:使用<resultMap>+<collection>做配置,通過column條件,執行select查詢
  • 優點:簡化多表查詢操作
  • 缺點:執行多次sql語句,浪費資料庫效能