Mybatis学习
简介
什么是mybatis?
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
第一个mybatis程序
从 XML 中构建 SqlSessionFactory
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。
从 XML 文件中构建 SqlSessionFactory 的实例非常简单,建议使用类路径下的资源文件进行配置。 但也可以使用任意的输入流(InputStream)实例,比如用文件路径字符串或 file:// URL 构造的输入流。MyBatis 包含一个名叫 Resources 的工具类,它包含一些实用方法,使得从类路径或其它位置加载资源文件更加容易。
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
XML 配置文件中包含了对 MyBatis 系统的核心设置,包括获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器(TransactionManager)。后面会再探讨 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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
当然,还有很多可以在 XML 文件中配置的选项,上面的示例仅罗列了最关键的部分。 注意 XML 头部的声明,它用来验证 XML 文档的正确性。environment 元素体中包含了事务管理和连接池的配置。mappers 元素则包含了一组映射器(mapper),这些映射器的 XML 映射文件包含了 SQL 代码和映射定义信息。
编写mybatis工具类
package utils;
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 java.io.IOException;
import java.io.InputStream;
/**
* @author Shi Jun Yi
* @version 1.0
* @date 2021/1/26 23:03
*/
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
//使用mybatis的第一步 获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
// SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
// 你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
有了这个工具类就可以操作数据库了。
操作数据库
- 编写实体类
package entity;
/**
* @author Shi Jun Yi
* @version 1.0
* @date 2021/1/28 15:30
* 实体类
*/
public class Student {
private String sno;
private String sname;
private String ssex;
private String sbirthday;
private String saddress;
private String sdept;
public Student() {
}
public Student(String sno, String sname, String ssex, String sbirthday, String saddress, String sdept) {
this.sno = sno;
this.sname = sname;
this.ssex = ssex;
this.sbirthday = sbirthday;
this.saddress = saddress;
this.sdept = sdept;
}
@Override
public String toString() {
return "Student{" +
"sno='" + sno + '\'' +
", sname='" + sname + '\'' +
", ssex='" + ssex + '\'' +
", sbirthday='" + sbirthday + '\'' +
", saddress='" + saddress + '\'' +
", sdept='" + sdept + '\'' +
'}';
}
public String getSno() {
return sno;
}
public void setSno(String sno) {
this.sno = sno;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public String getSsex() {
return ssex;
}
public void setSsex(String ssex) {
this.ssex = ssex;
}
public String getSbirthday() {
return sbirthday;
}
public void setSbirthday(String sbirthday) {
this.sbirthday = sbirthday;
}
public String getSaddress() {
return saddress;
}
public void setSaddress(String saddress) {
this.saddress = saddress;
}
public String getSdept() {
return sdept;
}
public void setSdept(String sdept) {
this.sdept = sdept;
}
}
- Dao接口
public interface StudentDao {
List<Student> getStudent();
}
- 接口实现类由原来的impl转化成一个配置文件
- 实现studentdao接口方法的配置文件:
<?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">
<!--namespace=绑定一个对应的Dao/mapper接口-->
<mapper namespace="dao.StudentDao">
<!--id对应接口的方法名 resultType对应接口中泛型的实体类-->
<select id="getStudent" resultType="entity.Student">
select * from test.student
</select>
</mapper>
测试
抛异常:org.apache.ibatis.binding.BindingException: Type interface dao.StudentDao is not known to the MapperRegistry.
注意点:
资源过滤问题
<!--资源过滤-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
测试类
package dao;
import entity.Student;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import utils.MybatisUtils;
import java.util.List;
/**
* @author Shi Jun Yi
* @version 1.0
* @date 2021/1/28 16:45
*/
public class StudentDaoTest {
@Test
public void test(){
//第一步:获取sqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//第二步:执行SQL
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
List<Student> student = studentDao.getStudent();
for (Student student1 : student) {
System.out.println(student1);
}
//关闭sqlsession
sqlSession.close();
}
}
增删改
注意点:增删改需要提交事务
- insert
//接口
int addStudent(Student student);
//配置文件
<insert id="addStudent" parameterType="entity.Student">
insert into test.student VALUES(#{sno},#{sname},#{ssex},#{sbirthday},#{saddress},#{sdept})
</insert>
//测试
@Test
public void addStudent(){
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
mapper.addStudent(new Student("177723","王哥","男","1988-08-25 00:00:00","河北","计算机"));
sqlSession.commit();
System.out.println("插入成功! ");
//关闭sqlsession
sqlSession.close();
}
- update
//接口
int updateStudent(Student student);
//配置文件
<update id="updateStudent" parameterType="entity.Student">
update test.student set sname =#{sname},ssex=#{ssex} where sno=#{sno}
</update>
//测试
@Test
public void updateStudent(){
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
mapper.updateStudent(new Student("123","大海","女","","",""));
sqlSession.commit();
System.out.println("修改成功! ");
}
- delete
//接口
int deleteStudent(String sno);
//配置文件
<delete id="deleteStudent" parameterType="String">
delete from test.student where sno=#{sno}
</delete>
//测试
@Test
public void deleteStudent(){
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
mapper.deleteStudent("177723");
sqlSession.commit();
System.out.println("删除成功! ");
}
万能的Map
当我们的实体类或者数据表中的表,字段或者参数过多的时候 ,我们应该考虑用Map!
/**用map插入数据*/
void addStudent2(Map<String,String> map);
<!--用map插入-->
<insert id="addStudent2" parameterType="map">
insert into test.student VALUES(#{a},#{b},#{c},#{d},#{e},#{f})
</insert>
多个参数用Map 或者是 注解
模糊查询
模糊查询怎么写?操作不当会造成sql注入
- java代码执行的时候加上通配符 %value%
List<Student> studentByno2 = studentMapper.getStudentByno2("%张%");
- 在sql拼接中使用通配符
select * from test.student where sname like "%"#{value}"%"
那什么是sql注入?
在JDBC当中用Statement进行查询的时候用的就是字符串拼接,像这个样子:
String name = "'盖伦' OR 1=1";
select * from hero where name = '盖伦' OR 1=1
当我们进行参数查询的时候用Statement就会产生sql注入问题,拼接起来就是全部执行,会把所有的数据都查出来,会造成资源浪费,而是用PreparedStatement就不会产生这个问题,这就是sql注入问题。
配置优化
核心配置文件相关属性
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
环境配置(environments)
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中,现实生活中我们也需要配备多个环境来进行测试。
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
- 事务管理器的配置(比如:type=“JDBC”)。 //默认的
- 数据源的配置(比如:type=“POOLED”)。 //默认的
事务管理器:
在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
- JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
- MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。
注意:如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。
数据源
dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
- 大多数 MyBatis 应用程序会按示例中的例子来配置数据源。虽然数据源配置是可选的,但如果要启用延迟加载特性,就必须配置数据源。
有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]")
- UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择。 性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。
- POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。
- JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。
属性(properties)
由于在mybatis核心配置文件中存在着大量写死的参数,因此我们可以通过读取配置文件来读取参数。
- 编写一个配置文件:
driver = com.mysql.jdbc.Driver
url = jdbc:mysql://127.0.1:3306/test?serverTimezone=UTC&characterEncoding=UTF-8
username = root
password = 123
2. 在核心配置文件中引入配置文件
<!--引入外部配置文件-->
<properties resource="db.properties" />
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
优先使用配置文件的参数配置
类型别名(typeAliases)
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
在没有配置别名之前,在Mapper中设置实体类参数的时候很麻烦,像这样:
但是当我们设置了别名之后就可以用类名啦!
设置别名依旧是在核心配置文件中设置
<!--类型别名-->
<typeAliases>
<typeAlias type="entity.Student" alias="Student" />
</typeAliases>
还有一种写法就是扫描这个entity包下的javabean文件,用类名作为别名(首字母小写)。
<!--类型别名-->
<typeAliases>
<package name="entity" />
</typeAliases>
注意:当我们的实体类比较少的情况下用第一种,当我们实体类比较多的情况下用第二种。
下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
设置(settings)
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。
置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true | false | true |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 | true | false | false |
useColumnLabel | 使用列标签代替列名。实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察。 | true | false | true |
useGeneratedKeys | 允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。 | true | false | False |
mapUnderscoreToCamelCase | 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 | true | false | False |
其他配置
MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
愿景
我们的愿景是成为 MyBatis 最好的搭档,就像 魂斗罗 中的 1P、2P,基友搭配,效率翻倍。
映射器(mappers)
我们一开始使用的就是最普通的方式来绑定我们的Mapper文件
- 方法一
<!--每一个mapper.xml都需要在mybatis核心配置文件当中进行注册-->
<mappers>
<mapper resource="dao/StudentMapper.xml"></mapper>
</mappers>
- 方法二
<!--每一个mapper.xml都需要在mybatis核心配置文件当中进行注册-->
<mappers>
<mapper class="dao.StudentMapper"/>
</mappers>
我们也可以使用class来进行文件绑定,但是这个是有前提的!
- 接口必须和它的Mapper配置文件同名!
- 接口必须和它的Mapper文件在同一个包下!
- 方式三
使用包扫描也和方式二有同样的问题。
声明周期和作用域
不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。
SqlSessionFactoryBuilder
一旦创建了 SqlSessionFactory,就不再需要它了。
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
因此我们把它设置成局部变量。
SqlSessionFactory
就像是一个数据库链接池
一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
因此这个应该是全局的变量,,我们可以通过单例模式来设置成全局的(Application)
SqlSession
-
简单点说就是连接到数据库连接池的一个请求
-
用完必须关闭
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 用后必须关闭,否则会造成资源占用。
ResultMap结果集映射
场景:当我们实体类中的属性名与数据库中的字段名不一致的时候我们数据就查不出来,因此我们有两种方法来解决它。
- 方法一
<select id="getStudentByno" parameterType="String" resultType="Course">
select cno as no,cname as name from test.course where cno = #{cno}
</select>
- 方法二
<!--结果集映射-->
<resultMap id="Course" type="course">
<!--column 数据库中的字段 property 实体类中的属性-->
<result column="cno" property="no"/>
<result column="cname" property="name"/>
</resultMap>
<select id="getStudentByno" resultMap="Course">
select * from test.course where cno = #{cno}
</select>
resultMap
元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets
数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作。
日志
当我们的程序出现了异常之后,我们需要排错,以前我们就是用debug和输出语句,现在我们用日志工厂!
- SLF4J
- LOG4J (掌握)
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING(掌握)
- NO_LOGGING
在mybatis中具体使用哪个,在日志中设置。
标准日志工厂STDOUT_LOGGING
在mybatis核心配置文件中配置
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
Log4j
什么是Log4j?
Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器,我们也可以控制每一条日志的输出格式,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
大佬经验:面向百度编程!
- 导包
<!-- 加入log4j支持 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
- 编写配置文件 log4j.properties
#设置输出位置
log4j.rootLogger=debug, stdout, R
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# Pattern to output the caller's file name and line number.
# 输出格式
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n
# , 以滚动的方式输出到文件,文件名是example.log,文件最大100k, 最多滚动5个文件
log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=./log/example.log
log4j.appender.R.MaxFileSize=100KB
# Keep one backup file
log4j.appender.R.MaxBackupIndex=5
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n
配置文件解释:https://how2j.cn/k/log4j/log4j-config/1082.html#step4162
如何使用
- 在要用log4j的类中导入包
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
- 然后创建日志对象
//日志对象,参数为当前类的class
static Logger logger = Logger.getLogger(Student.class);
- 日志级别
logger.info("info:***");
logger.debug("debug:***");
logger.error("error:***");
**注意:**在mybatis核心配置文件当中name 和value千万不能复制错误,哪怕是一个空格也不可以,否则就会抛找不到类名的异常。
使用注解进行增删改查
复杂查询环境搭建
表结构设置外键达到一对多和多对一的关系:
CREATE TABLE category (
id int AUTO_INCREMENT,
name varchar(255) ,
PRIMARY KEY (id)
) DEFAULT CHARSET=utf8;
CREATE TABLE record (
id int AUTO_INCREMENT,
spend int,
cid int,
comment varchar(255) ,
date Date,
PRIMARY KEY (id),
CONSTRAINT `fk_record_category` FOREIGN KEY (`cid`) REFERENCES `category` (`id`)
) DEFAULT CHARSET=utf8;
多对一
方法一:
实践:
<mapper namespace="dao.TeachingMapper">
<select id="getList" resultMap="teachingcourse">
select * from test.` teaching`
</select>
<resultMap id="teachingcourse" type="teaching">
<result property="tno" column="tno" />
<association property="cno" column="cno" javaType="Course" select="getCourse"/>
</resultMap>
<select id="getCourse" resultType="Course">
select * from test.course where cno=#{cno}
</select>
</mapper>
方法二:
<!--方法二 按照结果嵌套处理-->
<select id="getList2" resultMap="teachingcourse2">
select t.tno,c.cno,c.cname
from ` teaching` t,course c
where c.cno = t.cno;
</select>
<resultMap id="teachingcourse2" type="teaching">
<result property="tno" column="tno" />
<association javaType="Course" property="cno">
<result property="cname" column="cname" />
<result property="cno" column="cno" />
</association>
</resultMap>
一对多
小结
- 方法一是sql语句复杂 但是映射简单(推荐 因为sql可以调试)
- 方法二是sql简单 但是映射复杂
动态SQL
什么是动态SQL:动态SQL就是指根据不同的条件生成不同的SQL语句
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。
如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。
if
choose (when, otherwise)
trim (where, set)
foreach
if
choose(when otherwise)
trim(where set)
所谓的动态SQL本质还是SQL语句,只是我们可以在SQL层面去执行一个逻辑代码(分支,选择)
SQL片段
可以实现代码的复用,直接引用SQL片段皆可。
注意事项:
- 最好基于单表来定义SQL片段
- 不要存在where标签
foreeach
动态SQL就是在拼接sql语句,我们只要按照SQL的正确性,按照SQL的格式,去排列组合就可以了
面试高频:
- Mysql引擎
- InnoDB底层原理
- 索引
- 索引优化
缓存
缓存简介
查询 : 频繁的连接数据库很消耗资源!
所以我们将查到的结果放到内存当中进行临时存储-内存
等我们下次再次访问相同的数据的时候就避免了连接数据库,这样就快了很多
缓存也是为了解决高并发系统的性能问题
为什么要使用缓存?
- 减少和数据库的交互次数,减少系统的开销,提高系统的效率
什么样的数据可以使用缓存?
- 经常查询并且不经常改变的数据。
一级缓存
基本上就是这样。这个简单语句的效果如下:
- 映射语句文件中的所有 select 语句的结果将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
- 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
- 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
缓存失效的情况?
-
查询不同的东西
-
增删改操作之后可能会改变原来的数据,所以必定会进行刷新缓存
-
查询不同的Mapper
-
手动清理缓存
小结:一级缓存是默认开启的,只在一次sqlsession中生效,也就是拿到连接关闭连接这个区间
一级缓存就像是一个map
二级缓存
- 二级缓存也称全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
- 基于namespace级别的缓存,一个名称空间对应一个二级缓存
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:
<cache/>
步骤:
- 开启全局缓存
cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 true | false true
- 在当前Mapper.xml当中使用
<cache/>
这些属性可以通过 cache 元素的属性来修改。比如:
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
-
测试
当一级缓存挂掉的时候就会启动二级缓存并把数据丢给二级缓存。
注意:我们需要将实体类进行序列化,否得就会报错!
*
小结:
- 所有的数据都会先放在一级缓存当中,只有当会话提交或者关闭的时候才会提交到二级缓存当中
- 只要开启了二级缓存,在同一个Mapper下就有效
缓存原理
自定义缓存-ehcache
- EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。
- Ehcache是一种广泛使用的开源Java分布式缓存。
- 主要面向通用缓存,Java EE和轻量级容器。
- 它具有内存和磁盘存储,缓存加载器,缓存扩展,缓存异常处理程序,一个gzip缓存servlet过滤器,支持REST和SOAP api等特点。
注意:现在基本上被Redis代替了!!
Redis数据库来做缓存,K-V
mybatis执行流程