【SSM开发框架】Mybatis加载策略与注解开发

笔记输出来源:拉勾教育Java就业急训营
如有侵权,私信立删

修改时间:2020年2月19日
作者:pp_x
邮箱:[email protected]

MyBatis加载策略

延迟加载

  • 实际开发过程中很多时候我们并不需要总是在加载用户信息时就一定要加载他的订单信息。此时就是我们所说的延迟加载
  • 就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载
  • 延迟加载基于嵌套查询

延迟加载优缺点

  • 优点
    • 先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快
  • 缺点
    • 因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降
  • 在多表中
    • 一对多,多对多:通常情况下采用延迟加载
    • 一对一(多对一):通常情况下采用立即加载

延迟加载的实现

局部延迟加载

  • 在association和collection标签中都有一个fetchType属性,通过修改它的值,可以修改局部的加载策略
<resultMap id="userOrderMap" type="com.lagou.domain.User">
        <id property="id" column="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="ordersList" ofType="com.lagou.domain.Orders" column="id"
                    select="com.lagou.mapper.OrderMapper.findByUid" fetchType="lazy" ></collection>

    </resultMap>

    <select id="findAllWithOrder2" resultMap="userOrderMap">
        SELECT * FROM USER
    </select>

设置触发延迟加载的方法

  • 在配置了延迟加载策略后,发现即使没有调用关联对象的任何方法,但是在你调用当前对象的equals、clone、hashCode、toString方法时也会触发关联对象的查询
  • 可以在配置文件中使用lazyLoadTriggerMethods配置项覆盖掉上面四个方法。
<settings> 
	<!--所有方法都会延迟加载--> 
	<setting name="lazyLoadTriggerMethods" value="toString()"/> 
</settings>

全局延迟加载

  • 在Mybatis的核心配置文件中可以使用setting标签修改全局的加载策略。
<settings> 
	<!--开启全局延迟加载功能--> 
	<setting name="lazyLoadingEnabled" value="true"/> 
</settings>

注意局部加载策略优先级高于全局加载策略

Mybatis缓存

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

一级缓存

  • 一级缓存是SqlSession级别的缓存,是默认开启的
  • 所以在参数和SQL完全一样的情况下,我们使用同一个SqlSession对象调用一个Mapper方法,往往只执行一次SQL,因为使用SelSession第一次查询后,MyBatis会将其放在缓存中,以后再查询的时候,如果没有声明需要刷新,并且缓存没有超时的情况下,SqlSession都会取出当前缓存的数据,而不会再次发送SQL到数据库。
    在这里插入图片描述
/*
        验证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(1);
        System.out.println(user1);

        // clearCache: 手动清空缓存
        //sqlSession.clearCache();

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

        sqlSession.close();

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

二级缓存

  • 二级缓存是namspace级别(跨sqlSession)的缓存,是默认不开启的
  • 二级缓存的开启需要进行配置,实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的。也就是要求实现Serializable接口,配置方法很简单,只需要在映射XML文件配置<cache/>就可以开启二级缓存了
    在这里插入图片描述
  • xml核心配置
  <!--
       因为cacheEnabled的取值默认就为true,所以这一步可以省略不配置。
       为true代表开启二级缓存;为false代表不开启二级缓存。
   -->
        <setting name="cacheEnabled" value="true"/>
  • 映射文件xml
 <!--当前映射开启二级缓存-->
    <cache></cache>

 <!--根据id查询用户
        useCache="true" 代表当前这个statement是使用二级缓存
    -->
    <select id="findById" resultType="com.lagou.domain.User" parameterType="int" useCache="true">
        SELECT * FROM user WHERE id = #{id}
    </select>
  • 二级缓存是mapper映射级别的缓存,多个SqlSession去操作同一个Mapper映射的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
  • 映射语句文件中的所有select语句将会被缓存
  • 映射语句文件中的所有insert、update和delete语句会刷新缓存

二级缓存的脏读问题

  • mybatis的二级缓存因为是namespace级别,所以在进行多表查询时会产生脏读问题
  • 不推荐使用,经常使用第三方缓存技术

Mybatis注解

  • 这几年来注解开发越来越流行,Mybatis也可以使用注解开发方式,这样我们就可以减少编写Mapper映射文件了

常用注解

  • @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>

代码实现

public interface UserMapper {
    
    
    /**
     * 查询用户
     * @return
     */
    @Select("select * from user")
    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);

  • 核心配置文件
<mappers> 
<!--扫描使用注解的Mapper类-->
 <mapper class="com.lagou.mapper.UserMapper"></mapper>
</mappers>

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

  • 可以使用 **@Results、@Result,@One、@Many **注解组合完成复杂关系的配置
    -
  • 一对一
/**
     * 根据id查询用户
     */
    @Select("select *  from user where id = #{id}")
    public User findById(Integer uid);
    
 /**
     * 查询所有订单 同时查询订单所属的用户信息
     */
    @Select("select * from orders")
    @Results({
    
    //代替的就是resultMap标签
            @Result(property = "id",column = "id",id = true),//代表为主键字段
            @Result(property = "ordertime",column = "ordertime"),
            @Result(property = "total",column = "total"),
            @Result(property = "uid",column = "uid"),
            @Result(property = "user",javaType = User.class,column = "uid" ,one = @One(select="com.lagou.mapper.UserMapper.findById",fetchType = FetchType.EAGER))
             })
    public List<Orders> findAllWithUser();
  • 一对多
 /**
     * 查询所有用户及关联的订单信息
     */
    @Select("select * from user")
    @Results({
    
    
            @Result(property = "id",column = "id",id = true),
            @Result(property = "username",column = "username"),
            @Result(property = "sex",column = "sex"),
            @Result(property = "birthday",column = "birthday"),
            @Result(property = "address",column = "address"),
            @Result(property = "ordersList",javaType = List.class,column = "id",many = @Many(select = "com.lagou.mapper.OrderMapper.findByUid",fetchType = FetchType.EAGER))
    })
    public List<User> findAllWithOrder();
  • 多对多
 /**
     * 查询所有永固及关联的角色信息
     *
     */
    @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.lagou.mapper.RoleMapper.findAllByUid",fetchType = FetchType.EAGER))
    })
    public List<User> findAllWithRole();

基于注解的二级缓存

@CacheNamespace
public interface UserMapper {
    
    ...}

注解延迟加载

  • fetchType = FetchType.LAZY 表示懒加载
  • fetchType = FetchType.EAGER 表示立即加载
  • fetchType = FetchType.DEFAULT 表示使用全局配置

注解开发和xml开发

  • 注解开发和xml配置优劣分析
    • 注解开发和xml配置相比,从开发效率来说,注解编写更简单,效率更高
    • 从可维护性来说,注解如果要修改,必须修改源码,会导致维护成本增加。xml维护性更强

猜你喜欢

转载自blog.csdn.net/weixin_46303867/article/details/113870441