zoukankan      html  css  js  c++  java
  • mybatis学习日记3

    1.mybatis的延迟加载

    问题:在一对多中,当我们有一个用户,他有100个账户

    在查询用户的时候,要不要把关联的账户查出来?

    在查询账户的时候,要不要把关联的用户查出来?

    解决:在查询用户的时候,用户下的账户信息应该是,什么时候使用,什么时候查询出来,如果每次查询用户的时候都把关联的账户信息查询出来,会浪费内存空间

    但是在查询账户时,账所属的用户信息应该是随着账户查询时一起查询出来

    1.1.什么是延迟加载:

    延迟加载:在真正使用数据时才发起查询,不用的时候不查询,所以是按需加载(懒加载)。

    1.2.延迟加载的优缺点

    延迟加载的好处:先从单表查询,需要时再从关联表去关联查询,大大提高数

    据库性能,因为查询单表要比关联查询多张表速度要快。

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

    2.多对一、一对一延迟加载的实现

    使用assocation实现延迟加载

    需求:查询账户信息同时查询用户信息

    2.1 账户的持久层 DAO 接口

    public interface IAccountDao {
    
        /**
        *查询所有账户,同时获取账户的所属用户名称以及它的地址信息 
        * @return 
        */
    
        List<Account> findAll();
    
    }

    2.2. 账户的持久层映射文件

    <?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.IAccountDao">
    <!-- 建立对应关系 -->
    <resultMap type="account" id="accountMap">
    <id column="aid" property="id"/>
    <result column="uid" property="uid"/> <result column="money" property="money"/> <!-- 它是用于指定从表方的引用实体属性的 --> <association property="user" javaType="user" select="com.itheima.dao.IUserDao.findById" column="uid"> </association> </resultMap> <select id="findAll" resultMap="accountMap"> select * from account </select> </mapper>

    select: 填写我们要调用的 select 映射的 id

    column : 填写我们要传递给 select 映射的参数

    2.3.用户的持久层接口

    public interface IUserDao { 
        
           /**
    
           *根据 id 查询
    
           * @param userId 
     
           * @return 
       
           */ 
    
            User findById(Integer userId); 
    }

    2.4.用户的持久层映射文件

    <?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" >
    select * from user where id = #{uid}
    </select> </mapper>

    2.5.开启 Mybatis 的延迟加载策略

    我们需要在 Mybatis 的配置文件 SqlMapConfig.xml 文件中添加延迟加载的配置。

    <!-- 开启延迟加载的支持 -->
    
    <settings>
    
     <setting name="lazyLoadingEnabled" value="true"/>
    
     <setting name="aggressiveLazyLoading" value="false"/>
     
    </settings>

    2.6.编写测试只查账户信息不查用户信息。

    @Test
     public void testFindAll(){ 
       
        //执行操作
        
        List<Account> accounts = accountDao.findAll();
     }

    3.一对多,多对多实现延迟加载

    我们也可以在一对多关系配置的<collection>结点中配置延迟加载策略。

    <collection>结点中也有 select 属性,column 属性。

    需求: 完成加载用户对象时,查询该用户所拥有的账户信息。

    3.1.在 User 实体类中加入 List<Account>属性

    public class User implements Serializable { 
     
      private Integer id;
      private String username;
      private Date birthday;
      private String sex;
      private String address;
      private List<Account> accounts;
      public List<Account> getAccounts() {
      return accounts;  }
      public void setAccounts(List<Account> accounts) {
       this.accounts = accounts; }
      public Integer getId() {
       return id;  }
      public void setId(Integer id) {
       this.id = id;  }
      public String getUsername() {
       return username;  }
      public void setUsername(String username) {
       this.username = username;  } 
      public Date getBirthday() {
       return birthday;  }
      public void setBirthday(Date birthday) {
       this.birthday = birthday;  }
      public String getSex() {
       return sex;  }
      public void setSex(String sex) {
       this.sex = sex;  }
      public String getAddress() {
       return address;  }
      public void setAddress(String address) {
       this.address = address;  }
      @Override
      public String toString() {   return "User [id=" + id + ", username=" + username + ", birthday=" + birthday + ", sex=" + sex + ", address="     + address + "]";  } } 6

    3.2.编写用户持久层接口的方法

    /**
    *查询所有用户,同时获取出每个用户下的所有账户信息
    *@return
    */
    List<User> findAll();

    3.3.编写账户持久层接口的方法

    /** 
      * 根据用户 id 查询账户信息
       * @param uid
       * @return
       */
      List<Account> findByUid(Integer uid); 

    3.4.编写用户持久层映射配置

    <resultMap type="user" id="userMap">
    
     <id column="id" property="id"></id>
    
     <result column="username" property="username"/>
    
     <result column="address" property="address"/>
    
     <result column="sex" property="sex"/>
    
     <result column="birthday" property="birthday"/>
    
     <!-- collection 是用于建立一对多中集合属性的对应关系 ofType 用于指定集合元素的数据类型 select 是用于指定查询账户的唯一标识(账户的 dao 全限定类名加上方法名称) column 是用于指定使用哪个字段的值作为条件查询 -->
    
     <collection property="accounts" ofType="account" select="com.itheima.dao.IAccountDao.findByUid" column="id"> 
    
    </collection>
    
     </resultMap>
    
    <!-- 配置查询所有操作 -->
    
     <select id="findAll" resultMap="userMap">
    
     select * from user
    
     </select>

    <collection>标签: 主要用于加载关联的集合对象

    select 属性: 用于指定查询 account 列表的 sql 语句,所以填写的是该 sql 映射的 id

    column 属性: 用于指定 select 属性的 sql 语句的参数来源,上面的参数来自于 user 的 id 列,所以就写成 id 这一 个字段名了

    3.5.编写账户持久层映射配置

    <!-- 根据用户 id 查询账户信息 -->
    
     <select id="findByUid" resultType="account" parameterType="int"> 
     
        select * from account where uid = #{uid}
    
     </select>

    3.6.在主配置文件中配置延迟加载

    <!-- 开启延迟加载的支持 -->
    
    <settings>
    
     <setting name="lazyLoadingEnabled" value="true"/>
    
     <setting name="aggressiveLazyLoading" value="false"/>
     
    </settings>

    3.7.测试只加载用户信息

    @Test
     public void testFindAll() {
     //6.执行操作 List<User> users = userDao.findAll();
     }

    4.立即加载

    不管用不用,只要一调用方法,马上发起查询

    在对应的四种表关系中:一对多,多对一,一对一,多对多

    一对多、多对多:通常情况下我们都是采用延迟加载

    多对一、一对一: 通常情况下我们都是采用立即加载

    5.mybatis中的缓存

    什么是缓存:存在于内存中的临时数据

    为什么要使用缓存:减少和数据库的交互次数,提高执行效率

    什么样的数据能使用缓存,什么样的数据不能使用缓存:

    适用于缓存:经常查询并且不经常改变的。

                         数据的正确与否对最终的结果影响不大。

    不适用于缓存:经常改变的数据

                             数据的正确与否对最终结果影响很大的。

    例如:商品的库存,银行的汇率,股市的牌价

    5.1.mybatis的一级缓存和二级缓存

    一级缓存:它指的是mybatis中SqlSession对象的缓存(一级缓存是 SqlSession 级别的缓存,只要 SqlSession 没有 flush 或 close,它就存在。 )

                      当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供的一块区域中。

                      该区域的结果是一个Map,当我们再次查询同样的数据,mybatis会先去SqlSession中查询是否有,有的话直接拿出来用。

                      当SqlSession对象消失的时候,mybatis的一级缓存也就消失了。

    二级缓存:它指的是mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。

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

    使用配置文件开发使用步骤:

    第一步:让mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
    <settings>
    
     <!-- 开启二级缓存的支持 -->
    
     <setting name="cacheEnabled" value="true"/>
    
     </settings> 

    因为 cacheEnabled 的取值默认就为 true,所以这一步可以省略不配置。为 true 代表开启二级缓存;为 false 代表不开启二级缓存

    第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
    <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>
    第三步:让当前的操作支持二级缓存(在select标签中配置)
    <!-- 根据 id 查询 -->
     <select id="findById" resultType="user" parameterType="int" useCache="true">
     select * from user where id = #{uid} </select>

              将 UserDao.xml 映射文件中的<select>标签中设置 useCache=”true”代表当前这个 statement 要使用 二级缓存,如果不使用二级缓存可以设置为 false。

              注意:针对每次查询都需要最新的数据 sql,要设置成 useCache=false,禁用二级缓存。

              二级缓存存的是数据,不是对象,查找的时候会新创一个对象,把数据拿给他,这样前后两次就不是同一个对象了

              一级缓存不用开启或者配置,自己就有的

    6.mybatis注解开发

    6.1.mybatis常用注解说明

    @Insert:实现新增

    @Update:实现更新

    @Delete:实现删除

    @Select:实现查询

    @Result:实现结果集封装

    @Results:可以与@Result 一起使用,封装多个结果集

    @ResultMap:实现引用@Results 定义的封装

    @One:实现一对一结果集封装

    @Many:实现一对多结果集封装

    @SelectProvider: 实现动态 SQL 映射

    @CacheNamespace:实现注解二级缓存的使用

    6.2.复杂关系映射的注解说明 

    • @Results 注解

                 代替的是标签<resultMap>

                 该注解中可以使用单个@Result 注解,也可以使用@Result 集合 @Results({@Result(),@Result()})或@Results(@Result())

    • @Resutl 注解

               代替了 <id>标签和<result>标签

               @Result 中 属性介绍:

                     id 是否是主键字段

                     column 数据库的列名

                     property 需要装配的属性名

                     one 需要使用的@One 注解(@Result(one=@One)()))

                     many 需要使用的@Many 注解(@Result(many=@many)()))

    • @One 注解(一对一) 

                   代替了<assocation>标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。

             @One 注解属性介绍:

                      select  指定用来多表查询的 sqlmapper

                      fetchType 会覆盖全局的配置参数 lazyLoadingEnabled。

              使用格式: 

                      @Result(column=" ",property="",one=@One(select=""))

    • @Many 注解(多对一)

                代替了<Collection>标签,是是多表查询的关键,在注解中用来指定子查询返回对象集合。 

                注意:聚集元素用来处理“一对多”的关系。需要指定映射的 Java 实体类的属性,属性的 javaType (一般为 ArrayList)但是注解中可以不定义; 

               使用格式: 

                        @Result(property="",column="",many=@Many(select="")) 

    6.3.使用mybatis注解实现多对一、一对一

    需求:根据账户信息,查询对应的用户信息

    6.3.1.编写用户实体类

    public class User implements Serializable { 
     
      private Integer userId;
    
      private String userName;
    
      private Date userBirthday;
    
      private String userSex;
    
      private String userAddress;
    
      public Integer getUserId() {
    
       return userId;  }
    
      public void setUserId(Integer userId) {
    
       this.userId = userId;  }
    
      public String getUserName() {
    
       return userName;  } 
      public void setUserName(String userName) {
    
       this.userName = userName;  }
    
      public Date getUserBirthday() {
    
       return userBirthday;  }
    
      public void setUserBirthday(Date userBirthday) {   
         this.userBirthday = userBirthday;  }
    
      public String getUserSex() {
    
       return userSex;  }
    
      public void setUserSex(String userSex) {
    
       this.userSex = userSex;  }
     
       public String getUserAddress() {
    
         return userAddress;  } 
    
       public void setUserAddress(String userAddress) {   
         this.userAddress = userAddress;  }
    
      @Override
    
      public String toString() {
    
       return "User [userId=" + userId + ", userName=" + userName + ", userBirthday=" + userBirthday + ", userSex="     + userSex + ", userAddress=" + userAddress + "]";
    
      }
     } 

    6.3.2.编写账户的实体类

    public class Account implements Serializable { 
     
      private Integer id;
    
      private Integer uid;
    
      private Double money;   
     //多对一关系映射:从表方应该包含一个主表方的对象引用
    
      private User user;
    
      public User getUser() {
    
       return user;  }
    
      public void setUser(User user) {
    
       this.user = user;  } 
     
       public Integer getId() {
    
       return id;  }
    
      public void setId(Integer id) {
    
       this.id = id;  }
    
      public Integer getUid() {
    
       return uid;  }
    
      public void setUid(Integer uid) { 
    
      this.uid = uid;  }
    
      public Double getMoney() {
    
       return money;  }
    
      public void setMoney(Double money) {
    
       this.money = money;  }
    
      @Override  public String toString() {
    
       return "Account [id=" + id + ", uid=" + uid + ", money=" + money + "]";
    
              }
         }

    注意: 此处我们故意和数据库表的列名不一致。

    6.3.3.添加账户的持久层接口并使用注解配置

    public interface IAccountDao {
        /** 
          * 查询所有账户,采用延迟加载的方式查询账户的所属用户
          * @return 
       */
      @Select("select * from account")
    
      @Results(id="accountMap",value= {
                                       
                                 @Result(id=true,column="id",property="id"),
    @Result(column="uid",property="uid"),
    @Result(column="money",property="money"),
    @Result(column="uid",property="user",one=@One(select="com.itheima.dao.IUserDao.findById",
    fetchType=FetchType.LAZY)
    )
    })
    List<Account> findAll();

    一对一:(根据账户而言)

    property对应要封装的user属性,用uid这个字段去查column=“uid”,指向select,select要找到实现功能的那个方法findbyId,写全限定类名(包名+类名+方法名)

    one里边有select和fetchType

    6.3.4.添加用户的持久层接口并使用注解配置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);
    }

    6.3.5.测试

    6.4.使用mybatis注解实现多对多、一对多

    需求:当查询用户信息的时候,也要查询出对应的账户信息

    6.4.1.用户实体类

    public class User implements Serializable { 
     
      private Integer userId;
    
      private String userName;
    
      private Date userBirthday;
    
      private String userSex;
    
      private String userAddress;   
    
     //一对多关系映射:主表方法应该包含一个从表方的集合引用
      private List<Account> accounts;
    
        public List<Account> getAccounts() {
       return accounts;  } 
    
     public void setAccounts(List<Account> accounts) {
             this.accounts = accounts;  }
      public Integer getUserId() {
    
       return userId;  }
    
      public void setUserId(Integer userId) { 
      
        this.userId = userId;  }
    
      public String getUserName() {
    
       return userName;  }
    
      public void setUserName(String userName) {
    
       this.userName = userName;  } 
       
       public Date getUserBirthday() {
    
       return userBirthday;  }
    
      public void setUserBirthday(Date userBirthday) {   
           this.userBirthday = userBirthday;  }
    
      public String getUserSex() {
    
       return userSex;  }
    
      public void setUserSex(String userSex) {
    
       this.userSex = userSex;  }
    
      public String getUserAddress() {
    
       return userAddress;  }
    
      public void setUserAddress(String userAddress) {   
            this.userAddress = userAddress;  }
    
      @Override
      public String toString() {
    
       return "User [userId=" + userId + ", userName=" + userName + ", userBirthday=" + userBirthday + ", userSex="     + userSex + ", userAddress=" + userAddress + "]";
    
      } 
    } 

    6.4.2.编写用户的持久层接口并使用注解配置

    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"),
    @Result(column="id",property="accounts",many=@Many(select="com.itheima.dao.IAccountDao.findByUid",
    fetchType=FetchType.LAZY)
    )
    })
    List<User> findAll();
    }

    @Many: 相当于<collection>的配置 select 属性:代表将要执行的 sql 语句

    fetchType 属性:代表加载方式,一般如果要延迟加载都设置为 LAZY 的值

    6.4.3.编写账户的持久层接口并使用注解配置

    public interface IAccountDao {
    
        /**
           *根据用户 id 查询用户下的所有账户
           *@param userId * @return 
       */ 
     
      @Select("select * from account where uid = #{uid} ") 
    
         List<Account> findByUid(Integer userId); 
    }

    6.4.4.测试

    7.mybatis 基于注解的二级缓存 

     7.1.在 SqlMapConfig 中开启二级缓存支持 

    <!-- 配置二级缓存 --> 
    <settings> 
      <!-- 开启二级缓存的支持 --> 
      <setting name="cacheEnabled" value="true"/>
    </settings>

    7.2.在持久层接口中使用注解配置二级缓存 

    @CacheNamespace(blocking=true)
      //mybatis 基于注解方式实现配置二级缓存 
      public interface IUserDao {......}
  • 相关阅读:
    seata 1.3.0 seata Global lock wait timeout
    Tika解析word文件
    我的第一款微信小程序:iteye附件下载器,希望大家好好爱惜
    读书《尸检报告》 [英]卡拉·瓦伦丁 / 中信出版集团2019-08
    读书《另一种选择》 [美] 谢丽尔·桑德伯格 / 中信出版集团2017-08
    读书《不朽的失眠》 张晓风 / 四川人民出版社2018-09
    Uniapp 修改内置组件样式无效解决方法
    Android studio中.9图片的含义及制作教程
    Diff算法
    js new一个对象的过程,实现一个简单的new方法
  • 原文地址:https://www.cnblogs.com/bzbz/p/12001269.html
Copyright © 2011-2022 走看看