任务三:加载策略及注解开发

Mybatis任务三:加载策略及注解开发

MyBatis加载策略

1.1 什么是延迟加载?

问题

通过前面的学习,我们已经掌握了Mybatis中一对一,一对多,多对多关系的配置及实现,可以实现对象的关联查询。实际开发过程中很多时候我们并不需要总是在加载用户信息时就一定要加载他的订单信息。此时就是我们所说的延迟加载。

举个栗子

  • 在一对多中,当我们有一个用户,它有个100个订单

    • 在查询用户的时候,要不要把关联的订单查出来?
    • 在查询订单的时候,要不要把关联的用户查出来?
  • 回答

    • 在查询用户时,用户下的订单应该是,什么时候用,什么时候查询。
    • 在查询订单时,订单所属的用户信息应该是随着订单一起查询出来。

延迟加载

就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载

  • 优点先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表 速度要快。

  • 缺点:因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时 间,所以可能造成用户等待时间变长,造成用户体验下降

  • 在多表中:

    • 一对多,多对多:通常情况下采用延迟加载
    • 一对一(多对一):通常情况下采用立即加载
  • 注意:延迟加载是基于嵌套查询来实现的

1.2 实现

1.2.1 局部延迟加载

当某一个方法需要延迟加载时,就在其配置的mapper.xml文件中的resultMap中进行单独配置,在association和collection标签中都有一个fetchType属性,通过修改它的值,可以修改局部的加载策略。 fetchType=“lazy”:表示延迟加载策略; fetchType=“eager”:表示立即加载策略

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>

        <!--
            fetchType="lazy":表示延迟加载策略
            fetchType="eager":表示立即加载策略
            -->
        <collection property="orderList" ofType="com.myLagou.entity.Orders" column="id"
                    select="com.myLagou.mapper.OrderMapper.findAllByUid"
                    fetchType="lazy">
        </collection>
    </resultMap>

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

1.2.2 设置触发延迟加载的方法

大家在配置了延迟加载策略后,发现即使没有调用关联对象的任何方法,但是在你调用当前对象的equals、clone、hashCode、toString方法时也会触发关联对象的查询。

我们可以在mbatis核心配置文件中使用lazyLoadTriggerMethods配置项覆盖掉上面四个方法。

sqlMapConfig.xml

<settings>
        <!--所有方法都会延迟加载
            value="toString()"表示设置了延迟加载的当前对象调用toString方法时会进行延迟加载
        -->
        <setting name="lazyLoadTriggerMethods" value="toString()"/>
    </settings>

测试

 /*一对多嵌套查询的需求:查询所有用户,与此同时查询出该用户具有的订单信息*/
    @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);
            System.out.println("==========================");
            //当设置了延迟加载后,若需要用到用户的订单信息,就是要如下方法去触发所延迟的sql语句
            List<Orders> orderList = user.getOrderList();
            for (Orders orders : orderList) {
                System.out.println(orders);
            }
            System.out.println();
        }
        sqlSession.close();
    }

1.2.3 全局延迟加载

当所有的方法都需要延迟加载时,如果一个一个在其配置的mapper.xml文件中的resultMap中进行配置,在association和collection标签中添加一个fetchType属性,那么将会很麻烦,代码过于繁琐重复。所以可以在Mybatis的核心配置文件中可以使用setting标签修改全局的加载策略

sqlMapConfig.xml

 <settings>
        <!--所有方法都会延迟加载    
            value="toString()"表示设置了延迟加载的当前对象调用toString方法时会进行延迟加载。当实体类重写了toString方法,且要开启全局延迟策略,则需要这句代码来覆盖掉toString方法,防止全局加载失效。
        -->
        <setting name="lazyLoadTriggerMethods" value="toString()"/>

        <!--开启全局延迟加载功能     全局延迟不需要与association和collection标签中的fetchType配合使用-->
        <setting name="lazyLoadingEnabled" value="true"/>
    </settings>

注意

局部的加载策略优先级高于全局的加载策略。

当开启全局延迟加载策略时,适合于立即加载的一对一嵌套查询就需要开启局部立即加载策略

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>

        <association property="user" javaType="com.myLagou.entity.User"
                     select="com.myLagou.mapper.UserMapper.findById" column="uid" fetchType="eager">
                        <!--fetchType="eager"开启立即加载,局部的加载策略优先级高于全局的加载策略-->
        </association>
    </resultMap>
<!--    /*一对一嵌套查询:查询所有的订单,并且查出每个订单所属的用户信息*/-->
    <select id="findAllWithUser2" resultMap="orderMap2">
        select * from orders
    </select>

MyBatis缓存

2.1 为什么使用缓存?

当用户频繁查询某些固定的数据时,第一次将这些数据从数据库中查询出来,保存在缓存中。当用户再次查询这些数据时,不用再通过数据库查询,而是去缓存里面查询。减少网络连接和数据库查询带来的损耗,从而提高我们的查询效率,减少高并发访问带来的系统性能问题。

**一句话概括:**经常查询一些不经常发生变化的数据,使用缓存来提高查询效率

像大多数的持久化框架一样,Mybatis也提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提高性能。 Mybatis中缓存分为一级缓存,二级缓存

2.2 一级缓存

2.2.1 介绍

一级缓存是SqlSession级别的缓存,是默认开启的

所以在参数和SQL完全一样的情况下,我们使用同一个SqlSession对象调用一个Mapper方法,往往只执行一次SQL,因为使用SelSession第一次查询后,MyBatis会将其放在缓存中,以后再查询的时候,如果没有声明需要刷新,并且缓存没有超时的情况下,SqlSession都会取出当前缓存的数据,而不会再次发送SQL到数据库。

在这里插入图片描述

2.2.2 验证

2.2.2.1 添加日志文件

log4j.properties

### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=c:/mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### set log levels - for more verbose logging change 'info' to 'debug' ###

log4j.rootLogger=debug, stdout

添加依赖

    <!--log4j日志记录-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.7</version>
        </dependency>
2.2.2.2 测试

在测试类中添加测试一级缓存的方法


    /*验证mybatis中的一级缓存*/
    @Test
    public void testOneCache() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        //根据id查询用户信息
        User user1 = userMapper.findById(11);//第一次查询,查询的是数据库
        System.out.println(user1);

        User user2 = userMapper.findById(11);//第二次查询,查询的是一级缓存
        System.out.println(user2);

        sqlSession.close();
    }

通过debug查看日记信息,可以发现第一次查询时控制台有输出sql语句

在这里插入图片描述

在执行第二次查询时,却没有控制台还是只有第一次查询时的sql语句,并没有新的sql输出

在这里插入图片描述

我们可以发现,虽然在上面的代码中我们查询了两次,但最后只执行了一次数据库操作,这就是Mybatis提供给我们的一级缓存在起作用了。因为一级缓存的存在,导致第二次查询id为1的记录时,并没有发出sql语句从数据库中查询数据,而是从一级缓存中查询。

在这里插入图片描述

4.2.3 分析

一级缓存是SqlSession范围的缓存,执行SqlSession的C(增加)U(更新)D(删除)操作,或者调用clearCache()、commit()、close()方法,都会清空缓存

在这里插入图片描述

  1. 第一次发起查询用户id为41的用户信息,先去找缓存中是否有id为41的用户信息,如果没有,从数据库 查询用户信息。

  2. 得到用户信息,将用户信息存储到一级缓存中。

  3. 如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这 样做的目的为了让缓存中存储的是最新的信息,避免脏读

  4. 第二次发起查询用户id为41的用户信息,先去找缓存中是否有id为41的用户信息,缓存中有,直接从缓 存中获取用户信息。

4.2.4 清除

/*验证mybatis中的一级缓存被清空*/
    @Test
    public void testClearOneCache() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        //根据id查询用户信息
        User user1 = userMapper.findById(11);//第一次查询,查询的是数据库
        System.out.println(user1);

        //手动调用clearCache方法,手动清空缓存,还可以通过增删改操作来使缓存清空
        sqlSession.clearCache();
        //当在UserMapper.xml文件中配置flushCache="true"说明了每次查询时都清空缓存,就不需要调用此方法了

        User user2 = userMapper.findById(11);//第二次查询,查询的是一级缓存
        System.out.println(user2);

        sqlSession.close();
    }
  1. 第一次发起查询用户id为11的用户信息,先去找缓存中是否有id为11的用户信息,如果没有,从数据库 查询用户信息。

  2. 得到用户信息,将用户信息存储到一级缓存中。

  3. sqlSession调用clearCache方法,手动使得缓存被清空 (sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这 样做的目的为了让缓存中存储的是最新的信息,避免脏读)。

  4. 第二次发起查询用户id为11的用户信息,先去找缓存中是否有id为11的用户信息,由于缓存被手动清空了,所以需要重新去数据库中查询,再将查询结果存储到一级缓存中。

除了调用清空方法和执行增删改等事务操作可以清空一级缓存,还可以在XxxMapper.xml文件中指定某个方法每次查询时都会清空缓存

UserMapper.xml

<!--/*根据id查询用户*/-->
  <!--flushCache="true"表示每次查询时,都会清除缓存-->
    <select id="findById" resultType="user" parameterType="int" flushCache="true">
        select * from user where id = #{id}
    </select>

2.3 二级缓存

2.3.1 介绍

二级缓存是namspace级别(跨sqlSession)的缓存,是默认不开启的,namespace级别也可以理解为mapper映射级别

二级缓存的开启需要进行配置,实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的

也就是要求实现Serializable接口,配置方法很简单,只需要在映射XML文件配置 <cache/> 就可以开启二级缓存了

在这里插入图片描述

2.3.2 验证

a)配置mybatis核心配置文件

   <settings>
 

        <!--显示开启二级缓存-->
         <!--因为cacheEnabled的取值默认就为true,所以这一步可以省略不配置。
         为true代表开启二级缓存;为false代表不开启二级缓存。 -->
        <setting name="cacheEnabled" value="true"/>
    </settings>

b)配置UserMapper.xml映射

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

    <!--当前mapper映射开启二级缓存    如果希望更细致的在具体某个方法是配置,就可以在其方法上使用useCache属性-->
    <cache></cache>

<!--/*根据id查询用户*/-->
    <!--flushCache="true"表示每次查询时,都会清除缓存-->
    <!--useCache="true":表示当前这个statement使用二级缓存-->
    <select id="findById" resultType="user" parameterType="int" useCache="true">
        select * from user where id = #{id}
    </select>
    
</mapper>

c)修改User实体

二级缓存的开启需要进行配置,实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的,使用User类需要实现可序列化接口Serializable

public class User implements Serializable {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

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

    // 代表当前用户关联的角色列表
    private List<Role> roleList;
    
    //get、set等方法略。。。
    
}

d)测试结果

测试类

/*验证mybatis中的二级缓存*/
    @Test
    public void testTwoCache() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

        SqlSession sqlSession1 = sqlSessionFactory.openSession();

        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
        //第一次查询
        User user1 = userMapper1.findById(1);
        System.out.println(user1);
        //只有执行了sqlSession.close()或者sqlSession.commit(),那么一级缓存中的内容才会刷新到二级缓存中
        sqlSession1.close();

        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
        //第二次查询
        User user2 = userMapper2.findById(1);
        System.out.println(user2);

        sqlSession2.close();
    }

从debug运行结果可以看出,两次查询只有一次输出sql语句

在这里插入图片描述

2.3.3 分析

二级缓存是mapper映射级别的缓存,多个SqlSession去操作同一个Mapper映射的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的

在这里插入图片描述

  1. 映射语句文件中的所有select语句将会被缓存。

  2. 映射语句文件中的所有insert、update和delete语句会刷新缓存

2.3.4 注意问题(脏读)

mybatis的二级缓存因为是namespace级别,所以在进行多表查询时会产生脏读问题

在这里插入图片描述

2.4 小结

  1. mybatis的缓存,都不需要我们手动存储和获取数据。mybatis自动维护的。

  2. mybatis开启了二级缓存后,那么查询顺序:二级缓存–》一级缓存–》数据库

  3. 注意:mybatis的二级缓存会存在脏读问题,需要使用第三方的缓存技术解决问题。

MyBatis注解

3.1 MyBatis常用注解

这几年来注解开发越来越流行,Mybatis也可以使用注解开发方式,这样我们就可以减少编写Mapper映射文件了。我们先围绕一些基本的CRUD来学习,再学习复杂映射多表操作。

* @Insert:实现新增,代替了<insert> </insert>

* @Delete:实现删除,代替了<delete> </delete>

* @Update:实现更新,代替了<update> </update>

* @Select:实现查询,代替了<select> </select>

* @Result:实现结果集封装,代替了<result> </result>

* @Results:可以与@Result 一起使用,封装多个结果集,代替了<resultMap> </resultMap>

* @One:实现一对一结果集封装,代替了<association> </association>

* @Many:实现一对多结果集封装,代替了<collection> </collection>

3.2 MyBatis注解的增删改查【重点】

重新创建一个模块为mybatis_anno,用来测试mybatis的注解开发

3.2.1 创建UserMapper接口

package com.myLagou.mapper;

import com.myLagou.entity.User;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import java.util.List;

/**
 * @author zhy
 * @create 2022-08-10 14:11
 */

/*使用注解的方式来开发mybatis*/
public interface UserMapper {
    /*查询所有用户*/
    @Select(value = "select * from user")//@Select注解表示查询,其括号里的value就是调用findAll此方法时需要执行的sql语句
    public List<User> findAll();

    /*添加用户*/
    @Insert("insert into user(username,birthday,sex,address) values(#{username}, #{birthday}, #{sex}, #{address})")
    public void save(User user);

    /*更新用户部分信息*/
    @Update("update user set username = #{username}, birthday = #{birthday} where id = #{id}")
    public void update(User user);

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

3.2.2 编写核心配置文件

  <!--我们使用了注解替代的映射文件,所以我们只需要加载使用了注解的Mapper接口即可--> 
<mappers> 
    <!--扫描使用注解的Mapper类--> 
    <mapper class="com.myLagou.mapper.UserMapper"></mapper> 
</mappers>


<!--我使用的这一种-->
<mappers>
        <!--批量加载映射文件-->
        <package name="com.myLagou.mapper"/>
    </mappers>

3.2.3 测试代码

MybatisAnnoTest类

package com.myLagou.test;

import com.myLagou.entity.User;
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.After;
import org.junit.Before;
import org.junit.Test;

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

/**
 * @author zhy
 * @create 2022-08-10 14:22
 */
public class MybatisAnnoTest {
    private SqlSessionFactory sqlSessionFactory;
    private SqlSession sqlSession;

    /*@Before注解表示在 @Test 这个注解标注的方法之前先执行*/
    @Before
    public void before() throws IOException{
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
         sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
         sqlSession = sqlSessionFactory.openSession();
    }

    /*@After注解表示在 @Test 这个注解标注的方法执行之后再执行*/
    @After
    public void after(){
        sqlSession.commit();
        sqlSession.close();
    }

    /*测试查询所有方法*/
    @Test
    public void testSelect()  {
        //前面创建的sqlSessionFactory以及sqlSession以及被抽取到模板方法中
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.findAll();
        for (User user : userList) {
            System.out.println(user);
        }
    }


    /*测试添加方法*/
    @Test
    public void testSave(){
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = new User();
        user.setUsername("肖奈");
        user.setBirthday(new Date());
        user.setSex("男");
        user.setAddress("众多女性的心里!!!");
        mapper.save(user);
        System.out.println(user);
    }

    /*测试更新方法*/
    @Test
    public void testUpdate(){
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = new User();
        user.setUsername("肖奈斯哈");
        user.setBirthday(new Date());
        user.setId(12);
        mapper.update(user);

        System.out.println(user);
    }

    /*测试删除方法*/
    @Test
    public void testDelete(){
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.delete(3);
    }

}

3.3 使用注解实现复杂映射开发

之前我们在映射文件中通过配置 <resultMap>、<association>、<collection> 来实现复杂关系映射。

使用注解开发后,我们可以使用 @Results、@Result,@One、@Many 注解组合完成复杂关系的配置

在这里插入图片描述

3.4 一对一查询

3.4.1 介绍

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

一对一查询语句

SELECT * FROM orders; 
SELECT * FROM `user` WHERE id = #{订单的uid};

3.4.2 代码实现

a)OrderMapper接口

/*一对一查询所有订单,与此同时查询出该订单所属的用户*/
    @Select( "select * from orders" )
    /*@Results注解替代的就是resultMap标签
    * @Result注解替代的就是id和result标签,在@Result注解中使用id = true来区分主键字段
    * */
    @Results({
            @Result(property = "id", column = "id", id = true),
            @Result(property = "ordertime", column = "ordertime"),
            @Result(property = "total", column = "total"),
            @Result(property = "uid", column = "uid"),
            /*下面开始配置Order类中的User对象属性,javaType指定类型,one = @One()来配置剩下的sql操作*/
            @Result(property = "user", javaType = User.class,column = "uid",one = @One(
                    /*select = "namespace.id"用来配置所需要调用的另外的sql,在使用注解时,也就是xxxMapper的全路径.方法名*/
                    select = "com.myLagou.mapper.UserMapper.findById", fetchType = FetchType.EAGER
                    /*fetchType = FetchType.EAGER设置此一对一查询为立即加载策略*/
            ))
    })
    public List<Orders> findAllWithUser();

b)UserMapper接口

 /*根据id查询用户*/
    @Select("select * from user where id = #{id}")
    public User findById(Integer uid);

c)测试代码

    /*测试一对一查询的注解方式*/
    @Test
    public void testFindAllWithUser(){
        OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
        List<Orders> ordersList = mapper.findAllWithUser();
        for (Orders orders : ordersList) {
            System.out.println(orders);
        }
    }
//注意跟一开始的模板代码结合

3.5 一对多查询

3.5.1 介绍

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

一对多查询语句

SELECT * FROM `user`;
SELECT * FROM orders where uid = #{用户id};

3.5.2 代码实现

a) UserMapper接口

/*一对多查询,查询用户,与此同时查询出该用户具有的订单*/ 
    @Select("select * from user")
    @Results({
            @Result(property = "id", column = "id", id=true),
            @Result(property = "username", column = "username"),
            @Result(property = "birthday", column = "birthday"),
            @Result(property = "sex", column = "sex"),
            @Result(property = "address", column = "address"),
            /*many = @Many()来配置一对多查询剩下的sql操作*/
              @Result(property = "orderList", javaType = List.class, column = "id", many = @Many(
                      select = "com.myLagou.mapper.OrderMapper.findOrderByUid"
              )),
    })
    public List<User> findAllWithOrders();

b) OrderMapper接口

  /*根据用户id查询该用户所拥有的订单*/
    @Select("select * from orders where uid = #{uid}")
    public List<Orders> findOrderByUid(Integer uid);

c)测试代码

/*测试一对多查询的注解方式*/
    @Test
    public void testFindAllWithOrders(){
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.findAllWithOrders();
        for (User user : userList) {
            System.out.println(user);
            System.out.println("=========================================");
            //因为配置了延迟加载策略,所以需要手动触发第二部分的sql查询
            List<Orders> orderList = user.getOrderList();
            for (Orders orders : orderList) {
                System.out.println(orders);
            }
            System.out.println();//换行
        }
    }

对于一对多的注解查询分析

在这里插入图片描述

3.6 多对多查询

多对多查询拆分来看就是两个一对多的查询

3.6.1 介绍

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

多对多查询语句

SELECT * FROM `user`; 

SELECT * FROM role r INNER JOIN user_role ur ON r.`id` = ur.`rid`
	WHERE ur.`uid` = #{用户id};

3.6.2 代码实现

a)UserMapper接口

   /*多对多查询   查询所有用户以及用户所关联的角色*/
    @Select("select * from user")
    @Results({
            @Result(property = "id", column = "id", id=true),
            @Result(property = "username", column = "username"),
            @Result(property = "birthday", column = "birthday"),
            @Result(property = "sex", column = "sex"),
            @Result(property = "address", column = "address"),
                @Result(property = "roleList", javaType = List.class, column = "id", many = @Many(
                        select = "com.myLagou.mapper.RoleMapper.findAllByUid"
                ))
    })
    public List<User> findAllWithRole();

b)RoleMapper接口

public interface RoleMapper {
    
    /*根据用户id查询该用户具有的角色信息*/
    @Select("select * from sys_role r inner join sys_user_role ur on ur.roleid = r.id where ur.userid = #{uid}")
    public List<Role> findAllByUid(Integer uid);
}

c)测试代码

    /*测试多对多查询的注解方式*/
    @Test
    public void testFindAllWithRole(){
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.findAllWithRole();
        for (User user : userList) {
            System.out.println(user);
            System.out.println("=========================================");
            //因为配置了延迟加载策略,所以需要手动触发第二部分的sql查询
            List<Role> roleList = user.getRoleList();
            for (Role role : roleList) {
                System.out.println(role);
            }
            System.out.println();//换行
        }
    }

3.7 基于注解的二级缓存

3.7.1 配置SqlMapConfig.xml文件开启二级缓存的支持

    <settings>

        <!--显示开启二级缓存-->
         <!--因为cacheEnabled的取值默认就为true,所以这一步可以省略不配置。
         为true代表开启二级缓存;为false代表不开启二级缓存。 -->
        <setting name="cacheEnabled" value="true"/>
    </settings>

3.7.2 在Mapper接口中使用注解配置二级缓存

@CacheNamespace//使用此注解开启了此Mapper的二级缓存
public interface UserMapper {
    
        .
        .
        .
}

3.7.3 测试

   /*测试注解实现二级缓存*/
    @Test
    public void testCache(){
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        SqlSession sqlSession2 = sqlSessionFactory.openSession();

        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
        UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);

        User user1 = userMapper1.findById(11);
        System.out.println(user1);
        //先将sqlSession1给关闭,才能将缓存从一级刷新到二级缓存
        sqlSession1.close();

        User user2 = userMapper2.findById(11);
        System.out.println(user2);
        sqlSession2.close();
    }

3.8 注解延迟加载

不管是一对一还是一对多 ,在注解配置中都有fetchType的属性

* fetchType = FetchType.LAZY 表示懒加载

* fetchType = FetchType.EAGER 表示立即加载

* fetchType = FetchType.DEFAULT 表示使用全局配置

一对一 一般选择立即加载,一对多或者多对多一般选择延迟加载

3.9 小结

* 注解开发和xml配置优劣分析

1.注解开发和xml配置相比,从开发效率来说,注解编写更简单,效率更高。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值