JavaWeb框架 - Mybatis05

Mybatis框架延迟加载、缓存和注解开发

  1. Mybatis中的延迟加载:
    • 什么是延迟加载:就是用到数据的时候才进行加载,不需要用到数据的时候就不加载数据,延迟加载又称为懒加载或者是叫按需加载。与之对应的就是立即加载:不管用不用,只要一调用方法,马上发起查询。
    • 使用延迟加载的坏处:只有使用数据的时候才会从数据库中查询数据,这样在大批量的数据的时候,用户等待的时间也会增长,造成用户的体验下降。
    • 两种加载策略的使用场景:
      • 在在对应的四种表关系中,一对多、多对多通常情况下采用延迟加载。
      • 多对一、一对一通常情况下采用立即加载。
    • Mybatis中延迟加载的实现:
      • Mybatis中使用resultMap来进行一对一、一对多和多对多关系的操作,多表关系的映射是通过association和collection来完成的。而association和collection同样是具备延迟加载的功能。
      • SqlMapConfig.xml中开启延迟加载的策略:
        <!--配置属性 使用的是setting标签 -->
        <settings>
           <!-- 开启mybatis支持延迟加载-->
            <setting name="lazyLoadingEnabled" value="true"/>
            <!--aggressiveLazyLoading:是否按需加载属性 在 3.4.1 及之后的版本默认值为 false 不用再手动的配置-->
            <setting name="aggressiveLazyLoading" value="false"/>
        </settings>
        
        • lazyLoadingEnabled:是否启用延迟加载,mybatis默认为false,不启用延迟加载。lazyLoadingEnabled属性控制全局是否使用延迟加载,特殊关联关系也可以通过嵌套查询中fetchType属性单独配置(fetchType属性值lazy或者eager)。

        • aggressiveLazyLoading:是否按需加载属性,现在默认值为false,lazyLoadingEnabled属性启用时只要加载对象,就会加载该对象的所有属性;关闭该属性则会按需加载,即使用到某关联属性时,实时执行嵌套查询加载该属性。

      • UserMapper中的配置:(使用collection来实现)
        <resultMap id="UserResultMap" type="user">
            <id property="id" column="id"></id>
            <result property="username" column="username"></result>
            <result property="address" column="address"></result>
            <result property="sex" column="sex"></result>
            <result property="birthday" column="birthday"></result>
            <!-- 配置user对象中的accounts对象的映射一对多 ofType:表示的是集合中元素的类型 没有配置类名需要使用全限定类名
            resultMap中<id property="id" column="id">
            和collection中</id><id property="id" column="zid"></id> 一致的情况下 需要给和collection中column属性起别名只要不一致就行
            -->
            <collection property="accounts" ofType="account" select="com.itheima.dao.AccountDao.findAccountByUid" column="id"></collection>
        </resultMap>
        <select id="findAllUserAndAccount" resultMap="UserResultMap">
            <!--sql语句的结尾的分号写不写都行 -->
            select * from user
        </select> 	
        
  2. Mybatis中的缓存:和大多数的持久层框架一样,mybatis中提供了缓存的策略,通过缓存来减少查询数据库的次数,从而提高性能。
    • mybatis中缓存的分类:
      • 一级缓存:一级缓存是SqlSession级别的存在,只要SqlSession没有被flush或者是close,他就存在。
      • 二级缓存:二级缓存是mapper映射级别的缓存(同一个namespace),多个SqlSession去操作同一个mapper映射的SQL语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
    • 一级缓存的使用:
      • 用户持久层映射文件的配置: 使用的属性是useCache=“true”
        <?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.IUserDao">
        	<!-- 根据 id 查询 -->
        	<select id="findById" resultType="UsEr" parameterType="int" useCache="true">
        		select * from user where id = #{uid}
        	</select>
        </mapper>
        
      • 对一级缓存的分析:当调用 SqlSession 的修改,添加,删除,commit(),close()等就会清空缓存。
        *
        • 第一次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,如果没有,从数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。
        • 如果 sqlSession 去执行 commit 操作(执行插入、更新、删除),清空 SqlSession 中的一级缓存,这样做的目的是为了让缓存中存储的是最新的信息,避免脏读
        • 第二次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,缓存中有,直接从缓存中获取用户信息。
    • 二级缓存的使用:MyBatis二级缓存是和命名空间绑定的 ,即二级缓存需要配置在 Mapper.xml 映射文件中
      或者配置在 Mapper 接口中
      • 二级缓存的结构图:
        *
      • 二级缓存的开启和关闭步骤:
        • 开启:在主配置文件中开启二级缓存:

          <settings>
          	<!-- 开启二级缓存的支持 --> 
          	<setting name="cacheEnabled" value="true"/>
          </settings>
          <!--因为 cacheEnabled 的取值默认就为 true,所以这一步可以省略不配置。为 true 代表开启二级缓存;为
          false 代表不开启二级缓存。--> 
          
        • 配置相关的mapper映射文件:
          <cache>标签表示当前这个 mapper 映射将使用二级缓存,区分的标准就看 mapper 的 namespace 值。

          <?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.IUserDao">
          	<!-- 开启二级缓存的支持 -->
          	<cache></cache>
          </mapper>
          
          • cache标签中的属性:(常用的属性)
            • eviction (收回策略)
              • LRU (最近最少使用的) 移除最长时间不被使用的对象,这是默认值
              • FIFO (先进先出〉 按对象进入缓存的顺序来移除它们
              • SOFT (软引用) 移除基于垃圾回收器状态和软引用规则的对象
              • WEAK (弱引用) 更积极地移除基于垃圾收集器状态和弱引用规则的对象
            • flushinterval (刷新间隔〉。可以被设置为任意正整数单位是毫秒 默认的是不设置表示的是没有刷新时间
            • size (引用数目) 可以被设置为任意正整数,要记住缓存的对象数目和运行环境的可用内存资源数 默认值是1024
            • readOnly (只读)。属性可以被设置为 true/false 。只读的缓存会给所有调用者返回缓存对象的相同实例 读写的缓存会通过序列化返回缓存对象的拷贝 这样的方式比较慢但是安全 默认值是false
        • 在statement上面使用userCache属性:使用二级缓存就设置为true 不使用就设为false

          <!-- 根据 id 查询 --> 
          <select id="findById" resultType="user" parameterType="int" useCache="true">
          	select * from user where id = #{uid}
          </select>
          
        • 使用默认的二级缓存效果:

          • 映射语句文件中的所有 SELECT 语句将会被缓存
          • 映射语句文件中的所有 INSERT UPDATE DELETE 语句会刷新缓存
          • 缓存会使用 Least Recently Used LRU ,最近最少使用的)算法来收回
          • 根据时间表(如 no Flush Interval ,没有刷新间隔),缓存不会以任何时间顺序来刷新
          • 缓存会存储集合或对象(无论查询方法返回什么类型的值)的 10 个引用。
          • 缓存会被视为 read/write (可读/可写)的 意味着对象检索不是共享的,而且可以安全地被调用者修改,而不干扰其 调用者或线程所做的潜在修改
        • 【注意】1 如果每次查询都要使用最新的数据就不能使用二级缓存。2 使用二级缓存,所缓存的类必须实现Serializable接口,必须能够使用序列化的方式来保存对象。

    • 集成 EhCache 缓存:EhCache是一个Java 进程内的缓存框架,具有快速、精干等特点 使用步骤:
      • 添加项目依赖:在pom.xml 添加如下依赖:
        <dependency> 
        	<groupid>org.mybatis.caches</groupid> 
        	<artifactid>mybatis-ehcache</artifactid> 
        	<version>l.0 .3</version> 
        </dependency>
        
      • 配置 EhCache:在src/main/resources 目录下新增 ehcache.xml 文件:
        <?xml version 1. encoding UTF ?〉
        <ehcache xmlns : xsi="http://www.w3.org/2001/XMLSchema-instance "
        	xsi : noNamespaceSchemaLocation=" ehcache . xsd" 
        	updateCheck="false " monitoring="autodetect" 
        	dynam cConf g= true
        	<diskStore path=" D:/cache" />
        	<defaultCache 
        		maxElementsinMemory="3000" 
        		eternal="false"
        		copyOnRead="true" 
        		copyOnWrite="true" 
        		timeToidleSeconds="3600" 
        		timeToLiveSeconds="3600" 
        		overf lowToDisk="true" 
        diskPersistent="true" />
        </ehcache>	
        
        • 配置中有两个重要的属性:
          • copyOnRead 的含义是,判断从缓存中读取数据时是返回对象的引用还是复制一个对象返回。默认情况下是 false ,即返回数据的引用,这种情况下返回的都是相同的对象,和 MyBatis默认缓存中的只读对象是相同的
          • copyOnWrite 的含义是 ,判断写入缓存时是直接缓存对象的引用还是复制 个对象然后缓存,默认也是 false 如果想使用可读写缓存,就需要将这两个属性配置为 true ,如果使用只读缓存,可以不配置这两个属性,使用默认值 false 即可
      • 修改 Mapper文件中的缓存配置 ehcache-cache 提供了两种缓存实现org .mybatis . caches . ehcache . EhcacheCache org . mybatis.caches . ehcache.LoggingEhcache第二种是带日志的缓存 只需要设type属性就能使用EhCache缓存了
        <mapper namespace= "全限定类名">
        	<cache type="org.mybatis caches ehcache.EhcacheCache"/>
        	<!--其他的配置-->
        </mapper >
        
    • 集成 Redis 缓存(至于Redis有多牛以前的博客已经讲解了 就不赘述了)直接使用步骤:
      • 添加项目依赖pom.xml 添加依赖:

        <dependency> 
        	<groupid>org.mybatis.caches</groupid> 
        	<artifactid>mybatis-redis</artifactid> 
        	<version>l.0 .0-beta2</version> 
        </dependency> 
        
      • 进行Redis 的配置:Redis的安装 和使用 也就不说了 配置完成之后启动Redis 在src/main /resources 目录下新增 redis. properties 文件:

        host=localhost 
        port=6379 
        connectionTimeout=5000 
        soTimeout=5000
        password= 
        database=0
        clientName=	
        
      • 修改 Mapper.xml 中的缓存配置redis-cache 提供了 MyBatis 缓存实现, org mybatis caches redis RedisCache 需要注意的就是:RedisCache 在保存缓存数据和获取缓存数据时,使用了 Java 序列化和反序列化,因此还需要保证被缓存的对象必须 Serializable 接口

        <mapper namespace="全限定类名">
        <cache type="org.mybatis.caches.redis.RedisCache" />
        <!--其他的配置-->
        </mapper>
        
    • 使用二级缓存可能出现的问题:脏数据的出现 解决方式:就需要用到参照缓存了 当某几个表可以作为一个业务整体时,通常是让几个会关联的表同时使用同 一个二级缓存,这样就能解决脏数据问题
    • 二级缓存适用的场景:(通俗的说就是不容器出现脏数据)
      • 以查询为主的应用中,只有尽可能少的增、删、改操作
      • 绝大多数以单表操作存在时,由于很少存在互相关联的情况,因此不会出现脏数据
      • 可以按业务划分对表进行分组时 如关联的表比较少,可以通过参照缓存进行配
  3. Mybatis中的注解开发:
    • Mybatis中的常用注解:
      • Mybatis中实现CRUD使用的注解: 就是和对应的标签作用一样

        • @Insert:实现新增
        • @Update:实现更新
        • @Delete:实现删除
        • @Select:实现查询
        • @SelectKey 获取自增主键 不是用主键自增 而是用序列也能获取
        • @Options 获取自增主键 只能获取自增主键
        @SelectKey(keyColumn="id",keyProperty="id",resultType=Integer.class,before = false, statement = { "select last_insert_id()" })
        @Options(useGeneratedKeys =true, keyProperty =”id”)
        
        @Insert("insert into user(username, address, sex, birthday) values (#{username},#{address},#{sex},#{birthday})")
        void saveUser(User user);
        @Update("update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id = #{id}")
        void updateUser(User user);
        @Delete("delete from user where id = #{id}")
        void deleteUser(Integer id);
        @Select("select * from user where id = #{id}")
        User findById(Integer id);
        

        除了上面 种注解可以使用简单的 SQL 外, MyBatis 还提供了 Provider 注解,分别@SelectProvider@InsertProvider@UpdateProvider@DeleteProvider同样可以实现查询、插入、更新、删除操作:(不常用)Provider 的注解中提供了两个必填属性 type method type 配置的是个包含method属性指定方法的类,这个类必须有空的构造方法,这个方法的指的就是要执行的 SQL 语句, 并且method 属性指定的方法的返回值必须是 String类型

        @SelectProvider(type = PrivilegeProvider.class , method = "selectByid") 
        SysPrivilege selectByid(Long id); 
        

        其中 PrivilegeProvider 类代码如下:

        public class PrivilegeProv der { 
        	public String selectByid(final Long id) { 
        		return new SQL{) { 
        			SELECT "id, privilege name, privilege url"); 
        			FROM ("sys privilege"); 
        			WHERE ("id= #{id }"); 
        		} . toString () ;
        	}
        }
        
      • @Param注解的作用是给参数命名,参数命名后就能根据名字得到参数值,正确的将参数传入sql语句中

        List<SysRole> selectRo lesByUseridAndRoleEnabled( @Param (”user Id”) Long user Id, @Param (” enable d ”) Integer enabled) ;
        
      • Mybatis中和结果集封装有关的注解:

        • @Result:实现结果集封装
        • @Results:可以与@Result 一起使用,封装多个结果集
        • @ResultMap:实现引用@Results 定义的封装
        • @One:实现一对一结果集封装
        • @Many:实现一对多结果集封装
      • MyBatista中的实现复杂关系映射和缓存的使用:

        • @SelectProvider: 实现动态 SQL 映射
        • @CacheNamespace:实现注解二级缓存的使用
    • Mybatis中的单表的CRUD(注解开发)
      • 持久层接口的编写:使用注解

        public interface IUserDao {
        /**
        * 查询所有用户
        * @return
        */
        @Select("select * from user")
        @Results(id="userMap",
        	value= {
        	@Result(id=true,column="id",property="userId"),
        	@Result(column="username",property="userName"),
        	@Result(column="sex",property="userSex"),
        	@Result(column="address",property="userAddress"),
        	@Result(column="birthday",property="userBirthday")
        })
        List<User> findAll();
        /**
        * 根据 id 查询一个用户
        * @param userId
        * @return
        */
        @Select("select * from user where id = #{uid} ")
        @ResultMap("userMap")
        User findById(Integer userId);
        /**
        * 保存操作
        * @param user
        * @return
        */
        @Insert("insert into 
        user(username,sex,birthday,address)values(#{username},#{sex},#{birthday},#{address}
        )")
        @SelectKey(keyColumn="id",keyProperty="id",resultType=Integer.class,before = 
        false, statement = { "select last_insert_id()" })
        int saveUser(User user);
        /**
        * 更新操作
        * @param user
        * @return
        */
        @Update("update user set 
        username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id 
        =#{id} ")
        int updateUser(User user);
        /**
        * 删除用户
        * @param userId
        * @return
        */
        @Delete("delete from user where id = #{uid} ")
        int deleteUser(Integer userId);
        /**
        * 查询使用聚合函数
        * @return
        */
        @Select("select count(*) from user ")
        int findTotal();
        /**
        * 模糊查询
        * @param name
        * @return
        */
        @Select("select * from user where username like #{username} ")
        List<User> findByName(String name);
        }
        //通过注解方式,我们就不需要再去编写 UserDao.xml 映射文件了。使用注解非常的方便
        
      • 主配置文件的编写:

        <?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
         <configuration>
        	<!-- 配置 properties 文件的位置 -->
        	 <properties resource="jdbcConfig.properties"></properties>
        	<!-- 配置别名的注册 --> 
        	<typeAliases> 
        		<package name="com.itheima.domain"/>
        	</typeAliases>
        	<!-- 配置环境 --> 
        	<environments default="mysql">
        		<!-- 配置 mysql 的环境 --> 
        		<environment id="mysql">
        			<!-- 配置事务的类型是 JDBC --> 
        			<transactionManager type="JDBC"></transactionManager>
        			<!-- 配置数据源 --> 
        			<dataSource type="POOLED">
        				 <property name="driver" value="${jdbc.driver}"/>
        				<property name="url" value="${jdbc.url}"/>
        				<property name="username" value="${jdbc.username}"/>
        				<property name="password" value="${jdbc.password}"/>
        			</dataSource>
        		</environment>
        	</environments>
        	<!-- 配置映射信息 --> 
        	<mappers>
        	<!-- 配置 dao 接口的位置,它有两种方式
        		第一种:使用 mapper 标签配置 class 属性
        		第二种:使用 package 标签,直接指定 dao 接口所在的包
        		--> 
        		<package name="com.itheima.dao"/>
        	</mappers>
        </configuration>
        
      • 外部的properties文件的编写

        jdbc.driver=com.mysql.jdbc.Driver
        jdbc.url=jdbc:mysql://localhost:3306/eesy_mybatis
        jdbc.username=root
        jdbc.password=root
        
    • 使用注解实现复杂关系映射:
      • 在使用配置文件的形式的时候,复杂关系的映射使用的是<resultMap>标签来实现,而使用注解实现复杂关系的映射使用的是@Results 注解,@Result 注解,@One 注解,@Many 注解。使用注解使用的是@resultMap注解
      • 注解的说明:
        • @Results 注解

          • 代替的是标签<resultMap>
          • 该注解中可以使用单个@Result 注解,也可以使用@Result 集合
            @Results({@Result(),@Result()})或@Results(@Result())
        • @Resutl 注解

          • 代替了 <id>标签和<result>标签
          • @Result 中 属性介绍:
            • id 是否是主键字段
            • column 数据库的列名
            • property 需要装配的属性名
        • one 需要使用的@One 注解(@Result(one=@One)()))

          • @One 注解(一对一)
            代替了<assocation>标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。
          • @One 注解属性介绍:
            • select 指定用来多表查询的 sqlmapper
            • fetchType 会覆盖全局的配置参数 lazyLoadingEnabled。。
          • 使用格式:
            @Result(column=" “,property=”",one=@One(select=""))
          @Select("select * from Account")
          @Results( id = "resultMap", value = {
                  @Result(column = "id" , property = "id", id = true),
                  @Result(column = "money",property = "money"),
                  @Result(column = "uid",property = "uid"),
                  @Result(property = "user",column = "uid" ,one=@One(select = "com.qst.dao.IUserDao.findById",fetchType = FetchType.EAGER))
              })
          
        • @Many 注解(多对一)

          • many 需要使用的@Many 注解(@Result(many=@many)()))
          • 代替了<Collection>标签,是是多表查询的关键,在注解中用来指定子查询返回对象集合。
            注意:聚集元素用来处理“一对多”的关系。需要指定映射的 Java 实体类的属性,属性的 javaType(一般为 ArrayList)但是注解中可以不定义;
          • 使用格式:
            Result(property="",column="",many=@Many(select=""))
          @Select("select * from employee")
          @Results(id="resultMap",  value = {
                  @Result(column = "id" , property = "userId" ,id=true),
                  @Result(column = "gender" ,property = "gender"),
                  @Result(column = "email",property = "email"),
                  @Result(column = "did",property = "did"),
                  @Result(property = "account",column = "id" ,
                          many = @Many(select = "com.qst.dao.IAccountDao.selectbyId",
                                  fetchType = FetchType.LAZY))
          })
          
    • 基于注解实现二级缓存:
      • 首先在主配置文件中开启支持二级缓存:

        <!-- 配置二级缓存 --> 
        <settings>
        	<!-- 开启二级缓存的支持 --> 
        	<setting name="cacheEnabled" value="true"/>
        </settings>
        
      • 在持久层接口上使用注解配置二级缓存:

        @CacheNamespace(blocking=true)//mybatis 基于注解方式实现配置二级缓存
        public interface IUserDao {}
        
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

上山打卤面

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值