在面试中是否时常被问到:是否了解数据库的调优?今天Mybatis中的数据库调优基础技术来了.Mybatis中支持缓存机制,有Cache的数据库连接技术才能让查询的效率更高,
文章目录
缓存(Cache)
内存中的一块存储空间,服务于某个应用程序,旨在将频繁读取的数据临时保存在内存中,使二次快速访问
无缓存:用户在访问相同数据时,需要发起多次对数据库的直接访问,导致大量IO,读写硬盘的操作,效率低小 |
---|
有缓存:首次访问时,查询数据库,将数据存储到缓存中;再次访问时,直接访问缓存,减少IO,硬盘读写次数,提高效率 |
---|
在数据库中,最耗时的操作不是删除或者插入,往往是查询
.
所以我们这里针对的缓存的优化,也主要是针对了查询操作的优化.
- 要把经常查询的结果存入到缓存中
- 不把不经常查询的结果存入到缓存,减少缓存的浪费
- 不把经常查询但是又经常修改(跟新,添加,删除)的数据存入到缓存,这些经常修改的数据存入缓存是无效的数据,下次需要的时候又需要重新查询,因为已经做了修改.
- 总的来说:
把频繁查询但是很少改动的数据存入缓存
一级缓存
SqlSession级别的缓存,
同一个SqlSession
的发起多次重构查询,会将数据保存在一级缓存中
- 注意:无需任何配置,默认开启一级缓存
但是只是在同一个SqlSession中发起的查询,在实际的业务逻辑中,我们基本上不会在同一个SqlSession中查询结束了接着再查询一次.
二级缓存
SqlSessionFactory级别的缓存,同一个SqlSessionFactory构建的SqlSessionFactory发起的多次同构查询,会将数据保存在二级缓存中.
- 注意:在SqlSession.commit()或者SqlSession.close()之后生效
- 为什么要在SqlSession.commit()或者SqlSession.close()之后生效?
因为只有在commit或者close之后,才会断定你的查询操作结束了,才会把查询的结果放入到缓存中去.
可以看到SqlSessionFactory在全局我们只创建了一个,那么它的共享范围就比较大 |
---|
开启全局缓存
是MyBatis中极为重要的调整设置,他们会改变MyBatis的运行行为,其他纤细配置可参考官方文档
- 要注意在配置文件中书写的位置:configuration (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?)>
- 标签是可有可无的,但是如果有,那么必须保持位置
<!--下面这些是对mybatis的核心配置 层级和标签的名称是固定的,如果有需要就按照这个模板写就可以 -->
<!-- 引入资源文件jdbc.properties-->
<properties resource="jdbc.properties" />
<!-- 注意书写顺序-->
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
<typeAliases>
<!-- 这里种定义方式选择一种 -->
<!-- <typeAlias type="per.leiyu.User" alias="User" />-->
<!-- 自动扫描包名,默认该包中的所有实体类的自己本身的名字作为原来的全路径的别名-->
<package name="per.leiyu"/>
</typeAliases>
<!-- 这里仅仅展示了部分Mybatis的配置,主要是为了说明settings的配置的书写>
在映射文件中添加cache标签,才能把查询的数据放入缓存中
标签 |
---|
- 我们配置了mybatis的配置.默认二级缓存开启,但是并不是所有的查询结果都能进入到缓存中,配置这个标签,就相当于发送邀请函,收到邀请函的查询的结果才能放入缓存中
之前提到放入到缓存中数据必须是频繁查询但是极少修改的数据
因为我们相当于在缓存中维护了一个数据库中真实数据的副本,如果不发生增删改,那么缓存中的数据就和数据库中的数据是一致的,一旦发生了增删改,我们要求缓存中的数据要及时的销毁掉,不然就查询到了"脏数据",MyBatis已经帮我们集成好了这样的功能,并且会在我们发生增删改时删除发生了修改的相关数据库
- 也就是说:
mybatis缓存中的数据维护直到其发生修改
- 什么叫移除相关数据库的数据?
- 属于同一个mapper就是相关的数据库
Druid连接池
概念
Rruid是阿里巴巴开源平台上的一个项目,整个项目由数据库连接池.插件框架和SQL解析器组成.该项目主要是为了扩展JDBC的一些限制,可以让程序员实现一些特殊的需求,比如向密匙服务请求凭证.统计SQL信息,SQL性能收集.SQL注入检查.SQL翻译等.程序员可以通过定制来实现自己需要的功能
在Maven中使用Druid连接池
1.引入依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.12</version>
</dependency>
2.创建DruidDataSourceFactory
MyDruidDataSourceFactory并继承PooledDataSourceFactory,并替换数据源
import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.datasource.pooled.PooledDataSourceFactory;
/** * 连接池工厂 * @author 雷雨 * @date 2020/6/18 9:16 */
public class MyDruidDataSourceFactory extends PooledDataSourceFactory {
public MyDruidDataSourceFactory(){
this.dataSource= new DruidDataSource();//替换数据源
}
}
3. 把连接池安装到项目中
目的是在需要使用到连接池的地方,使用我们安装的连接池Druid,而不是默认的连接池
修改mybatis的配置文件中默认使用的连接池
修改mybatis的配置文件中默认使用的连接池 |
---|
- 注意属性名必须和com.alibaba.druid.pool.DruidAbstractDataSource中一致
PageHelper
概念
PageHelper是适用于MyBatis框架的一份分页插件,使用方式极为便捷,支持任何复杂的单表,多表分页查询操作
开发步骤
引入依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.0.0</version>
</dependency>
在mybatis配置文件中注册
在mybatis配置文件中注册 |
---|
测试分页功能
测试分页功能 |
---|
注意事项:
- 只有PageHelper.startPage()方法之后的第一个查询会有执行分页
- 分页插件不支持带有for update的查询语句
- 分页插件不支持嵌套查询,由于嵌套方式会导致结果集被折叠,所以无法保证分页结果数量正确
MyBatis的#和$的区别
#{attribute}属于预编译占位符SQL
也就是说执行SQL语句时,使用#{xxx}的话,那么#{xxx}在实际的SQL语句中充当的是?占位符,随后在给?占位符赋值
${attribute}属于字符串拼接SQL,而非预编译占位符,会有SQL注入攻击的问题,不建议在常规SQL中使用,常用于可解决动态升降序的问题
$符号参数绑定 |
---|
-
$符号参数取的是形式参数的getxxx方法
- 比如Liset queryUserByUser(User user)
那么${username}取的是getusername的方法
-
也就是说$符号对于取实体参数是不容易发生错误的
-
但是取基本参数就会发生错误
User queryUserById(Integer id)
id那么就会出错,因为符号实际上取的是Integer.getid()方法
因此会报错
使用$符号总结:
- 如果参数是简单类型的,需要加上@param(“xxx”)注解,否则会报错
- $符号不支持像#{arg0}这样的参数取值方式
- $符号取字符类型要加单引号,不然会导致sql语句语法错误
了解了#和KaTeX parse error: Expected 'EOF', got '#' at position 17: …号取参数的两种方式?该如何选择#̲和?
- #符号取参数实际上是使用了预编译占位符
- 预编译占位符的使用,
不会有Sql注入的风险
,但是不能处理我们需要自己想要添加的内容(比如实现升序降序排列时我们需要添加字符串"desc"."sec")
- 应用灵活,对于参数的类型,基本上不限制
- 取值方式多样
- $符号取参数实际上是字符串的拼接
会有sql注入的风险
,可以随意的添加我们需要的字符串
- 应用不够灵活,只能取实体类型的参数,取实体类型中某一字符串类型的参数时需要加单引号
- 取值方式不够灵活
我是雷雨佳,一个
普本科
的学生,主要专注于Java后端和大数据开发
如果这篇文章有帮助到你,希望你给我一个
大大的赞
如果有什么问题,希望你能留言
和我一起研究
,学习靠自觉,分享靠自愿