一、延迟加载
在一对多查询时,希望延迟加载,比如在查询用户信息时,要求用户的账户信息能够延迟加载
开启延迟加载
SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="jdbcConfig.properties"></properties>
<!--开启Mybatis支持延迟加载-->
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<typeAliases>
<package name="com.liaoxiang.domain"></package>
</typeAliases>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</dataSource>
</environment>
</environments>
<mappers>
<package name="com.liaoxiang.dao"></package>
</mappers>
</configuration>
IUserDao.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.liaoxiang.dao.IUserDao">
<!-- 定义User的resultMap-->
<resultMap id="userAccountMap" 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.liaoxiang.dao.IAccountDao.findAccountByUid"
column="id">
</collection>
</resultMap>
<!-- 查询所有 -->
<select id="findAll" resultMap="userAccountMap">
select * from user
</select>
<!-- 根据id 查询用户 -->
<select id="findById" parameterType="INT" resultType="user">
select * from user where id = #{uid}
</select>
</mapper>
IAccountDao.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.liaoxiang.dao.IAccountDao">
<!-- 定义封装account和user的resultMap -->
<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 查询时,所需要的参数的值,就根据uid 的值去查用户
-->
<association property="user" javaType="user"
select="com.liaoxiang.dao.IUserDao.findById"
column="uid" >
</association>
</resultMap>
<!-- 查询所有账户 -->
<select id="findAll" resultMap="accountUserMap">
select * from account
</select>
<!-- 根据用户id查询账户列表 -->
<select id="findAccountByUid" resultType="account">
select * from account where uid = #{uid}
</select>
</mapper>
测试方法:
@Test
public void testFindAll(){
List<User> users = userDao.findAll();
/*for(User user : users){
System.out.println(user);
//System.out.println(user.getAccounts());
System.out.println("-----------------------------------------------------------");
}*/
}
执行测试方法:
注释掉SqlMapConfig.xml
里面的延迟加载设置:
二、缓存
1、测试一级缓存
@Test
public void testFirstLevelCache(){
User user1 = userDao.findById(41);
User user2 = userDao.findById(41);
System.out.println(user1 == user2);
}
两次获取的对象为同一个对象,两次查询只发送了一次sql语句
清除一级缓存
@Test
public void testFirstLevelCache(){
User user1 = userDao.findById(41);
//1、清空缓存
// 关闭sqlSession
//sqlSession.close();
//再次获取SqlSession对象
//sqlSession = factory.openSession();
//2、此方法也可以清空缓存
sqlSession.clearCache();
userDao = sqlSession.getMapper(IUserDao.class);
User user2 = userDao.findById(41);
System.out.println(user1 == user2);
}
两次获取的对象不再相等,且查询发送两次sql语句
一级缓存时SqlSession范围的缓存,当调用SqlSession的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。
2、测试二级缓存
配置开启二级缓存
1、在SqlMapConfig.xml
中配置
2、在IUserDao.xml
中:<cache/>
3、在查询语句中:useCache="true"
<configuration>
<!-- 配置数据库properties-->
<properties resource="jdbcConfig.properties"></properties>
<!-- 配置缓存 -->
<settings>
<!--默认值就为true -->
<setting name="cacheEnabled" value="true"/>
</settings>
<!--使用typeAliases配置别名,它只能配置domain中类的别名 -->
<typeAliases>
<package name="com.liaoxiang.domain"></package>
</typeAliases>
<!--配置环境-->
<environments default="mysql">
<!-- 配置mysql的环境-->
<environment id="mysql">
<!-- 配置事务 -->
<transactionManager type="JDBC"></transactionManager>
<!--配置连接池-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</dataSource>
</environment>
</environments>
<!-- 配置映射文件的位置 -->
<mappers>
<package name="com.liaoxiang.dao"></package>
</mappers>
</configuration>
IUserDao.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.liaoxiang.dao.IUserDao">
<!--开启user支持二级缓存-->
<cache/>
<!-- 查询所有 -->
<select id="findAll" resultType="user">
select * from user
</select>
<!-- 根据id查询用户 开启二级缓存-->
<select id="findById" parameterType="INT" resultType="user" useCache="true">
select * from user where id = #{uid}
</select>
<!-- 更新用户信息-->
<update id="updateUser" parameterType="user">
update user set username=#{username},address=#{address} where id=#{id}
</update>
</mapper>
测试
@Test
public void testFirstLevelCache(){
// 创建第一个 sqlSession1
SqlSession sqlSession1 = factory.openSession();
IUserDao dao1 = sqlSession1.getMapper(IUserDao.class);
User user1 = dao1.findById(41);
System.out.println(user1);
sqlSession1.close();//一级缓存消失
// 创建第二个 sqlSession2
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);
}
二级缓存中存放的是数据而不是对象,当第二次查询时,会从二级缓存中获取数据,创建一个新的对象,所以是两个不同的对象
在SpringBoot中配置mybatis的延迟加载:
mybatis:
configuration:
lazy-loading-enabled: true
type-aliases-package: com.liaoxiang.model
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.liaoxiang.mapper.UserMapper">
<resultMap id="BaseResultMap" type="user">
<id column="user_id" property="userId"></id>
<result column="user_name" property="userName"/>
<result column="password" property="password"/>
<result column="name" property="name"/>
<result column="enabled" property="enabled"/>
</resultMap>
<resultMap id="includeRolesUserMap" type="com.liaoxiang.model.User" extends="BaseResultMap">
<collection property="roles" ofType="com.liaoxiang.model.Role"
select="com.liaoxiang.mapper.RoleMapper.findRolesByUid"
column="user_id">
</collection>
</resultMap>
<select id="findAllUser" resultMap="includeRolesUserMap">
select * from user
</select>
<select id="findUserByUserName" resultMap="BaseResultMap" parameterType="String">
select * from user where user_name = #{userName}
</select>
</mapper>
RoleMapper.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.liaoxiang.mapper.RoleMapper">
<resultMap id="BaseResultMap" type="role">
<id column="role_id" property="roleId"></id>
<result column="role_name" property="roleName"/>
<result column="role_authority" property="roleAuthority"/>
</resultMap>
<select id="findRolesByUid" resultMap="BaseResultMap" parameterType="Integer">
SELECT r.* FROM role r, user_role ur WHERE ur.rid=r.role_id AND ur.uid=#{uid}
</select>
<select id="findAllRole" resultMap="BaseResultMap">
select * from role
</select>
</mapper>
执行测试方法,查看日志
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserTest {
@Autowired
private UserService userService;
@Test
public void test1(){
List<User> users = userService.findAll();
/*for (User user : users) {
System.out.println(user);
}*/
}
}
注释掉延迟加载执行同样的查询所有user的方法时,同时查询了role
springboot整和mybatis后,一级缓存不起作用。。。
发送一个查询请求,在service中连续查询两次,发现执行了两次sql语句
public User findUserByUserName(String userName) {
User user = userMapper.findUserByUserName(userName);
User user1 = userMapper.findUserByUserName(userName);
System.out.println(user == user1);
return user;
}
在service类上加上@Transactional
注解后,此时一级缓存能够生效,但是在使用postman发送两个相同查询的时候,每次都只查询一次,两次查询也会执行两次sql语句,原因有空再分析!!