5/10day51_查询&多表

mybatis查询&多表.png

回顾

1. 创建java工程,导入相关jar包

2. 根据user表编写User实体类

3. 编写UserMapper接口

4. 编写UserMapper.xml映射文件

5. 复制SqlMapConfig.xml (mybatis-config.xml)

6. 测试查询...
1589073275902.png

优化测试方法

public class UserMapperTest {

    private SqlSession sqlSession = null;

    // 此方法在测试方法执行之前,执行
    @Before
    public void before() {
        // 获取sqlSession对象
        sqlSession = MyBatisUtils.openSession();// 此方法必须线程内独享....
    }

    // 此方法在测试地方法执行之后,执行
    @After
    public void after() {
        // 关闭sqlSession
        MyBatisUtils.close(sqlSession);
    }

    @Test
    public void testFindAll() throws Exception {
        // 执行sql
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> list = userMapper.findAll();
        System.out.println(list);

    }
}

mybatis查询和多表

1. 单表查询【重点】

2. mapper映射文件深入讲解【重点】

3. 回顾表关系【重点】

4. 多表查询【重点】

一 Mybatis单表查询

1.1 resultMap标签

  • 如果数据库返回结果的列名和要封装的实体的属性名完全一致的话用 resultType 属性

  • 如果数据库返回结果的列名和要封装的实体的属性名有不一致的情况用 resultMap 属性

    • 使用resultMap==手动建立对象关系映射==。

① UserMapper接口

    // ResultMap标签
    public List<User> findAllResultMap();

② UserMapper.xml

    <!--
        resultMap 手动建立映射
            id="userResultMap"
            type="cn.itcast.domain.User" 建立映射的java类型
        id 标签 主键
            column="uid" 列名
            property="id" 实体属性名
       result 标签 普通字段
            column="name" 列名
            property="username" 实体属性名
    -->
    <resultMap id="userResultMap" type="cn.itcast.domain.User">
        <id column="uid" property="id"></id>
        <result column="name" property="username"></result>
        <result column="bir" property="birthday"></result>
        <result column="gender" property="sex"></result>
        <result column="address" property="address"></result>
    </resultMap>

    <!--
        模拟表与实体的属性名不一致情况
    -->
    <select id="findAllResultMap" resultMap="userResultMap">
        SELECT id AS uid, username AS `name`,birthday AS bir ,sex AS gender ,address FROM `user`
    </select>

③ 测试

    // resultMap标签
    @Test
    public void testFindAllResultMap() throws Exception {
        // 获取代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // 执行sql
        List<User> list = userMapper.findAllResultMap();
        for (User user : list) {
            System.out.println(user);
        }
    }

1.2 多条件查询(二种)

需求

根据id和username查询user表

① UserMapper接口

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

// 多条件查询,方式二
public List<User> findByIdAndUsername2(User user);

② UserMapper.xml

<!--
        多条件查询方式一
            如果传递多个参数 parameterType属性省略不写...
    -->
<select id="findByIdAndUsername1"  resultType="cn.itcast.domain.User">
    select * from user where id = #{id} and username = #{username}
</select>

<!--
        多条件查询方式二
            其实mybatis这哥们传递一个参数 parameterType也可以省略【不太建议...】
    -->
<select id="findByIdAndUsername2" parameterType="cn.itcast.domain.User"  resultType="cn.itcast.domain.User">
    select * from user where id = #{id} and username = #{username}
</select>

③ 测试

// 多条件查询
@Test
public void test01()throws Exception{
    // 获取代理对象
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    // 方式一
    /*List<User> list = userMapper.findByIdAndUsername1(41, "老王");
        System.out.println(list);*/

    // 方式二
    User user = new User();
    user.setId(41);
    user.setUsername("老王");
    List<User> list = userMapper.findByIdAndUsername2(user);
    System.out.println(list);
}

1.3 模糊查询(四种)

需求

根据username模糊查询user表

① UserMapper接口

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

    // 模糊查询,方式二
    public List<User> findByUsername2(String username);

    // 模糊查询,方式三
    public List<User> findByUsername3(String username);

    // 模糊查询,方式四
    public List<User> findByUsername4(String username);

② UserMapper.xml

    <!--
        模糊查询,方式一
            java代码与sql语句有耦合
    -->
    <select id="findByUsername1" parameterType="string" resultType="User">
        select * from user where username like #{username}
    </select>

    <!--
        模糊查询,方式二【了解】
            mysql5.5版本之前,此拼接不支持多个单引号
            oracle数据库,除了别名的位置,其余位置都不能使用双引号
    -->
    <select id="findByUsername2" parameterType="string" resultType="User">
        select * from user where username like "%" #{username} "%"
    </select>

    <!--
        模糊查询,方式三【此方式,会出现sql注入...】
            ${} 字符串拼接,如果接收的简单数据类型,表达式名称必须是value
    -->
    <select id="findByUsername3" parameterType="string" resultType="User">
        select * from user where username like '%${value}%'
    </select>

    <!--
        模糊查询,方式四【掌握】
            使用concat()函数拼接
            注意:oracle数据库 concat()函数只能传递二个参数...  可以使用函数嵌套来解决
    -->
    <select id="findByUsername4" parameterType="string" resultType="User">
        select * from user where username like concat(concat('%',#{username}),'%')
    </select>

③ 测试

    // 模糊查询
    @Test
    public void test02()throws Exception{
        // 获取代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        // 方式一
        // List<User> list = userMapper.findByUsername1("%王%");
        // 方式二
        // List<User> list = userMapper.findByUsername2("王");
        // 方式三
        // List<User> list = userMapper.findByUsername3("王");
        // 方式四
        List<User> list = userMapper.findByUsername4("王");
        System.out.println(list);
    }

1.4 ${} 与 #{} 区别【面试题】

${}:底层 Statement

  1. sql与参数拼接在一起,会出现sql注入问题
  2. 每次执行sql语句都会编译一次
  3. 接收简单数据类型,命名:${value}
  4. 接收引用数据类型,命名: ${属性名}
  5. 字符串类型需要加 '${value}'

org.apache.ibatis.scripting.xmltags.TextSqlNode

#{}:底层 PreparedStatement

  1. sql与参数分离,不会出现sql注入问题
  2. sql只需要编译一次
  3. 接收简单数据类型,命名:#{随便写}
  4. 接收引用数据类型,命名:#{属性名}

二 Mybatis映射文件深入

环境搭建

1589078295166.png

2.1 返回主键

应用场景

向数据库保存一个user对象后, 然后在控制台记录下此新增user的主键值(id)

① UserMapper接口

public interface UserMapper {

    // 返回主键,方式一
    public void save1(User user);

    // 返回主键,方式二
    public void save2(User user);
}

② UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.itcast.dao.UserMapper">

    <!--
        返回主键,方式一 useGeneratedKeys属性
            useGeneratedKeys="true" 开启新增主键返回功能
            keyColumn="id"  user表中主键列
            keyProperty="id" user实体主键属性

            注意:仅支持主键自增类型的数据库 MySQL 和 SqlServer , oracle不支持

    -->
    <insert id="save1" parameterType="User" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
        insert into user(username,birthday,sex,address)
          values(#{username},#{birthday},#{sex},#{address})
    </insert>

    <!--
        返回主键,方式二  <selectKey>
             keyColumn="id" user表中主键列
             keyProperty="id" user实体主键属性
             resultType="int" user实体主键属性类型
             order="AFTER"  表示此标签内部sql语句在insert执行之前(执行),还是之后执行(执行)
                AFTER 之后执行【在自增主键时】
                BEFORE 之前执行【使用指定主键时】

    -->
    <insert id="save2" parameterType="User" >
        <selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
            SELECT LAST_INSERT_ID()
        </selectKey>
        insert into user(username,birthday,sex,address)
          values(#{username},#{birthday},#{sex},#{address})
    </insert>
</mapper>

③ 测试

public class UserMapperTest {

    private SqlSession sqlSession = null;

    // 此方法在测试方法执行之前,执行
    @Before
    public void before() {
        // 获取sqlSession对象
        sqlSession = MyBatisUtils.openSession();// 此方法必须线程内独享....
    }

    // 此方法在测试地方法执行之后,执行
    @After
    public void after() {
        // 关闭sqlSession
        MyBatisUtils.close(sqlSession);
    }

    // 返回主键
    @Test
    public void test01() throws Exception {
        // 获取代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        User user = new User();
        user.setUsername("xiao六");
        user.setBirthday(new Date());
        user.setAddress("中国");
        user.setSex("男");
        // 方式一
        // userMapper.save1(user);
        userMapper.save2(user);

        System.out.println("新增时,主键返回:" + user.getId());
    }
}

2.2 动态SQL

2.2.1 什么是动态SQL

需求

把id和username封装到user对象中,将user对象中不为空的属性作为查询条件

1589022259909.png

这个时候我们执行的sql就有多种可能

-- 如果id和用户名不为空
select * from user where id= #{id} and username = #{username}

-- 如果只有id
select * from user where id= #{id} 

-- 如果只有用户名
select * from user where username = #{username}

-- 如果id和用户名都为空
select * from user

像上面这样, 根据传入的参数不同, 需要执行的SQL的结构就会不同,这就是动态SQL

2.2.2 if 条件判断

需求

把id和username封装到user对象中,将user对象中不为空的属性作为查询条件

① UserMapper接口

    // if 条件判断
    public List<User> findByIdAndUsernameIf(User user);

② UserMapper.xml

    <!--
        if标签 条件判断
        where标签  相当于 where 1=1 功能,如果没有条件情况下 where语句不在sql语句拼接
            可以去掉第一个 and 或者 or
    -->
    <select id="findByIdAndUsernameIf" parameterType="User" resultType="User">
        select * from user
        <where>
            <if test="id != null">
                and id= #{id}
            </if>
            <if test="username !=null">
                and username = #{username}
            </if>
        </where>
    </select>

③ 测试

    // if判断
    @Test
    public void test02()throws Exception{
        // 获取代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        // 拼接条件
        User param = new User();
       // param.setId(41);
        // param.setUsername("老王");

        List<User> list = userMapper.findByIdAndUsernameIf(param);

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

2.2.3 set 用于update语句

需求

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

① UserMapper接口

    // set 更新
    public void updateIf(User user);

② UserMapper.xml

<!--
        set标签 更新 ,将条件中的最后一个逗号抹除
    -->
<update id="updateIf" parameterType="User">
    update user
    <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>

③ 测试

    @Test
    public void test03()throws Exception{
        // 获取代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // 设置更新内容
        User user = new User();
        user.setId(50);
        user.setUsername("小四");

        userMapper.updateIf(user);
    }

2.2.4 foreach 用于循环遍历【重点】

需求

根据多个id查询,user对象的集合

select * from user where id in (41,43,46);
* <foreach>标签用于遍历集合,它的属性:

    • collection:代表要遍历的集合元素

    • open:代表语句的开始部分

    • close:代表结束部分

    • item:代表遍历集合的每个元素,生成的变量名

    • sperator:代表分隔符

练习三个版本

  1. 普通list集合
  2. 普通array数组
  3. 实体属性list集合

① UserMapper

    // foreach标签,普通list集合
    public List<User> findByList(List<Integer> ids);

    // foreach标签,普通array数组
    public List<User> findByArray(Integer [] ids);

    // foreach标签,实体属性list集合
    public List<User> findByQueryVo(QueryVo queryVo);

② UserMapper.xml

    <!--
        foreach标签,普通list集合
            传递 普通类型list集合   collection="list" 属性取值:collection、list
    -->
    <select id="findByList" parameterType="list" resultType="User">
        select * from user where id in
        <foreach collection="list" open="(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </select>


    <!--
        foreach标签,普通array数组
            传统 普通类型array数组  collection="array" 属性取值 array
    -->
    <select id="findByArray" parameterType="int" resultType="User">
         select * from user where id in
         <foreach collection="array" open="(" close=")" item="id" separator=",">
             #{id}
         </foreach>
    </select>

    <!--
        foreach标签,实体属性list集合
            传递 实体中list属性集合的话,collection="ids" 取值,实体的属性名
    -->
    <select id="findByQueryVo" parameterType="QueryVo" resultType="User">
         select * from user where id in
        <foreach collection="ids" open="(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </select>

③ 测试

    //  foreach标签
    @Test
    public void test04()throws Exception{
        // 获取代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        // 普通list集合
       /* List ids = new ArrayList();
        ids.add(41);
        ids.add(46);
        List list = userMapper.findByList(ids);*/
       // 普通array数组
      /* Integer[] ids = {41,46,49};
        List<User> list = userMapper.findByArray(ids);*/

      // 实体属性list集合
        List ids = new ArrayList();
        ids.add(41);
        ids.add(46);
        QueryVo queryVo = new QueryVo();
        queryVo.setIds(ids);
        List<User> list = userMapper.findByQueryVo(queryVo);
        System.out.println(list);
    }

2.3 SQL片段

应用场景

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

    <!--
        foreach标签,普通list集合
            传递 普通类型list集合   collection="list" 属性取值:collection、list
    -->
    <select id="findByList" parameterType="list" resultType="User">
        <include refid="selectUser"></include> where id in
        <foreach collection="list" open="(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </select>


    <!--
        foreach标签,普通array数组
            传统 普通类型array数组  collection="array" 属性取值 array
    -->
    <select id="findByArray" parameterType="int" resultType="User">
        <include refid="selectUser"></include>  where id in
         <foreach collection="array" open="(" close=")" item="id" separator=",">
             #{id}
         </foreach>
    </select>

    <!--
        foreach标签,实体属性list集合
            传递 实体中list属性集合的话,collection="ids" 取值,实体的属性名
    -->
    <select id="findByQueryVo" parameterType="QueryVo" resultType="User">
         <include refid="selectUser"></include> where id in
        <foreach collection="ids" open="(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </select>


    <!--
        将当前映射文件的共同的sql代码抽取一个片段,实现sql的复用性...
         id="selectUser" 当前sql片段的唯一标识
    -->
    <sql id="selectUser">
         select id,username,birthday,sex,address from user
    </sql>

2.4 知识小结

MyBatis映射文件配置

<select>:查询

<insert>:插入

<update>:修改

<delete>:删除

<selectKey>:返回主键

<where>:where条件

<if>:if判断

<foreach>:for循环

<set>:set设置

<sql>:sql片段抽取

三 表关系回顾

在关系型数据库当中,表关系分为三种

* 特殊情况:
    一个订单只能从属于一个用户,mybatis框架就把这个多对一看做成一对一来实现
    
* 数据建立表关系:通过主外键关联

* 实体建立关系:通过属性关联
1589091271542.png

四 MyBatis多表查询

1589091428909.png

环境搭建

1589092722809.png

4.1 一对一(多对一)

一对一查询模型

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

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

1589091522985.png

① 实体和表映射关系

SELECT * FROM orders o INNER JOIN `user` u ON o.`uid` = u.`id` WHERE o.`id` = 1
1589092976533.png

② Order实体类

// 订单实体类
public class Order {

    private Integer id;

    private Date ordertime;

    private Double money;

    // 一个订单从属于一个用户
    private User user;
}

③ OrderMapper接口

public interface OrderMapper {

    // 一对一关联查询
    public Order findByIdWithUser(Integer id);
}

④ OrderMapper.xml

1589093842562.png
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.itcast.dao.OrderMapper">


    <resultMap id="orderMap" type="cn.itcast.domain.Order">
        <id column="id" property="id"></id>
        <result column="ordertime" property="ordertime"></result>
        <result column="money" property="money"></result>
        <!--
            一对一多表关联 association标签
                 property="user" 关联实体的属性名
                 javaType="cn.itcast.domain.User" 关联实体java类型
        -->
        <association property="user" javaType="cn.itcast.domain.User">
            <id column="uid" property="id"></id>
            <result column="username" property="username"></result>
            <result column="birthday" property="birthday"></result>
            <result column="sex" property="sex"></result>
            <result column="address" property="address"></result>
        </association>
    </resultMap>


    <!--
        一对一关联查询
            resultType:单表映射封装
            resultMap:多表查询必须手动映射封装
    -->
    <select id="findByIdWithUser" parameterType="int" resultMap="orderMap">
        SELECT * FROM orders o INNER JOIN `user` u ON o.`uid` = u.`id` WHERE o.`id` = #{id}
    </select>
</mapper>

⑤ 测试

public class OrderMapperTest {

    private SqlSession sqlSession = null;

    // 此方法在测试方法执行之前,执行
    @Before
    public void before() {
        // 获取sqlSession对象
        sqlSession = MyBatisUtils.openSession();// 此方法必须线程内独享....
    }

    // 此方法在测试地方法执行之后,执行
    @After
    public void after() {
        // 关闭sqlSession
        MyBatisUtils.close(sqlSession);
    }


    // 一对一关联测试
    @Test
    public void test01()throws Exception{
        // 获取代理对象
        OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);

        // 根据id查询
        Order order = orderMapper.findByIdWithUser(1);
        System.out.println(order);
    }
}

4.2 一对多

一对多查询模型

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

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

1589095235966.png

① 实体和表关系

SELECT *,o.id AS oid FROM `user` u INNER JOIN orders o ON u.`id` = o.`uid` WHERE u.`id`=41
1589095492155.png

② User实体类

public class User {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    // 一个用户具有多个订单
    private List<Order> orderList;
}

③ UserMapper接口

public interface UserMapper {

    // 一对多关联
    public User findByIdWithOrders(Integer id);
}

④ UserMapper.xml

1589096423076.png
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.itcast.dao.UserMapper">


    <resultMap id="userMap" type="cn.itcast.domain.User">
        <id column="id" property="id"></id>
        <result column="username" property="username"></result>
        <result column="birthday" property="birthday"></result>
        <result column="sex" property="sex"></result>
        <result column="address" property="address"></result>
        <!--
            一对多关联 collection标签
                property="orderList" 关联实体集合的属性名
                ofType="cn.itcast.domain.Order" 关联实体的java类型(集合泛型的类型)
        -->
        <collection property="orderList" ofType="cn.itcast.domain.Order">
            <id column="oid" property="id"></id>
            <result column="ordertime" property="ordertime"></result>
            <result column="money" property="money"></result>
        </collection>
    </resultMap>

<!--
    一对多关联
-->
    <select id="findByIdWithOrders" parameterType="int" resultMap="userMap">
       SELECT *,o.id AS oid FROM `user` u INNER JOIN orders o ON u.`id` = o.`uid` WHERE u.`id`=#{id}
    </select>
</mapper>

⑤ 测试

1589096245643.png
public class UserMapperTest extends BaseMapperTest {

    // 一对多测试
    @Test
    public void test01() throws Exception {
        // 获取代理
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        User user = userMapper.findByIdWithOrders(41);

        System.out.println(user);
    }
}

4.3 多对多(由二个一对多组成)

多对多查询的模型

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

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

在mybatis中多对多实现,跟一对多步骤是一样,区别就在于sql语句

1589096723649.png

① 实体和表关系

SELECT * FROM `user` u 
    INNER JOIN user_role ur ON u.`id` = ur.`uid`  -- 用户连接中间表
    INNER JOIN role r ON ur.`rid` = r.`id`  -- 再根据中间表连接角色
    WHERE u.id = 41 -- 用户id 作为条件
1589097185348.png

② User和Role实体

public class Role {

    private Integer id;

    private String roleName;

    private String roleDesc;
}
public class User {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    // 一个用户具有多个角色
    private List<Role> roleList;
}

③ UserMapper接口

    // 多对多关联
    public User findByIdWithRoles(Integer id);

④ UserMapper.xml

1589097841134.png
    <resultMap id="userWithRoleMap" type="cn.itcast.domain.User">
        <id column="id" property="id"></id>
        <result column="username" property="username"></result>
        <result column="birthday" property="birthday"></result>
        <result column="sex" property="sex"></result>
        <result column="address" property="address"></result>
        <!--
            多对多实现步骤和一对多是一样的(区别在于sql语句)
        -->
        <collection property="roleList" ofType="cn.itcast.domain.Role">
            <id column="rid" property="id"></id>
            <result column="role_name" property="roleName"></result>
            <result column="role_desc" property="roleDesc"></result>
        </collection>
    </resultMap>
    
    <!--
        多对多关联
    -->
    <select id="findByIdWithRoles" parameterType="int" resultMap="userWithRoleMap">
        SELECT * FROM `user` u 
            INNER JOIN user_role ur ON u.`id` = ur.`uid`  -- 用户连接中间表
            INNER JOIN role r ON ur.`rid` = r.`id`  -- 再根据中间表连接角色
            WHERE u.id = #{id} -- 用户id 作为条件
    </select>

⑤ 测试

    // 多对多测试(根据用户查询角色)
    @Test
    public void test02()throws Exception{
        // 获取代理
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        User user = userMapper.findByIdWithRoles(41);

        System.out.println(user);
    }

作业:以角色为中心,查询多个用户

4.4 知识小结

一对一配置:使用<resultMap>+<association>做配置
    association:
        property:关联的实体属性名
        javaType:关联的实体类型(别名)

一对多配置:使用<resultMap>+<collection>做配置
    collection:
        property:关联的集合属性名
        ofType:关联的集合泛型类型(别名)

多对多配置:使用<resultMap>+<collection>做配置
    collection:
        property:关联的集合属性名
        ofType:关联的集合泛型类型(别名)
        
多对多的配置跟一对多很相似,难度在于SQL语句的编写。

4.5 优化测试

public class BaseMapperTest {

    protected SqlSession sqlSession = null;

    // 此方法在测试方法执行之前,执行
    @Before
    public void before() {
        // 获取sqlSession对象
        sqlSession = MyBatisUtils.openSession();// 此方法必须线程内独享....
    }

    // 此方法在测试地方法执行之后,执行
    @After
    public void after() {
        // 关闭sqlSession
        MyBatisUtils.close(sqlSession);
    }
}
public class OrderMapperTest extends BaseMapperTest { // 继承父类,就可以直接使用 父类的方法和成员变量了

    // 一对一关联测试
    @Test
    public void test01() throws Exception {
        // 获取代理对象
        OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);

        // 根据id查询
        Order order = orderMapper.findByIdWithUser(1);
        System.out.println(order);
    }
}

老师下午总结

回顾

1.resultMap标签的作用以及常见的异常

resultmap标签的作用,什么时候需要使用到resultmap标签:

    1. 单表查询的时候实体类的属性名与表的列名不对应的情况下。

        2. 多表查询的时候我们也要使用。

① 实体类

package com.itheima.model;

import java.util.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<Role> roleList;


    // 一个用户具有多个订单
    private List<Order> 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<Order> getOrderList() {
        return orderList;
    }

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

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

②接口

package com.itheima.dao;

import com.itheima.model.User;

import java.util.List;

public interface UserDao {

    public List<User> findAll();
}


③接口对应的Mapper文件

<?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.UserDao">

   <!-- resultmap的使用场景:
            1. 多表查询的时候
            2. 实体类的属性名与表的列名不对应
    -->
    <resultMap id="UserMapper" type="user">
       <!-- property 实体类的属性名
        column 表的列名-->
        <result property="username" column="name"/>
    </resultMap>

    <!--注意: 一旦使用resultmap标签,那么指定返回值类型的时候不是使用resultType了,而是使用reusltmap-->
    <select id="findAll" resultMap="UserMapper">
        select * from user
    </select>
</mapper>

④测试类(使用该测试之前必须把MyBatisUtils工具类导入)

package com.itheima.test;

import com.itheima.dao.UserDao;
import com.itheima.model.User;
import com.itheima.utils.BaseMapperTest;
import com.itheima.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class MyBatisTest extends BaseMapperTest {

    @Test
    public void test01(){
        SqlSession sqlSession = MyBatisUtils.openSession();
        //获取接口代理对象
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        List<User> list = userDao.findAll();
        System.out.println("查询结果:"+ list);
    }

}


2.多表查询

  1. association 封装单个对象的属性
  2. collection 封装一个集合属性

一对一

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

①创建用户实体类

public class User {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
}

②创建订单实体类,并且把一个订单只属于某一个客户的关系描述清楚

public class Order {

    private Integer id;

    private Integer uid;

    private Date ordertime;

    private Double money;
    
    private User user;
}

③编写接口

package com.itheima.dao;

import com.itheima.model.Order;

public interface OrderDao {

    //根据订单的id查找订单
    public Order findById(int id);

}

④编写接口对应的Mapper文件

<?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.OrderDao">

    <!--如果是多表查询,那么需要使用reusltMap标签-->
    <resultMap id="OrderMap" type="order">
        <id property="id" column="oid"/>
        <result property="ordertime" column="ordertime"/>
        <result property="money" column="money"/>
        <!--如果你的属性是一个对象,那么我们则需要使用association标签-->
        <association property="user" javaType="User">
            <id column="id" property="id"/>
            <result column="name" property="username"/>
            <result column="birthday" property="birthday"/>
            <result column="sex" property="sex"/>
            <result column="address" property="address"/>
        </association>
    </resultMap>


    <select id="findById" resultMap="OrderMap">
        SELECT * ,o.`id` AS oid FROM USER u INNER JOIN orders o ON o.`uid`=u.`id` WHERE o.`id`=#{id}
    </select>
</mapper>

⑤测试

   //测试一对一的关系
    @Test
    public void test02(){
        SqlSession sqlSession = MyBatisUtils.openSession();
        //获取接口代理对象
        OrderDao orderDao = sqlSession.getMapper(OrderDao.class);
        Order order = orderDao.findById(1);

        System.out.println("订单信息:"+ order);
    }

今日重点

  1. resultmap使用场景以及基本使用
  2. 模糊查询, 模糊查询只需要练习最后一种。concat(concat,"%");
  3. 动态sql语句
  4. 多表查询 Collection标签 association标签

作业: 今天帅哥的代码你们练习完毕发给组长,我会问组长或者班主任要。

时间: 明天晚上九点钟之前.

学习反馈

反馈 回复
好像模糊查询还有一种bind标签(这种不讲是实际开发不用吗) bind便签是用于绑定一个变量去使用的,目前企业里面用得不多。另外mybatis里面还有很多的标签,这里只会讲解最为常用标签.如果同学们有兴趣那么可以自己去了解一下。
讲一下多对多的执行流程 等会老师会讲解的。
突然学好多标签,还不怎么接受得来;还有多表查询觉得复杂,又听不懂 等会老师会讲解的。
感觉学的一团糟。。。很多知识只是抄的顺手。。不理解的地方很多 当你抄代码之前一定要问清楚即将要抄的语句,我们抄代码之前一定要明白该语句的作用。 否则今晚抄完,还没有睡着就已经忘记了。 代码:先看得懂,然后再去抄,才能自己写。
看不懂的情况可以问同学,问老师。
今天内容还是比较多,麻烦老师再讲一下多表查询这里的java代码实现,还不是很清楚 等会老师会讲解的。

答疑区

疑问 回复
SqlSession放成员变量位置,有何线程安全问题? 说的是需要自己手动关闭。不释放资源的话会造成内存泄露。 后面是用spring整合之后就不用自己释放资源了。
SqlSession放成员变量位置,有何线程安全问题? 1,SqlSession作为一个接口,其并没有线程安全性的问题,我们常说的线程安全问题是SqlSession的一个实现DefaultSqlSession,
2.并发操作使用了同一个DefaultSqlSession的实例,而同一个DefaultSqlSession的实例使用的是同一个Executor对象,
为什么需要用别名,然后还特地给别名弄映射呢? 考虑你的类的字段和表字段可能不一致的场景。
那我要是换一个别名,映射是不是也得跟着改,这样岂不是很麻烦,增加了很大工作量,还不如不写别名 是的.我们讲的是字段名称和类中的属性名称不一致的解决方案.实际开发尽量避免这种不一致.就不用搞这种映射关系了.
‘%${value}%’ 括号中为什么只能写value mybatis源码写死的.因此只能写value
‘%${value}% 这种是字符串拼接 会产生 sql注入问题concat()也是字符串拼接呀,为什么不会有sql注入问题 ${value}是拼接sql
#{key} 这个是预编译sql
嵌套怎么理解 concat(concat('%',#{name}),'%') 首先里面的concat使用%与name进行拼接,得到结果%#{name},然后外层再使用concat让%#{name}与%再进行拼接
返回主键的意义在哪 刚刚于帅老师说了一个应用场景,比如:我们添加了一个部门之后,需要马上给该部门添加员工
嗯嗯我的意思就是刚 创建就直接获取的意义 是吧? 假如我们使用的是ajax, 添加完一个部门的时候,马上弹出一个框配置该部门的员工,那么这个表单是不是就要拿到刚刚的部门主键了。
老师,多个单引号拼接是指MySQL会将多个连续的单引号包裹的多个字符串自动拼接成一个大的字符串吗? 是的,就是一个字符串拼接。
临时变量item的值必须和#{}中的值一致是吗? 是的
foreach标签的item的属性的值是固定的吗 item的属性值是可以改变的
foreach标签对于数组类型的,怎么前面的传入的类型(就是parameterType)又不需要带[]了,不是int[]而是int了.这也是别名吗 不是别名,这里写int相当于是写了容器中存放的类型.
为什么不写uid属性 直播间屏幕一直在更新.你突然问一句:为什么不写uid属性?
到底哪里为什么不写?请结合上下文,完整描述自己的问题.谢谢.
JavaType属性的是怎么写 就是当前属性对象的类型, 比如Order类中包含User,那么该user属性的javaType类型则是User
before这个俩注解,能用private修饰吗 不可以
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267