Mybatis的延迟加载
一、什么是延迟加载
延迟加载:
就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载.
好处:
先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。
坏处:
因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。
二、实现需求
需求:
查询账户(Account)信息并且关联查询用户(User)信息。如果先查询账户(Account)信息即可满足要求,当我们需要查询用户(User)信息时再查询用户(User)信息。把对用户(User)信息的按需去查询就是延迟加载。
association、collection 具备延迟加载功能。
三、使用association实现延迟加载
需求:
查询账户信息同时查询用户信息。
3.1 账户的持久层dao接口
package com.llb.dao; import com.llb.domain.Account; import java.util.List; /** * Ceate By llb on 2019/8/7 */ public interface AccountMapper { /** * 查询账户所对应的的用户 */ List<Account> findAccountAndUser(); }
3.2 账户的持久层映射文件
<?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.llb.dao.AccountMapper"> <!--定义封装account和user的resultMap--> <resultMap id="accountMap" type="com.llb.domain.Account"> <id property="id" column="id"></id> <result property="uid" column="uid"></result> <result property="money" column="money"></result> <!--一对一的关系映射 JavaType:对应的是哪个类 select 指定的内容:查询用户的唯一标识 --> <association property="user" column="uid" javaType="com.llb.domain.User" select="com.llb.dao.UserMapper.findById" ></association> </resultMap> <select id="findAccountAndUser" resultMap="accountMap"> select * from account </select> </mapper>
3.3 用户的持久层接口
package com.llb.dao; import com.llb.domain.User; import java.util.List; /** * 用户的持久层接口 * Ceate By llb on 2019/8/5 */ public interface UserMapper { /** * 根据id查询所有用户 * @return */ User findById(Integer id); /** * 查询出所有用户,包含账户信息 * @return */ List<User> findAccountAndUser(); }
3.4 用户的持久层映射文件
<?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.llb.dao.UserMapper"> <!--配置 查询结果的列名和实体类的属性名的对应关系--> <resultMap id="userMap" type="com.llb.domain.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集合的映射 ofType:表示集合的类型--> <collection property="accounts" ofType="com.llb.domain.Account"> <id property="id" column="aid"></id> <result property="uid" column="uid"></result> <result property="money" column="money"></result> </collection> </resultMap> <!--查询出用户所对应的账户--> <select id="findAccountAndUser" resultMap="userMap"> select * from user left outer join account on user.id = account.uid </select> <!--根据id查询用户--> <select id="findById" resultType="user" parameterType="int"> select * from user where id = #{id} </select> </mapper>
3.5 开启mybatis的延迟加载策略
进入 Mybaits 的官方文档,找到 settings 的说明信息:
![](https://img2018.cnblogs.com/blog/1685989/201908/1685989-20190809083224443-1140531189.png)
我们需要在 Mybatis 的配置文件 SqlMapConfig.xml 文件中添加延迟加载的配置。
<!--配置参数,延迟加载--> <settings> <!--开启mybatis支持延迟加载--> <setting name="lazyLoadingEnabled" value="true"/> <!--不配置也可以:默认为false;允许触发方法进行延迟加载,否则立即加载--> <setting name="aggressiveLazyLoading" value="false"></setting> </settings>
3.6 编写测试只查账户信息不查用户信息
package com.llb.test; import com.llb.dao.AccountMapper; import com.llb.dao.UserMapper; import com.llb.domain.Account; import com.llb.domain.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.After; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.util.List; /** * Ceate By llb on 2019/8/5 */ public class AccountTest { InputStream in = null; AccountMapper mapper = null; SqlSession sqlSession = null; /** * 在测试方法执行前执行 * @throws IOException */ @Before public void init() throws IOException { //1.读取配置文件,生成字节流 in = Resources.getResourceAsStream("SqlMapConfig.xml"); //2.获取sqlSessionFactory对象 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); //3.获取sqlSession对象 sqlSession = factory.openSession(); //4.获取dao的代理对象 mapper = sqlSession.getMapper(AccountMapper.class); } /** * 测试方法执行后执行 * @throws IOException */ @After public void destory() throws IOException { sqlSession.commit(); //6.释放资源 sqlSession.close(); in.close(); } /** * 查询出账户所对应的user */ @Test public void testFindAccountAndUser(){ List<Account> accountUser = mapper.findAccountAndUser(); // for (Account account: accountUser) { // System.out.println(account); // } } }
3.7 测试结果
当不进行配置时,立即加载,查询account所对应的user,一起将user查询出来:
配置后,对account进行查询放入到list集合中,并没有涉及到user对象,所以就没有发出 SQL 语句查询账户所关联的 User 对象的查询。:
四、使用collection实现延迟加载
同样我们也可以在一对多关系配置的<collection>结点中配置延迟加载策略。
<collection>结点中也有 select 属性,column 属性。
需求:
完成加载用户对象时,查询该用户所拥有的账户信息。
4.1 在User实体类中加入List<Account>属性
package com.llb.domain; import java.io.Serializable; import java.util.Date; import java.util.List; /** * Ceate By llb on 2019/8/5 */ public class User implements Serializable{ private Integer id; private String username; private String address; private String sex; private Date birthday; //一对多关系。一的方包含多的一方所有对象 private List<Account> accounts; @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", address='" + address + '\'' + ", sex='" + sex + '\'' + ", birthday=" + birthday + ", accounts=" + accounts + '}'; } public List<Account> getAccounts() { return accounts; } public void setAccounts(List<Account> accounts) { this.accounts = accounts; } 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 getAddress() { return address; } public void setAddress(String address) { this.address = address; } 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; } }
4.2 编写用户持久层接口的方法和配置文件
UserMapper.class:
package com.llb.dao; import com.llb.domain.User; import java.util.List; /** * 用户的持久层接口 * Ceate By llb on 2019/8/5 */ public interface UserMapper { /** * 根据id查询所有用户 * @return */ User findById(Integer id); /** * 查询出所有用户,包含账户信息 * @return */ List<User> findAccountAndUser(); }
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.llb.dao.UserMapper"> <!--配置 查询结果的列名和实体类的属性名的对应关系--> <resultMap id="userMap" type="com.llb.domain.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集合的映射 ofType:表示集合的类型--> <collection property="accounts" ofType="com.llb.domain.Account" select="com.llb.dao.AccountMapper.findAccountById" column="id"></collection> </resultMap> <!--查询出用户所对应的账户--> <select id="findAccountAndUser" resultMap="userMap"> select * from user </select> <!--根据id查询用户--> <select id="findById" resultType="user" parameterType="int"> select * from user where id = #{id} </select> </mapper>
4.3 使用测试方法进行测试
package com.llb.test; import com.llb.dao.AccountMapper; import com.llb.dao.UserMapper; import com.llb.domain.Account; import com.llb.domain.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.After; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.util.List; /** * Ceate By llb on 2019/8/5 */ public class UserTest { InputStream in = null; UserMapper mapper = null; SqlSession sqlSession = null; /** * 在测试方法执行前执行 * @throws IOException */ @Before public void init() throws IOException { //1.读取配置文件,生成字节流 in = Resources.getResourceAsStream("SqlMapConfig.xml"); //2.获取sqlSessionFactory对象 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); //3.获取sqlSession对象 sqlSession = factory.openSession(); //4.获取dao的代理对象 mapper = sqlSession.getMapper(UserMapper.class); } /** * 测试方法执行后执行 * @throws IOException */ @After public void destory() throws IOException { sqlSession.commit(); //6.释放资源 sqlSession.close(); in.close(); } /** * 查询出所有用户所对应的账户 */ @Test public void findUserAndAccount(){ List<User> users = mapper.findAccountAndUser(); } }
测试结果:
我们没有使用Accout,也只对User进行了查询。
Mybatis缓存
源码:https://github.com/PopsiCola/SSM-mybatis/tree/association_lazy
欢迎star