Mybatis 之三多表查詢和註解開發

一、Mybatis多表查詢

多表關係分析

1. 一對多關係:
		用戶和訂單,分類和商品,部門和員工...
2. 多對多關係:
		訂單和商品,學生和課程,用戶和角色...
3. 一對一關係:
		公民和身份證號..
4. 多對一關係:
		訂單和用戶

多表關係的維護

1. 一對多關係:
	在多表的一方添加一個字段作爲外鍵,字段名稱自定義(一般是主表的名稱_id),字段的類型一般和主表的主鍵保持一致.
2.多對多關係:
		在開發中,多對多關係通常引入一張中間表,通過中間表就可以將多對多拆分成兩個一對多了,
		在中間表中一般存放另外兩張表的主鍵,還可以將這兩個字段設置爲聯合主鍵
3. 一對一關係:
		兩個實體合二爲一
		外鍵唯一對應
		主鍵對應

Mybatis中:
	對一查詢:
	對多查詢:
別名統一配置
<typeAliases>
    <!--
		使用typeAliases配置別名,它只能配置domain中類的別名
		其中: 類名就是別名(不區分大小寫)
 	-->
    <package name="cn.kinggm520.domain"></package>
</typeAliases>
映射文件統一配置
<mappers>
    <!--
 		注意:
		1. package標籤配置的是路徑或包名
				name="cn/kinggm520/dao"  正確
				name="cn.kinggm520.dao"  正確
		2. 使用package配置方式,映射配置文件名稱,必須和接口名稱完全一致.否則解析錯誤
	-->
    <package name="cn/kinggm520/dao"></package>
    <package name="cn.kinggm520.dao"></package>   
</mappers>

1、 一對一查詢(使用的模型實際上是一對多 但是設置的數據爲一對一)

user表:
在這裏插入圖片描述
orders表:
在這裏插入圖片描述

在這裏插入圖片描述
當前用戶表和訂單表的關係爲,一個用戶有多個訂單,一個訂單隻從屬於一個用戶
一對一查詢的需求:查詢一個訂單,與此同時查詢出該訂單所屬的用戶

1.1、 一對一查詢的語句

對應的sql語句:select * from orders o,user u where o.uid=u.id;

查詢的結果如下:
在這裏插入圖片描述

1.2、創建Order和User實體

(省略了getter和setter方法)

public class Order {

    private int id;
    private Date ordertime;
    private double total;

    //代表當前訂單從屬於哪一個客戶
    private User user;
}

public class User {
    
    private int id;
    private String username;
    private String password;
}
1.3、創建OrderDao接口
public interface OrderDao {
// 一對一關係
    public List<Order> findAll();
}

2、 一對一實現方式一 (瞭解 逐個屬性封裝)

2.1、 修改OrderDao.xml
<?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="cn.kingm520.dao.OrderDao">
    <resultMap id="orderMap" type="order">
        <id column="oid" property="id"></id>
        <result column="ordertime" property="ordertime"></result>
        <result column="total" property="total"></result>
        <!--用戶信息-->
        <result column="uid" property="user.id"></result>
        <result column="username" property="user.username"></result>
        <result column="password" property="user.password"></result>
     
    </resultMap>

    <select id="findAll" resultMap="orderMap">
       SELECT *,o.id oid FROM orders o,USER u WHERE o.uid = u.id;
    </select>
</mapper>
2) 編寫測試類
public class OrderDemo {
    private SqlSession sqlSession;
    @Before
    public void init() throws IOException {
        InputStream in = Resources.getResourceAsStream("SqlMapperConfig.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        sqlSession = factory.openSession();
    }


    @Test
    public void findAll() {
        OrderDao dao = sqlSession.getMapper(OrderDao.class);
        List<Order> orders = dao.findAll();
        for (Order order : orders) {
            System.out.println(order);
        }
    }

}

3、 一對一實現方式二 (瞭解 使用子類封裝User和Order類的全部屬性)

繼承方式實現

3.1、 編寫SQL語句
-- 查詢訂單和所屬用戶
SELECT *,o.id oid FROM orders o,USER u WHERE o.uid = u.id;
3.2、 定義UserOrder類
爲了能夠封裝上面SQL語句查詢結果,定義一個UserOrder類中要包含訂單信息同時還要包含用戶信息.
	方式一: 
		創建一個UserOrder類, 屬性包含訂單信息和用戶信息. 顯然這樣比較麻煩,代碼冗餘
	方式二:
		我們要在定義UserOrder類的同時可以繼承Order類
public class UserOrder extends Order {
    private int uid;
    private String username;
    private String password;
 
}

3.3、 定義持久層Dao接口方法
public List<Order> findAll();
3.4、 配置訂單映射配置文件
<select id="findAll" resultType="userOrder">
    SELECT *,o.id oid FROM orders o,USER u WHERE o.uid = u.id;
</select>

總結

爲什麼 UserOrder extends  Order就可以封裝查詢的結果集呢?
-- 查詢訂單和所屬用戶
SELECT *,o.id oid FROM orders o,USER u WHERE o.uid = u.id;

底層原理分析:
	1. 執行SQL語句,獲取ResultSet結果集對象
	2. 使用反射機制,根據UserOrder對象的屬性,獲取結果集中對應字段的值,並調用對象的SetXxx方法封裝.
			對象屬性: 由對象的getXxx和SetXxx方法確定. 
			而UserOrder繼承Order,就可以調用父類getXxx和setXxx方法,因此可以獲取父類屬性,封裝數據

4.、一對一實現方式三 ( 掌握 使用 < association> 標籤封裝)

4.1、 修改OrderDao.xml配置文件
<?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="cn.kinggm520.dao.OrderDao">
    <resultMap id="orderMap" type="order">
        <id column="oid" property="id"></id>
        <result column="ordertime" property="ordertime"></result>
        <result column="total" property="total"></result>

        <!--用戶信息 
        property Order類中的屬性
        javaType  該屬性的類型
        column  列名
        property  對應的屬性名
         -->
        <association property="user" javaType="user">
            <id column="id" property="id"></id>
            <result column="username" property="username"></result>
            <result column="password" property="password"></result>
        </association>

    </resultMap>

    <select id="findAll" resultMap="orderMap">
       SELECT *,o.id oid FROM orders o,USER u WHERE o.uid = u.id;
    </select>
    
</mapper>
4.2、測試
@Test
public void findAll() {
    OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
    List<Order> orders = orderMapper.findAll();
    for (Order order : orders) {
        System.out.println(order);
    }
}

控制檯打印信息:
Order{id=1, ordertime=‘2020-01-01’, total=100.0, user=User{id=1, username=‘秀逗’, password=‘123123’}}
Order{id=2, ordertime=‘2020-01-02’, total=200.0, user=User{id=2, username=‘四眼’, password=‘123123’}}
Order{id=3, ordertime=‘2020-01-03’, total=300.0, user=User{id=3, username=‘黃黃’, password=‘123123’}}

5、一對多查詢

5.1、 一對多查詢的模型

用戶表和訂單表的關係爲,一個用戶有多個訂單,一個訂單隻從屬於一個用戶

一對多查詢的需求:查詢一個用戶,與此同時查詢出該用戶具有的訂單

在這裏插入圖片描述

5.2、 一對多查詢的語句

對應的sql語句:select *,o.id oid from user u left join orders o on u.id=o.uid;

查詢的結果如下:

在這裏插入圖片描述

5.3、 修改相應實體

(省略getter和setter方法 以及重寫的toString方法)

public class Order {
    private int id;
    private Date ordertime;
    private double total;
    //代表當前訂單從屬於哪一個客戶
    private User user;
}

public class User {
    private int id;
    private String username;
    private String password;
  
    //代表當前用戶具備哪些訂單
    private List<Order> orders;
}
5.4、 創建UserMapper接口
public interface UserMapper {
    public List<User> findAll();
}

5.5、 配置UserMapper.xml
<?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="cn.kinggm520.dao.UserMapper">
    <resultMap id="userMap" type="User">
        <!--用戶信息-->
        <id property="id" column="id"/>
        <id property="username" column="username"/>
        <id property="password" column="password"/>
     
        <!--
          訂單信息
              property: 對象屬性名稱
              ofType: 設置集合的泛型  List<Order> orders
      -->
        <collection property="orders" ofType="Order">
            <id property="id" column="oid"/>
            <id property="ordertime" column="ordertime"/>
            <id property="total" column="total"/>
        </collection>
    </resultMap>

    <select id="findAll" resultMap="userMap">
        SELECT *,o.id oid FROM USER u LEFT JOIN orders o ON u.id=o.uid
    </select>
</mapper>
5.6 、測試
  @Test
    public void findAll() {
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = userMapper.findAll();
        for (User user : userList) {
            System.out.println(user);
        }
    }

控制檯打印結果爲:
User{id=1, username=‘秀逗’, password=‘123123’, orders=[Order{id=1, ordertime=‘2020-01-01’, total=100.0}, Order{id=4, ordertime=‘2020-03-30’, total=500.0}]}
User{id=2, username=‘四眼’, password=‘123123’, orders=[Order{id=2, ordertime=‘2020-01-02’, total=200.0}]}
User{id=3, username=‘黃黃’, password=‘123123’, orders=[Order{id=3, ordertime=‘2020-01-03’, total=300.0}]}

6、多對多查詢

-- 創建角色表
CREATE TABLE role(
 id INT PRIMARY KEY AUTO_INCREMENT,
 rolename VARCHAR(50),
 roledesc VARCHAR(100)
);
INSERT INTO `role`(`id`,`rolename`,`roledesc`) VALUES ( '1','校長','負責全面工作');
INSERT INTO `role`(`id`,`rolename`,`roledesc`) VALUES ( '2','老師','負責講課');
INSERT INTO `role`(`id`,`rolename`,`roledesc`) VALUES ( '3','學生','負責聽課');

-- 創建用戶角色關係表
CREATE TABLE user_role (
	user_id INT,
	role_id INT
);
INSERT INTO `user_role`(`user_id`,`role_id`) VALUES ( '1','1');
INSERT INTO `user_role`(`user_id`,`role_id`) VALUES ( '1','2');
INSERT INTO `user_role`(`user_id`,`role_id`) VALUES ( '2','2');
INSERT INTO `user_role`(`user_id`,`role_id`) VALUES ( '2','3');

數據表如下圖:
在這裏插入圖片描述

6.1、 多對多查詢的模型

用戶表和角色表的關係爲,一個用戶有多個角色,一個角色被多個用戶使用

多對多查詢的需求:查詢用戶同時查詢出該用戶的所有角色

6.2 、多對多查詢的語句

對應的sql語句:

SELECT * FROM USER u,user_role ur,role r WHERE u.id = ur.user_id AND ur.role_id = r.id;

查詢的結果如下:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-3NQp45Fz-1585548222082)(img\圖片7.png)]

6.3 、 修改User實體 創建Role實體
public class User {
    private int id;
    private String username;
    private String password;
    //代表當前用戶具備哪些角色
    private List<Role> roles;
  

public class Role {
    private int id;
    private String rolename;
    private String roledesc;
}
6.4 、 添加UserMapper接口方法
List<User> findAll();
6.5、 配置UserMapper.xml
<?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="cn.kinggm520.dao.UserMapper">

<resultMap id="userMapper" type="user">
    <!--用戶信息-->
    <id column="user_id" property="id"></id>
    <result column="username" property="username"></result>
    <result column="password" property="password"></result>
   

    <!--角色信息-->
    <collection property="roles" ofType="role">
        <id column="role_id" property="id"></id>
        <result column="rolename" property="rolename"></result>
        <result column="roledesc" property="roledesc"></result>
    </collection>
</resultMap>

<select id="findAll" resultMap="userMapper">
    SELECT * FROM USER u,user_role ur,role r WHERE u.id = ur.user_id AND ur.role_id = r.id
</select>

</mapper>
6.6 、測試結果
  @Test
    public void findAll() {
        UserDao dao = sqlSession.getMapper(UserDao.class);
        List<User> users = dao.findAll();
        for (User user : users) {
            System.out.println(user);
        }
    }

控制檯打印信息:
User{id=1, username=‘秀逗’, password=‘123123’, orders=null, roles=[Role{id=1, rolename=‘校長’, roledesc=‘負責全面工作’}, Role{id=2, rolename=‘老師’, roledesc=‘負責講課’}]}

User{id=2, username=‘四眼’, password=‘123123’, orders=null, roles=[Role{id=2, rolename=‘老師’, roledesc=‘負責講課’}, Role{id=3, rolename=‘學生’, roledesc=‘負責聽課’}]}

7、知識小結

MyBatis多表配置方式:

一對一配置:使用做配置

一對多配置:使用+做配置

多對多配置:使用+做配置

二、Mybatis的註解開發

1、 MyBatis的常用註解

這幾年來註解開發越來越流行,Mybatis也可以使用註解開發方式,這樣我們就可以減少編寫Mapper映射文件了。我們先圍繞一些基本的CRUD來學習,再學習複雜映射多表操作。

@Insert:實現新增

@Update:實現更新

@Delete:實現刪除

@Select:實現查詢

@Result:實現結果集封裝

@Results:可以與@Result 一起使用,封裝多個結果集

@One:實現一對一結果集封裝

@Many:實現一對多結果集封裝

2、MyBatis的增刪改查

我們完成簡單的user表的增刪改查的操作

① 編寫UserMapper接口
public interface UserMapper {
    @Select("select * from user")
    public List<User> findAll();

    @Insert("insert into user values(#{id},#{username},#{password})")
    void save(User user);

    @Update("update user set username=#{username},password=#{password} where id=#{id}")
    public void update(User user);

    @Delete("delete from user where id=#{id}")
    public void delete(int id);

    @Select("select * from user where id=#{id}")
    public User findById(int id);
}

② 編寫測試類
public class UserDemo {
    private UserMapper userMapper;

    @Before
    public void init() throws IOException {
        InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);

        // 設置自動提交事務
        SqlSession sqlSession = factory.openSession(true);
        userMapper = sqlSession.getMapper(UserMapper.class);
    }

    @Test
    public void findAll() {
        List<User> users = userMapper.findAll();
        for (User user : users) {
            System.out.println(user);
        }
    }

    // 添加
    @Test
    public void saveUser() {
        User user = new User();
        user.setUsername("傑克");
        user.setPassword("7777");
        userMapper.save(user);
    }

    // 修改
    @Test
    public void updateUser() {
        User user = new User();
        user.setId(5);
        user.setUsername("傑克");
        user.setPassword("9999");
      
        userMapper.update(user);
    }

    // 刪除
    @Test
    public void deleteUser() {
        userMapper.delete(5);
    }

    // 根據id查詢
    @Test
    public void findById() {
        User user = userMapper.findById(1);
        System.out.println(user);
    }
}

修改MyBatis的核心配置文件,我們使用了註解替代映射文件,所以我們只需要加載使用了註解的Dao接口即可

<mappers>
    <!--掃描使用註解的類-->
    <mapper class="cn.kinggm520.dao.UserMapper"></mapper>
</mappers>

或者指定掃描包含映射關係的接口所在的包也可以

<mappers>
    <!--掃描使用註解的類所在的包-->
    <package name="cn.kinggm520.dao"></package>
</mappers>

3 模糊/統計查詢/保存返回id

2.3.1 在UserDao接口添加方法
   // 模糊查詢
    @Select("select * from user where username like #{username}")
    public List<User> findByName(String username);

    // 統計查詢
    @Select("select count(*) from user")
    public int findTotal();

    /**
     * 保存用戶,並返回最後保存用戶的id
     * before
     *      1. false : 表示插入成功以後執行
     *      2. true:  表示插入之前執行
     */
    @SelectKey(keyProperty = "id", keyColumn = "id", resultType = Integer.class, before = false, statement = "select last_insert_id()")
    @Insert("insert into  user (username,password) values(#{username},#{password})")
    public void saveLastUser(User user);
2.3.2 測試
// 模糊查詢
    @Test
    public void findByName() {
        List<User> users = userMapper.findByName("%張%");
        for (User user : users) {
            System.out.println(user);
            System.out.println("----------------------------------");
        }
    }

    // 統計查詢
    @Test
    public void findTotal() {
        int total = userMapper.findTotal();
        System.out.println(total);

    }


    // 添加
    @Test
    public void saveLastUser() {
        User user = new User();
        user.setUsername("傑克");
        user.setPassword("7777");
        System.out.println("保存之前: " +user);
        userMapper.saveLastUser(user);
        System.out.println("保存之後: " +user);
    }

4 、MyBatis的註解實現複雜映射開發 (和xml方式大同小異)

實現複雜關係映射之前我們可以在映射文件中通過配置來實現,使用註解開發後,我們可以使用@Results註解,@Result註解,@One註解,@Many註解組合完成複雜關係的配置

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-kVYjLDSG-1585548222083)(img\圖片10.png)]

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Wl91wTuJ-1585548222084)(img\圖片11.png)]

2.4.1 一對一查詢

一對一查詢的模型

用戶表和訂單表的關係爲,一個用戶有多個訂單,一個訂單隻從屬於一個用戶

一對一查詢的需求:查詢一個訂單,與此同時查詢出該訂單所屬的用戶

一對一查詢的語句

對應的sql語句:

select * from orders;
select * from user where id=查詢出訂單的uid;
創建Order和User實體
public class Order {

    private int id;
    private String ordertime;
    private double total;

    //代表當前訂單從屬於哪一個客戶
    private User user;
}

public class User {
    
    private int id;
    private String username;
    private String password;

}
創建OrderMapper接口
public interface OrderMapper {
    List<Order> findAll();
}
使用註解配置OrderMapper
public interface OrderMapper {
    @Select("select * from orders")
    @Results({
            @Result(id = true, property = "id", column = "id"),
            @Result(property = "ordertime", column = "ordertime"),
            @Result(property = "total", column = "total"),
            @Result(property = "user",
                    column = "uid",
                    javaType = User.class,
                    one = @One(select = "cn.kinggm520.dao.UserMapper.findById")
            )
    })
    List<Order> findAll();
}



/*
 @Result(property = "user", //要封裝的屬性名稱
 		 column = "uid", //根據哪個字段去查詢user表的數據
         javaType = User.class, //要封裝的實體類型
         //select屬性 代表查詢哪個接口的方法獲得數據
         one = @One(select = "cn.kinggm520.mapper.UserMapper.findById"))
*/
public interface UserMapper {
    @Select("select * from user where id=#{id}")
    public User findById(int id);
}

測試結果
public class AnnoDemo {
    private OrderMapper orderMapper;

    @Before
    public void init() throws IOException {
        InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);

        // 設置自動提交事務
        SqlSession sqlSession = factory.openSession(true);
        orderMapper = sqlSession.getMapper(OrderMapper.class);
    }

    @Test
    public void findAllOne2One() {
        List<Order> orders = orderMapper.findAll();
        for (Order order : orders) {
            System.out.println(order);
        }
    }
}

2.4.2、一對多查詢

一對多查詢的模型

用戶表和訂單表的關係爲,一個用戶有多個訂單,一個訂單隻從屬於一個用戶

一對多查詢的需求:查詢一個用戶,與此同時查詢出該用戶具有的訂單

一對多查詢的語句

對應的sql語句:

select * from user;
select * from orders where uid=查詢出用戶的id;
修改User和Order實體
public class Order {
    private int id;
    private Date ordertime;
    private double total;

    //代表當前訂單從屬於哪一個客戶
    private User user;
}

public class User {
    
    private int id;
    private String username;
    private String password;
    //代表當前用戶具備哪些訂單
    private List<Order> orders;
}
創建UserMapper接口
List<User> findAllUserAndOrder();
使用註解配置Mapper
public interface UserMapper {
    @Results({
            @Result(property = "id",column = "id",id = true),
            @Result(property = "username",column = "username"),
            @Result(property = "password",column = "password"),
        
            @Result(property = "orders",column = "id",javaType = List.class,
                    many = @Many(select = "cn.kinggm520.dao.OrderMapper.findById")
            )
    })
    @Select("select * from user")
    List<User> findAllUserAndOrder();
}

public interface OrderMapper {
   @Select("select * from orders where uid = #{uid}")
    public Order findById();
}
測試結果
public class AnnoDemo {
    private UserMapper userMapper;

    @Before
    public void init() throws IOException {
        InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);

        // 設置自動提交事務
        SqlSession sqlSession = factory.openSession(true);
        userMapper = sqlSession.getMapper(UserMapper.class);
    }

    @Test
    public void findAllUserAndOrder() {
        List<User> all = userMapper.findAllUserAndOrder();
        for (User user : all) {
            System.out.println(user.getUsername());
            List<Order> orderList = user.getOrders();
            for (Order order : orderList) {
                System.out.println(order);
            }
            System.out.println("-----------------------------");
        }
    }
}

2.4.3 多對多查詢

2.6.1 多對多查詢的模型

用戶表和角色表的關係爲,一個用戶有多個角色,一個角色被多個用戶使用

多對多查詢的需求:查詢用戶同時查詢出該用戶的所有角色

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-3qr9joSF-1585548222088)(img\圖片18.png)]

2.6.2 多對多查詢的語句

對應的sql語句:

select * from user;

select * from role r,user_role ur where r.id=ur.role_id and ur.user_id=用戶的id
2.6.3 創建Role實體,修改User實體
public class User {
    private int id;
    private String username;
    private String password;
    //代表當前用戶具備哪些訂單
    private List<Order> orderList;
    //代表當前用戶具備哪些角色
    private List<Role> roleList;
}

public class Role {
    private int id;
    private String rolename;

}
添加UserMapper接口方法
List<User> findAllUserAndRole();
使用註解配置Mapper
public interface UserMapper {
    @Select("select * from user")
    @Results({
        @Result(id = true,property = "id",column = "id"),
        @Result(property = "username",column = "username"),
        @Result(property = "password",column = "password"),
      
        @Result(property = "roleList",column = "id",
                javaType = List.class,
                many = @Many(select = "cn.kinggm520.mapper.RoleMapper.findByUid"))
})


List<User> findAllUserAndRole();

}

public interface RoleMapper {
    @Select("select * from role r,user_role ur where r.id=ur.role_id and ur.user_id=#{uid}")
    List<Role> findByUid(int uid);
}

測試結果
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> all = mapper.findAllUserAndRole();
for(User user : all){
    System.out.println(user.getUsername());
    List<Role> roleList = user.getRoleList();
    for(Role role : roleList){
        System.out.println(role);
    }
    System.out.println("----------------------------------");
}

所有測試結果和xml方式結果相同

三、擴展- 延遲加載

問題分析

	通過前面的學習,我們已經掌握了 Mybatis 中一對一,一對多,多對多關係的配置及實現,可以實現對象的
關聯查詢。實際開發過程中很多時候我們並不需要總是在加載用戶信息時就一定要加載他的訂單信息。此時就是我
們所說的延遲加載。

例如:
	在一對多中,當我們用一個用戶,他有100個訂單.
	1. 在查詢用戶時,要不要把關聯的訂單查出來?
	2. 在查詢訂單時,要不要把關聯的用戶查出來?
	
思路:
	在查詢用戶時:用戶下的訂單,應該是什麼時候使用,什麼時候去查詢.  -- 延遲加載
	在查詢訂單時:訂單的所屬用戶信息,應該隨着訂單查詢時就一起查詢出來. -- 立即加載

3.1 概述

3.1.1 何爲延遲加載?

延遲加載:
	就是在需要用到數據時才進行加載,不需要用到數據時就不加載數據。延遲加載也稱懶加載.
	好處:先從單表查詢,需要時再從關聯表去關聯查詢,大大提高數據庫性能,因爲查詢單表要比關聯查詢多張錶速
度要快。
	壞處:因爲只有當需要用到數據時,纔會進行數據庫查詢,這樣在大批量數據查詢時,因爲查詢工作也要消耗
時間,所以可能造成用戶等待時間變長,造成用戶體驗下降。

3.1.2 實現需求

需求:
	查詢訂單(Order)信息並且關聯查詢用戶(User)信息。如果先查詢訂單(Order)信息即可滿足要
求,當我們需要查詢用戶(User)信息時再查詢訂單(order)信息。把對用戶(User)信息的按需去查詢就是延遲加
載。
	mybatis實現多表操作時,我們使用了resultMap來實現一對一,一對多,多對多關係的操作。主要
是通過 association、collection 實現一對一及一對多映射。association、collection 具備延遲加載功
能。

3.2 使用 assocation 實現延遲加載

需求:
	查詢訂單信息同時查詢用戶信息。

3.2.1 創建OrderMapper接口

public interface OrderMapper {
    public List<Order> findAll();

}

3.2.2 編寫OrderMapper.xml映射文件

<?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="cn.kinggm520.mapper.OrderMapper">
    <resultMap id="orderMapper" type="order">
        <!--1. 封裝訂單信息-->
        <id property="id" column="id"/>
        <result property="ordertime" column="ordertime"/>
        <result property="total" column="total"/>

        <!--屬性分析:
				property: Role的屬性名
				javaType: user屬性對應的類型
				column: 根據orders表的哪個字段,去查詢user表
				select: 調用namespace+id對應的SQL語句,執行查詢
				fetchType:
                	fetchType="lazy": 延遲加載  
					fetchType="eager": 立即加載
        -->
        <association property="user" javaType="user" column="uid" select="cn.kinggm520.dao.UserMapper.findById" fetchType="lazy">
            <id property="id" column="uid"/>
            <result property="username" column="username"/>
            <result property="password" column="password"/>
        </association>
    </resultMap>


    <select id="findAll" resultMap="orderMapper">
           select * from orders
    </select>

</mapper>

3.2.3 編寫UserMapper接口

public interface UserMapper {
    public User findById(int id);
}

3.2.4 編寫UserMapper.xml映射文件

<?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="cn.kinggm520.dao.UserDao">
    <!--實現延遲加載的效果, 提供findById查詢SQL-->
    <select id="findById" parameterType="int" resultMap="userMap">
        select * from user where id = #{id}
    </select>
</mapper>

3.2.5 開啓 Mybatis 的延遲加載策略

進入 Mybaits 的官方文檔,找到 settings 的說明信息:

設置名 描述 有效值 默認值
lazyLoadingEnabled 延遲加載的全局開關。當開啓時,所有關聯對象都會延遲加載。 特定關聯關係中可通過設置 fetchType 屬性來覆蓋該項的開關狀態。 true | false false
aggressiveLazyLoading 當開啓時,任何方法的調用都會加載該對象的所有屬性。 否則,每個屬性會按需加載(參考 lazyLoadTriggerMethods)。 true | false false (在 3.4.1 及之前的版本默認值爲 true)

我們需要在 Mybatis 的配置文件 SqlMapConfig.xml 文件中添加延遲加載的配置。

 <settings>
      <!--開啓延遲加載 總開關-->
      <setting name="lazyLoadingEnabled" value="true"/>
      <setting name="aggressiveLazyLoading" value="false"></setting>
</settings>

3.2.6 編寫測試只查訂單信息不查用戶信息

public class OrderDemo {
    private SqlSession sqlSession;


    @Before
    public void init() throws IOException {
        InputStream in = Resources.getResourceAsStream("SqlMapperConfig.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        sqlSession = factory.openSession();
    }
     @After
    public void release() {
        session.commit();
        session.close();
    }

    /**
     *
     * 查詢訂單和所屬用戶信息
     *      訂單和用戶: 對一查詢
     */
    @Test
    public void findAll() {
        List<Order> orders = mapper.findAll();
        for (Order order : orders) {
            System.out.println(order.getOrdertime()+"---"+order.getTotal());
            //System.out.println(order.getOrdertime()+"---"+order.getUser());
        }
    }

}

​ 我們發現,因爲本次只是將 Order對象查詢出來放入 List 集合中,並沒有涉及到 User對象,所以就沒有
發出 SQL 語句查詢賬戶所關聯的 User 對象的查詢。

3.3 使用 collection實現延遲加載

3.3.1 創建OrderDao接口

public interface UserDao {
    List<User> findAll();
}

3.3.2 編寫UserDao.xml映射文件

<?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="cn.kinggm20.mapper.UserDao">
    <resultMap id="userMapper" type="user">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="password" column="password"/>
        
        <collection property="orders" ofType="Order" column="id" select="cn.kinggm520.mapper.OrderMapper.findByUid" fetchType="lazy">
            <id property="id" column="id"/>
            <result property="ordertime" column="ordertime"/>
            <result property="total" column="total"/>
        </collection>
    </resultMap>

    <select id="findAll" resultMap="userMapper">
        select * from user
    </select>


    <select id="findById" resultType="user" parameterType="int">
        select * from user where id = #{id}
    </select>
</mapper>

3.2.2 編寫OrderDao.xml映射文件

  <select id="findByUid" resultType="order" parameterType="int">
        select * from orders where uid = #{uid}
    </select>

3.2.4 編寫測試只查訂單信息不查用戶信息

public class UserDemo {
    private SqlSession sqlSession;


    @Before
    public void init() throws IOException {
        InputStream in = Resources.getResourceAsStream("SqlMapperConfig.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        sqlSession = factory.openSession();
    }
     @After
    public void release() {
        session.commit();
        session.close();
    }

     /**
     *
     * 查詢用戶和訂單信息
     *      用戶和訂單: 一對多查詢
     */
    @Test
    public void findAll() {
        List<User> users = mapper.findAll();
        for (User user : users) {
            System.out.println(user.getUsername()+user.getOrders());
        }
    }


}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章