mybatis中的延迟加载:

在查询用户时,用户下的账户信息应该是什么时候使用,什么时候查询的。

什么是延迟加载:
在真正使用数据时才发起查询,不用的时候不查询。按需加载(懒加载)
什么是立即加载
不管用不用,只要一调用方法,马上发起查询。

一对多,多对多:通常情况下我们都是采用延迟加载。
一对一,多对一:通常情况下我们都是采用立即加载。
<!-- 定义封装account和user的resultMap -->
    <resultMap id="accountUserMap" type="account">
        <id property="id" column="aid"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!-- 一对一的关系映射:配置封装user的内容-->
        <association property="user" column="uid" javaType="user">
            <id property="id" column="id"></id>
            <result column="username" property="username"></result>
            <result column="address" property="address"></result>
            <result column="sex" property="sex"></result>
            <result column="birthday" property="birthday"></result>
        </association>
    </resultMap>
配置延迟加载再提供这些封装没有意义,因为没有查数据是不可能封装的。
这时候需要使用一个新的属性select。
<!-- 一对一的关系映射:配置封装user的内容-->
        <association property="user" column="uid" javaType="user">
            <id property="id" column="id"></id>
            <result column="username" property="username"></result>
            <result column="address" property="address"></result>
            <result column="sex" property="sex"></result>
            <result column="birthday" property="birthday"></result>
        </association>

<!-- 定义封装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查询时,所需要的参数的值。
        -->
        <association property="user" column="uid" javaType="user" select="com.itheima.dao.IUserDao.findById">
        </association>

从这张图片中可以看出并没有延迟的效果。
要想真正实现延迟的功能我们需要进行配置。
百度搜索mybatis官网

点击settings(设置)


如果我们此时把AccountTest.java中的testFindAll()方法的遍历给注释上
    @Test
    public void testFindAll(){
        List<Account> accounts = accountDao.findAll();
//        for(Account account : accounts){
//            System.out.println("--------每个account的信息------------");
//            System.out.println(account);
//            System.out.println(account.getUser());
//        }
    }
此时发现其只执行了一条语句,没有查询user的操作了。
-----------------------------------------------------------------------------------------------------------
一对多延迟加载
实体类user中
public class User implements Serializable {

    private Integer id;
    private String username;
    private String address;
    private String sex;
    private Date birthday;

    //一对多关系映射:主表实体应该包含从表实体的***引用
    private List<Account> accounts;
在IUserDao.xml中
    <!-- 定义User的resultMap-->
    <resultMap id="userAccountMap" 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***的映射 -->
        <collection property="accounts" column="id" ofType="account" select="com.itheima.dao.IAccountDao.findAccountByUid">
        </collection>
    </resultMap>
在dao.IAccountDao中
    List<Account> findAccountByUid(Integer uid);
在IAccountDao.xml中
<select id="findAccountByUid" parameterType="Integer" resultType="account">
        SELECT * FROM account WHERE uid = #{uid}
</select>
在test包下的UserTest.java
    /**
     * 测试查询所有
     */
    @Test
    public void testFindAll(){
        List<User> users = userDao.findAll();
//        for(User user : users){
//            System.out.println("-----每个用户的信息------");
//            System.out.println(user);
//            System.out.println(user.getAccounts());
//        }
    }
最后别忘了主配置文件的开启步骤
<configuration>
    <!-- 配置properties-->
    <properties resource="jdbcConfig.properties"></properties>

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

    <!--使用typeAliases配置别名,它只能配置domain中类的别名 -->
    <typeAliases>
        <package name="com.itheima.domain"></package>
    </typeAliases>
这就是一对多情况下的延迟加载,其实他们的思想都是在用的时候调用对方配置文件
中的一个配置来实现查询的功能。
-----------------------------------------------------------------------------------------------------------
mybatis中的缓存
什么是缓存:
存在于内存中的临时数据
为什么使用缓存:
减少和数据库的交互次数,提高执行效率。
什么样的数据能使用缓存,什么样的数据不能使用。
适用于缓存:
经常查询并且不经常改变的。
数据的正确与否对最终结果影响不大。
不适用于缓存:
经常改变的数据
数据的正确性与否对最终结果影响很大的。
例如:商品的库存,银行的汇率,股市的牌价。
mybatis中的一级缓存和二级缓存
一级缓存:它指的是mybatis中SqlSession对象的缓存。
当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供的一块区域中。
该区域的结构是一个Map。当我们再次查询同样的数据,mybatis会先去sqlsession中
查询是否有,有的话直接拿出来用。
当SqlSession对象消失时,mybatis的一级缓存也就消失了。

在UserTest.java中
    @Test
    public void testFirstLevelCache() {
        User user1 = userDao.findById(41);
        System.out.println(user1);
        User user2 = userDao.findById(41);
        System.out.println(user2);

        System.out.println(user1==user2);
    }

可以看到只发起了一次查询,换句话说,第一次是查询,第二次是从缓存中取。
    @Test
    public void testFirstLevelCache() {
        User user1 = userDao.findById(41);
        System.out.println(user1);

        sqlSession.close();
        sqlSession=factory.openSession();

        userDao=sqlSession.getMapper(IUserDao.class);
        User user2 = userDao.findById(41);
        System.out.println(user2);

        System.out.println(user1==user2);
    }

    @Test
    public void testFirstLevelCache() {
        User user1 = userDao.findById(41);
        System.out.println(user1);

        //sqlSession.close();
        //sqlSession=factory.openSession();
        sqlSession.clearCache();//此方法也可以清空缓存

        userDao=sqlSession.getMapper(IUserDao.class);
        User user2 = userDao.findById(41);
        System.out.println(user2);

        System.out.println(user1==user2);
    }





可以看到在更新之后再次查询的时候并没有从缓存中取,而是直接发起了一次新的查询。原因是一级缓存是SqlSession范围
的缓存,当调用SqlSession的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。
-----------------------------------------------------------------------------------------------------------
二级缓存:



它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象
创建的SqlSession共享其缓存。
二级缓存的使用步骤:
第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
<settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>
第二步:让当前的映射文件支持二级缓存(在IUserDao.xml配置)
第三步:让当前的操作支持二级缓存(在select标签中配置 )
<?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">

    <!--让当前的映射文件支持二级缓存开启user支持二级缓存-->
    <cache/>

    <!-- 查询所有 -->
    <select id="findAll" resultType="user">
        select * from user
    </select>

    <!-- 根据id查询用户 第三步:让当前的操作支持二级缓存(在select标签中配置 )-->
    <select id="findById" parameterType="INT" resultType="user" useCache="true">
        select * from user where id = #{uid}
    </select>

</mapper>
配置好之后,我们启动test方法,发现第二次直接是从缓存中获取,但是结果是false。
这个原因是二级缓存中存放的是数据,而不是对象。


-----------------------------------------------------------------------------------------------------------
Mybatis注解开发
Mybatis注解开发可以减少编写Mapper映射文件。

环境搭建
单表CRUD(***Dao方式)
多表查询操作
缓存的配置

只要使用注解开发,但是在配置路径文件下同时包含了IUserDao.xml,
此时不管用不用这个IUserDao.xml都会报错。

如果实体类User的属性名称和user表中的列名称不相同
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;
    }
    ......
IUserDao.java
package com.itheima.dao;

import com.itheima.domain.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;

import java.util.List;

/**
 * 在mybatis中,针对CRUD一共有四个注解。
 * @Select @Insert @Update @Delete
 */
public interface IUserDao {

    /**
     * 查询所有用户
     * @return
     */
    @Select("select * from user")
    @Results(value = {
            @Result(id=true,column = "id",property = "userId"),
            @Result(column = "username",property = "userName"),
            @Result(column = "birthday",property = "userBirthday"),
            @Result(column = "sex",property = "userSex"),
            @Result(column = "address",property = "userAddress")
    })
    List<User> findAll();

    @Insert("insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})")
    void saveUser(User user);

}
mybatis的@Results注解,
跟进去@Results,
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Results {
  /**
   * The name of the result map.
   */
  String id() default "";
  Result[] value() default {};
}
所以就是
@Result(value={
@Result
})
跟进去@Result,
public @interface Result {
  boolean id() default false;

  String column() default "";

  String property() default "";
    ......
发现有column和property,发现和之前用xml的很像,之前在xml还有一个唯一标志,
在这里有一个boolean类型的id,默认值是false,如果是id字段的话,把它设置为true。
这时我们就可以配置了
@Results(value={
@Result(id=true,column="id",property="userId"),
@Result(column="username",property="userName"),
@Result(column="birthday",property="userBirthday"),
@Result(column="sex",property="userSex"),
@Result(column="address",property="userAddress")
})
但是此时这样用不到别的方法上,所以此时我们需要给这个@Results加上一个名称id,
@Results(id="userMap",value={
@Result(id=true,column="id",property="userId"),
@Result(column="username",property="userName"),
@Result(column="birthday",property="userBirthday"),
@Result(column="sex",property="userSex"),
@Result(column="address",property="userAddress")
})

在想用的地方,直接@ResultMap
@Select("select * from user where id = #{id}") @ResultMap("userMap")
User findById(Integer userId);
点进去@ResultMap,
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ResultMap {
  String[] value();
}
有一个value属性,
所以标准的写法应该是
@ResultMap(value={"userMap"})
如果只有一个属性且是value的时候,可以省略,如果数组中只有一个值是大括号也可以省略。

在@Result中
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Result {
  boolean id() default false;

  String column() default "";

  String property() default "";

  Class<?> javaType() default void.class;

  JdbcType jdbcType() default JdbcType.UNDEFINED;

  Class<? extends TypeHandler> typeHandler() default UnknownTypeHandler.class;

  One one() default @One;

  Many many() default @Many;
}

进入@One,
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface One {
  String select() default "";

  FetchType fetchType() default FetchType.DEFAULT;

}
select表示的是指向的是如何查询用户的标志,
点进FetchType,
public enum FetchType {
  LAZY, EAGER, DEFAULT
}
LAZY:延迟加载
EAGER:立即加载

IAccountDao.java
import java.util.List;

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(property = "user",column = "uid",one=@One(select="com.itheima.dao.IUserDao.findById",fetchType= FetchType.EAGER))
    })
    List<Account> findAll();
}
-----------------------------------------------------------------------------------------------------------
Mybatis注解开发一对多的查询配置
查询用户,并且带有其所有的账户信息
实体类user
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;
    }
在IUserDao.java下
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 = "birthday",property = "userBirthday"),
            @Result(column = "sex",property = "userSex"),
            @Result(column = "address",property = "userAddress"),
            @Result(column = "id",property = "accounts",
                    many = @Many(select = "com.itheima.dao.IAccountDao.findAccountByUid",
                            fetchType = FetchType.LAZY))
    })
    List<User> findAll();
}
在IAccountDao.java下
@Select("select * from account where uid=#{userId}")
    List<Account> findAccountByUid(Integer userId);
测试
@Test
    public void testFindAll() {
        List<User> users=userDao.findAll();
        for (User user:users) {
            System.out.println(user);
            System.out.println(user.getAccounts());
        }
    }

我们采用的是延迟加载的方式加载的,如果想看到延迟加载,把打印给注释上
    @Test
    public void testFindAll() {
        List<User> users=userDao.findAll();
//        for (User user:users) {
//            System.out.println(user);
//            System.out.println(user.getAccounts());
//        }
    }



mybatis注解开发使用二级缓存
IUseDao.java
没有配置二级缓存之前

CacheNamespace(blocking = true)
CacheNamespace(blocking = true)
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 = "birthday",property = "userBirthday"),
            @Result(column = "sex",property = "userSex"),
            @Result(column = "address",property = "userAddress"),
            @Result(column = "id",property = "accounts",
                    many = @Many(select = "com.itheima.dao.IAccountDao.findAccountByUid",
                            fetchType = FetchType.LAZY))
    })
    List<User> findAll();
发现并没有发起查询
<?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>

    <!--配置开启二级缓存(不配置默认也是开启的)-->
    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>