目录
- 一、延迟加载
- 立即加载
- 延迟加载
- 实操案例
- 二、缓存
- 一级缓存
- 二级缓存
一、延迟加载
立即加载
概念: 当查询主对象时,立即执行所关联的sql语句,一次性将对象数据全部查询出来。
行为: 执行SELECT * FROM order WHERE id = #{id}后,立马执行SELECT * FROM user WHERE id = #{userId}。
使用场景: 多对一查询
优点: 数据一次性加载完毕,响应速度快。
缺点: 可能查询出不需要的数据,造成数据库资源和网络传输的浪费,降低性能。
使用: 默认使用立即加载。
延迟加载
概念: 当查询主对象时,只查询主对象的数据。关联对象的数据不会立即查询,只用程序第一次真正访问关联对象时,才会加载关联对象的数据。
行为: 先执行SELECT * FROM order WHERE id = #{id},当调用order.getUser()方法时,才执行SELECT * FROM user WHERE id = #{userId}。
使用场景: 一对多查询
优点: 按需加载,避免了不必要的数据库查询。
缺点: 当第一次访问关联对象时,会有个短暂的查询延迟。后续访问多个关联对象,会产生多次数据库查询。
使用: 需要手动开启延迟加载。
- 在主配置文件中开启延迟加载:
lazyLoadingEnabled为true开启延迟加载,aggressiveLazyLoading控制延迟加载的行为,默认值为false,按需加载 - 在子配置文件中:在
<association>标签或<collection>标签中设置fetchType指定哪个关联查询使用延迟加载。fetchType属性值:lazy- 延迟加载;eager- 立即加载
实操案例
将一对多查询和多对一查询都进行延迟加载演示:
多对一延迟加载实操案例演示:
- 在AccountMapper接口类中编写方法
package com.tx.mapper; import com.tx.entity.Account; import java.util.List; public interface AccountMapper { // 延迟加载:多对一查询,查询某一个用户的所有账户信息 public List<Account> findAccountAll();} - 在AccountMapper.xml中进行配置和SQL语句
<mapper namespace="com.tx.mapper.AccountMapper"><!--延迟加载:多对一查询--><!--内连接查询--><select id="findAccountAll" resultMap="accountMap">select * from account</select><!--通过用户的id查询账户信息--><select id="findByUid" parameterType="int" resultType="account">select * from account where uid = #{uid}</select><!--配置映射--><resultMap id="accountMap" type="account"><result property="id" column="id" /><result property="uid" column="uid" /><result property="money" column="money" /><!--在多的一方指定关联查询的延迟加载--><association property="user" javaType="user"select="com.tx.mapper.UserMapper.findUserById" column="uid" fetchType="lazy"><id property="id" column="id"/><result property="username" column="username" /><result property="birthday" column="birthday"/><result property="sex" column="sex"/><result property="address" column="address" /></association></resultMap></mapper> - 在UserMapper接口类中编写方法
package com.tx.mapper; import com.tx.entity.User; import java.util.List; public interface UserMapper { // 延迟加载:多对一查询 public List<User> findUserById(Integer uid);} - 在UserMapper.xml中进行配置文件
<mapper namespace="com.tx.mapper.UserMapper"><!--配置延迟加载:多对一查询--><select id="findUserById" parameterType="int" resultType="user">select * from user where id = #{id}</select></mapper> - 在主配置文件中开启延迟加载
<configuration><!--配置延迟加载--><settings><!-- 开启延迟加载 --><setting name="lazyLoadingEnabled" value="true"/><!-- 将积极加载改为消极加载及按需加载 --><setting name="aggressiveLazyLoading" value="false"/></settings><!--定义类型别名--><typeAliases><package name="com.tx.entity" /></typeAliases><!--主配置文件--><!--配置环境们--><environments default="mysql"><!--配置环境--><environment id="mysql"><!--配置事务的类型,使用本地事务策略--><transactionManager type="JDBC"></transactionManager><!--配置是否使用连接池 POOLED表示使用链接池,UNPOOLED表示不使用连接池--><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql:///mybatis_db"/><property name="username" value="root"/><property name="password" value="root"/></dataSource></environment></environments><!--加载映射文件--><mappers><mapper resource="mapper/UserMapper.xml"></mapper><mapper resource="mapper/AccountMapper.xml"></mapper></mappers></configuration> - 测试代码
package com.tx.test; import com.tx.entity.Account; import com.tx.entity.User; import com.tx.mapper.AccountMapper; import com.tx.mapper.UserMapper1; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.After; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.util.List; // 入门程序 public class Test01 { private InputStream in; private SqlSessionFactory factory; private SqlSession session; @Before public void init() throws IOException { // 1. 加载主配置文件,目的是构建SqlSessionFactory对象 in = Resources.getResourceAsStream("SqlMapConfig.xml"); // 2. 创建SqlSessionFactory(Sql会话工厂)对象 factory = new SqlSessionFactoryBuilder().build(in); // 3. 获取session对象,使用SqlSessionFactory工厂对象创建SqlSession对象 session = factory.openSession(); } @After public void destory() throws IOException { // 5. 释放资源 session.close(); in.close(); } // 4.1 延迟加载:多对一查询 @Test public void testFindAccountAll(){ // 4.1.1 通过session创建Mapper接口的代理对象 AccountMapper mapper = session.getMapper(AccountMapper.class); // 4.1.2 执行方法 List<Account> list = mapper.findAccountAll();for (Account account:list){System.out.println("开始……");System.out.println(account.getMoney());System.out.println(account.getUser().getUsername());System.out.println("结束……");System.out.println();}}}

一对多延迟加载实操案例演示:
- 在UserMapper接口类中编写方法
package com.tx.mapper; import com.tx.entity.User; import java.util.List; public interface UserMapper1 { // 延迟加载:一对多查询 public List<User> findUserAll();} - 在UserMapper.xml中进行配置和SQL语句
<mapper namespace="com.tx.mapper.UserMapper1"><!--配置延迟加载:一对多查询--><select id="findUserAll" resultMap="userMap">select * from user</select><!--数据封装--><resultMap id="userMap" type="user"><id property="id" column=""/><result property="username" column="username"/><result property="birthday" column="birthday"/><result property="sex" column="sex"/><result property="address" column="address"/><!--select="":使用账号的方法查询column="":使用id值去查询账号--><collection property="accounts" ofType="account"select="com.tx.mapper.AccountMapper1.findAccountByUid" column="id" fetchType="lazy"><id property="id" column="id"/><result property="uid" column="uid"/><result property="money" column="money"/></collection></resultMap></mapper> - 在AccountMapper接口类中编写方法
package com.tx.mapper; import com.tx.entity.Account; import java.util.List; public interface AccountMapper1 { // 延迟加载:一对多查询 public List<Account> findAccountByUid(Integer uid);} - 在AccountMapper.xml中进行配置和SQL语句
<mapper namespace="com.tx.mapper.AccountMapper1"><!--延迟加载:一对多查询--><!--通过用户的id查询账户信息--><select id="findAccountByUid" parameterType="int" resultType="account">select * from account where uid = #{uid}</select></mapper> - 在主配置文件中开启延迟加载
<configuration><!--配置延迟加载--><settings><!-- 开启延迟加载 --><setting name="lazyLoadingEnabled" value="true"/><!-- 将积极加载改为消极加载及按需加载 --><setting name="aggressiveLazyLoading" value="false"/></settings><!--定义类型别名--><typeAliases><package name="com.tx.entity" /></typeAliases><!--主配置文件--><!--配置环境们--><environments default="mysql"><!--配置环境--><environment id="mysql"><!--配置事务的类型,使用本地事务策略--><transactionManager type="JDBC"></transactionManager><!--配置是否使用连接池 POOLED表示使用链接池,UNPOOLED表示不使用连接池--><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql:///mybatis_db"/><property name="username" value="root"/><property name="password" value="root"/></dataSource></environment></environments><!--加载映射文件--><mappers><mapper resource="mapper/UserMapper1.xml"></mapper><mapper resource="mapper/AccountMapper1.xml"></mapper><mapper resource="mapper/UserMapper2.xml"></mapper></mappers></configuration> - 测试代码
// 4.2 延迟加载:一对多查询 @Test public void testFindUserAll(){ // 4.2.1 通过session创建Mapper接口的代理对象 UserMapper1 mapper = session.getMapper(UserMapper1.class); // 4.2.2 执行方法 List<User> list = mapper.findUserAll();for (User user:list){System.out.println("开始……");System.out.println(user.getUsername());System.out.println(user.getAccounts());System.out.println("结束……");System.out.println();}}

二、缓存
MySQL使用sql语句缓存。例如,select * from user和select * from user,看起来时同一个表,但是,在MySQL缓存中,用第二表查询不到User表中的数据
MyBtais将数据进行缓存。
一级缓存
级别: SqlSession级别
- SqlSession对象使用Map集合(Key存储执行的SQL语句,value存放查询的对象)存储相互的缓存数据。
- 查询时,先从SqlSession的缓存中查找,如果有,直接返回;如果没有,查询数据库。
- 一级缓存的生命周期和SqlSession的生命周期相同,SqlSession对象关闭,一级缓存也关闭。
session.clearCache();:清除缓存- 调用调用SqlSession的update、insert、delete、commit和close等方法的时候也会清空缓存。
实操案例:
- 在UserMapper2接口类中编写方法
package com.tx.mapper; import com.tx.entity.User; public interface UserMapper2 { // 一/二级缓存 public User findById(Integer id); } - 在UserMapper2.xml中配置Sql语句
<select id="findById" parameterType="int" resultType="user">select * from user where id = #{id} </select> - 测试代码
package com.tx.test; import com.tx.entity.User; import com.tx.mapper.UserMapper2; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.After; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.io.InputStream; public class Test02 { private InputStream in; private SqlSessionFactory factory; private SqlSession session; @Before public void init() throws IOException { // 1. 加载主配置文件,目的是构建SqlSessionFactory对象 in = Resources.getResourceAsStream("SqlMapConfig.xml"); // 2. 创建SqlSessionFactory(Sql会话工厂)对象 factory = new SqlSessionFactoryBuilder().build(in); // 3. 获取session对象,使用SqlSessionFactory工厂对象创建SqlSession对象 session = factory.openSession(); } @After public void destory() throws IOException { // 5. 释放资源 session.close(); in.close(); } // 4.1 一级缓存:会话级别 @Test public void testFindById(){ // 获取代理对象 UserMapper2 mapper = session.getMapper(UserMapper2.class); // 调用方法,通过主键查询 // 先查询一级缓存,没有数据。 // 会查数据库,都会有sql语句,把查询出来的数据存储到一级缓存中 User user = mapper.findById(1); System.out.println(user); System.out.println("=================================="); // 清除缓存 // session.clearCache(); // 在查询一次 // 先查询一级缓存,存在数据。 // 从缓存中把数据返回,就没有sql语句 User user1 = mapper.findById(1); // 这两个user对象地址一样 System.out.println(user1); } }
二级缓存
级别: SqlSessionFactory级别
需要手动开启二级缓存:
- 在主配置文件开启二级缓存
<!--配置延迟加载--><settings><!-- 开启延迟加载 --><setting name="lazyLoadingEnabled" value="true"/><!-- 将积极加载改为消极加载及按需加载 --><setting name="aggressiveLazyLoading" value="false"/><!--开启二级缓存--><setting name="cacheEnabled" value="true"/></settings> - 在子配置文件开启二级缓存
<!--开启二级缓存--><cache/>
除此之外,还有一个重要的条件:
- 实体类一定要继承
Serializable对象流
实操案例:
- 测试代码
// 4.2 二级缓存:会话工厂级别
// 二级缓存的使用对象地址不同,但是也是从缓存加载。原因是二级缓存存储的是零散数据,组装出来的对象
@Test
public void testFindById2(){
// 获取代理对象
UserMapper2 mapper = session.getMapper(UserMapper2.class);
// 调用方法,通过主键查询
// 先查询一级缓存,没有数据。
// 会查数据库,都会有sql语句,把查询出来的数据存储到一级缓存中
User user = mapper.findById(1);
System.out.println(user);
System.out.println("==================================");
// 关闭会话
session.close();
// 获取session对象 和 获取代理对象
session = factory.openSession();
UserMapper2 mapper1 = session.getMapper(UserMapper2.class);
// 在查询一次
// 先查询一级缓存,存在数据。
// 从缓存中把数据返回,就没有sql语句
User user1 = mapper1.findById(1);
// 这两个user对象地址一样
System.out.println(user1);
}
