一.延迟加载和立即加载
1.引入
问题:在一对多中,当我们有一个用户,它有100个账户。
在查询用户的时候,要不要把关联的账户查出来?
在查询账户的时候,要不要把关联的用户查出来?
回答:在查询用户时,用户下的账户信息应该是,什么时候使用,什么时候查询的。
在查询账户时,账户的所属用户信息应该是随着账户查询时一起查询出来。
2.是什么
延迟加载:在真正使用数据时才发起查询,不用的时候不查询。按需加载(懒加载)
立即加载:不管用不用,只要一调用方法,马上发起查询。
3.何时用
在对应的四种表关系中:一对多,多对一,一对一,多对多
一对多,多对多:通常情况下我们都是采用延迟加载。<br>
多对一,一对一:通常情况下我们都是采用立即加载。
4. association实现延迟加载(一对一)
4.1 编写实体类
public class Account implements Serializable {
private Integer id;
private Integer uid;
private Double money;
private User user;
public class User implements Serializable {
private Integer id;
private String username;
private String address;
private String sex;
private Date birthday;
4.2 编写IAccountDao配置文件
<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.javasm.dao.IUserDao.findById"></association>
</resultMap>
<!--查询所有账户-->
<select id="findAll" resultMap="accountUserMap">
select * from account
</select>
4.3 编写IUserDao配置文件
<!--根据ID查询用户-->
<select id="findById" parameterType="int" resultType="User">
select * from user where id=#{id}
</select>
4.4 Mybatis配置文件
<!--配置延迟加载策略-->
<settings>
<!--打开延迟加载的开关-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--将积极加载改为消极加载即按需加载(3.4.1以上版本可不配默认是false)-->
<setting name="aggressiveLazyLoading" value="false"></setting>
</settings>
4.5 Account测试类
@Test
public void testFindAll(){
List<Account> accounts = accountDao.findAll();
}
4.6 测试对比
没开延迟加载:一次执行了三次sql语句
开启延迟加载:
5. collection实现延迟加载(一对多)
5.1 编写实体类
public class User implements Serializable {
private Integer id;
private String username;
private String address;
private String sex;
private Date birthday;
private List<Account> accounts;
public class Account implements Serializable {
private Integer id;
private Integer uid;
private Double money;
5.2 编写IUserDao配置文件
<resultMap id="AllUserAccount" 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" ofType="account" select="com.javasm.dao.IAccountDao.findAccountByUid" column="id"></collection>
</resultMap>
<!--查询所有用户-->
<select id="findAll" resultMap="AllUserAccount">
select * from user
</select>
5.3 编写IAccountDao配置文件
<!--据用户ID查询账户列表-->
<select id="findAccountByUid" resultType="account" >
select * from account where uid = #{id}
</select>
5.4 User测试对比
@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());
}
}
@Test
public void testfindAll(){
List<User> users = userDao.findAll();
}
二.缓存
什么是缓存:存在于内存中的临时数据。
为什么使用缓存:减少和数据库的交互次数,提高执行效率。
什么样的数据能使用缓存,什么样的数据不能使用
适用于缓存:
经常查询并且不经常改变的。 数据的正确与否对最终结果影响不大的。
不适用于缓存:
经常改变的数据 数据的正确与否对最终结果影响很大的。 例如:商品的库存,银行的汇率,股市的牌价。
1.一级缓存(默认开启)
一级缓存:
它指的是Mybatis中SqlSession对象的缓存。
当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供一块区域中。
该区域的结构是一个Map。当我们再次查询同样的数据,mybatis会先去sqlsession中
查询是否有,有的话直接拿出来用。
当SqlSession对象消失(sqlSession.close();sqlSession.clearCache();//此方法也可以清空缓存)时,mybatis的一级缓存也就消失了。
public class UserTest {
private InputStream in;
private SqlSessionFactory factory;
private SqlSession sqlSession;
private IUserDao userDao;
@Before
public void init()throws Exception{
in = Resources.getResourceAsStream("SqlMapConfig.xml");
factory = new SqlSessionFactoryBuilder().build(in);
sqlSession = factory.openSession(true);
userDao = sqlSession.getMapper(IUserDao.class);
}
@After
public void destroy()throws Exception{
sqlSession.close();
in.close();
}
@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);
}
发起了二次查询,且不是同一个对象
1.触发一级缓存的情况
当SqlSession的修改,添加,删除,commit();close();等方法时,就是清空一级缓存。
@Test
public void testClearlCache(){
User user1 = userDao.findById(41);
System.out.println(user1);
user1.setUsername("update user clear cache");
user1.setAddress("北京市海淀区");
userDao.updateUser(user1);
User user2 = userDao.findById(41);
System.out.println(user2);
System.out.println(user1 == user2);
}
2.二级缓存
1.二级缓存的使用
二级缓存:
它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
二级缓存的使用步骤:
第一步:让Mybatis框架支持二级缓存(在Mybaits-Config.xml中配置)
第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
第三步:让当前的操作支持二级缓存(在select标签中配置)
1.1 Mybatis框架支持二级缓存默认是true(在Mybaits-Config.xml中配置)
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
1.2 当前的映射文件支持二级缓存(在IUserDao.xml中配置)
<!--开启user支持二级缓存-->
<cache/>
1.3 当前的操作支持二级缓存(在select标签中配置)
<!-- 根据id查询用户 -->
<select id="findById" parameterType="INT" resultType="user" useCache="true">
select * from user where id = #{uid}
</select>
2.测试二级缓存
public class SecondLevelCacheTest {
private InputStream in;
private SqlSessionFactory factory;
@Before
public void init()throws Exception{
in = Resources.getResourceAsStream("SqlMapConfig.xml");
factory = new SqlSessionFactoryBuilder().build(in);
}
@After
public void destroy()throws Exception{
in.close();
}
@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();
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);
}
}
上图查询结果为false,是因为
二级缓存存放的内容是数据,而不是对象。每次从二级缓存中取数据时,都会填充到一个新创建的对象中。