延迟加载就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载.
**好处:**先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。
**缺点:**因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。
一个账户只属于一个用户
account账户表
字段 | 类型 |
---|---|
accountId | int(11) |
UID | int(11) |
MONEY | double |
user表
字段 | 类型 |
---|---|
id | int(11) |
username | varchar(32) |
birthday | datetime |
sex | char(1) |
address | varchar(256) |
public class Account {
private int accountId;
private int uid;
private double money;
// 一个账户对应一个用户
private User user;
}
public class User {
private int id;
private String username;
private Date birthday;
private String sex;
private String address;
// 一个用户,对应多个账户
private List<Account> accounts;
}
public interface IUserDao {
/**
* 通过id查找用户
*/
User findUserById(int id);
}
public interface IAccountDao {
/**
* 查询全部账户(需要查询账户的用户)
*/
List<Account> findAll();
}
对IUserDao.java接口进行映射
xml version ="1.0" encoding ="UTF- - 8" ?>
< mapper namespace ="com.itheima.dao.IUserDao" >
< select id ="findById" resultType ="user">
select * from user where id=#{id}
select>
mapper>
对IAccountDao.java接口进行映射
xml version ="1.0" encoding ="UTF- - 8" ?>
>
<mapper namespace="com.itheima.dao.IAccountDao">
<resultMap id="accountResultMap" type="account">
<id property="accountId" column="accountId">id>
<result property="uid" column="uid">result>
<result property="money" column="money">result>
<association property="user" javaType="user" column="uid"
select="com.itheima.dao.IUserDao.findById">association>
resultMap>
注意:SqlMapConfig配置标签是有顺序的,顺序如下
1 properties 属性 2 settings 配置全局参数 3 typeAliases 类型别名 4 typeHandlers 类型处理器 5 objectFactory 对象工厂 6 plugins 插件 7 environments 环境集合属性对象 8 databaseIdProvider 多数据库支持 9 mappers 映射器
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
settings>
<resultMap id="accountResMap" type="account">
<result property="accountId" column="accountId">result>
<result property="uid" column="uid">result>
<result property="money" column="money">result>
<association property="user" column="uid" javaType="user" fetchType="lazy" select="com.zmysna.dao.IUserDao.findUserById">
association>
resultMap>
public class RelativeTest {
private SqlSession sqlSession;
//准备工作,加载配置文件并创建SqlSession
@Before
public void before() {
InputStream in = this.getClass().getClassLoader().getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
sqlSession = factory.openSession();
}
//提交事务并关闭SqlSession
@After
public void after() {
sqlSession.commit();
sqlSession.close();
}
//一对一延迟加载测试
@Test
public void o2oTest() {
//创建dao层映射对象
IAccountDao mapper = sqlSession.getMapper(IAccountDao.class);
//调用方法得到所有账户
List<Account> accounts = mapper.findAll();
//遍历并打印
accounts.forEach((account)->{
System.out.println(account.getAccountId());
System.out.println(account.getUser().getAddress());
});
}
}
调用findAll()方法时不执行SQL语句,延迟加载,访问accounts时才执行SQL语句
###2. Mybatis缓存策略
像大多数的持久化框架一样, Mybatis 也提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提高性能。Mybatis中有一级缓存和二级缓存。
一级缓存是基于 SqlSessoion 的缓存,一级缓存的内容不能跨 sqlsession。由 mybatis自动维护。
二级缓存是基于映射文件的缓存,缓存范围比一级缓存更大。不同的 sqlsession 可以访问二级缓存的内容。哪些数据放入二级缓存需要自己指定。
一级缓存默认存在,不由用户控制,范围是同一个SqlSession对象。
public interface IUserDao {
@Select("select * from user where id = #{uid}")
User findUserById(int id);
}
public class AnnoTest {
private SqlSession sqlSession;
private IUserDao userDao;
private InputStream in;
@After
public void after() throws IOException {
sqlSession.commit();
sqlSession.close();
in.close();
}
@Before
public void before() throws IOException {
in = Resources.getResourceAsStream("SqlMapConfig.xml");
sqlSession = new SqlSessionFactoryBuilder().build(in).openSession();
userDao = sqlSession.getMapper(IUserDao.class);
}
@Test
public void findUserById(){
//两次查询同一条数据
System.out.println(userDao.findUserById(46));
System.out.println(userDao.findUserById(46));
}
}
结果
两次查询只执行一次SQL语句,第二次查询使用了一级缓存的数据。
二级缓存是mapper映射级别的缓存,多个SqlSession取操作同一个Mapper映射的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
在SqlMapConfig下面开启二级缓存
<settings>
<setting name="cacheEnabled" value="true">setting>
settings>
<mapper namespace="com.itheima.dao.IUserDao">
<cache/>
<select id="findById" resultType="user" useCache="true">
SELECT * FROM USER WHERE id=#{id}
select>
mapper>
public class User implements Serializable{
private int id;
private String username;
private Date birthday;
private String sex;
private String address;
private List<Account> accounts;
}
// 测试缓存
@Test
public void testCache() throws Exception{
SqlSession session1 = factory.openSession();
IUserDao userDao = session1.getMapper(IUserDao. class);
// 主键查询
User user1 = userDao.findById(41);
System. out .println(user1); //发送 select..
session1.close();
SqlSession session2 = factory.openSession();
IUserDao userDao2 = session2.getMapper(IUserDao. class);
User user2 = userDao2.findById(41); //没有发送查询,说明使用了使用二级缓存的数据
System. out .println(user2);
session2.close();
}
使用不同的sqlSession去实现二级缓存测试,第二次查询时使用缓存,结果如下。