Mybatis 任务二:配置文件深入

Mybatis 任务二:配置文件深入

课程任务主要内容:

* Mybatis 高级查询

* 映射配置文件深入

* 核心配置文件深入

* Mybatis 多表查询

* Mybatis 嵌套查询

Mybatis 高级查询

1.1 ResultMap 属性

建立对象关系映射

  • resultType:如果实体的属性名与表中字段名一致(前提),将查询结果自动封装到实体类中
  • ResultMap:如果实体的属性名与表中字段名不一致,可以使用ResutlMap 实现手动封装到实体类中
  1. 编写UserMapper 接口
public interface UserMapper {

	public List<User> findAllResultMap();

}
  1. 编写UserMapper.xml
<!--id:唯一标识此resultMap
        type:封装后实体的类型,要使用全类名,当在mybatis核心配置类中起了别名之后,可以使用别名-->
    <resultMap id="userResultMap" type="com.myLagou.entity.User">
        <!--手动配置映射关系-->
        <!--property用来配置实体类中的属性名,column用来配置数据库表中的字段名  -->
        <!--id:用来配置主键-->
        <id property="id" column="id"></id>
        <!--配置其他属性-->
        <result property="usernameabc" column="username"></result>
        <result property="birthdayabc" column="birthday"></result>
        <result property="sexabc" column="sex"></result>
        <result property="addressabc" column="address"></result>
        补充:如果有查询结果有 字段与属性是对应的,可以省略手动封装 【了解】
    </resultMap>

    <!--查询所有用户-->
    <!--ResultMap:如果实体的属性名与表中字段名不一致,可以使用ResultMap 实现手动封装到实体类中-->
    <select id="findAllResultMap" resultMap="userResultMap">
        select * from user
    </select>

3)代码测试

//测试resultMap
    @Test
    public void test2() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //获取UserMapper的代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        //当前返回的其实就是基于UserMapper所产生的代理对象,底层就是JDK的动态代理, 实际类型就是一个Proxy

        List<User> userList = mapper.findAllResultMap();

        sqlSession.close();

        for (User user : userList) {
            System.out.println(user);
        }
    }

1.2 多条件查询(三种)

需求

根据id 和username 查询 user 表

User实体类

package com.myLagou.entity;

import java.util.Date;

/**
 * @author zhy
 * @create 2022-08-07 14:11
 */
public class User {
    private Integer id;
    private String usernameabc;
    private Date birthdayabc;
    private String sexabc;
    private String addressabc;



    public User() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsernameabc() {
        return usernameabc;
    }

    public void setUsernameabc(String usernameabc) {
        this.usernameabc = usernameabc;
    }

    public Date getBirthdayabc() {
        return birthdayabc;
    }

    public void setBirthdayabc(Date birthdayabc) {
        this.birthdayabc = birthdayabc;
    }

    public String getSexabc() {
        return sexabc;
    }

    public void setSexabc(String sexabc) {
        this.sexabc = sexabc;
    }

    public String getAddressabc() {
        return addressabc;
    }

    public void setAddressabc(String addressabc) {
        this.addressabc = addressabc;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", usernameabc='" + usernameabc + '\'' +
                ", birthdayabc=" + birthdayabc +
                ", sexabc='" + sexabc + '\'' +
                ", addressabc='" + addressabc + '\'' +
                '}';
    }
}

1)方式一

使用 #{arg0}-#{argn} 或者 #{param1}-#{paramn} 获取参数

UserMapper 接口

public interface UserMapper {

	public List<User> findByIdAndUsername1(Integer id, String username);

}

UserMapper.xml

   <!--多条件查询方式1-->
    <!--当有多个参数时,占位符#{}与参数之间的匹配关系为:#{arge0}对应第一个参数,#{arge1}对应第二个参数,依此类推-->
<!--    <select id="findByIdAndUsername1" resultMap="userResultMap">-->
<!--        select * from user where id = #{arg0} and username = #{arg1}-->
<!--    </select>-->

    <!--当有多个参数时,占位符#{}与参数之间的匹配关系为:#{param1}对应第一个参数,#{param2}对应第二个参数,依此类推-->
    <select id="findByIdAndUsername1" resultMap="userResultMap">
        select * from user where id = #{param1} and username = #{param2}
    </select>

测试

//测试多条件查询方式1
    @Test
    public void test3() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //获取UserMapper的代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        //当前返回的其实就是基于UserMapper所产生的代理对象,底层就是JDK的动态代理, 实际类型就是一个Proxy

        List<User> username1 = mapper.findByIdAndUsername1(1, "子慕");

        sqlSession.close();

        for (User user : username1) {
            System.out.println(user);
        }
    }

2)方式二

使用注解,引入 @Param() 注解获取参数

UserMapper 接口

 /*多条件查询方式2*/
    public List<User> findByIdAndUsername2(@Param("id") Integer id, @Param("username")String username);

UserMapper.xml

<!--多条件查询方式2
        在接口中的方法参数位置使用@Param("xxx"),
        在select语句中的占位符#{}中使用param声明的xxx来匹配参数
    -->
    <select id="findByIdAndUsername2" resultMap="userResultMap">
        select * from user where id = #{id} and username = #{username}
    </select>

测试

 //测试多条件查询方式2
    @Test
    public void test4() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //获取UserMapper的代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        //当前返回的其实就是基于UserMapper所产生的代理对象,底层就是JDK的动态代理, 实际类型就是一个Proxy

        List<User> username1 = mapper.findByIdAndUsername2(1, "子慕");

        sqlSession.close();

        for (User user : username1) {
            System.out.println(user);
        }
    }

3)方式三(推荐)

使用pojo 对象传递参数

UserMapper 接口

/*多条件查询方式3*/
    public List<User> findByIdAndUsername3(User user);

UserMapper.xml

<!--多条件查询方式3
        使用整个实体对象来做参数
        占位符#{}中所写的参数要与实体类中的属性名一致
    -->
    <select id="findByIdAndUsername3" resultMap="userResultMap" parameterType="User">
        select * from user where id = #{id} and username = #{usernameabc}
    </select>

测试

  //测试多条件查询方式3
    @Test
    public void test5() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //获取UserMapper的代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        //当前返回的其实就是基于UserMapper所产生的代理对象,底层就是JDK的动态代理, 实际类型就是一个Proxy

        User user1 = new User();
        user1.setId(1);
        user1.setUsernameabc("子慕");
        List<User> username1 = mapper.findByIdAndUsername3(user1);

        sqlSession.close();

        for (User user : username1) {
            System.out.println(user);
        }
    }

1.3 模糊查询

需求

根据username 模糊查询user 表

1)方式一

使用#{}占位符

UserMapper 接口

/*模糊查询方式1*/
    public List<User> findByUsername1(String username);

UserMapper.xml

<!--模糊查询方式1-->
    <!--当所传递参数parameterType为基本数据类型或者是String类型,且传递参数只有一个时,那么#{}中的参数随便写,但是一般遵循见名知意原则-->
    <select id="findByUsername1" resultMap="userResultMap" parameterType="String">
        select * from user where username like #{username}
    </select>
    <!--#{}在进行实际参数的转换的时候,替换真正的参数值时会自动的帮我们添加上单引号-->

测试

   //测试多模糊查询方式1
    @Test
    public void test6() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //获取UserMapper的代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        //findByUsername1("子慕")如果只写成这样,那么就相对于等值查询,只会查出username=子慕的数据,而不是模糊查询
        //要想是模糊查询,就需要添加上通配符%,如下所示:
        List<User> username1 = mapper.findByUsername1("%子慕%");

        sqlSession.close();
        for (User user : username1) {
            System.out.println(user);
        }
    }

2)方式二

使用${},进行sql原样拼接

UserMapper 接口

public interface UserMapper {
    /*模糊查询方式2*/
    public List<User> findByUsername2(String username);
}

UserMapper.xml

<!--模糊查询方式2  使用${}占位符-->
    <!--当所传递参数parameterType为基本数据类型或者是String类型,且传递参数只有一个时,那么${}中的值只能写value-->
    <select id="findByUsername2" resultMap="userResultMap" parameterType="String">
        select * from user where username like '${value }'
    </select>
    <!--${}实际上是进行sql原样拼接,不会替换真正的参数值时不会自动的加上单引号,所以需要我们自己手动添加-->

测试

//测试多模糊查询方式2
    @Test
    public void test7() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //获取UserMapper的代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        
        List<User> username1 = mapper.findByUsername2("%子慕%");

        sqlSession.close();
        for (User user : username1) {
            System.out.println(user);
        }
    }

3)方式三

UserMapper 接口

public interface UserMapper {

public List<User> findByUsername3(String username);

}

UserMapper.xml

<mapper namespace="com.lagou.mapper.UserMapper">

<!--不推荐使用,因为会出现sql 注入问题-->

<select id="findByUsername3" parameterType="string" resultType="user">

select * from user where username like '%${value}%'

</select>

</mapper>

测试

//测试 模糊查询方式3
    @Test
    public void testFindByUsername3() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //获取UserMapper的代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        
        //在sql语句中添加了通配符后再下面的代码中就不需要了,如下所示:
        List<User> username1 = mapper.findByUsername3("子慕");

        sqlSession.close();
        for (User user : username1) {
            System.out.println(user);
        }
    }

4)方式四(推荐)

UserMapper 接口

public interface UserMapper {

public List<User> findByUsername4(String username);

}

UserMapper.xml

<!--/*模糊查询方式4*/-->
    <select id="findByUsername4" resultMap="userResultMap" parameterType="String">
        <!--推荐使用,concat() 字符串拼接函数
        注意:在 Oracle 中,concat() 函数只能传递二次参数,我们解决方案是嵌套拼接-->
        select * from user where username like concat(concat('%',#{username}),'%');
    </select>

测试

//测试 模糊查询方式4
    @Test
    public void testFindByUsername4() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //获取UserMapper的代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        //在sql语句中添加了通配符后再下面的代码中就不需要了,如下所示:
        List<User> username1 = mapper.findByUsername4("子慕");

        sqlSession.close();
        for (User user : username1) {
            System.out.println(user);
        }
    }

5)${} #{} 区别【面试题】

#{} :表示一个占位符号

  • 通过#{} 可以实现 preparedStatement(预编译对象) 向占位符中设置值,自动进行 java 类型(实体中属性的类型)和 jdbc 类型(表中字段类型)转换,#{}可以有效防止sql 注入

  • #{} 可以接收简单类型值或 pojo 属性值。

  • 如果 parameterType 传输单个简单类型值, #{} 括号中可以是value 或其它名称。

${} :表示拼接sql

  • 通过${} 可以将 parameterType 传入的内容拼接在 sql 中且不进行jdbc 类型转换,会出现 sql 注入问题

  • ${} 可以接收简单类型值或 pojo 属性值。

  • 如果 parameterType 传输单个简单类型值, ${} 括号中只能是value

    • 补充:TextSqlNode.java 源码可以证明

Mybatis 映射文件深入

2.1 返回主键

应用场景

我们很多时候有这种需求,向数据库插入一条记录后,希望能立即拿到这条记录在数据库中的主键值。

2.1.1 useGeneratedKeys

UserMapper接口

public interface UserMapper {

    // 返回主键
    public void save(User user);

}

UserMapper.xml

<!--添加用户并且获取返回主键的值,方式1-->
    <!--
    useGeneratedKeys:声明返回主键,值为true表示返回,值为false表示不返回
    keyProperty:把返回的主键的值封装到实体中的指定的属性上
    -->
    <insert id="saveUser" parameterType="User" useGeneratedKeys="true" keyProperty="id">
        insert into user(username, birthday, sex, address) values(#{username}, #{birthday}, #{sex}, #{address})
    </insert>

注意:只适用于主键自增的数据库,mysql 和sqlserver 支持,oracle 不行

测试

 //测试添加用户并且获取返回主键的值,方式1
    @Test
    public void test8() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //获取UserMapper的代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        User user1 = new User();
        user1.setUsername("杨洋");
        user1.setBirthday(new Date());
        user1.setSex("男");
        user1.setAddress("我心里");
        
        System.out.println(user1);//user1的id为null
        
        mapper.saveUser(user1);
        sqlSession.commit();
        sqlSession.close();

        System.out.println(user1);//user1的id有值
    }

2.1.2 selectKey

UserMapper接口

public interface UserMapper {

    // 返回主键
    public void save(User user);

}

UserMapper.xml

  <!--添加用户并且获取返回主键的值,方式1-->
    <!--
    selectKey:获取返回指定的主键值,适用范围广,支持所有类型数据库
    order="AFTER":表示selectKey中的语句在执行sql语句之后执行(在MySQL、SQL Server数据库中是使用after,
                    但是在oracle数据库中使用的是before
    keyColumn:指定主键对应数据库表中对应的列名
    keyProperty:指定主键封装到实体的 指定 属性中
    resultType:指定主键类型
    -->
    <insert id="saveUser2" parameterType="user">
        <selectKey order="AFTER" keyColumn="id" keyProperty="id" resultType="int">
            SELECT LAST_INSERT_ID();
        </selectKey>
        insert into user(username, birthday, sex, address) values(#{username}, #{birthday}, #{sex}, #{address})

    </insert>

测试

//测试添加用户并且获取返回主键的值,方式2
    @Test
    public void test9() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //获取UserMapper的代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        User user1 = new User();
        user1.setUsername("于途");
        user1.setBirthday(new Date());
        user1.setSex("男");
        user1.setAddress("我心里");

        System.out.println(user1);//user1的id为null

        mapper.saveUser2(user1);
        sqlSession.commit();
        sqlSession.close();

        System.out.println(user1);//user1的id有值
    }

2.2 动态 SQL

应用场景

当我们要根据不同的条件,来执行不同的 sql 语句的时候,需要用到动态sql。

2.2.1 动态 SQL 之<if>标签

需求

根据id 和username 查询,但是不确定两个都有值。

a)UserMapper 接口

public List<User> findByIdAndUsernameIf(User user);

b)UserMapper.xml 映射

    <!--动态sql的if标签:多条件查询-->
    <!-- /*test里面写的就是表达式*/
        /*#{}中的值也需要跟传递过来的parameterType实体类中的属性名一样*/-->
<!--    <select id="findByIdAndUsernameIf" parameterType="user" resultMap="userResultMap">-->
<!--        select * from user where 1=1-->
<!--            <if test="id != null">-->
<!--                and id = #{id}-->
<!--            </if>-->
<!--            <if test="username != null">-->
<!--                and username = #{username}-->
<!--            </if>-->
<!--    </select>-->
    <!--
        根据上述select查询语句中的where 1=1 可以直接改写为如下代码
        下述代码中的<where>标签就相对于where 1=1,但是如果没有条件的话,不会拼接上where关键字,只有有条件的时候才会自动拼接上
    -->
    <select id="findByIdAndUsernameIf" parameterType="user" resultMap="userResultMap">
        select * from user
        <where>
            <if test="id != null">
                and id = #{id}
            </if>
            <if test="username != null">
                and username = #{username}
            </if>
        </where>
    </select>

c)测试代码

//测试 动态sql的if标签:多条件查询
    @Test
    public void tes10() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //获取UserMapper的代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        User user1 = new User();
        //user1.setUsername("杨洋");//只有username时可以查询出
//        user1.setId(1);//只有id值的时候也可以查出
        user1.setId(1);
        user1.setUsername("子慕");//id和username都有的时候也可以查出
        List<User> userList = mapper.findByIdAndUsernameIf(user1);

        sqlSession.close();

        for (User user : userList) {
            System.out.println(user);
        }

    }

2.2.2 动态 SQL 之标签

需求

如果有id 只使用id 做查询,没有 id 的话看是否有username,有username 就根据username 做查询,如果都没有,就不带条件。

a)UserMapper 接口

public List<User> findByIdAndUsernameChoose(User user);

b)UserMapper.xml 映射


c)测试代码


2.2.3 动态 SQL <set>标签

需求

动态更新user 表数据,如果该属性有值就更新,没有值不做处理。

a)UserMapper 接口

//动态sql的set标签:动态更新
    public void updateIf(User user);

b)UserMapper.xml 映射

 <!--//动态sql的set标签:动态更新-->
    <update id="updateIf" parameterType="user">
        update user
        <!--<set>标签:在更新的时候,会自动的添加set关键字,还会去掉最后一个条件的逗号,
        代替了之前直接硬编码的update语句

        -->
        <set>
            <if test="username != null">
                username = #{username},
            </if>
            <if test="birthday != null">
                birthday = #{birthday},
            </if>
            <if test="sex != null">
                sex = #{sex},
            </if>
            <if test="address != null">
                address = #{address},
            </if>
        </set>
        where id = #{id}
    </update>

c)测试代码

//测试 动态sql的set标签:动态更新
    @Test
    public void tesUpdateIf() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //获取UserMapper的代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        User user1 = new User();

        user1.setId(10);
        user1.setUsername("杨洋内娱第一帅哥");
        user1.setAddress("住在我心坎里!!!");
        mapper.updateIf(user1);

        sqlSession.commit();
        sqlSession.close();
        System.out.println(user1);
    }

2.2.4 动态 SQL 之<foreach>标签

foreach 主要是用来做数据的循环遍历

例如:select * from user where id in (1,2,3) 在这样的语句中,传入的参数部分必须依靠foreach 遍历才能实现。

  • <foreach>标签用于遍历集合,它的属性:
    • collection:代表要遍历的集合元素
    • open:代表语句的开始部分
    • close:代表结束部分
    • item:代表遍历集合的每个元素,生成的变量名
    • sperator:代表分隔符
a)当传递参数的容器为集合类型

UserMapper 接口


/*动态sql的foreach标签:多值查询,*/
    public List<User> findByList(List<Integer> ids);

UserMaper.xml 映射

<!--动态sql的foreach标签:多值查询,根据多个id值查询用户,多个id组成一个集合-->
    <select id="findByList" parameterType="list" resultType="user">
        <!--原始的sql语句:select * from user id in(1,2,3)-->

        <!--使用动态sql语句如下-->
        select * from user
        <where>
            <!--collection:代表要遍历的集合元素,当要遍历的集合为普通类型(如基本数据类型或者String)
                            collection 属性值就可以写:collection 或者 list
                open:代表foreach中语句开始的部分
                close:代表foreach中语句结束的部分
                item:代表遍历集合中的每个元素生成的变量名
                separator:分隔符,将item中的变量进行分隔
                #{}中所写的值要与item所写的值一致
                          -->
            <foreach collection="collection" open="id in (" close=")" item="id" separator=",">
                #{id}
            </foreach>
        </where>
    </select>

测试代码

//测试 动态sql的foreach标签:多值查询,根据多个id值查询用户,多个id组成一个集合
    @Test
    public void tesFindByList() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //获取UserMapper的代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(10);
        list.add(11);

        List<User> userByList = mapper.findByList(list);
        for (User user : userByList) {
            System.out.println(user);
        }

    }
b)当传递参数的容器为数组类型

UserMapper 接口

/*动态sql的foreach标签:多值查询,多值组成一个数组时*/
    public List<User> findByArray(Integer[] ids);

UserMaper.xml 映射

 <!--动态sql的foreach标签:多值查询,根据多个id值查询用户,多个id组成一个数组-->
    <select id="findByArray" parameterType="int" resultType="user">
        select * from user
        <where>
            <!--collection:代表要遍历的数组元素,collection 属性值就可以写:array
                    open:代表foreach中语句开始的部分
                    close:代表foreach中语句结束的部分
                    item:代表遍历集合中的每个元素生成的变量名
                    separator:分隔符,将item中的变量进行分隔
                    #{}中所写的值要与item所写的值一致
                              -->
            <foreach collection="array" open="id in (" close=")" item="id" separator=",">
                #{id}
            </foreach>
        </where>
    </select>

测试代码

  //测试 动态sql的foreach标签:多值查询,根据多个id值查询用户,多个id组成一个数组
    @Test
    public void tesFindByArray() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //获取UserMapper的代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        Integer[] ids = {1,10,11};

        List<User> userArray = mapper.findByArray(ids);
        for (User user : userArray) {
            System.out.println(user);
        }
    }
c)pojo

QueryVo

public class QueryVo {
	private List<Integer> ids; 
}

核心配置文件

<!--设置实体别名-->
<typeAliases>

    <typeAlias type="com.lagou.domain.User" alias="user"></typeAlias>

    <typeAlias type="com.lagou.domain.QueryVo" alias="queryVo"></typeAlias>

</typeAliases>

UserMapper 接口

public List<User> findByPojo(QueryVo queryVo);

UserMaper.xml 映射

<!--
如果查询条件为复杂类型pojo 对象,collection 属性值为:集合或数组的属性名
-->

<select id="findByPojo" parameterType="queryVo" resultType="user">

    SELECT * FROM `user`
    <where>
        <foreach collection="ids" open="id in(" close=")" item="id" separator=",">
        #{id}
        </foreach>
    </where>
</select>

测试代码

// foreach 标签 pojo
@Test
public void testFindByPojo() throws Exception {

    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

    List<Integer> ids = new ArrayList<>();
    ids.add(46);
    ids.add(48);
    ids.add(51);

    QueryVo queryVo = new QueryVo();

    queryVo.setIds(ids);

    List<User> list = userMapper.findByPojo(queryVo);
    System.out.println(list);
}

2.3 SQL 片段

应用场景

映射文件中可将重复的 sql 提取出来,使用时用 include 引用即可,最终达到 sql 重用的目的

<!--在于CRUD操作的同级下,使用<sql>标签进行重复代码的抽取-->
<!--抽取重复的sql代码-->
    <sql id="selectUser">
        select * from user
    </sql>
<!--动态sql的foreach标签:多值查询,根据多个id值查询用户,多个id组成一个集合-->
    <select id="findByList" parameterType="list" resultType="User">
       
        <!--引入被抽取的sql语句-->
        <include refid="selectUser"></include>
        <where>
            
            <foreach collection="collection" open="id in (" close=")" item="id" separator=",">
                #{id}
            </foreach>
        </where>
    </select>

2.4 知识小结

MyBatis 映射文件配置

  • <select>:查询

  • <insert>:插入

  • <update>:修改

  • <delete>:删除

  • <selectKey>:返回主键

  • <where>:where 条件

  • <if>:if 判断

  • <foreach>:for 循环

  • <set>:set 设置

  • <sql>:sql 片段抽取

Mybatis 核心配置文件深入

3.1 plugins 标签

MyBatis 可以使用第三方的插件来对功能进行扩展,分页助手PageHelper 是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据

开发步骤:

①导入通用PageHelper 的坐标

②在mybatis 核心配置文件中配置PageHelper 插件

③测试分页数据获取

①导入通用 PageHelper 坐标

 <!-- 分页助手 -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>3.7.5</version>
        </dependency>
        <dependency>
            <groupId>com.github.jsqlparser</groupId>
            <artifactId>jsqlparser</artifactId>
            <version>0.9.1</version>
        </dependency>

②在 mybatis 核心配置文件中配置PageHelper 插件

 <plugins>
        <!--配置分页-->
        <plugin interceptor="com.github.pagehelper.PageHelper">
            <!--dialect:指定方言,也就是说明整个分页需要支持指定数据库的特有语法-->
            <property name="dialect" value="mysql"/>
        </plugin>
    </plugins>

③测试分页代码实现

//测试 分页助手
    //核心配置文件深入,plugin标签配置pageHelper
    @Test
    public void tesPageHelper() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //获取UserMapper的代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        //要想把查询结果分页展示,就需要在查询之前,先设置分页参数
        //设置分页参数
        /*startPage(参数1(表示当前页),参数2(表示每页显示的条数))  */
        PageHelper.startPage(2, 2);

        List<User> userList = mapper.findAllResultMap();//获取所有用户
        for (User user : userList) {
            System.out.println(user);
        }

        //获取分页相关的其他数据
        PageInfo<User> userPageInfo = new PageInfo<User>(userList);//将查询出来的结果集作为参数传入
        //PageInfo就会根据传入的参数去计算分页的总条数,分页数等等
        System.out.println("总条数:" + userPageInfo.getTotal());
        System.out.println("总页数:" + userPageInfo.getPages());
        System.out.println("当前页:" + userPageInfo.getPageNum());
        System.out.println("是否是第一页:" + userPageInfo.isIsFirstPage());

        sqlSession.close();
    }

3.2 知识小结

MyBatis 核心配置文件常用标签:

1、properties 标签:该标签可以加载外部的 properties 文件

2、typeAliases 标签:设置类型别名

3、environments 标签:数据源环境配置标签

4、plugins 标签:配置MyBatis 的插件(可以自定义插件,也可以整合其他第三方插件)

Mybatis 多表查询

4.1 数据库表关系介绍

关系型数据库表关系分为

  • 一对一
  • 一对多

  • 多对多

举例

  • 人和身份证号就是一对一

    • 一个人只能有一个身份证号
    • 一个身份证号只能属于一个人
  • 用户和订单就是一对多,订单和用户就是多对一

    • 一个用户可以下多个订单
    • 多个订单属于同一个用户
  • 学生和课程就是多对多

    • 一个学生可以选修多门课程
    • 一个课程可以被多个学生选修
  • 特例

    • 一个订单只从属于一个用户,所以mybatis 将多对一看成了一对一

案例数据库环境准备

  • 创建orders表
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;

  • 往rders表中插入数据

    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');
    

  • 创建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;
    

  • 往sys_role表中插入数据

    INSERT INTO `sys_role` VALUES ('1', 'CTO', 'CTO');
    
    INSERT INTO `sys_role` VALUES ('2', 'CEO', 'CEO');
    

  • 创建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 `user` (`id`),
    
     CONSTRAINT `sys_user_role_ibfk_2` FOREIGN KEY (`roleid`) REFERENCES `sys_role` (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    

  • 往 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');
    

4.2 一对一(多对一)multitable

4.2.1 介绍

一对一查询模型

  • 用户表和订单表的关系为,一个用户有多个订单(一对多的关系),一个订单只从属于一个用户(一对一的关系)

  • 一对一查询的需求:查询一个订单,与此同时查询出该订单所属的用户

在这里插入图片描述

  • 一对一查询语句

    SELECT * FROM orders o LEFT JOIN USER u ON o.`uid`=u.`id`;
    
  • 查询结果

在这里插入图片描述

  • 结果的封装 需要自己手动配置resultMap

在这里插入图片描述

4.2.2 代码实现

1 )Order 实体

package com.myLagou.entity;

import java.util.Date;

/**
 * @author zhy
 * @create 2022-08-09 13:40
 */
public class Orders {
    private Integer id;
    private Date ordertime;
    private double total;
    private Integer uid;

    // 表示当前订单属于哪个用户
    private User user;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Date getOrdertime() {
        return ordertime;
    }

    public void setOrdertime(Date ordertime) {
        this.ordertime = ordertime;
    }

    public double getTotal() {
        return total;
    }

    public void setTotal(double total) {
        this.total = total;
    }

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }

    @Override
    public String toString() {
        return "Orders{" +
                "id=" + id +
                ", ordertime=" + ordertime +
                ", total=" + total +
                ", uid=" + uid +
                ", user=" + user +
                '}';
    }
}

2 )OrderMapper 接口

public interface OrderMapper {

    /*一对一关联查询:查询所有的订单,并且查出每个订单所属的用户信息*/
    public List<Orders> findAllWithUser();
}

3 )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="com.myLagou.mapper.OrderMapper">

    <resultMap id="orderMap" type="com.myLagou.entity.Orders">
        <!--配置Orders类真的主键-->
        <id property="id" column="id"></id>
        <!--配置其他属性-->
        <result property="ordertime" column="ordertime"></result>
        <result property="total" column="total"></result>
        <result property="uid" column="uid"></result>

        <!--配置Orders类中的User对象属性-->
        <!--
        association:在进行一对一关联查询配置时进行类中的对象属性的配置
        property配置Orders类中要封装的关联对象user属性
        javaType配置要封装的user的类型   -->
        <association property="user" javaType="com.myLagou.entity.User">
            <!--在配置user类的column时要注意避免与Orders类中的column混淆,
            因为orders表中的外键uid就是拿user表的主键id来设置的,所以为了避免id混淆,可以直接拿uid来使用-->
            <id property="id" column="uid"></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>
        </association>
    </resultMap>

    <!-- /*一对一关联查询:查询所有的订单,并且查出每个订单所属的用户信息*/-->
    <!--因为这查询返回的结果为Orders的一个集合,且Orders类中包含了User的信息,mybatis不能直接自动映射封装,所有需要自己手动配置resultMap-->
    <select id="findAllWithUser" resultMap="orderMap">
        SELECT * FROM orders o LEFT JOIN USER u ON o.`uid`=u.`id`;
    </select>
</mapper>

4 )测试代码

package com.myLagou.test;

import com.myLagou.entity.Orders;
import com.myLagou.entity.User;
import com.myLagou.mapper.OrderMapper;
import com.myLagou.mapper.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * @author zhy
 * @create 2022-08-09 14:21
 */
public class mybatisTest {

    /*一对一关联查询:查询所有的订单,并且查出每个订单所属的用户信息*/
    @Test
    public void testFindAllWithUser() 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();
    }
}

4.3 一对多

4.3.1 介绍

一对多查询模型

  • 用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户

  • 一对多查询的需求:查询一个用户,与此同时查询出该用户具有的订单(从用户的角度来看是一对多)

在这里插入图片描述

一对多查询语句

SELECT u.*, o.id oid, o.ordertime,o.total, o.uid FROM orders o RIGHT JOIN user u ON u.`id` = o.`uid`;

查询结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p62ktvoc-1660055717105)(D:\blog\Notes\lagou\SSM\Mybatis\任务二\Mybatis 任务二:配置文件深入.assets\查询结果2.png)]在这里插入图片描述

4.3.2 代码实现

1 )User 实体

package com.myLagou.entity;

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

/**
 * @author zhy
 * @create 2022-08-07 14:11
 */
public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    //表示一对多中多的那一方的关系使用集合
    // 代表当前用户具备的订单列表
    private List<Orders> orderList;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public List<Orders> getOrderList() {
        return orderList;
    }

    public void setOrderList(List<Orders> orderList) {
        this.orderList = orderList;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                ", orderList=" + orderList +
                '}';
    }
}

private Integer id;

private String username;

private Date birthday;

private String sex;

private String address;

// 代表当前用户具备的订单列表

private List orderList;

}

2 )UserMapper 接口

public interface UserMapper {

 /*一对多查询的需求:查询所有用户,与此同时查询出该用户具有的订单信息*/
    public List<User> findAllWithOrder();

} 

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.myLagou.mapper.UserMapper">

    <resultMap id="userMapper" type="com.myLagou.entity.User">
        <!--为了避免出现id混淆,就sql语句执行的时候给orders表的id起别名,所以此处就使用id即可-->
        <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 标签关联
            property="orderList" 封装到集合的属性名
            ofType="order" 封装集合的泛型类型
        -->
        <collection property="orderList" ofType="com.myLagou.entity.Orders">
            <!--配置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="userMapper">
        SELECT u.*, o.id oid, o.ordertime,o.total, o.uid FROM orders o RIGHT JOIN user u ON u.`id` = o.`uid`;
    </select>
</mapper>

4 )测试代码

    /*一对多查询的需求:查询一个用户,与此同时查询出该用户具有的订单信息*/
    @Test
    public void testFindAllWithOrder() 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> userList = mapper.findAllWithOrder();
        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.close();
    }

4.4 多对多

4.4.1 介绍

多对多查询的模型

  • 用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用

  • 多对多查询的需求:查询用户同时查询出该用户的所有角色

    在这里插入图片描述

  • 多对多查询语句

    • 先分开查询 先查出所有的用户的id对应的roleid

在这里插入图片描述

  • 将上述查询结果当作一张虚拟表再去与用户角色role表进行关联查询

在这里插入图片描述

  • 最终的sql语句为:
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;

4.4.2 代码实现

1 )User 和Role 实体

User实体类

package com.myLagou.entity;

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

/**
 * @author zhy
 * @create 2022-08-07 14:11
 */
public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    //表示一对多中多的那一方的关系使用集合
    // 代表当前用户具备的订单列表
    private List<Orders> orderList;

    // 代表当前用户关联的角色列表
    private List<Role> roleList;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public List<Orders> getOrderList() {
        return orderList;
    }

    public void setOrderList(List<Orders> orderList) {
        this.orderList = orderList;
    }

    public List<Role> getRoleList() {
        return roleList;
    }

    public void setRoleList(List<Role> roleList) {
        this.roleList = roleList;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                ", orderList=" + orderList +
                ", roleList=" + roleList +
                '}';
    }
}

Role实体类

package com.myLagou.entity;

/**
 * @author zhy
 * @create 2022-08-09 15:38
 */
public class Role {
    private Integer id;

    private String rolename;

    private String roleDesc;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getRolename() {
        return rolename;
    }

    public void setRolename(String rolename) {
        this.rolename = rolename;
    }

    public String getRoleDesc() {
        return roleDesc;
    }

    public void setRoleDesc(String roleDesc) {
        this.roleDesc = roleDesc;
    }

    @Override
    public String toString() {
        return "Role{" +
                "id=" + id +
                ", rolename='" + rolename + '\'' +
                ", roleDesc='" + roleDesc + '\'' +
                '}';
    }
}

2 )UserMapper 接口

public interface UserMapper {


    /*多对多关联查询需求:查询所有用户,与此同时查询出该用户具有的角色信息*/
    public List<User> findAllWithRole();
}

3 )UserMapper.xml 映射

   <!--多对多的resultMap配置-->
    <!--要封装到user实体类上,所以type为user-->
    <resultMap id="userRoleMap" type="com.myLagou.entity.User">
        <!--设置User表中的主键id-->
        <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="com.myLagou.entity.Role">
            <!--配置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 testFindAllWithRole() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

       /*mapper要调用的方法在哪个XxxMapper中,就使用哪个类,所以使用UserMapper.class*/
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> allWithRole = mapper.findAllWithRole();
        for (User user : allWithRole) {
            System.out.println(user);
        }
        sqlSession.close();
    }

public void testUserWithRole() throws Exception {

UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

List list = userMapper.findAllWithRole();

for (User user : list) {

System.out.println(user);

}

}

4.5 小结

MyBatis 多表关联配置方式

* 一对一配置:使用<resultMap>+<association>做配置

* 一对多配置:使用<resultMap>+<collection>做配置

* 多对多配置:使用<resultMap>+<collection>做配置

* 多对多的配置跟一对多很相似,难度在于SQL 语句的编写。

MyBatis 嵌套查询

5.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,将以上二步嵌套起来
		...

5.2 一对一嵌套查询

5.2.1 介绍

需求:查询一个订单,与此同时查询出该订单所属的用户

一对一查询语句

-- 先查询订单
SELECT * FROM orders;


-- 再根据订单 uid 外键,查询用户
SELECT * FROM `user` WHERE id = #{订单的uid};

在这里插入图片描述

5.2.2 代码实现

1) OrderMapper 接口

public interface OrderMapper {

/*一对一嵌套查询:查询所有的订单,并且查出每个订单所属的用户信息*/
    public List<Orders> findAllWithUser2();

} 

2) OrderMapper.xml 映射

<resultMap id="orderMap2" type="com.myLagou.entity.Orders">
        <!--配置Orders类真的主键-->
        <id property="id" column="id"></id>
        <!--配置其他属性-->
        <result property="ordertime" column="ordertime"></result>
        <result property="total" column="total"></result>
        <result property="uid" column="uid"></result>

        <!--存在问题
            问题1:怎么去执行嵌套的第二条sql?  ————通过select属性
            问题2:在执行第二条sql的时候,如何把第一条sql查询的结果uid作为参数传递给第二条sql  ————通过column

            select属性表示引入UserMapper.xml中的findById的查询语句
            column表示把第一条sql查询出来的uid作为参数传递给第二条sql语句,也就是传给select属性中的sql语句
        -->
        <association property="user" javaType="com.myLagou.entity.User"
                     select="com.myLagou.mapper.UserMapper.findById" column="uid">

        </association>
    </resultMap>
<!--    /*一对一嵌套查询:查询所有的订单,并且查出每个订单所属的用户信息*/-->
    <select id="findAllWithUser2" resultMap="orderMap2">
        select * from orders
    </select>

编写完OrderMapper这部分内容后,就把需要嵌套的第二条sql语句需要的代码编写到UserMapper部分中去,在OrderMapper.xml文件中通过association标签中的属性select来从UserMapper.xml中引入sql,使用column将第一条sql查询出来的uid当作结果传递给select属性

3) UserMapper 接口

public interface UserMapper {

    //为了实现一对一嵌套查询
    /*根据id查询用户*/
    public User findById(Integer id);
}

4) UserMapper.xml 映射

 <!--/*根据id查询用户*/-->
    <select id="findById" resultType="user" parameterType="int">
        select * from user where id = #{id}
    </select>

5)测试代码

/*一对一嵌套查询:查询所有的订单,并且查出每个订单所属的用户信息*/
    @Test
    public void testFindAllWithUser2() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        /*mapper要调用的方法在哪个XxxMapper中,就使用哪个类,所以使用UserMapper.class*/
        OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
        List<Orders> ordersList = mapper.findAllWithUser2();
        for (Orders orders : ordersList) {
            System.out.println(orders);
        }
        sqlSession.close();
    }

5.3 一对多嵌套查询

5.3.1 介绍

需求:查询一个用户,与此同时查询出该用户具有的订单

一对多查询语句

-- 先查询用户
SELECT * FROM `user`;


-- 再根据用户 id 主键,查询订单列表
SELECT * FROM orders where uid = #{用户 id};

5.3.2 代码实现

a)UserMapper 接口

public interface UserMapper {

/*一对多嵌套查询的需求:查询所有用户,与此同时查询出该用户具有的订单信息*/
    public List<User> findAllWithOrder2();

} 

b)UserMapper.xml 映射

    <!--一对多的嵌套查询-->
    <resultMap id="userOrderMap" type="com.myLagou.entity.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>

        <!--配置的select与column与之前一对一嵌套查询的作用一样-->
        <collection property="orderList" ofType="com.myLagou.entity.Orders" column="id"
                    select="com.myLagou.mapper.OrderMapper.findAllByUid">
        </collection>
    </resultMap>

<!--    /*一对多嵌套查询的需求:查询所有用户,与此同时查询出该用户具有的订单信息*/-->
    <select id="findAllWithOrder2" resultMap="userOrderMap">
        select * from user ;
    </select>

c)OrderMapper*接口

public interface OrderMapper {

   /*根据uid查询该用户对应的所有订单*/
    public List<Orders> findAllByUid(Integer id);

} 

d)OrderMapper.xml映射


<!--    /*根据uid查询该用户对应的所有订单*/-->
    <select id="findAllByUid" parameterType="int" resultType="com.myLagou.entity.Orders">
        select * from orders where uid = #{uid}
    </select>

e)测试代码

 /*一对多嵌套查询的需求:查询所有用户,与此同时查询出该用户具有的订单信息*/
    @Test
    public void testFindAllWithOrder2() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        /*mapper要调用的方法在哪个XxxMapper中,就使用哪个类,所以使用UserMapper.class*/
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.findAllWithOrder2();
        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.close();
    }

5.4 多对多嵌套查询

5.4.1 介绍

需求:查询用户同时查询出该用户的所有角色

多对多查询语句

-- 先查询用户
SELECT * FROM `user`;


-- 再根据用户 id 主键,查询角色列表
SELECT * FROM role r INNER JOIN user_role ur ON r.`id` = ur.`rid`
WHERE ur.`uid` = #{用户 id};

5.4.2 代码实现

a) UserMapper 接口

public interface UserMapper {

/*多对多嵌套查询需求:查询所有用户,与此同时查询出该用户具有的角色信息*/
    public List<User> findAllWithRole2();

} 

b)UserMapper.xml 映射

<!--多对多嵌套查询-->
    <resultMap id="userRole" type="com.myLagou.entity.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="com.myLagou.entity.Role" column="id"
                    select="com.myLagou.mapper.RoleMapper.findByUid">
        </collection>
    </resultMap>

<!--    /*多对多嵌套查询需求:查询所有用户,与此同时查询出该用户具有的角色信息*/-->
    <select id="findAllWithRole2" resultMap="userRole">
        select * from user
    </select>

c) RoleMapper 接口

public interface RoleMapper {

/*根据用户id来查询对应的角色*/
    public List<Role> findByUid(Integer uid);

} 

d) RoleMapper.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.myLagou.mapper.RoleMapper">

    <!--/*根据用户id来查询对应的角色*/
        public List<Role> findByUid(Integer uid);当方法传递的参数为基本数据类型或者为String,且只有一个参数时,parameterType可以省略不写
    -->
    <select id="findByUid"  resultType="role" >
        select * from sys_role r inner join sys_user_role ur on ur.roleid = r.id
            where ur.userid = #{uid}
    </select>
</mapper>

e)测试代码

    /*多对多嵌套查询需求:查询所有用户,与此同时查询出该用户具有的角色信息*/
    @Test
    public void testFindAllWithRole2() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        /*mapper要调用的方法在哪个XxxMapper中,就使用哪个类,所以使用UserMapper.class*/
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.findAllWithRole2();
        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.close();
    }

5.5 小结

  • 一对一配置:使用<resultMap>+<association>做配置,通过column 条件,执行 select 查询

  • 一对多配置:使用<resultMap>+<collection>做配置,通过column 条件,执行 select 查询

  • 多对多配置:使用<resultMap>+<collection>做配置,通过column 条件,执行 select 查询

  • 优点:简化多表查询操作

有的角色信息*/
public List findAllWithRole2();

}








**b)UserMapper.xml** **映射**

```xml
<!--多对多嵌套查询-->
    <resultMap id="userRole" type="com.myLagou.entity.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="com.myLagou.entity.Role" column="id"
                    select="com.myLagou.mapper.RoleMapper.findByUid">
        </collection>
    </resultMap>

<!--    /*多对多嵌套查询需求:查询所有用户,与此同时查询出该用户具有的角色信息*/-->
    <select id="findAllWithRole2" resultMap="userRole">
        select * from user
    </select>

c) RoleMapper 接口

public interface RoleMapper {

/*根据用户id来查询对应的角色*/
    public List<Role> findByUid(Integer uid);

} 

d) RoleMapper.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.myLagou.mapper.RoleMapper">

    <!--/*根据用户id来查询对应的角色*/
        public List<Role> findByUid(Integer uid);当方法传递的参数为基本数据类型或者为String,且只有一个参数时,parameterType可以省略不写
    -->
    <select id="findByUid"  resultType="role" >
        select * from sys_role r inner join sys_user_role ur on ur.roleid = r.id
            where ur.userid = #{uid}
    </select>
</mapper>

e)测试代码

    /*多对多嵌套查询需求:查询所有用户,与此同时查询出该用户具有的角色信息*/
    @Test
    public void testFindAllWithRole2() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        /*mapper要调用的方法在哪个XxxMapper中,就使用哪个类,所以使用UserMapper.class*/
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.findAllWithRole2();
        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.close();
    }

5.5 小结

  • 一对一配置:使用<resultMap>+<association>做配置,通过column 条件,执行 select 查询

  • 一对多配置:使用<resultMap>+<collection>做配置,通过column 条件,执行 select 查询

  • 多对多配置:使用<resultMap>+<collection>做配置,通过column 条件,执行 select 查询

  • 优点:简化多表查询操作

  • 缺点:执行多次sql 语句,浪费数据库性能

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值