十二、MyBatis框架(3)

1 一对一延迟加载

1.1 概述

订单与用户的关系:1个订单记录只能对应一个用户

  • 针对的是关联对象

用户和订单从面相对象的角度来说就是关联对象,当只需要订单数据,尚不需要用户数据的时候,就不应该去查询用户表,啥时候用到用户数据,啥时候查询

  • 一对一延迟加载

​ 关联的sql语句肯定要拆分了

  • association标签
    • column属性:关联查询条件的属性
    • select属性:指定另一个查询语句

1.2 延迟加载

一起查的sql语句:

SELECT u.id uid,u.username,u.sex,u.birthday,u.address,o.id,o.user_id,o.number,o.createtime,o.note
FROM orders o LEFT OUTER JOIN USER u ON u.id = o.user_id;

实现延迟加载,拆分SQL语句
拆分的是订单表的查询

<select id="queryOrdersUser"  resultMap="queryOrdersUserResultMap">
    SELECT o.id,o.user_id,o.number,o.createtime,o.note FROM orders o
</select>

手动配置映射:

<!--手动映射配置,数据表的列和pojo对象属性对应-->
<resultMap id="queryOrdersUserResultMap" type="orders">
    <!--配置orders-->
    <id column="id" property="id"/>
    <result column="user_id" property="userId"/>
    <result column="number" property="number"/>
    <result column="createtime" property="createtime"/>
    <result column="note" property="note"/>
    
    <!--
        配置用户数据,orders对象中包含user
        association 配置对应关系
        property 属性: 和orders对象的哪个属性关联
        javaType 属性: 属性数据类型
        column 属性: 列的意思,使用那个列作为条件进行第二次查询
        select 属性: 封装的第二次查询SQL语句
    -->
    <association property="user" javaType="user" column="user_id" select="queryUserByUserId">
       <!-- <id column="uid" property="id"/>
        <result column="username" property="username"/>
        <result column="sex" property="sex"/>
        <result column="birthday" property="birthday"/>
        <result column="address" property="address"/>-->
    </association>
</resultMap>

第二次查询的SQL语句

<!--拆分后的第二次SQL语句,查询用户,条件是用户的主键 = 订单表的user_id-->
<select id="queryUserByUserId" parameterType="Integer" resultType="user">
    SELECT u.id,u.username,u.sex,u.birthday,u.address from user u where id = #{id}
</select>
@Test
/**
 * 查询语句,SQL查询了订单和用户
 * 运行时期,输出订单数据,没有用户数据
 * 查询用户数据是浪费资源
 * 延迟查询,需要的就查,不需要不查,不用该代码
 */
public void testQueryOrdersUser(){
    SqlSession sqlSession = sqlSessionFactory.openSession();
    OrdersMapper mapper = sqlSession.getMapper(OrdersMapper.class);
    List<Orders> ordersList = mapper.queryOrdersUser();
    for (int i = 0; i < ordersList.size(); i++) {
        Orders orders =  ordersList.get(i);
        //输出对象,打印toString()
        System.out.println(orders.getId()+"::"+orders.getUserId()+"::"+orders.getNumber()+"::"+orders.getUser());
     //   System.out.println(orders);
    }
}

2 开启延迟加载配置

<settings>
    <!-- 开启延迟加载-->
    <setting name="lazyLoadingEnabled" value="true" />
    <!-- 关闭立即加载-->
    <setting name="aggressiveLazyLoading" value="false" />
    <!-- 设定tostring等方法延迟加载-->
    <setting name="lazyLoadTriggerMethods" value="true" />
</settings>

3 一对多延迟加载

一个用户对应多个订单:

SELECT u.id uid,u.username,u.sex,u.birthday,u.address,o.id,o.user_id,o.number,o.createtime,o.note
FROM USER u LEFT OUTER JOIN orders o ON u.id = o.user_id;

以用户为基准,查询订单,一对多查询
SQL语句的拆分,拆分为查询用户

<select id="queryUserOrders" resultMap="queryUserOrdersResultMap">
    select u.id uid,u.username,u.sex,u.birthday,u.address from user u
</select>

配置一对多映射:

<!--一对多查询手动映射-->
<resultMap id="queryUserOrdersResultMap" type="user">
    <!--配置User表-->
    <id column="uid" property="id"/>
    <result column="username" property="username"/>
    <result column="sex" property="sex"/>
    <result column="birthday" property="birthday"/>
    <result column="address" property="address"/>

    <!--
            配置的是User对象的属性 ,不是单一的对象,是集合容器
            collection 配置的是一对多
            属性property: 配置pojo对象的属性
            属性ofType:集合泛型
            属性column: 以上一个SQL的那个列的结果,进行第二次查询
    -->
    <collection property="ordersList" ofType="orders" column="uid" select="queryOrderByUserId">
        <!--<id column="id" property="id"/>-->
        <!--<result column="user_id" property="userId"/>-->
        <!--<result column="number" property="number"/>-->
        <!--<result column="createtime" property="createtime"/>-->
        <!--<result column="note" property="note"/>-->
    </collection>
</resultMap>

配置第二次查询语句:

<!--
  配置第二次查询语句
  queryUserByUserId
-->
<select id="queryOrderByUserId" parameterType="Integer" resultType="orders">
    SELECT o.id,o.user_id,o.number,o.createtime,o.note FROM orders o where user_id=#{id}
</select>
@Test
/**
 * 多表查询
 * 用户对订单的一对多查询
 */
public void testQueryUserOrders(){
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList = mapper.queryUserOrders();
    for (int i = 0; i < userList.size(); i++) {
        User user =  userList.get(i);
        System.out.println(user.getUsername()+"::"+user.getOrdersList());
    }
}

4 MyBatis缓存

4.1 MyBatis一级缓存(同一个sqlsession)

一级缓存:是SqlSession级别的,也就是同一个SqlSession内执行相同select语句的时候,不再去查询数据库,而是从Mybatis内部的缓存内存结构去直接拿到数据。

  • 缓存失效时机:
    • sqlSession关闭
    • sqlSession提交事务(意味着可能是一个增删改的动作,需要更新缓存,那么这个时候Mybatis就会把已有的一级缓存给清理掉)

一级缓存针对SQLSession对象

4.2 MyBatis二级缓存(不同的sqlsession)

在这里插入图片描述

二级缓存针对:xxxMapper.xml文件
二级缓存默认是关闭的。
有风险:第一次查完,缓存中就有了,第二次将要查,然后有人执行了update操作,这样第二次读取的是缓存中的未更新的。不让使用二级缓存(有风险)。

5 MyBatis注解开发

注解开发: 注解取消xml配置文件

5.1 注解实现添加数据

@Insert注解

  • 注解属性value:写入SQL语句

@Options注解(功能一样:可替代SelectKey注解)

  • 实现添加新数据的主键封装
  • 注解属性
    • useGeneratedKeys:使用生成的主键,配置为true
    • keyProperty:主键封装的pojo对象属性

@SelectKey注解

  • 实现添加新数据的主键封装
  • 注解属性
    • statement:要执行的SQL语句
    • before:在添加SQL语句之前还是之后进行,配置为false
    • keyProperty:主键封装的pojo对象属性
@Insert("insert into user values (null,#{username},#{sex},#{birthday},#{address})")
@Options(useGeneratedKeys = true,keyProperty="id")
//@SelectKey(statement = "SELECT LAST_INSERT_ID ()",before = false,keyProperty = "id",resultType = Integer.class)
int saveUser(User user);

5.2 注解实现更新数据

@Update注解

  • 注解属性value:写入SQL语句
/**
 * 更新数据 @Update注解
 */
@Update("update user set username=#{username},sex=#{sex},address=#{address} where id=#{id}")
int updateUser(User user);

5.3 注解实现删除数据

注解属性value:写入SQL语句

//删除用户
@Delete("delete from user where id = #{id}")
void deleteUser(int id);

动态sql:

/**
 * 根据多个主键删除用户
 * 标签 foreach
 */
@Delete("<script>delete from user where id in <foreach" +
        " collection=\"list\" open=\"(\" close=\")\" separator=\",\" item=\"id\">" +
        "#{id}" +
        "</foreach></script>")
int deleteUserById(List<Integer> list);

5.4 注解实现主键查询用户数据

@Select注解

  • 注解属性value:写入SQL语句
/**
 * 定义方法,根据主键查询用户
 */
@Select("select id ,username,sex,birthday,address from user where id = #{id} ")
User queryUserById(Integer id);

动态sql:

/**
 * 查询用户表,条件不确定,使用动态SQL
 *
 */
@Select("<script>select id ,username,sex,birthday,address from user <where>" +
        "<if test=\"username!='' and username != null\">" +
        " and username like #{username}" +
        "</if>" +
        "<if test=\"sex!='' and sex !=null\">" +
        "  and sex = #{sex}" +
        "</if>" +
        "</where></script>")
List<User> queryUserByWhere(User user);

5.5 一对一的注解开发

@Results注解

  • 配置手动映射,取代resultMap标签

@Result注解

  • 配置手动映射,取代result标签
* 订单对用户的一对一查询
* 延迟加载
*
*  配置手动映射的注解
*  @Results
*  注解属性: Result[] value() default {};
*  属性是另一个注解数组
*
*  @Result注解
*    注解属性 boolean id() default false; 是不是主键
*    注解属性 column 数据表列名
*    注解属性 property pojo对象的属性名
*    注解属性 one 配置一对一的
*    注解属性 javaType 查询结果存储的数据类型
*
*    @One注解:
*      属性 select 关联的第二次查询语句
*        SQL2: 根据主键查询用户,写在了 UserMapper接口上
*        查询的条件 id = 订单表的user_id值
*      属性fetchType 加载类型
@Select(" select o.id,o.user_id,o.number,o.createtime,o.note from orders o")
@Results({
        @Result(id = true, column = "id",property = "id"),
        @Result(column = "userId",property = "user_id"),
        @Result(column = "number",property = "number"),
        @Result(column = "createtime",property = "createtime"),
        @Result(column = "note",property = "note"),
        @Result(column = "user_id" ,property = "user", javaType = User.class,
                one = @One(select = "com.itheima.mapper.UserMapper.queryUserById",fetchType = FetchType.LAZY) )
})
List<Orders> queryOrdersUser();

5.6 一对多注解开发

 * 定义方法,用户对订单一对多
 * 延迟加载
 * @Results配置映射
 *
 * 用户的主键,查询订单的外键 user_id
 *
 * select = "" 根据用户主键查询订单
@Select("select u.id ,u.username,u.sex,u.birthday,u.address from user u")
@Results({
        @Result(id = true,column = "id",property = "id"),
        @Result(column = "username" , property ="username" ),
        @Result(column = "sex" , property ="sex" ),
        @Result(column = "birthday" , property ="birthday" ),
        @Result(column = "address" , property ="address" ),
        @Result(column = "id",property ="ordersList", javaType = List.class,
                many = @Many(select = "com.itheima.mapper.OrdersMapper.queryOrdersByUserId",fetchType = FetchType.LAZY))
})
List<User> queryUserOrders();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值