主流框架一:Mybatis框架(5)Mybatis中的延迟加载,缓存和注解开发

一:Mybatis中的延迟加载

1.延迟加载概念

1.延迟加载
概念:在真正使用数据时才发起查询,不用的时候就不查询。也叫按需加载(懒加载)

2.立即加载
概念:不管用不用,只要一调用方法,马上发起查询。

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

一对多,多对多:通常情况下我们都是采用延迟加载。
多对一,一对一:通常情况下我们都是采用立即加载。

2.延迟加载的配置

我们需要在 Mybatis 的配置文件 SqlMapConfig.xml 文件中添加延迟加载的配置。在这里插入图片描述
根据API得知:配置后可延迟加载,如需查询时调用,配置value参数即可

<settings>
	<setting name="lazyLoadingEnabled" value="true"/>
	<setting name="aggressiveLazyLoading" value="false"/>
</settings>

3.延迟加载实现

主要是通过 association、collection 实现一对一及一对多映射。association、collection 具备延迟加载功能。

(1)association实现

在实现 查询账户信息同时查询用户信息一对一

在账户的映射文件中(IAccountDao.xml)

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

<!-- 建立对应关系 -->
<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">
	//引用com.itheima.dao.IUserDao.findById方法  传递这个uid的参数
	</association>
</resultMap>

<select id="findAll" resultMap="accountMap">
	select * from account
</select>

而用户的映射配置文件中只需要简单的进行实现,参数用uid

<!-- 根据 id 查询 -->
<select id="findById" resultType="user" parameterType="int" >
	select * from user where id = #{uid}
</select>

(2)collection实现

同样我们也可以在一对多关系配置的结点中配置延迟加载策略。
< collection > 结点中也有 select 属性,column 属性。

<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 是用于指定使用哪个字段的值作为条件查询(id作为查询条件)
	-->
	<collection property="accounts" ofType="account" select="com.itheima.dao.IAccountDao.findByUid" column="id">
	</collection>
</resultMap>

而账户的配置映射文件中,

<select id="findByUid" resultType="account" parameterType="int">
	select * from account where uid = #{uid}
</select>

在这里插入图片描述

二:Mybatis中的缓存

像大多数的持久化框架一样,Mybatis 也提供了缓存策略,通过缓存策略来减少数据库的查询次数,减少和数据库的交互次数,提高执行效率。

经常查询并且不经常改变的,数据的正确与否对最终结果影响不大的适用于缓存

1.一级缓存

它指的是Mybatis中SqlSession对象的缓存。当我们执行查询之后,查询的结果会同时存入倒SqlSession为我们提供一块区域中。该区域的结构是一个Map。

当我们再次查询同样的数据,mybatis会先去sqlsession中查询是否有,有的话直接拿出来用当SqlSession对象消失时,mybatis的一级缓存也就消失了。

注:一级缓存是 SqlSession 级别的缓存,只要 SqlSession 没有 flush 或 close,它就存在。

@Test
public void testFindById() {
//6.执行操作
	User user = userDao.findById(41);
	System.out.println("第一次查询的用户:"+user);
	User user2 = userDao.findById(41);
	System.out.println("第二次查询用户:"+user2);
	System.out.println(user == user2);
}

我们可以发现,虽然在上面的代码中我们查询了两次,但最后只执行了一次数据库操作,这就是 Mybatis 提供给我们的一级缓存在起作用了。

因为一级缓存的存在, 导致第二次查询 id为41的记录时,并没有发出sql语句从数据库中查询数据,而是从一级缓存中查询。

如果 sqlSession 去执行 commit 操作(执行插入、更新、删除)清空 SqlSession 中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。

@Test
public void testClearlCache(){
	//1.根据 id 查询用户
	User user1 = userDao.findById(41);
	System.out.println(user1);
	//2.更新用户信息
	user1.setUsername("update user clear cache");
	user1.setAddress("北京市海淀区");
	userDao.updateUser(user1);
	//3.再次查询 id 为 41 的用户
	User user2 = userDao.findById(41);
	System.out.println(user2);
	System.out.println(user1 == user2);
}

当执行sqlSession.close()后,再次获取sqlSession并查询id=41的User对象时,又重新执行了sql语句,从数据库进行了查询操作。

2.二级缓存

它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建SqlSession共享其缓存。在这里插入图片描述

如果 SqlSession3 去执行相同 mapper 映射下 sql,执行 commit 提交(即执行增删改操作),将会清空该 mapper 映射下的二级缓存区域的数据

sqlSession2 去执行查询操作,查找与 sqlSession1 相同的用户信息,首先会去缓存中找是否存在数据 如果存在直接从缓存中取出数据。

二级缓存开启与关闭

第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)

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

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

第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)

<cache>标签表示当前这个 mapper 映射将使用二级缓存,区分的标准就看 mapper 的 namespace 值。

<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,禁用二级缓存。

第四步:测试二级缓存

@Test
public void testFirstLevelCache(){
	SqlSession sqlSession1 = factory.openSession();
	IUserDao dao1 = sqlSession1.getMapper(IUserDao.class);
	User user1 = dao1.findById(41);
	System.out.println(user1);
	sqlSession1.close();//一级缓存消失,二级缓存仍然在factory中


	SqlSession sqlSession2 = factory.openSession();
	IUserDao dao2 = sqlSession2.getMapper(IUserDao.class);
	User user2 = dao2.findById(41);
	System.out.println(user2);
	sqlSession2.close();
	
	System.out.println(user1 == user2);
}

我们发现执行了两次查询,并且在执行第一次查询后,我们关闭了一级缓存,再去执行第二次查询时,我们发现并没有对数据库发出 sql 语句,所以此时的数据就只能是来自于我们所说的二级缓存。(factory里面数据依然存在)

三:Mybatis中的注解开发

1.环境搭建

创建SqlMapConfig.xml主配置文件,由于注解开发则不需要映射配置文件

<configuration>
    <!-- 引入外部的配置文件-->
    <properties resource="jdbcConfig.properties"></properties>
    <!-- 配置别名的注册 -->
	<typeAliases>
		<package name="com.itheima.domain"/>
	</typeAliases>
    <!--配置环境-->
    <environments default="mysql">
        <!-- 配置mysql的环境-->
        <environment id="mysql">
            <!-- 配置事务 -->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"></property>
                <property name="url" value="${jdbc.url}"></property>
                <property name="username" value="${jdbc.username}"></property>
                <property name="password" value="${jdbc.password}"></property>
            </dataSource>
        </environment>
    </environments>
    <!-- 配置映射文件的位置 -->
    <mappers>
        <package name="com.itheima.dao"></package>
    </mappers>
</configuration>

持久层接口使用注解

public interface IUserDao {
	@Select(value = "select * from user")
	List<User> findAll();
...}

然后进行典型的测试test即可

/**
* 测试查询所有
*/
@Test
public void testFindAll() {
	List<User> users = userDao.findAll();
	for(User user : users) {
	System.out.println(user);
}

2.单表CRUD操作(代理Dao方式)

如若我们实体类的属性名和数据库表的列名不一致。则需要配置 @Results:封装多个结果集

/**
     * 查询所有用户
     * @return
     * @Results的id为名称标记,@Result的id为是否为主键
     * column为数据库列名,property为实体类属性
     */
    @Select(value = "select * from user")
    @Results(id = "userMap", value = {
            @Result(id = true, column = "id", property = "userId"),
            @Result(column = "username", property = "userName"),
            @Result(column = "address", property = "userAddress"),
            @Result(column = "sex", property = "userSex"),
            @Result(column = "birthday", property = "userBirthday"),

    }

    )
    List<User> findAll();

其他方法也需要引用,则直接获取@Results的id

@Select(value = "select * from user where id = #{id}")
@ResultMap(value = {"userMap"})
User findById(Integer userId);

3.多表查询操作

(1)使用注解实现一对一复杂关系映射

实现复杂关系映射之前我们可以在映射文件中通过配置< resultMap >来实现。

在使用注解开发时我们需要借助@Results 注解,@Result 注解,@One 注解,@Many 注解。

(1)说明:
@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=""))

(2)实体类

public class Account implements Serializable {
	private Integer id;
	private Integer uid;
	private Double money;
	//多对一关系映射:从表方应该包含一个主表方的对象引用
	private User user;
	...}

(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",
		//这个数据库中的uid对应user实体类
		property="user",
		//对一,实现findById方法
		one=@One(select="com.itheima.dao.IUserDao.findById",
		fetchType=FetchType.LAZY)//并且LAZY延迟加载
	)
	})
	
List<Account> findAll();

}

(2)使用注解实现一对多复杂关系映射

例如:查询用户信息时,也要查询他的账户列表。使用注解方式实现。一个用户具有多个账户信息,所以形成了用户(User)与账户(Account)之间的一对多关系。

实体类:

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 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,返回的是一个list集合
		many=@Many(
		select="com.itheima.dao.IAccountDao.findByUid",
		fetchType=FetchType.LAZY
		)
	)
	})
List<User> findAll();
}

@Many:
相当于<collection>的配置
select 属性:代表将要执行的 sql 语句(对多的语句)
fetchType 属性:代表加载方式,一般如果要延迟加载都设置为 LAZY 的值

4. 缓存的配置

前面提到了二级缓存在非注解开发中的配置步骤,现在学习在注解开发中的配置。

(1)首先在SqlMapConfig中开启二级缓存支持

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

(2)在持久层接口(IUserDao)中使用注解配置二级缓存

@CacheNamespace(blocking=true)//mybatis 基于注解方式实现配置二级缓存
public interface IUserDao {
   ...
}
发布了47 篇原创文章 · 获赞 18 · 访问量 4876

猜你喜欢

转载自blog.csdn.net/qq_43605085/article/details/97366016