延迟加载
介绍
- 延迟加载:不会立即查询,而是实际使用数据时再查询
association
和collection
都是在加载关系对方的数据,但在查询一方时,不一定需要另一方数据,此时可以利用延迟加载特性 做出适应
打开mysql日志功能
- 找到mysql的C:\ProgramData\MySQL\MySQL Server 5.7\my.ini文件,修改配置,将mysql日志输出到本地磁盘,用于观测mysql实际执行了哪些查询动作
general-log=1 //1,打开日志功能。0,关闭日志功能 general_log_file="d:/LAPTOP-BT26GJ86.log" //日志生成路径
association
fetchType
:默认eager,急加载
,加载一个实体时,定义急加载的属性会立即从数据库中加载。fetchType
:lazy懒加载
,加载一个实体时,定义懒加载的属性不会马上从数据库中加载,用到时才会加载。select
:使用到property规定的属性,就会去触发select设置的方法。一个可以查询passoport的方法:queryOne
column="uid"
是将本次查询的uid列的值传递给方法:queryOne
- 例子:
<association property="user" column="uid" select="com.xxx.mapper.UserMapper.findByIdUser" fetchType="lazy"> </association>
collection
- 属性作用同上
- 会触发查询的方法有:
toString, hashCode, equals, clone
- 例子:
<collection property="list" column="id" select="com.xxx.mapper.OrderMapper.findById" fetchType="lazy"></collection>
缓存
介绍
- 缓存:将数据库的数据临时的存储起来,以更好的支持查询。
- 问题:如果有数据,查询频繁且更新极少,此种数据如果依然每次到数据库查询,效率偏低。
- 解决:将如上数据,临时的存储到内存中,提供对外界的查询服务,进而减少和数据库的通信,提高查询效率。
- 原理:当查询数据时,查询结果会被缓存在某个内存区域中
- 每次发起查询时,会先找到缓存,从中尝试获取数据,如果没有找到数据,再去查数据库,并将在数据库中查到的结果存入缓存,以供后续查询使用。
- MyBatis作为持久层框架,缓存管理自然是他的本职工作。
- 支持了两种缓存:
一级缓存,二级缓存
一级缓存
- 存储位置:SqlSession;即一个SqlSession对象发起查询后,查询结果会缓存在自己内部
- 有效范围:同一个SqlSession的多次查询;即,同一个SqlSession的多次相同sql的查询可以使用一级缓存
- 开启:不用任何配置,默认启动。
- 清除:
sqlSession.clearCache();
二级缓存
- 存储位置:SqlSessionFactory;同一个SqlSessionFactory创建的所有SqlSession发起的查询,查询结果都会缓存在SqlSessionFactory内部。
- 有效范围:同一个SqlSessionFactory
- 开启:二级缓存是
默认开启
- 二级缓存的使用:
- 在需要使用二级缓存的dao的mapper映射文件中,添加
<cache/>
标签即可- 当前mapper中的所有查询,都进入二级缓存
- 缓存数据中涉及的pojo一定要实现
Serialiable
序列化。<cache/>
标签的意思是使用二级缓存- 注意:二级缓存必须在
sqlSession.commit()
或sqlSession.close()
之后才生效- 清除:
sqlSession.rollback();//则查询的结果不会进入二级缓存
- 关闭二级缓存: 在
mybatis_config.xml
配置文件添加如下设置:
<settings> <setting name="cacheEnabled" value="false"/> </settings>
二级缓存结构
二级缓存存储结构
- 意思就是一个map的key存储对象,value中存SQL和数据。然后对象中有个map属性,还是key中存对象,value中存SQL和数据,嵌套n层(我也不知道几层,不多但是我没数)。
- 就是这样的结构,原因不明
清除
- 二级缓存是以
namespace
为单位组织的,当某个namespace
中发生数据改动,则namespace
中缓存的所有数据会被mybatis清除。- 当进行增删改时,二级缓存自动清除。
cache-ref
- 问题:不适用cache-ref时,延迟加载两个查询语句分别在两个缓存中,而且修改数据时,不会清除二级缓存。这个明显和上面分析的不对
- 所以使用cache-ref将两个查询语句会放到一个缓存中,这样执行时才会消除二级缓存。
- 和关系属性相关
- 注意如果
<collection>
中没有使用select
关联查询,则不存在此问题。- 作用:让两个查询放在一个缓存中,一起创建,一起销毁
- 代码示例:
<cache-ref namespace="com.xxx.dao.UserDAO"/>
写到dao的Mapper映射文件中
PageHelper分页
使用
- 添加依赖:
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>RELEASE</version> </dependency>
- mybatis_config.xml配置文件中添加设置:(注意顺序)
<plugins> <!-- com.github.pagehelper为PageHelper类所在包名 --> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <!-- 页号自动回归到合理数值 --> <property name="reasonable" value="true"/> </plugin> </plugins>
- 使用spring时(和上面等价配置),添加如下设置:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="plugins"> <array> <bean class="com.github.pagehelper.PageInterceptor"></bean> </array> </property> </bean>
- 测试使用:
//使用: PageHelper.startPage(2,3);// 第2页,每页3条数据,pageNum,pageSize PageHelper.orderBy("id desc");//可以选择设置排序(可选) List<User> users = mapper.queryAllUsers();//必须PageHelper后的第一个查询语句,才会被PageHelp增强处理(可观测mysql日志) //包装一个PageInfo,其中会持有所有分页会用到的信息:当前页号,每页多少条,共多少页,是否为第一页/最后一页,是否有下一页等。 PageInfo<User> pageInfo=new PageInfo<User>(users);
PageInfo对象 概览
重要提示
PageHelper.startPage
方法重要提示- 只有紧跟在
PageHelper.startPage
方法后的第一个Mybatis的查询(Select)方法才会被分页。- 请不要配置多个分页插件
- 请不要在系统中配置多个分页插件(使用Spring时,
mybatis-config.xml
和Spring<bean>
配置方式,请选择其中一种,不要同时配置多个分页插件)!- 分页插件不支持带有
for update
语句的分页
- 对于带有
for update
的sql,会抛出运行时异常,对于这样的sql建议手动分页,毕竟这样的sql需要重视。- 分页插件不支持嵌套结果映射
- 由于嵌套结果方式会导致结果集被折叠,因此分页查询的结果在折叠后总数会减少,所以无法保证分页结果数量正确。
for update
是在数据库中上锁用的,可以为数据库中的行上一个排它锁。当一个事务的操作未完成时候,其他事务可以读取但是不能写入或更新。
另外增加一点bind
<bind name="name_pattern" value="'%'+name+'%'"/>
创建一个变量,并绑定在当前上下文- 代码示例:
<select id="queryUsers" parameterType="User" resultType="User"> <bind name="name_pattern" value="'%'+name+'%'"/> SELECT id,name,gender,regist_time FROM t_user2 <trim prefix="where" prefixOverrides="and|or"> <if test="name != null"> name like #{name_pattern} </if> <if test="id>=10"> OR id>#{id} </if> <if test="gender == false"> AND gender=#{gender} </if> </trim> </select>