Mybatis高阶

延迟加载

介绍

  • 延迟加载:不会立即查询,而是实际使用数据时再查询
  • associationcollection 都是在加载关系对方的数据,但在查询一方时,不一定需要另一方数据,此时可以利用延迟加载特性 做出适应

打开mysql日志功能

  • 找到mysql的C:\ProgramData\MySQL\MySQL Server 5.7\my.ini文件,修改配置,将mysql日志输出到本地磁盘,用于观测mysql实际执行了哪些查询动作
general-log=1  //1,打开日志功能。0,关闭日志功能
general_log_file="d:/LAPTOP-BT26GJ86.log"  //日志生成路径

association

  • fetchType:默认eager,急加载,加载一个实体时,定义急加载的属性会立即从数据库中加载。
  • fetchType:lazy 懒加载,加载一个实体时,定义懒加载的属性不会马上从数据库中加载,用到时才会加载。
  • select:使用到property规定的属性,就会去触发select设置的方法。一个可以查询passoport的方法:queryOne
  • column="uid"是将本次查询的uid列的值传递给方法:queryOne
  • 例子:
  <association property="user" column="uid" select="com.xxx.mapper.UserMapper.findByIdUser" fetchType="lazy"> </association>

collection

  • 属性作用同上
  • 会触发查询的方法有:toString, hashCode, equals, clone
  • 例子:
  <collection property="list" column="id" select="com.xxx.mapper.OrderMapper.findById" fetchType="lazy"></collection>

缓存

介绍

  • 缓存:将数据库的数据临时的存储起来,以更好的支持查询。
  • 问题:如果有数据,查询频繁且更新极少,此种数据如果依然每次到数据库查询,效率偏低。
  • 解决:将如上数据,临时的存储到内存中,提供对外界的查询服务,进而减少和数据库的通信,提高查询效率。
  • 原理:当查询数据时,查询结果会被缓存在某个内存区域中
  • 每次发起查询时,会先找到缓存,从中尝试获取数据,如果没有找到数据,再去查数据库,并将在数据库中查到的结果存入缓存,以供后续查询使用。
  • MyBatis作为持久层框架,缓存管理自然是他的本职工作。
    • 支持了两种缓存:一级缓存,二级缓存

一级缓存

  • 存储位置:SqlSession;即一个SqlSession对象发起查询后,查询结果会缓存在自己内部
  • 有效范围:同一个SqlSession的多次查询;即,同一个SqlSession的多次相同sql的查询可以使用一级缓存
  • 开启:不用任何配置,默认启动。
  • 清除:sqlSession.clearCache();

二级缓存

  • 存储位置:SqlSessionFactory;同一个SqlSessionFactory创建的所有SqlSession发起的查询,查询结果都会缓存在SqlSessionFactory内部。
  • 有效范围:同一个SqlSessionFactory
  • 开启:二级缓存是默认开启
  • 二级缓存的使用:
    • 在需要使用二级缓存的dao的mapper映射文件中,添加<cache/>标签即可
    • 当前mapper中的所有查询,都进入二级缓存
    • 缓存数据中涉及的pojo一定要实现Serialiable序列化。
    • <cache/>标签的意思是使用二级缓存
  • 注意:二级缓存必须在sqlSession.commit()sqlSession.close() 之后才生效
  • 清除:sqlSession.rollback();//则查询的结果不会进入二级缓存
  • 关闭二级缓存: 在mybatis_config.xml配置文件添加如下设置:
  <settings>
      <setting name="cacheEnabled" value="false"/>
  </settings>

二级缓存结构

二级缓存存储结构

mybatis

  • 意思就是一个map的key存储对象,value中存SQL和数据。然后对象中有个map属性,还是key中存对象,value中存SQL和数据,嵌套n层(我也不知道几层,不多但是我没数)。
  • 就是这样的结构,原因不明

清除

  • 二级缓存是以 namespace 为单位组织的,当某个 namespace 中发生数据改动,则 namespace 中缓存的所有数据会被mybatis清除。
  • 当进行增删改时,二级缓存自动清除。

cache-ref

  • 问题:不适用cache-ref时,延迟加载两个查询语句分别在两个缓存中,而且修改数据时,不会清除二级缓存。这个明显和上面分析的不对
    • 所以使用cache-ref将两个查询语句会放到一个缓存中,这样执行时才会消除二级缓存。
  • 和关系属性相关
  • 注意如果<collection>中没有使用select关联查询,则不存在此问题。
  • 作用:让两个查询放在一个缓存中,一起创建,一起销毁
  • 代码示例:
    • <cache-ref namespace="com.xxx.dao.UserDAO"/>写到dao的Mapper映射文件中

PageHelper分页

使用

  • 添加依赖:
  <dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper</artifactId>
      <version>RELEASE</version>
  </dependency>
  • mybatis_config.xml配置文件中添加设置:(注意顺序)
  <plugins>
      <!-- com.github.pagehelper为PageHelper类所在包名 -->
      <plugin interceptor="com.github.pagehelper.PageInterceptor">
          <!-- 页号自动回归到合理数值 -->
          <property name="reasonable" value="true"/>
      </plugin>
  </plugins>
  • 使用spring时(和上面等价配置),添加如下设置:
  <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="plugins">
     <array>
        <bean class="com.github.pagehelper.PageInterceptor"></bean>
      </array>
    </property>
  </bean>
  • 测试使用:
  //使用:
  PageHelper.startPage(2,3);// 第2页,每页3条数据,pageNum,pageSize
  PageHelper.orderBy("id desc");//可以选择设置排序(可选)
  List<User> users = mapper.queryAllUsers();//必须PageHelper后的第一个查询语句,才会被PageHelp增强处理(可观测mysql日志)
  //包装一个PageInfo,其中会持有所有分页会用到的信息:当前页号,每页多少条,共多少页,是否为第一页/最后一页,是否有下一页等。
  PageInfo<User> pageInfo=new PageInfo<User>(users);

PageInfo对象 概览

mybatis

重要提示

  • PageHelper.startPage方法重要提示
  • 只有紧跟在PageHelper.startPage方法后的第一个Mybatis的查询(Select)方法才会被分页。
  • 请不要配置多个分页插件
    • 请不要在系统中配置多个分页插件(使用Spring时,mybatis-config.xmlSpring<bean>配置方式,请选择其中一种,不要同时配置多个分页插件)!
  • 分页插件不支持带有for update语句的分页
    • 对于带有for update的sql,会抛出运行时异常,对于这样的sql建议手动分页,毕竟这样的sql需要重视。
  • 分页插件不支持嵌套结果映射
    • 由于嵌套结果方式会导致结果集被折叠,因此分页查询的结果在折叠后总数会减少,所以无法保证分页结果数量正确。
  • for update是在数据库中上锁用的,可以为数据库中的行上一个排它锁。当一个事务的操作未完成时候,其他事务可以读取但是不能写入或更新。

另外增加一点bind

  • <bind name="name_pattern" value="'%'+name+'%'"/> 创建一个变量,并绑定在当前上下文
  • 代码示例:
<select id="queryUsers" parameterType="User" resultType="User">
   <bind name="name_pattern" value="'%'+name+'%'"/>
   SELECT id,name,gender,regist_time
   FROM t_user2
   <trim prefix="where" prefixOverrides="and|or">
       <if test="name != null">
           name like #{name_pattern}
       </if>
       <if test="id>=10">
           OR id>#{id}
       </if>
       <if test="gender == false">
           AND gender=#{gender}
       </if>
   </trim>
</select>


转载请注明:Memory的博客 » 点击阅读原文

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦