MyBatis03

MyBatis(第3天) 动态SQL和多表关联

动态SQL的概念和环境搭建

之前我们编写SQL语句的时候都是将SQL语句固定写好的

SELECT * FROM product WHERE brand='小米' AND type='ai' AND size=55;

UPDATE user SET username=#{username}, birthday=#{birthday}, sex=#{sex}, address=#{address} WHERE id=#{id};

目标

学习动态SQL的概念

搭建动态SQL的环境

什么是动态SQL

动态SQL指的是:在程序运行时,根据传入的参数情况,拼接最终执行的sql语句。

[外链图片转存失败(img-S8eJah2c-1565267219535)(/)]

搭建动态SQL的环境

模块名:mybatis_day02_04_dynamic_sql

配置mybatis核心配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration  
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <properties resource="jdbc.properties"/>

    <typeAliases>
        <package name="com.itheima.entity"/>
    </typeAliases>

    <environments default="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="dirver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    
    <mappers>
        <package name="com.itheima.dao"/>
    </mappers>
</configuration>
测试
public class TestApp {
    // 省略其他sqlSession的创建
    
    @Test
    public void testFindUserBySex() {
        List<User> list = userMapper.findUserByNameAndSex(new QueryVo("%王%", "女"));
        for (User user : list) {
            System.out.println("user = " + user);
        }
    }
}

小结

  1. 搭建mybatis环境

动态SQL:if标签

目标

用户名和性别查询用户

学习动态SQL语句:if标签的使用

if标签的格式
<if test="条件">
  SQL语句
</if>
if标签的作用

如果满足条件,就会拼接这个SQL。

需求实现

定义QueryVo实体类

public class QueryVo {
    private String username;
    private String sex;
    
    // 省略构造方法/getter/setter
}

声明UserMapper接口方法

package com.itheima.dao;

import com.itheima.entity.User;
import java.util.List;

public interface UserMapper {
    /*
     根据用户名称和性别查询用户
     */
    List<User> findUserByNameAndSex(QueryVo vo);
}

配置mappr映射文件

<?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.itheima.dao.UserMapper">
    <select id="findUserByNameAndSex" parameterType="queryvo" resultType="User">
        SELECT * FROM user WHERE username LIKE #{username} AND sex=#{sex};
    </select>
</mapper>

UserMapper.xml

  1. if:判断用户名称不为空,且不为空字符串,则用户名称作为查询条件
  2. if:判断用户性别不为空,且不为空字符串,则用户性别作为查询条件
<?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.itheima.dao.UserMapper">
    <!--直接根据用户名和性别查询-->
<!--    <select id="findUserByNameAndSex" parameterType="queryvo" resultType="User">
        SELECT * FROM user WHERE username LIKE #{username} AND sex=#{sex};
    </select>-->
    
    <!--
        判断用户名称不为空,且不为空字符串,则用户名称作为查询条件
        判断用户性别不为空,且不为空字符串,则用户性别作为查询条件
    -->
    <select id="findUserByNameAndSex" parameterType="User" resultType="User">
        select * from user where
        <if test="username!=null and username!=''">
          username like '%${username}%'
        </if>
        <!-- &&表示与操作,要转义 -->
        <if test="sex!=null &amp;&amp; sex!=''">
          and sex = #{sex}
        </if>
    </select>
</mapper>

测试

  1. 通过用户名和性别查询多个用户
  2. 只设置性别
  3. 名字和性别一个都不设置
@Test
public void testFindUserBySex() {
    List<User> list = userMapper.findUserByNameAndSex(new QueryVo("", ""));
    for (User user : list) {
        System.out.println("user = " + user);
    }
}

疑问:if标签如果第1个条件没有,会出现什么情况?如何解决这个问题?

因为SQL语句拼接语句不正确,出现问题。

[外链图片转存失败(img-0RBlK533-1565267219538)(/)]

小结

动态SQL中if标签

if标签的含义做判断如果条件为true就拼接SQL

if标签的格式:

<if test"条件">
	SQL
</if>

动态SQL:where标签的作用

目标

学习where标签的使用

where标签作用
  1. 相当于where关键字,自动补全where这个关键字
  2. 去掉多余的and和or关键字
需求实现
UserMapper.xml

if标签写在where标签内部

  1. if:判断用户名称不为空,且不为空字符串,则用户名称作为查询条件
  2. if:判断用户性别不为空,且不为空字符串,则用户性别作为查询条件
<select id="findUserByNameAndSex" parameterType="queryvo" resultType="User">
    SELECT * FROM user
    <where>
        <if test="username != null and username != ''">
            AND username LIKE #{username}
        </if>

        <!--&&表示与操作,要转义-->
        <if test="sex != null &amp;&amp; sex != ''">
            AND sex=#{sex};
        </if>
    </where>
</select>

小结

  1. if标签的作用:

    做判断,如果满足条件就拼接SQL

  2. where标签的作用

    1. 替代WHERE关键字
    2. 如果有条件会自动添加WHERE关键字
    3. 如果没有条件就不添加WHERE关键字
    4. 去掉多余的AND OR

动态SQL:set标签

目标

使用set标签对修改字段拼接

编写修改SQL语句存在的问题

之前我们编写修改的SQL语句时是根据用户ID更新用户所有字段的数据

<!--根据用户ID更新用户所有字段的数据, 这样存在问题,没有值的字段也会被更新为null,最好是有数据的字段更新,没有数据的字段不更新-->
<update id="updateUser" parameterType="user">
	UPDATE user SET username=#{username}, birthday=#{birthday}, sex=#{sex}, address=#{address}
	WHERE id=#{id};
</update>

更新数据的时候,有些为空则不用更新,怎么来处理呢?

set标签的作用
  1. 用在update语句中,相当于set关键字
  2. 去掉SQL代码片段中后面多余的逗号
需求

根据id修改用户部分数据

实现

声明mapper接口方法

/**
 更新用户
 */
int updateUser(User user);

配置mapper映射文件:根据id修改用户部分数据

<!--这样存在问题,没有值的字段也会被更新为null,最好是有数据的字段更新,没有数据的字段不更新-->
<update id="updateUser" parameterType="user">
    UPDATE user SET username=#{username}, birthday=#{birthday}, sex=#{sex}, address=#{address} WHERE id=#{id};
</update>

测试

@Test
public void testUpdateUser() throws IOException {
    User u = new User();
    u.setId(26);
    u.setUsername("大王");
    userMapper.updateUser(u);
}

这样存在问题,没有值的字段也会被更新为null,最好是有数据的字段更新,没有数据的字段不更新。

[外链图片转存失败(img-DT8gZiXd-1565267219538)(/)]

[外链图片转存失败(img-h37QFajs-1565267219539)(/)]

使用set标签进行判断,如果有值就更新,没有值就不更新。

<update id="updateUser" parameterType="user">
    UPDATE user
        <set>
            <if test="username != null and username != ''">
                username=#{username},
            </if>

            <if test="birthday != null">
                birthday=#{birthday},
            </if>

            <if test="sex != null and sex != ''">
                sex=#{sex},
            </if>

            <if test="address != null and address != ''">
                address=#{address}
            </if>
        </set>
        WHERE id=#{id};
</update>

set标签,可以按照条件拼接set后面的内容
[外链图片转存失败(img-CjI7kmSx-1565267219539)(/)]

小结

set标签的作用是?

相当于Set关键字,会自动去掉多余的,

foreach标签:遍历集合1

MyBatis得到数组中的数据动态拼接SQL

int row = userMapper.deleteUsers(new int[]{1, 3, 68});

delete from user where id in (1, 3, 6, 8);
使用数组来封装要删除的所有的id

目标

foreach标签:遍历集合得到基本类型的数据

需求

遍历集合拼接SQL语句,实现批量删除用户

foreach标签介绍
foreach标签的属性作用
collection要遍历的集合,2个取值:list或array
item设置变量名,代表每个遍历的元素
separator每次遍历完后添加一个分隔符
#{变量名.属性}来引用每个属性中值
实现

声明mapper接口方法

/**
 批量删除用户
 */
int deleteUsers(int[] ids);

配置mapper映射文件

<delete id="deleteUsers" parameterType="list">
    <!--DELETE FROM user WHERE id in (1, 2, 3);-->
    DELETE FROM user WHERE id in 
    <!--
        collection: 取值为array表示遍历的是数组
        open: 遍历前添加的符号
        close: 遍历最后添加的符号
        separator:分隔符
        item: 表示每个元素的变量名
    -->
    <foreach collection="array" open="(" close=");" separator="," item="temp">
        #{temp}
    </foreach>
</delete>

测试

@Test
public void testDeleteUser() throws IOException {
    int row = userMapper.deleteUsers(new int[]{24, 25, 26});
    System.out.println("row = " + row);
}

小结

foreach遍历数组格式

[外链图片转存失败(img-wF0FGOhp-1565267219540)(/)]

foreach标签:遍历集合2

目标

foreach标签:遍历集合得到自定义类型的数据

需求

使用list集合保存多个User对象,添加到数据库中

[外链图片转存失败(img-2h2qhMsK-1565267219540)(/)]

提问:一条insert语句插入多条记录的MySQL语句如何编写?

-- 同时插入多条记录
insert into user (username,birthday,sex,address) values 
(#{username},#{birthday},#{sex},#{address}),
(#{username},#{birthday},#{sex},#{address}),
(#{username},#{birthday},#{sex},#{address});

/*
(#{username},#{birthday},#{sex},#{address}) 要循环的内容
*/
实现

mapper接口批量添加用户的方法

/**
  批量添加用户
  */
int addUsers(List<User> users);

配置mapper映射文件

批量新增用户,参数类型是:list

<!--批量添加多条记录-->
<insert id="addUsers" parameterType="list">
    insert into user (username,birthday,sex,address) values
    <!--
    collection 要遍历的集合使用list
    item 设置变量名,代表每个遍历的元素
    separator 每次遍历完后添加一个分隔符
    #{变量名.属性} 来引用每个属性中值
    -->
    <foreach collection="list" item="user" separator=",">
        (#{user.username},#{user.birthday},#{user.sex},#{user.address})
    </foreach>
</insert>

测试

@Test
public void testAddUsers() throws IOException {
    List<User> users = new ArrayList<>();
    users.add(new User(null,"牛魔王", Date.valueOf("1980-01-30"),"男","火焰山"));
    users.add(new User(null,"红孩儿", Date.valueOf("2009-05-08"),"男","火云洞"));
    users.add(new User(null,"玉面狐狸", Date.valueOf("2005-11-01"),"女","狐狸洞"));

    int row = userMapper.addUsers(users);
    System.out.println("添加数据影响的行数 = " + row);
}

结果

[外链图片转存失败(img-bochmsYm-1565267219541)(/)]

小结

foreach遍历集合

<foreach collection="数组写array, List集合写list" open="在循环前要拼接的内容" close="在循环结束后要拼接的内容" separator="每次循环后要拼接的" item="循环得到的数据会放到这个变量名中">
	取出数据进行拼接

</foreach>

sql和include标签

我们发现在接口映射文件中会出现很多相同的SQL语句,每个地方都写一遍有些麻烦。我们可以把相同的SQL语句抽取出来,在需要的地方引入即可。

目标

学习sql和include标签的使用

sql和include标签的作用
  1. sql标签:定义一段SQL语句,起个名字可以重用。
  2. include标签:引入上面定义的SQL代码段。
需求实现

UserMapper.xml

<!--抽取重复的SQL并取个名字-->
<sql id="commont">
    INSERT INTO user (username,birthday,sex,address) VALUES
</sql>
<insert id="addUsers" parameterType="list">
    <!--INSERT INTO user (username,birthday,sex,address) VALUES-->
    <!--引入上面的的SQL-->
    <include refid="commont"/>

    <foreach collection="list" close=";" separator="," item="user">
        (#{user.username},#{user.birthday},#{user.sex},#{user.address})
    </foreach>
</insert>

小结

  1. sql标签的作用:抽取一段SQL语句

  2. include标签的作用:引入之前抽取的SQL语句,达到重复使用的目的

[外链图片转存失败(img-ZxfOSxQA-1565267219542)(/)]

[外链图片转存失败(img-fwuPqDnc-1565267219542)(/)]

回顾表之间的关系

目标

回顾表之间的关系

表之间的关系分类
  1. 一对一关联关系

    人和身份证的关系

    夫妻关系

    [外链图片转存失败(img-avhNeP9A-1565267219542)(/)]

  2. 一对多关联关系

    一个用户,有多个订单

    一个老师,有多个学生

    一个教室,有多个人

    [外链图片转存失败(img-46liH68m-1565267219543)(/)]

  3. 多对多关联关系

    老师与学生: 一个老师教多个学生(一对多),一个学生由多个老师教学(一对多)

    用户与角色: 一个用户有多个角色,一个角色有多个用户

    在实际项目中,多对多关系通过中间表,看成两个一对多关联关系。

    [外链图片转存失败(img-GMPsfz7N-1565267219543)(/)]

多表关联:一对一关联(重要)

目标

需求:查询1号用户和他的扩展信息,一对一关联

[外链图片转存失败(img-1NexQc2W-1565267219545)(/)]

步骤
  1. 创建数据库表
  2. 创建模块
  3. 写实体类
  4. dao接口
  5. 接口映射
  6. 测试
创建数据库表

用户基本信息表与用户扩展信息表的关系

[外链图片转存失败(img-FfcGKr3t-1565267219545)()]

[外链图片转存失败(img-yxtVEpPh-1565267219546)(/)]

用户表与用户扩展信息表

DROP TABLE IF EXISTS USER;

-- 创建用户基本表
CREATE TABLE USER (
  id INT PRIMARY KEY AUTO_INCREMENT,
  username VARCHAR(20) NOT NULL,
  birthday DATE,
  sex CHAR(1) DEFAULT '男',
  address VARCHAR(50)
);

INSERT INTO USER VALUES (NULL, '孙悟空','1980-10-24','男','花果山水帘洞');
INSERT INTO USER VALUES (NULL, '白骨精','1992-11-12','女','白虎岭白骨洞');
INSERT INTO USER VALUES (NULL, '猪八戒','1983-05-20','男','福临山云栈洞');
INSERT INTO USER VALUES (NULL, '蜘蛛精','1995-03-22','女','盤丝洞');
 
SELECT * FROM USER; 
-- 用户扩展信息表,一个用户对应一条用户扩展信息
CREATE TABLE user_info (
   id INT PRIMARY KEY,   -- 既是主键又是外键
   height DOUBLE,  -- 身高厘米
   weight DOUBLE,   -- 体重公斤
   married TINYINT,    -- 是否结婚,1为结婚,0为未婚
   FOREIGN KEY (id) REFERENCES USER(id)
);

-- 插入用户扩展信息表
INSERT INTO user_info VALUES(1,185,90,1),(2,170,60,0);
SELECT * FROM user_info;

编写关联查询sql语句,查询1号用户和他的扩展信息

SELECT * FROM USER u INNER JOIN user_info i ON u.id = i.id WHERE u.id=1;

查询结果

[外链图片转存失败(img-mrcdrii1-1565267219547)()]

创建模块

创建新的模块:mybatis_day03_01_multi_table

写实体类

类之间的关系
[外链图片转存失败(img-rDgVZ9jC-1565267219548)(/)]

User类

// 用户基本信息类
public class User {
    private Integer id; // 主键
    private String username; // 用户名
    private Date birthday; // 生日
    private String sex; // 性别
    private String address; // 地址

    private UserInfo userInfo; // 用户扩展信息类
 	// 省略getter/setter   
}

UserInfo类

// 用户扩展信息类
public class UserInfo {
    private Integer id; // 主键
    private Double height; // 身高
    private Double weight; // 体重
    private Boolean married; // 是否结婚

    // 省略getter/setter   
}

UserMapper接口

编写方法通过uid查找用户和信息

/**
  通过uid查找用户和扩展信息
 */
User findUserAndInfo(int uid);
接口映射UserMapper.xml的配置

association标签的作用:用来指定类的关联关系

association标签的属性说明
property指定另一方对象的属性名字,如:userInfo
resultMap

配置步骤:

  1. 定义User的映射UserOneUserInfoMap,包含主键和所有的属性,无论属性名与列名是否相同。

  2. 使用association定义一对一关联映射,指定:property,column,javaType,子标签UserInfo字段的映射。

代码

<resultMap id="UserOneUserInfoMap" type="user">
    <id property="id" column="id"/>
    <result property="username" column="username"/>
    <result property="birthday" column="birthday"/>
    <result property="sex" column="sex"/>
    <result property="address" column="address"/>

    <!--一对一关联关系,User类中包含一个UserInfo类,名称为userInfo-->
    <association property="userInfo" javaType="com.itheima.entity.UserInfo">
        <id property="id" column="id"/>
        <result property="height" column="height"/>
        <result property="weight" column="weight"/>
        <result property="married" column="married"/>
    </association>
</resultMap>

<select id="findUserAndInfo" parameterType="int" resultMap="UserOneUserInfoMap">
    SELECT * FROM USER u INNER JOIN user_info i ON u.id = i.id WHERE u.id=#{id};
</select>
测试
public class TestUserMapper {
    private static SqlSessionFactory factory;  //会话工厂
    private SqlSession session;  //会话
  

    // 创建工厂对象
    // 创建会话对象,自动提交事务
    @Before
    public void begin() throws IOException {
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        factory = builder.build(inputStream);
        session = factory.openSession();
    }

    // 关闭会话
    @After
    public void end() {
        session.commit();
        session.close();
    }

    @Test
    public void testFindUserAndInfo() {
        userMapper = session.getMapper(UserMapper.class);  // 得到代理对象
        User user = userMapper.findUserAndInfo(1);
        System.out.println("user = " + user);
    }
}
注意

关联查询的时候使用resultMap封装结果时,即使字段名和类的属性名相同也需要指定。

小结

  1. 一对一关联查询实体类的关系?

    [外链图片转存失败(img-wfhW7msJ-1565267219549)(/)]

  2. 在接口映射文件种使用哪个标签指定一对一关联?

[外链图片转存失败(img-8e19BnAP-1565267219549)(/)]

多表关联:一对多关联(重要)

目标

需求:查询某个用户,并且查询关联用户的多个订单信息

分析用户订单数据模型

[外链图片转存失败(img-HcY0cXv9-1565267219549)(/)]

步骤
  1. 创建数据库表
  2. 写实体类
  3. dao接口
  4. 接口映射
  5. 测试
创建数据库表
-- 创建订单表
CREATE TABLE order_form(
    oid INT PRIMARY KEY AUTO_INCREMENT ,   -- 主键
    user_id INT NOT NULL,   -- 用户id,外键
    number VARCHAR(20),   -- 订单编号
    create_time DATETIME,  -- 下单时间
    note VARCHAR(100),   -- 备注
    FOREIGN KEY(user_id) REFERENCES USER(id)   -- 外键约束,关联主表的主键
);

-- 添加订单数据
INSERT INTO order_form VALUES(NULL, 1,'10001001', NOW(), '小米9袋'),(NULL, 1,'10001002', NOW(), '9袋小米'),(NULL, 1,'10001003', NOW(), '小米9手机');
INSERT INTO order_form VALUES(NULL, 2,'10001004', NOW(), '逃生锤'),(NULL, 2,'10001005', NOW(), '安全带');

SELECT * FROM order_form;

表与表的关系

[外链图片转存失败(img-H8ewVtHh-1565267219550)(/)]

查询某个用户,并且查询关联用户的多个订单信息

select * from user u inner join order_form o on u.id = o.user_id where u.id=1;

查询结果

[外链图片转存失败(img-3KBV6UpS-1565267219550)(/)]

写实体类

[外链图片转存失败(img-LBlcZLSj-1565267219551)(/)]

修改用户订单类

  1. 添加一个用户类
  2. 生成get和set方法
package com.itheima.entity;

import java.sql.Timestamp;

/**
 订单实体类
 */
public class OrderForm {
    private Integer oid;  //主键
    private Integer userId;  //外键
    private String number;  //订单号
    private Timestamp createTime; //下单时间
    private String note;  //备注信息

	// 省略getter/setter/toString
}

用户实体类对象

  1. 建立用户到订单的一对多关联关系,在User中创建List<OrderForm> orders
package com.itheima.entity;

import java.sql.Date;
import java.util.List;

/**
 用户实体类对象 */
public class User {

    private Integer id;   //注:使用大写
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    private List<OrderForm> orders; //对应的所有订单信息

    private UserInfo userInfo;  //对应的用户信息

	// 省略getter/setter/toString
}
声明UserMapper接口方法
/*
 查询指定的用户数据
 并且关联查询用户的所有订单数据
 */
User findUserAndOrders(int uid);
UserMapper.xml映射配置文件

collection的作用:一对多的关联映射

collection的属性说明
property多方属性的名字,指:orders
javaType属性的类型
ofType每个元素的类型:OrderForm
resultMap指定订单的映射

[外链图片转存失败(img-mw2Add85-1565267219551)()]

接口映射文件配置
  1. 定义订单的映射orderMap

  2. collection:配置一对多关联关系,指定property,ofType,resultMap为orderMap

  3. 查询某个用户,并且查询关联的多个订单信息

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="com.itheima.dao.UserMapper">

	...
    
    <resultMap id="useMapMore" type="user">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="birthday" column="birthday"/>
        <result property="sex" column="sex"/>
        <result property="address" column="address"/>

        <!--一对多关系,一个User包含多个订单,使用collection-->
        <collection property="orders" javaType="List" ofType="OrderForm">
            <id property="oid" column="oid"/>
            <result property="userId" column="user_id"/>
            <result property="number" column="number"/>
            <result property="createTime" column="create_time"/>
            <result property="note" column="note"/>
        </collection>
    </resultMap>

    <!--查询指定的用户数据,并且关联查询用户的所有订单数据-->
    <select id="findUserAndOrders" parameterType="int" resultMap="useMapMore">
        SELECT * FROM USER u INNER JOIN order_form o ON u.id = o.user_id WHERE u.id=#{uid};
    </select>
</mapper>
测试
  1. 查询1号用户的所有订单
  2. 得到用户信息
  3. 输出这个用户所有的订单信息
@Test
public void testFindUserAndOrders() {
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.findUserAndOrders(1);
    System.out.println("user = " + user);

    List<OrderForm> orders = user.getOrders();
    for (OrderForm order : orders) {
        System.out.println(order);
    }
}
执行效果

[外链图片转存失败(img-hB8XX2u3-1565267219552)()]

小结

  1. 实体类

    [外链图片转存失败(img-RdfOfOAq-1565267219552)(/)]

  2. 查询的结果映射

    [外链图片转存失败(img-rOP54VaG-1565267219552)(/)]

多表关联:多对多关联

目标

多对多关联关系,可以通过中间表看成两个双向的一对多关联关系。

用户与角色多对多关系模型

一个用户对应多种角色
一种角色可以有多个用户

[外链图片转存失败(img-wIzqPkKu-1565267219552)(/)]

[外链图片转存失败(img-UjMO9bI5-1565267219553)(/)]

步骤
  1. 创建数据库表
  2. 写实体类
  3. dao接口
  4. 接口映射
  5. 测试
创建角色表和中间表
/*创建角色表*/
CREATE TABLE `role` (
role_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '角色 id(主键)',
role_name VARCHAR(32) NOT NULL COMMENT '角色名称',
role_detail VARCHAR(100) DEFAULT NULL COMMENT '角色描述'
);
-- 插入角色记录
INSERT INTO role(role_name,role_detail) VALUES('校长','全校的管理者');
INSERT INTO role(role_name,role_detail) VALUES('讲师','传道授业解惑');
INSERT INTO role(role_name,role_detail) VALUES('班主任','班级的灵魂');
INSERT INTO role(role_name,role_detail) VALUES('助教','大家的朋友');
SELECT * FROM role;

/*创建用户角色中间表*/
CREATE TABLE user_role (
user_id INT NOT NULL COMMENT '用户 id',
role_id INT NOT NULL COMMENT '角色 id',
PRIMARY KEY (user_id,role_id), -- 复合主键
FOREIGN KEY (user_id) REFERENCES `user`(id),
FOREIGN KEY (role_id) REFERENCES role(role_id)
);
INSERT INTO user_role(user_id,role_id) VALUES(1,1); -- 1 号用户对应 1 号角色
INSERT INTO user_role(user_id,role_id) VALUES(2,2);
INSERT INTO user_role(user_id,role_id) VALUES(6,2);
INSERT INTO user_role(user_id,role_id) VALUES(1,3);
INSERT INTO user_role(user_id,role_id) VALUES(2,1);
INSERT INTO user_role(user_id,role_id) VALUES(2,4);
SELECT * FROM user_role;
需求:查询 1 号用户有哪些角色
SELECT u.*, r.* FROM USER u INNER JOIN user_role ur ON u.id = ur.user_id INNER JOIN role r ON
ur.role_id = r.role_id WHERE u.id = 1;

[外链图片转存失败(img-yPb3Ik3p-1565267219553)(/)]

[外链图片转存失败(img-pXaTFlTV-1565267219553)(/)]

类之间的关系

[外链图片转存失败(img-6esugyj3-1565267219554)(/)]

Role 角色实体类,对应多个用户
public class Role {
    private Integer roleId;
    private String roleName;
    private String roleDetail;

    // 一个角色对应多个用户
    private List<User> users;
 	// 省略get/set/构造方法
}
public class User {
    private Integer id; // 主键
    private String username; // 用户名
    private Date birthday; // 生日
    private String sex; // 性别
    private String address; // 地址

    private UserInfo userInfo; // 用户扩展信息类

    private List<OrderForm> orders; // 用户对应的所有订单信息

    private List<Role> roles; // 一个用户对应多个角色
 	// 省略get/set/构造方法
}
声明 UserMapper 接口
/**
通过 uid 查找用户和他的所有角色
*/
User findRolesByUserId(int uid);

/**
通过 role_id 查找角色和他的所有用户
*/
Role findUsersByRoleId(int role_id);
配置 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="com.itheima.dao.UserMapper">
    <resultMap id="useRoleMapMore" type="user">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="birthday" column="birthday"/>
        <result property="sex" column="sex"/>
        <result property="address" column="address"/>

        <collection property="roles" javaType="list" ofType="role">
            <id property="roleId" column="role_id"/>
            <result property="roleName" column="role_name"/>
            <result property="roleDetail" column="role_detail"/>
        </collection>
    </resultMap>

    <!--多对多,通过用户id查询到一个用户里面的多个角色-->
    <select id="findRolesByUserId" parameterType="int" resultMap="useRoleMapMore">
        SELECT u.*, r.* FROM USER u INNER JOIN user_role ur ON u.id = ur.user_id INNER JOIN role r ON ur.role_id = r.role_id WHERE u.id = #{id};
    </select>


    <resultMap id="useRoleMapMore2" type="role">
        <id property="roleId" column="role_id"/>
        <result property="roleName" column="role_name"/>
        <result property="roleDetail" column="role_detail"/>
        
        <collection property="users" javaType="list" ofType="user">
            <id property="id" column="id"/>
            <result property="username" column="username"/>
            <result property="birthday" column="birthday"/>
            <result property="sex" column="sex"/>
            <result property="address" column="address"/>
        </collection>
    </resultMap>
    <!--多对多,通过用户id查询到一个用户里面的多个角色-->
    <select id="findUsersByRoleId" parameterType="int" resultMap="useRoleMapMore2">
        SELECT u.*, r.* FROM USER u INNER JOIN user_role ur ON u.id = ur.user_id INNER JOIN role r ON ur.role_id = r.role_id WHERE r.role_id = #{role_id};
    </select>
</mapper>
执行测试
@Test
public void test01() {
    Role role = userMapper.findUsersByRoleId(1);

    System.out.println("role = " + role);
    for (User user : role.getUsers()) {
        System.out.println(user);
    }
}

[外链图片转存失败(img-GCGqy5wI-1565267219555)(/)]

小结

多对多在数据库里建立第三张表中间表

在实体类中配置,双向一对多

[外链图片转存失败(img-KdNV5X37-1565267219555)(/)]

配置接口映射文件

[外链图片转存失败(img-6f4SevCq-1565267219555)(/)]

关联映射小结

一对一

类之间的关系

[外链图片转存失败(img-4sfgJhJT-1565267219555)(/)]

配置关系

[外链图片转存失败(img-O3svCOhP-1565267219556)(/)]

一对多

类之间的关系

[外链图片转存失败(img-LyI3bmJx-1565267219556)(/)]

配置关系

[外链图片转存失败(img-4OqRPm6D-1565267219556)(/)]

association:一对一的延迟加载(重要)

复制模块
  1. 复制项目为mybatis_day03_02_lazy
  2. 删除UserMapper,UserMapper.xml和TestUserMapper.java中多余的代码

刚才我们一对一使用表连接查询,直接查询出两张表中的数据

SELECT * FROM USER u INNER JOIN user_info i ON u.id = i.id WHERE u.id=1;

[外链图片转存失败(img-3a8m6rre-1565267219556)(/)]

有时候查询用户信息,不需要他的扩展信息。但后面有可能又需要用到,这时候可以通过延迟加载来实现。

目标

学习1对1的延迟加载

延迟加载介绍

延迟加载概念:也叫懒加载。指的是按需加载,在实际用到数据的时候才加载。

如:查询用户信息,不需要他的扩展信息。但后面有可能又需要用到,这时候可以通过延迟加载来实现。当需要扩展信息的时候,再发送一条SQL语句来查询扩展信息。好处是,只有在需要的时候才查询相应数据。提升查询的效率。相当于每次只查询1张表,而不是一次使用表连接查询所有的信息。

一对一关联查询使用标签:association

一对多关联查询使用标签:collection

需求
  1. 通过id查询1号用户User的基本信息

  2. 使用延迟加载的方式,关联查询出对应的用户扩展信息UserInfo

SQL语句分析
SELECT * FROM USER WHERE id=1;
SELECT * FROM user_info WHERE id=1;
配置步骤
  1. 配置association的1对1关联查询

  2. 关联属性再发送一条SQL语句去查询从表中的数据

修改UserMapper接口中的方法
/**
 持久层接口:UserMapper 
 */
public interface UserMapper {

    /**
     通过id查询1个用户
     */
    User findUserById(int id);

    /**
     通过id查询1个用户扩展信息
     */
    UserInfo findUserInfoById(int id);
}
修改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="com.itheima.dao.UserMapper">
    <resultMap id="useMapOne" type="user">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="birthday" column="birthday"/>
        <result property="sex" column="sex"/>
        <result property="address" column="address"/>

        <!--一对一关联关系,User类中包含一个UserInfo类,名称为userInfo-->
        <association property="userInfo" column="id" javaType="UserInfo" select="findUserInfoById" fetchType="lazy"/>
    </resultMap>

    <select id="findUserById" parameterType="int" resultMap="useMapOne">
        SELECT * FROM USER WHERE id=#{uid};
    </select>

    <select id="findUserInfoById" parameterType="int" resultType="UserInfo">
        SELECT * FROM user_info WHERE id=#{uid};
    </select>
</mapper>
只查询用户基本数据
  1. 通过id查询用户对象
  2. 输出用户名和性别的属性

注意:

不要使用断点调试,无法看到懒加载

不要直接输出user对象,因为会触发toString()方法,导致立即加载userInfo对象

@Test
public void testFindUserAndInfo() {
    User user = userMapper.findUserById(1);
    System.out.println(user.getUsername());
    System.out.println("----------------");

    System.out.println(user.getUserInfo());
}
测试延迟加载用户数据

查询用户之后,再查询用户扩展信息

执行效果

[外链图片转存失败(img-Zq6H976E-1565267219557)()]

小结

  1. association标签
association标签的属性说明
property一对一对象的成员变量名
column这个字段的值会作为第二个SQL语句的参数
select要延迟执行的第二条SQL
  1. 一对一延迟加载需要将一个SQL拆成两个SQL

    [外链图片转存失败(img-9AgzW8JM-1565267219557)(/)]

  2. 配置映射的关系

[外链图片转存失败(img-a2zl6u3C-1565267219557)(/)]

  1. 配置延迟加载属性

[外链图片转存失败(img-qxXNJ2b8-1565267219558)(/)]

collection:一对多的延迟加载(重要)

目标

一对多的延迟加载

需求

查询1号用户的数据,并且关联查询出这个用户所有的订单数据,使用延迟加载方式实现。

SQL语句分析
SELECT * FROM USER u INNER JOIN order_form o ON u.id = o.user_id WHERE u.id=1;
--  查询1号用户数据
SELECT * FROM USER WHERE id=1;

--  查询1号用户的订单表,使用延迟加载方式实现
SELECT * FROM order_form WHERE user_id=1;

查询结果

[外链图片转存失败(img-0kFg8M8F-1565267219558)()]

UserMapper接口

添加方法:通过userId查询所有的订单

/**
 通过id查询1个用户
 */
User findUserById(int id);

/**
通过userId查询这个用户所有的订单信息
*/
List<OrderForm> findOrdersByUserId(int userId);
修改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="com.itheima.dao.UserMapper">
    <resultMap id="useMapOne" type="user">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="birthday" column="birthday"/>
        <result property="sex" column="sex"/>
        <result property="address" column="address"/>

        <!--一对一关联关系,User类中包含一个UserInfo类,名称为userInfo-->
        <association property="userInfo" column="id" javaType="UserInfo" select="findUserInfoById" fetchType="lazy"/>

        <!--多对多,通过用户id查询到一个用户里面的多个角色-->
        <collection property="orders" column="id" javaType="List" ofType="OrderForm" select="findOrdersByUserId" fetchType="lazy"/>
    </resultMap>

    <select id="findUserById" parameterType="int" resultMap="useMapOne">
        SELECT * FROM USER WHERE id=#{uid};
    </select>

    <select id="findUserInfoById" parameterType="int" resultType="UserInfo">
        SELECT * FROM user_info WHERE id=#{uid};
    </select>

    <select id="findOrdersByUserId" parameterType="int" resultType="OrderForm">
        SELECT * FROM order_form WHERE user_id=#{uid};
    </select>
</mapper>
测试
  1. 不查询订单数据,只输出用户的名字和性别

  2. 测试延迟加载订单数据,输出所有的订单

@Test
public void testFindUserAndOrder() {
    User user = userMapper.findUserById(1);
    System.out.println(user.getUsername());
    System.out.println("----------------");

    // System.out.println(user.getOrders());
    for (OrderForm order : user.getOrders()) {
        System.out.println(order);
    }
}
效果

[外链图片转存失败(img-qI6q43Js-1565267219558)()]

配置sqlMapConfig.xml,开启MyBatis延迟加载

如果有多个延迟加载需要配置。可以在sqlMapConfig.xml核心配置文件中统一配置一次即可

开启延迟加载的settings中的lazyLoadingEnabled

<!--全局设置-->
<settings>
    <!--开启延迟加载-->
    <setting name="lazyLoadingEnabled" value="true"/>
</settings>

小结

  1. collection标签的属性

    collection标签的属性说明
    property类中多方的成员变量名
    column这个字段的值会作为延迟加载的SQL的参数
    select延迟加载的SQL

[外链图片转存失败(img-9Zvts30b-1565267219559)(/)]

[外链图片转存失败(img-VWxjxs84-1565267219559)(/)]

[外链图片转存失败(img-EnTmWhnn-1565267219560)(/)]

Mybatis一级缓存

现在我们每次执行相同的SQL语句都是去数据库中查询,存在效率问题。Mybatis提供了缓存方案可以提高重复查询数据的效率。

目标

学习Mybatis一级缓存

Mybatis缓存介绍

Mybatis 框架提供了缓存策略,通过缓存策略可以减少查询数据库的次数,提升系统性能。在 Mybatis 框架中
缓存分为一级缓存和二级缓存。

[外链图片转存失败(img-cixcUkgQ-1565267219560)(/)]

一级缓存概述

一级缓存是 sqlSession 范围的缓存,只能在同一个 sqlSession 内部有效。它本身已经存在,一级缓存可以直
接使用,不需要手动处理。

一级缓存测试
public class TestCache {
    // 测试一级缓存
    @Test
    public void testPrimaryCache() throws IOException {
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory factory = builder.build(inputStream);
        SqlSession session = factory.openSession();
        UserMapper userMapper = session.getMapper(UserMapper.class);  // 得到代理对象

        /*
         * 1.先根据id=1去一级缓存找
         * 2.没有找到,再查询数据库
         * 3.查询到结果后,放入一级缓存
         */
        User user1 = userMapper.findUserById(1);

        /*
         * 1.先根据id=1去一级缓存找
         * 2.找到,就直接返回,不查询数据库
         */
        User user2 = userMapper.findUserById(1);

        session.commit(); // 提交事务,清空一级缓存
        session.close();
    }
}

[外链图片转存失败(img-zK44dPhj-1565267219560)(/)]

一级缓存分析

第一次查询数据时,会将查询的数据放入一级缓存中。后面的相同查询直接从缓存中获取。

一级缓存是 SqlSession 范围缓存。当调用 SqlSession 的修改、添加、删除、提交、关闭等方法时,一级缓存会被清空。

[外链图片转存失败(img-WIAICcI3-1565267219560)(/)]

清空一级缓存
public class TestCache {
    // 测试一级缓存
    @Test
    public void testPrimaryCache() throws IOException {
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory factory = builder.build(inputStream);
        SqlSession session = factory.openSession();
        UserMapper userMapper = session.getMapper(UserMapper.class);  // 得到代理对象

        /*
         * 1.先根据id=1去一级缓存找
         * 2.没有找到,再查询数据库
         * 3.查询到结果后,放入一级缓存
         */
        User user1 = userMapper.findUserById(1);

        // 调用clearCache()方法,清空一级缓存的内容
        session.clearCache();
        // 当调用 SqlSession 的修改、添加、删除、提交、关闭等方法时,一级缓存会被清空。
        // userMapper.deleteUserById(5);

        /*
         * 1.先根据id=1去一级缓存找
         * 2.找到,就直接返回,不查询数据库
         */
        User user2 = userMapper.findUserById(1);

        session.commit(); // 提交事务,清空一级缓存
        session.close();
    }
}

[外链图片转存失败(img-3eajT9sd-1565267219561)(/)]

小结

  1. 一级缓存的范围?

    同一个SqlSession有效

  2. 一级缓存何时失效?

    执行了增删改,提交事务,clearCache方法。清空一级缓存

Mybatis二级缓存

目标

学习Mybatis二级缓存

[外链图片转存失败(img-lNlY4jhQ-1565267219561)(/)]

二级缓存概述

二级缓存是 mapper 映射级别缓存,作用范围跨越SqlSession,即可以在多个 SqlSession 之间共享二级缓存
数据。

二级缓存关键点
  1. 实体类需要实现Serializable接口
  2. 至少要准备2个SqlSession,再进行测试。
配置二级缓存

在 sqlMapConfig.xml 配置开启二级缓存,打开mybatis官方文档,找到settings配置:

<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>
修改实体类实现Serializable接口
public class User implements Serializable {
    private Integer id; // 主键
    private String username; // 用户名
    private Date birthday; // 生日
    private String sex; // 性别
    private String address; // 地址
    // 省略其他
}
在 UserMapper.xml 开启二级缓存使用

二级缓存还需要在具体的 mapper 映射文件中明确开启,这样做的原因是缓存数据要消耗资源,只有在需要使
用的时候开启,可以避免资源的过度消耗。

<mapper namespace="com.itheima.dao.UserMapper">
    <!--开启二级缓存,当前Mapper里的所有查询的数据都会放入二级缓存中-->
    <cache/>
    
    <select id="findUserById" parameterType="int" resultMap="useMapOne">
        SELECT * FROM USER WHERE id=#{uid};
    </select>
</mapper>
测试
// 测试二级缓存
@Test
public void testSecondCache() throws IOException {
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
    SqlSessionFactory factory = builder.build(inputStream);


    SqlSession session1 = factory.openSession();
    UserMapper userMapper1 = session1.getMapper(UserMapper.class);  // 得到代理对象
    User user1 = userMapper1.findUserById(1);
    session1.close(); // 需要先关掉第一个sqlsession

    SqlSession session2 = factory.openSession();
    UserMapper userMapper2 = session2.getMapper(UserMapper.class);  // 得到代理对象
    User user2 = userMapper2.findUserById(1);

    session2.close();
}

[外链图片转存失败(img-STahTVz1-1565267219562)(/)]

小结

二级缓存范围,跨域多个SqlSession,只要是同一个Mapper就可以

二级缓存使用步骤:

  1. 类要实现Serializable接口

  2. 在Mybatis核心配置文件中添加

    <settings>
    	<setting name="cacheEnabled" value="true"/>
    </settings>
    
  3. 在接口的映射文件中配置

    <cache/>
    
  4. 测试,需要准备2个SqlSession

    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    SqlSessionFactory factory = builder.build(TestMybatis.class.getResourceAsStream("/sqlMapConfig.xml"));
    SqlSession sqlSession1 = factory.openSession();
    
    // 第一个SqlSession
    UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
    User user1 = mapper1.findUserById(1);
    sqlSession1.close(); // 需要关闭sqlSqlsession,sqlSession里面的数据才会保存到二级缓存中
    
    // 第二个SqlSession
    SqlSession sqlSession2 = factory.openSession();
    UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
    User user2 = mapper2.findUserById(1);
    sqlSession2.close();
    

注解实现:一对一关联查询

目标

使用注解实现一对一关联查询

概述

MyBatis框架中除了使用XML配置文件实现关系映射之外,也可以使用注解实现复杂的关系映射(一对一关联查询,一对多关联查询)。

复杂关系映射注解介绍
注解描述对应xml配置标签
@One用于一对一关联映射association
@Many用于一对多的关联映射collection
操作步骤
  1. 复制项目为mybatis_day03_03_ann_multi_table

  2. 删除UserMapper.xml文件,使用注解

  3. 修改sqlMapConfig.xml

    保持延迟加载的配置

sqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--定义类的别名-->
    <typeAliases>
        <package name="com.itheia.entity"/>
    </typeAliases>
    <!--环境配置-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <!--连接池的配置-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <!--实体类映射文件-->
    <mappers>
        <mapper class="com.itheima.dao.UserMapper"/>
    </mappers>
</configuration>
UserMapper接口
package com.itheima.dao;

import com.itheima.entity.OrderForm;
import com.itheima.entity.User;
import com.itheima.entity.UserInfo;

import java.util.List;

/**
 持久层接口:UserMapper */
public interface UserMapper {

    /**
     通过id查询1个用户
     */
    User findUserById(int id);

    /**
     通过id查询1个用户扩展信息,1对1
     */
    UserInfo findUserInfoById(int id);

    /**
     通过userId查询这个用户所有的订单信息,1对多
     */
    List<OrderForm> findOrdersByUserId(int userId);
}
需求

查询1个用户数据,并且采用延迟加载关联查询出用户扩展数据

在UserMapper接口中增加查询方法
  1. 编写方法:通过id查询用户扩展信息

    1. 方法名:findUserInfoById
    2. 使用@Select注解编写SQL语句
  2. 编写方法:通过id查询1个用户

    1. 方法名:findUserById
    2. @Select编写查询
    3. @Results配置1对1关联映射
代码
public interface UserMapper {
    // 通过uid查找用户和扩展信息
    @Select("SELECT * FROM user WHERE id=#{uid}")
    @Results({
            @Result(property = "id", column = "id", id = true), // 主键映射
            @Result(property = "userInfo", column = "id", javaType = UserInfo.class,
                    // select表示要查询的方法名 fetchType指定为LAZY表示延迟加载
                    one = @One(select = "findUserInfoById", fetchType = FetchType.LAZY))
    })
    User findUserById(int uid);

    // 通过uid查找用户和扩展信息
    @Select("SELECT * FROM user_info WHERE id=#{uid}")
    UserInfo findUserInfoById(int uid);
}
测试
  1. 只查询用户名和性别
  2. 同时查询用户扩展信息
@Test
public void testFindUserAndUserInfo() {
    User user = userMapper.findUserById(1);
    System.out.println(user.getUsername());
    System.out.println("----------------");
    System.out.println(user);

    System.out.println(user.getUserInfo());
}

小结

注解作用
@Select表示查询SQL语句
@Results对查询结果进行映射
@Resultcolumn:表中字段的名字
property:类中的成员变量
one :表示一对一的关系
@Oneselect:延迟要执行的SQL语句
fetchType: LAZY, 延迟加载

[外链图片转存失败(img-A6STX2Fb-1565267219563)(/)]

注解实现:一对多关联查询

目标

  1. 查询1号用户数据
  2. 关联查询出1号用户全部订单数据
  3. 采用延迟加载方式实现
UserMapper接口
  1. 通过user_id查询当前用户订单的方法

    1. 编写findOrdersByUserId方法
    2. 使用@Select注解
  2. 修改findUserById()方法,增加1对多延迟加载配置

代码

package com.itheima.dao;


import com.itheima.entity.OrderForm;
import com.itheima.entity.User;
import com.itheima.entity.UserInfo;
import org.apache.ibatis.annotations.*;
import org.apache.ibatis.mapping.FetchType;

import java.util.List;

/**
 持久层接口:UserMapper */
public interface UserMapper {

    /**
     通过id查询1个用户
     */
    @Select("select * from user where id=#{id}")
    @Results({
            @Result(column = "id", property = "id", id = true),  //主键映射
            // 一对一的配置
            @Result(column = "id", property = "userInfo",
                    //select表示要查询的方法名 fetchType指定为LAZY表示延迟加载
                    one = @One(select = "findUserInfoById", fetchType = FetchType.LAZY)),
            //一对多的配置
            @Result(column = "id", property = "orders",
                    many = @Many(select = "findOrdersByUserId", fetchType = FetchType.LAZY))

    })
    User findUserById(int id);

    /**
     通过id查询1个用户扩展信息,1对1
     */
    @Select("select * from user_info where id=#{id}")
    UserInfo findUserInfoById(int id);

    /**
     通过userId查询这个用户所有的订单信息,1对多
     */
    @Select("select * from order_form where user_id=#{id}")
    @Results({
            @Result(column = "user_id", property = "userId")
    })
    List<OrderForm> findOrdersByUserId(int userId);
}
测试
  1. 只查用户信息
  2. 查询订单信息
@Test
public void testFindUserById() {
    User user = userMapper.findUserById(1);
    System.out.println("用户名:" + user.getUsername());
    //延迟加载多方
    List<OrderForm> orders = user.getOrders();
    for (OrderForm order : orders) {
        System.out.println(order);
    }
    //System.out.println(user);  //toString()不会延迟加载,默认查询所有的信息
    //得到扩展信息
    //UserInfo userInfo = user.getUserInfo();
   // System.out.println("身高:" + userInfo.getHeight());
}

小结

@Result注解属性说明
column字段名,这个字段的值会作为下一个SQL语句的参数
property类中的成员变量
many一对多的配置
@Many注解属性说明
select要执行的SQL
fetchTypeLAZY懒加载

[外链图片转存失败(img-NKpcUFHv-1565267219563)(/)]

学习总结

  1. 掌握 MyBatis 多表关联查询

    一对一:association

    一对多:collection

    多对多: 就是两个一对多

  2. MyBatis 的延迟加载

    [外链图片转存失败(img-e4bKX94C-1565267219564)(/)]

  3. 注解实现多表关联查询

    [外链图片转存失败(img-VQKCk0fQ-1565267219564)(/)]

  4. 掌握二级缓存的开启配置

    1. 核心配置文件

      <settings>
          <setting name="cacheEnabled" value="true"/>
      </settings>
      
    2. 接口映射文件配置

      <!--这个Mapper里面所有的查询出的对象都会放到二级缓存中-->
      <cache/>
      
      //select表示要查询的方法名 fetchType指定为LAZY表示延迟加载
                one = @One(select = "findUserInfoById", fetchType = FetchType.LAZY)),
        //一对多的配置
        @Result(column = "id", property = "orders",
                many = @Many(select = "findOrdersByUserId", fetchType = FetchType.LAZY))
      

    })
    User findUserById(int id);

    /**
    通过id查询1个用户扩展信息,1对1
    */
    @Select(“select * from user_info where id=#{id}”)
    UserInfo findUserInfoById(int id);

    /**
    通过userId查询这个用户所有的订单信息,1对多
    */
    @Select(“select * from order_form where user_id=#{id}”)
    @Results({
    @Result(column = “user_id”, property = “userId”)
    })
    List findOrdersByUserId(int userId);
    }


 

#### 测试

1. 只查用户信息
2. 查询订单信息

```java
@Test
public void testFindUserById() {
    User user = userMapper.findUserById(1);
    System.out.println("用户名:" + user.getUsername());
    //延迟加载多方
    List<OrderForm> orders = user.getOrders();
    for (OrderForm order : orders) {
        System.out.println(order);
    }
    //System.out.println(user);  //toString()不会延迟加载,默认查询所有的信息
    //得到扩展信息
    //UserInfo userInfo = user.getUserInfo();
   // System.out.println("身高:" + userInfo.getHeight());
}

小结

@Result注解属性说明
column字段名,这个字段的值会作为下一个SQL语句的参数
property类中的成员变量
many一对多的配置
@Many注解属性说明
select要执行的SQL
fetchTypeLAZY懒加载
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值