延迟加载
什么是延迟加载
ResultMap可以实现高级映射(使用collection和association),collection和association具有延迟加载的功能。
需求:查询订单并且关联用户信息,如果先查询订单信息可以满足要求,当我们需要使用到 用户信息的时候,再去查询用户信息。把 用户信息 按需 查询就是延迟加载。
延迟加载:先从单表查询,需要的时候,再去关联的表查询,大大提升了数据库的性能,因为单表查询肯定比关联查询更快。
案例如下:
一:先修改配置Mybatis的配置文件,添加以下设置
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
二:Mapper接口
package com.hyx3.lazy.mapper;
import com.hyx3.lazy.pojo.OrderCustomer;
import com.hyx3.lazy.pojo.Orders;
import com.hyx3.lazy.pojo.User;
import java.util.List;
public interface OrderMapper {
//根据用户id找到订单
public List<Orders> findUserOrdersResultMap();
}
三:mapper.xml
<?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.hyx3.lazy.mapper.OrderMapper">
<resultMap id="orderUserMap" type="orders">
<id column="id" property="id"></id>
<result column="user_id" property="user_id"></result>
<result column="number" property="number"></result>
<result column="note" property="note"></result>
<association property="user" javaType="User" select="findUserById" column="user_id">
</association>
</resultMap>
<select id="findUserOrdersResultMap" resultMap="orderUserMap">
select * from orders;
</select>
<select id="findUserById" parameterType="int" resultType="User">
select * from user where id = #{id};
</select>
</mapper>
四:测试代码
package com.hyx3.lazy.test;
import com.hyx3.lazy.mapper.OrderMapper;
import com.hyx3.lazy.pojo.OrderCustomer;
import com.hyx3.lazy.pojo.Orders;
import com.hyx3.lazy.pojo.User;
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.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class Demo01 {
private SqlSessionFactory sqlSessionFactory;
@Before
public void fun0() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("lazy_sqlMapconfig.xml");
//通过配置创建会话工厂
sqlSessionFactory = new SqlSessionFactoryBuilder().build( inputStream );
}
@Test
public void fun1(){
SqlSession sqlSession = sqlSessionFactory.openSession();
OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
List<Orders> userOrders = mapper.findUserOrdersResultMap();
for(Orders oc : userOrders){
System.out.println(oc);
}
}
}
五:实现效果如下
可以看到,在断点处,此时没有加载用户信息时,sql语句之执行了查询所有订单
点击查询到的userOrders时,会触发查询用户数据
查询缓存
为什么要缓存
如果缓存(内存)中有数据,就不需要从数据库中去查询了,而是直接从内存中拿,大大提供系统性能。
查询商品ID为1000的商品
第一次查询时候,去数据库拿,写到内存中、并且返回
第2、3次,直接内存读,然后返回。
什么是查询缓存
Mybatis提供查询,用于减轻数据库的压力,提供数据库的性能。
Mybatis提供一级缓存、和二级缓存
一级缓存是SQLSession级别的缓存
在操作数据库是需要构建SQLSession对象,在对象有有一个数据接口(hashMap)用于存储缓存数据,不同的SQLSession之间缓存数据区域(hashMap)是互补影响。
二级缓存是Mapper基本的缓存
多个SQLSession去操作同一个Mapper的SQL语句,多个SQLSession可以共用二级缓存,二级缓存是跨SQLSession的。
一级缓存
原理
案例实现,查找一个用户根据id
User.class
package com.hyx3.cach.pojo;
import java.util.Date;
public class User {
private Integer id;
private String username;// 用户姓名
private String sex;// 性别
private Date birthday;// 生日
private String address;// 地址
public User() {
}
public User(String username, String sex, Date birthday, String address) {
this.username = username;
this.sex = sex;
this.birthday = birthday;
this.address = address;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "User{" + "id=" + id + ", username='" + username + '\'' + ", sex='" + sex + '\'' + ", birthday=" + birthday + ", address='" + address + '\'' + '}';
}
}
UserMapper.interface
package com.hyx3.cach.mapper;
import com.hyx3.cach.pojo.User;
public interface UserMapper {
/**
* 找到用户通过id
*/
public User findUserById(Integer id);
}
UserMapper.xml
<?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.hyx3.cach.mapper.UserMapper">
<select id="findUserById" parameterType="int" resultType="user">
select * from user where id = #{id}
</select>
</mapper>
测试代码
package com.hyx3.cach.test;
import com.hyx3.cach.mapper.UserMapper;
import com.hyx3.cach.pojo.User;
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.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
public class Dmo01 {
private SqlSessionFactory sqlSessionFactory;
@Before
public void fun0() throws IOException {
InputStream inputStream = Resources.getResourceAsStream( "cach_sqlMapconfig.xml" );
//通过配置创建会话工厂
sqlSessionFactory = new SqlSessionFactoryBuilder().build( inputStream );
}
@Test
public void fun(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User userById = mapper.findUserById(1);
System.out.println(userById);
User userById1 = mapper.findUserById(1);
System.out.println(userById1);
}
}
执行测试代码可以看到如下效果,sql语句只执行了一次,但是在方法中调用了两次方法
对UserMapper.interface与UserMapper.xml进行如下修改:
package com.hyx3.cach.mapper;
import com.hyx3.cach.pojo.User;
public interface UserMapper {
/**
* 找到用户通过id
*/
public User findUserById(Integer id);
/**
* 删除用户通过id
* @param id
*/
public void deleteUserById(int id);
}
<?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.hyx3.cach.mapper.UserMapper">
<select id="findUserById" parameterType="int" resultType="user">
select * from user where id = #{id}
</select>
<delete id="deleteUserById" parameterType="int">
delete from user where id = #{id}
</delete>
</mapper>
测试代码:
package com.hyx3.cach.test;
import com.hyx3.cach.mapper.UserMapper;
import com.hyx3.cach.pojo.User;
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.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
public class Dmo01 {
private SqlSessionFactory sqlSessionFactory;
@Before
public void fun0() throws IOException {
InputStream inputStream = Resources.getResourceAsStream( "cach_sqlMapconfig.xml" );
//通过配置创建会话工厂
sqlSessionFactory = new SqlSessionFactoryBuilder().build( inputStream );
}
@Test
public void fun(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User userById = mapper.findUserById(1);
System.out.println(userById);
mapper.deleteUserById(31);
sqlSession.commit();
User userById1 = mapper.findUserById(1);
System.out.println(userById1);
sqlSession.close();
}
}
运行代码可以看到下面的效果,sql语句执行了两次
因为进行了提交,一级缓存进行了清楚,所有需要再次查询
二级缓存(了解)
Mybatis二级缓存细粒度的数据级别的缓存实现不好。比如,对商品信息进行缓存,由于商品信息查询访问量大,但是要求用户每次查询都能查询到最新的商品信息,此时如果使用mybatis的二级缓存就无法实现当有一个商品变化时只刷新该商品的缓存,而不是全部商品信息。
因为mybatis的二级缓存区域以Mapper为单位划分的,当有一个商品信息变化,会将所有的商品信息的缓存全部清空。解决此类问题,在业务层根据需求对数据有针对性的缓存。
使用内存数据库来做缓存
Redis、memcache