mybatis_04

mybatis的延迟加载

  • 问题:在一对多查询中,当我们有一个用户,他有100个账户
    在查询用户的时候,用户下的账户信息应该是:什么时候使用,什么时候查询
    在查询账户时,账户的所属用户信息应该是:随着账户查询的时候一起查询出来

  • 延迟加载:在真正使用数据的时候才发起查询,不用的时候不查询,按需加载(懒加载)(一对多、多对多)

    • 好处:先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。
    • 坏处:
      因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。
  • 立即加载:不管用不用,只要一调用方法,马上发起查询(多对一、一对一)

  • 开启延迟加载

//SqlMapConfig.xml
    <!--配置参数-->
    <settings>
        <!--开启mybatis支持延迟加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

查询账户时按需显示用户信息

//IAccountDao.xml
<!--定义封装account和user的resultMap-->
    <resultMap id="accountUserMap" type="account">
        <id property="id" column="id"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!--一对一的关系映射,配置封装user的内容
            select属性指定的内容:查询用户的唯一标识
            column属性指定的内容:用户根据id查询时,(select映射)所需要的参数的值
        -->
        <association property="user" column="uid" javaType="com.itheima.domain.User" select="com.itheima.dao.IUserDao.findById">
        </association>
    </resultMap>

//IUserDao.xml
	<!--根据id查询用户-->
    <select id="findById" parameterType="Integer" resultType="user">
        select * from user where id=#{uid};
    </select>

配置完毕,当查询账户时,除非要求查询用户,否则数据库值查询账户的信息。

查询用户时按需显示账户信息

//IUserDao.xml
<collection property="accounts" ofType="account" select="com.itheima.dao.IAccountDao.findAccountByUid" column="id"></collection>

//IAccountDao.xml
	<!--根据用户id查询账户列表-->
    <select id="findAccountByUid" resultType="account">
        select * from account where uid = #{uid}
    </select>

mybatis的缓存

  • 缓存:存在于内存中的临时数据
  • 为什么使用缓冲:减少和数据库的交互次数,提供执行效率
  • 适用于缓冲:经常查询并且不经常改变的、数据的正确与否对最终结果影响不大的
  • 不适应于缓冲:经常改变的数据、数据的正确与否对最终结果影响很大的
Mybatis 一级缓存
  • 它指的是Mybatis中SqlSession对象的缓冲
    当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供的一块区域中,该区域的结构是一个map,当我们再次查询同样的数据,mybatis会先去SqlSession中查询是否有,有的话直接使用。
    当SqlSession对象消失时,mybatis的一级缓存也消失了。
  • 一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。
	/**
     * 测试缓存的同步
     */
    @Test
    public void testClearCache(){
        //1 根据id查询用户
        User user1 = dao.findById(41);
        System.out.println(user1);
        //如果不进行更新操作,那么数据未改变,不会删除一级缓存,数据库查询只发起一次,第二次查询的内容是从SqlSession获得的
        //2 更新用户信息
        user1.setUsername("update clear cache");
        user1.setAddress("北京市海淀区");
        dao.updateUser(user1);
        //再次查询用户
        User user2 = dao.findById(41);  //再次发起查询
        System.out.println(user2);

        System.out.println(user1 == user2);
    }
Mybatis 二级缓存
  • 它指的是mybatis中SqlSessionFactory对象的缓存,由同一个SqlSessionFactory对象创建的SqlSession共享其缓存
  • 开启二级缓存:
    1. 让mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
    2. 让当前的映射文件支持二级缓存 (在IUserDao.xml中配置)
    3. 让当前的操作支持二级缓存(在select标签中配置useCache=“true”)
//SqlMapConfig.xml
	<settings>
        <!-- 开启二级缓存的支持 -->
        <setting name="cacheEnabled" value="true"/>
    </settings>
    
//IUserDao.xml
 <!-- 开启二级缓存的支持 -->
    <cache></cache>
 <!--根据id查询用户-->
    <select id="findById" parameterType="Integer" resultType="user" useCache="true">
        select * from user where id=#{uid};
    </select>
* 注意:针对每次查询都需要最新的数据的sql,要设置成 useCache=false,禁用二级缓存。
//测试二级缓存查询
    @Test
    public void testSecondCache(){
        SqlSession session1 = factory.openSession();
        IUserDao dao1 = session1.getMapper(IUserDao.class);
        User user1 = dao1.findById(41);
        System.out.println(user1);
        session1.close();  //一级缓存消失

        SqlSession session2 = factory.openSession();
        IUserDao dao2 = session2.getMapper(IUserDao.class);
        User user2 = dao2.findById(41); //开启二级缓存后数据库只查询了一次
        System.out.println(user2);
        session2.close(); //一级缓存消失
		//二级缓存中存放的是数据而不是对象,虽然数据是一样的,但是会创建一个新的user对象存放数据
        System.out.println(user1 == user2); //false
    }

Mybatis 注解开发

  • 注解和xml配置只能存在一种
package com.itheima.dao;
/**
 * 在mybatis中,针对CRUD一共有4个注解
 * @Select @Insert @Update @Delete
 */
public interface IUserDao {  //配合包名得到全限定类名mapper的namespace("com.itheima.dao.IUserDao")
    //查询所有用户
    @Select("select * from user")  //sql语句
    List<User> findAll();//封装类型resultType(user)	id="findAll"
}
复杂关系映射的注解
  • @Results 注解
    代替的是标签<resultMap>
    该注解中可以使用单个@Result 注解,也可以使用@Result 集合
    @Results({@Result(),@Result()})或@Results(@Result())
    //查询所有用户
    @Select("select * from user")
    @Results(id="userMap",value={
            @Result(id=true,column = "id",property = "userId"), //主键 column数据库列名 property实体类属性
            @Result(column = "id",property = "userId"),
            @Result(column = "address",property = "userAddress"),
            @Result(column = "sex",property = "userSex"),
            @Result(column = "birthday",property = "userBirthday"),
    })
    List<User> findAll();
  • @One 注解(一对一)
    代替了标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。
    @One 注解属性介绍:
    select 指定用来多表查询的 sqlmapper
    fetchType 会覆盖全局的配置参数 lazyLoadingEnabled
//Account
//多对一(mybatis中称为一对一)的映射,一个账户只能属于一个用户
    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }


//IAccountDao
public interface IAccountDao {
    //查询所有账户并且获取每个账户所属的用户信息
    @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"),  //封装account
            @Result(property = "user",column = "uid",
                    one = @One(select="com.itheima.dao.IUserDao.findById",fetchType= FetchType.EAGER)) //封装user
    }) //对于账户查用户,一个账户只能属于一个用户,所以是一(多)对一关系,使用@One注解,一般是立即加载EAGER
    List<Account> findAll();
}


//IUserDao
//根据id查找用户
    @Select("select * from user where id=#{id}")
    @ResultMap("userMap")
    User findById(Integer uid);
  • @Many 注解(多对一)
    代替了标签,是是多表查询的关键,在注解中用来指定子查询返回对象集合。
    注意:聚集元素用来处理“一对多”的关系。需要指定映射的 Java 实体类的属性,属性的 javaType(一般为 ArrayList)但是注解中可以不定义;
//User
//一对多关系映射,一个用户对应多个账户
    private List<Account> accounts;

    public List<Account> getAccounts() {
        return accounts;
    }

    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts;
    }


//IUserDao
public interface IUserDao {
    //查询所有用户
    @Select("select * from user")  //除了注解解决实体类属性和数据库字段不匹配问题,也可以直接在sql中为查询字段起别名
    @Results(id="userMap",value={
            @Result(id=true,column = "id",property = "userId"), //主键 column数据库列名 property实体类属性
            @Result(column = "id",property = "userId"),
            @Result(column = "address",property = "userAddress"),
            @Result(column = "sex",property = "userSex"),
            @Result(column = "birthday",property = "userBirthday"),
            @Result(property = "accounts",column = "id",
                    many = @Many(select = "com.itheima.dao.IAccountDao.findAccountByUid",fetchType = FetchType.LAZY))
    })
    List<User> findAll();


//IAccountDao
//根据用户id查询账户
    @Select("select * from account where uid=#{userId}")
    List<Account> findAccountByUid(Integer userId);

注解的配置与xml的配置是相似的,@Results相当于ResultMap,fetchTypes是配置延迟加载,select则是association或者collection。
一对一/多对一,是在实体类中定义另一个实体类的对象作为属性,提供get/set方法获取。
多对一是在实体类中定义另一个实体类的List集合,提供get/set方法获取。

基于注解的二级缓存
//SqlMapConfig.xml
 <!--配置开启二级缓存,不配置也默认开启-->
    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>
在IUserDao中配置二级缓存
//mybatis 基于注解方式实现配置二级缓存
@CacheNamespace(blocking = true)  //开启二级缓存,默认false
public interface IUserDao {
	......
}


//test
//测试二级缓存
    @Test
    public void findByUid(){
        SqlSession session = factory.openSession();
        IUserDao userDao = session.getMapper(IUserDao.class);
        User user = userDao.findById(48);
        System.out.println(user);
        session.close(); //释放一级缓存
        //数据库只查询一次
        SqlSession session1 = factory.openSession();
        IUserDao userDao1 = session1.getMapper(IUserDao.class);
        User user1 = userDao1.findById(48);  //从cache中获取数据
        session1.close();
        System.out.println(user1);
        System.out.println(user==user1);  //false
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值