跳过正文
  1. 文章/
  2. Java/
  3. JavaEE/
  4. Mybatis/

1、Mybatis

·6902 字·14 分钟· loading · loading · ·
Java JavaEE Mybatis
GradyYoung
作者
GradyYoung
目录
Mybatis - 点击查看当前系列文章
§ 1、Mybatis 「 当前文章 」

Mybatis
#

  • Mybatis是一个优秀的基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注sql语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。
  • MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
  • iBATIS一词来源于internetabatis的组合,是一个基于Java的持久层框架。
  • iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAO)
  • 基于ORM(对象关系映射)思想的一个框架
  • MyBatis 是支持定制化SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
  • 官网地址:https://blog.mybatis.org/

优点
#

  • 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件;易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现
  • 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求
  • 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性
  • 提供映射标签,支持对象与数据库的orm字段关系映射
  • 提供对象关系映射标签,支持对象关系组建维护
  • 提供xml标签,支持编写动态sql

缺点
#

  • 编写sql语句工作量大,尤其字段多,关联表多
  • sql语句依赖于数据库,导致数据库一致性差,不可以更换数据库
  • 框架还是比较简陋,功能尚有缺失,虽然简化了数据绑定代码,但是整个底层数据库查询实际还是要自己写,工作量大,而且不太容易适应快速数据库修改
  • 二级缓存机制不佳

简单使用
#

1、导入mybatis依赖
#

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.11</version>
</dependency>

2、导入目标数据库的对应驱动
#

目前使用的是mysql5.7.28

<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.49</version>
</dependency>

3、创建表
#

CREATE TABLE `student` (
  `obj_id` varchar(70) NOT NULL COMMENT '主键',
  `name` varchar(255) DEFAULT NULL COMMENT '姓名',
  `age` int(11) DEFAULT NULL COMMENT '年龄',
  PRIMARY KEY (`obj_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

4、创建表对应实体类
#

注意:一般放在entity包下,体现该类为数据表实体

一般命名要求:表名和实体类类名相同,实体类首字母大写;表字段名和属性名相同

public class Student {

    private String obj_id;

    private String name;

    private String age;

    // getter、setter
}

5、编写持久层接口
#

注意:一般放在dao包下,体现该接口为数据表持久层接口

public interface StudentDao {
    
    /**
     * 查询全部学生
     * @return
     */
    List<Student> selectAll();
}

6、编写映射xml文件
#

注意:一般放在mapper包中,体现是持久层接口的映射文件;如果是Maven项目,一般放在resources/mapper包中。文件名一般与持久层接口名保持一致,例如:StudentDao.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">
<!--namespace用于区别不同映射文件的SQL语句,必须是唯一的,一般是对应持久层接口的全限定名-->
<mapper namespace="top.ygang.mybatisdemo.dao.StudentDao">
    
    <!-- id作为SQL语句的唯一标识,对应接口中的方法名 -->
    <!-- resultType:返回对象的类全名 -->
    <select id="selectAll" resultType="top.ygang.mybatisdemo.entity.Student">
        select * from student
    </select>

</mapper>

7、创建核心配置文件
#

命名固定为:mybatis-config.xml。并且放在src目录;如果是Maven项目,放在resources目录

<?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>
    <settings>
        <!-- 日志输入类,使用默认的标准输出 -->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    <!-- 配置环境,可以包含多个环境,default属性值指定使用哪个环境节点配置 -->
    <environments default="dev">
        <!-- 环境节点的配置 -->
        <environment id="dev">
            <!-- 事务管理器 -->
            <transactionManager type="JDBC"/>
            <!-- 数据源,type:设置数据源的类型,pooled为使用默认连接池 -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <!-- 管理sql映射文件 -->
    <mappers>
        <!-- 持久层接口映射文件路径 -->
        <mapper resource="mapper/StudentDao.xml"/>
    </mappers>
</configuration>

7、进行使用
#

public class Start {

    public static void main(String[] args) throws Exception{
        // 读取核心配置文件
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        // 由mybatis读核心配置文件,将读到的的信息缓存在SqlSessionFactory工厂对象中
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 从工厂中获取SqlSession对象,该对象可以进行数据库交互
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 获取持久层接口实现类对象
        StudentDao studentDao = sqlSession.getMapper(StudentDao.class);

        // 调用方法,执行sql
        List<Student> students = studentDao.selectAll();
        System.out.println(students);

        // 关闭资源,释放数据库链接
        sqlSession.close();
    }
}

少量SQL
#

在少量简单SQL的情况下,我们并不需要特别声明一个持久层接口的映射文件,我们可以使用mybatis提供的注解@Select@Update@Delete@Insert直接在持久层接口上面进行绑定sql。

例如上面的例子,我们的StudentDao接口中只有一个sql,而且简单,我们就不需要写StudentDao.xml ,而是将StudentDao修改下

public interface StudentDao {

    /**
     * 查询全部学生
     * @return
     */
    @Select("select * from student")
    List<Student> selectAll();
}

由于我们没有了StudentDao.xml,所以核心配置文件也需要改一下

<mappers>
    <!-- 持久层接口映射文件路径 -->
	<!-- <mapper resource="mapper/StudentDao.xml"/> -->
    <!-- 持久层接口全限定名 -->
    <mapper class="top.ygang.mybatisdemo.dao.StudentDao"/>
</mappers>

参数的获取
#

在MyBatis中,我们可以使用#{}${}来获取接口形参,二者的区别在于

  • #{}:是预编译,底层调用的是PreparedStatement,会在预处理之前把参数部分用一个占位符?替换,可以防止SQL注入,适用于值的操作
  • ${}:底层调用的是Statement,只是简单的字符串替换,SQL语句拼接,不能防止SQL注入,对于字符串的部分需要加单引号,如${a},如果拼接的是表名,列名,关键字,那么使用此方式最合适

单参数
#

接口只有一个参数的情况下,我们直接可以使用#{fieldName}获取

//接口
GoodType findById(int id);
<!--xml映射文件-->
<select id="findById">
    select * from goodtype where id=#{id}
</select>

多参数
#

方式一:通过在xml映射文件标签中参数以arg0-argn按照形参的顺序

//接口
boolean update1(String name,int id);
<!--xml映射文件-->
<update id="update1">
    update goodbyte set tname=#{arg0} where id=#{arg1}
</update>

方式二:通过在xml映射文件标签中参数以param1-paramn按照形参的顺序

//接口
boolean update2(String name,int id);
<!--xml映射文件-->
<update id="update2">
    update goodbyte set tname=#{param1} where id=#{param2}
</update>

方式三:采用在接口形参前添加注解@Param的方式**(常用)**

//接口
boolean update3(@Param("na")String name,@Param("i")int id);
<!--xml映射文件-->
<update id="update3">
    update goodbyte set tname=#{n} where id=#{i}
</update>

方式四:如果多个参数是同一个自定义对象的属性,使用对象

//接口
boolean update4(GoodType goodType);
<!--xml映射文件,#{xx}:xx为属性名-->
<update id="update4">
    update goodbyte set tname=#{tname} where id=#{tid}
</update>

方式五:使用Map集合的方式。缺点是,在编写sql语句的时候,无法确定map集合的key

//接口
boolean update5(Map<String,Object> map);
<!--xml映射文件,#{xx}:xx为map集合中对应的key-->
<update id="update5">
    update goodbyte set tname=#{key1} where id=#{key2}
</update>

核心配置文件
#

这个顺序是mybatis-config.xml中,configuration的子标签声明顺序,固定不可变

  • properties
  • settings
  • typeAliases
  • typeHandlers
  • objectFactory
  • objectWrapperFactory
  • reflectorFactory
  • plugins
  • environments
  • databaseIdProvider
  • mappers
<?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>
    <!--
        The content of element type "configuration" must match
        "(properties?,settings?,typeAliases?,typeHandlers?,
		objectFactory?,objectWrapperFactory?,
        reflectorFactory?,plugins?,environments?,
		databaseIdProvider?,mappers?)".
    -->
    <!--引入properties文件,此后就可以在当前文件中使用${}的方式访问value-->
    <properties resource="jdbc.properties"></properties>
    
    <!--日志实现类,STDOUT_LOGGING为默认标准输出日志-->
    <setting name="logImpl" value="STDOUT_LOGGING"/>

    <!--
        typeAliases:设置类型别名,即为某个具体的类型(全类名)设置一个别名
        在MyBatis的范围中,就可以使用别名表示一个具体的类型
    -->

    <typeAliases>
        <!--
        typeAlias标签属性:
            type:设置需要起别名的标签
            alias:设置某个类型的别名
            (有默认的别名,默认别名就是类名(不是全类名噢),且不区分大小写)
        -->
        <!--<typeAlias type="top.ygang.entity.Student" alias="Student"></typeAlias>-->
        <!--通过包设置类型别名,指定包下所有的类型将全部拥有默认的别名,即类名且不区分大小写-->
        <!--
        <package name="top.ygang.entity"/>
        -->
    </typeAliases>


    <!--
        environments:配置连接数据库的环境
        属性:
        default:设置默认使用环境的id
    -->
    <environments default="development">
        <!--
            environment:设置一个具体的连接数据库的环境
            属性:
            id:设置环境的唯一标识,不能重复
        -->
        <environment id="development">
            <!--
                transactionManager:设置事务管理器
                属性:
                type:设置事务的方式
                type="JDBC|MANAGED"
                JDBC:表示使用JDBC中原生的事务管理方式
                MANAGED:被管理,例如Spring,mybatis的事务都交给Spring来管理
            -->
            <transactionManager type="JDBC"/>
            <!--
                dataSource:设置数据源
                属性:
                type:设置数据源的类型
                type:"POOLED|UNPOOLED|JNDI"
                POOLED:表示使用数据库连接池
                UNPOOLED:不使用数据库连接池
                JNDI:使用上下文中的数据源
            -->
            <dataSource type="POOLED">
                <!--<property name="driver" value="com.mysql.jdbc.Driver"/>-->
                <!--<property name="url" value="jdbc:mysql://localhost:3306/test?serverTimezone=UTC"/>-->
                <!--<property name="username" value="root"/>-->
                <!--<property name="password" value="123456"/>-->
                <!--配置信息通过${}读取jdbc.properties中的配置信息进行替换-->
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <!--引入映射文件-->
    <mappers>
        <!--<mapper resource="mappers/UserMapper.xml"/>-->
        <!--
            以包的方式引入映射文件,但是必须满足两个条件:
            1、mapper接口和映射文件所在的包必须保持一致
            2、mapper接口的名字必须和映射文件的名字保持一致
        -->
        <!--
        <package name="com.atguigu.mybatis.mapper"/>
        -->
    </mappers>
</configuration>

配置log4j
#

1、引入依赖

<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

2、配置日志实现

<setting name="logImpl" value="LOG4J"/>

3、CLASSPATH下添加log4j.properties

TypeHandler
#

无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。

默认的TypeHandlers
#

img

自定义类型转换器
#

例如现在表里的某个字段是int类型,我们需要将他转换成Boolean类型。当大于等于0时为true;小于0时为false,我们就需要自定义类型转换器

1、继承BaseTypeHandler,并且使用注解@MappedJdbcTypes指定该转换器处理的数据库字段类型;使用@MappedTypes指明该转换器处理的Java类型

// DB字段类型
@MappedJdbcTypes(JdbcType.INTEGER)
// Java类型
@MappedTypes(Boolean.class)
public class MyTypeHandler extends BaseTypeHandler<Boolean> {
    /**
     * 增删改调用,从Java类型转DB类型
     */
    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int columnIndex, Boolean aBoolean, JdbcType jdbcType) throws SQLException {
        preparedStatement.setObject(columnIndex,aBoolean ? 1 : -1);
    }
    /**
     * 查询调用,从DB类型转Java类型
     */
    @Override
    public Boolean getNullableResult(ResultSet resultSet, String columnName) throws SQLException {
        int val = resultSet.getInt(columnName);
        return val >= 0;
    }
    /**
     * 查询调用,从DB类型转Java类型
     */
    @Override
    public Boolean getNullableResult(ResultSet resultSet, int columnIndex) throws SQLException {
        int val = resultSet.getInt(columnIndex);
        return val >= 0;
    }
    /**
     * 查询调用,从DB类型转Java类型
     */
    @Override
    public Boolean getNullableResult(CallableStatement callableStatement, int columnIndex) throws SQLException {
        int val = callableStatement.getInt(columnIndex);
        return val >= 0;
    }
}

配置转换器
#

全局配置
#

这种配置方法将会在整个项目中生效

<typeHandlers>
    <typeHandler handler="top.ygang.mybatisdemo.MyTypeHandler"/>
</typeHandlers>

局部配置
#

对于查询,可以在resultMapresult标签中配置

<resultMap id="result_map" type="top.ygang.mybatisdemo.entity.Student">
    <result typeHandler="top.ygang.mybatisdemo.MyTypeHandler" column="age" property="age"/>
</resultMap>

对于增删改

可以在insertdeleteupdate标签中配置

update student set name = 'tom' where obj_id = #{id,typeHandler=top.ygang.mybatisdemo.MyTypeHandler}

MaBatis映射文件中的标签
#

说明: clipboard.png

MyBatis加载
#

MyBatis根据对关联对象查询的select语句的执行时机,分为三种类型:直接加载(默认)、侵入式延迟加载、深度延迟加载。

懒加载(延迟加载)
#

就是按需加载,我们需要什么的时候再去进行什么操作。而且先从单表查询,需要时再从关联表去关联查询,能大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。

要求
#

1、必须是分布查询

2、关联关系使用association(对一)或collection(对多)

侵入式延迟加载
#

执行主加载对象的查询时,不会执行对关联对象的查询,只有在访问主加载对象详情时或关联对象详情时,才会执行关联对象的查询

<!--在setting节点中加入-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="true"/>

深度延迟加载(推荐)
#

执行主加载对象的查询时,不会执行对关联对象的查询,访问主加载对象详情时也不会执行对关联对象的查询,只有在访问关联对象的详情时,才会执行关联对象的查询

<!--在setting节点中加入-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>

开启延迟
#

(可选)在<association><collection>中添加**fetchType = “lazy”**属性开启延迟,但这里默认为开启,可以不加

缓存
#

引入缓存之后,第一次去查询数据库,第二次以后,如果数据库的数据没有发生改变,我们就不去查询数据库,我们查缓存。使用缓存之后,就减少了查询数据库的次数,从而提高了查询效率

mybatis中的缓存可以分为一级缓存和二级缓存

哪些数据使用放在缓存中,哪些数据不适合放在缓存中
#

1)不频繁更新的数据可以放在缓存中,频繁发生更新的数据,不要放在缓存中

2)财务数据不要放在缓存中,不要的数据可以放在缓存中

一级缓存
#

一级缓存mybatis自带,不要额外配置,属于sqlSession级别的缓存(和SqlSession相关的),默认开启

说明: clipboard.png

验证
#

Goods find1 = goodsDaoMapper.findByGid(1);
System.out.println(find1.getDname());
System.out.println("**********************************");
Goods find2 = goodsDaoMapper.findByGid(1);
System.out.println(find2.getDname());

结果日志

Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Opening JDBC Connection
Created connection 798244209.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@2f943d71]
==>  Preparing: select * from goods where gid=? 
==> Parameters: 1(Integer)
<==    Columns: gid, dname, buyprice, saleprice, remarks, tid, picPath, quantity, state
<==        Row: 1, java, 50, 80, 11111, 1, img/1.jpg, 1000, 1
<==      Total: 1
java
**********************************
java
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@2f943d71]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@2f943d71]
Returned connection 798244209 to pool.

两次相同的查询,只向数据库发送了一次SQL语句,说明,第二次查询是从缓存中查询,并不是从数据库查询

一级缓存失效
#

情况一
#

第一次查询的数据和第二次查询的数据不一样

情况二
#

如果查询的是同样的数据,调用了更新方法(增删改DML)的方法

情况三
#

调用了刷新方法

sqlSession.clearCache();

情况四
#

SqlSession对象不一样

情况五
#

(select标签中)flushCache="true"属性

二级缓存
#

如果系统需要二级缓存,那么需要人为配置,二级别缓存属于Mapper级别、SqlSessionFactory级别的缓存

将查询出的数据保存在二级缓存中需要通过调用sqlSession的commit方法才可以进行保存在二级缓存

主要用来解决一级缓存不能跨会话(SqlSession)共享的问题,范围是namespace 级别的,可以被多个SqlSession 共享(只要是同一个接口里面的相同方法,都可以共享),生命周期和应用同步

如果你的MyBatis使用了二级缓存,并且你的Mapper和select语句也配置使用了二级缓存,那么在执行select查询的时候,MyBatis会先从二级缓存中取输入,其次才是一级缓存,即MyBatis查询数据的顺序是:二级缓存 —> 一级缓存 —> 数据库

说明: clipboard.png

配置二级缓存的步骤
#

1、在核心配置文件的<setting>节点中添加

<setting name="cacheEnabled" value="true"></setting>

2、在xml映射文件中,配置

<!--
可选属性:
type="哪种缓存机制"
size="缓存的资源数量"
eviction="缓存回收策略"
flushInterval="刷新的间隔"
readOnly="只读true\false"
    true:会给所有的查询的用户,返回同一个的java对象
    false:会给每一个查询的用户,返回同一个java对象的克隆的副本,也就是不同的对象
-->
<cache></cache>

3、封装类实现序列化接口java.io.Serializable

要求
#

1、二级缓存是将对象通过序列化流存储到磁盘文件,需要封装类实现序列化,如果是多表联合查询,那么关联表对应的实体类也需要序列化

2、两个SqlSession对象必须是同一个SqlSessionFactory创建

验证
#

SqlSession sqlSession1 = factory.openSession();
SqlSession sqlSession2 = factory.openSession();
GoodsDaoMapper mapper1 = sqlSession1.getMapper(GoodsDaoMapper.class);
GoodsDaoMapper mapper2 = sqlSession2.getMapper(GoodsDaoMapper.class);
Goods byGid1 = mapper1.findByGid(1);
System.out.println(byGid1.getDname());
sqlSession1.commit();
System.out.println("***********************************");
Goods byGid2 = mapper2.findByGid(1);
System.out.println(byGid2.getDname());
sqlSession1.close();
sqlSession2.close();

结果日志

Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Cache Hit Ratio [dao.GoodsDaoMapper]: 0.0
Opening JDBC Connection
Created connection 1297149880.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4d50efb8]
==>  Preparing: select * from goods where gid=? 
==> Parameters: 1(Integer)
<==    Columns: gid, dname, buyprice, saleprice, remarks, tid, picPath, quantity, state
<==        Row: 1, java, 50, 80, 11111, 1, img/1.jpg, 1000, 1
<==      Total: 1
java
***********************************
Cache Hit Ratio [dao.GoodsDaoMapper]: 0.5
java
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4d50efb8]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4d50efb8]
Returned connection 1297149880 to pool.

同一SqlSessionFactory对象,所产生的不同的SqlSession对象,也可以共享缓存

缓存穿透
#

缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据查询。这时的用户很可能是攻击者,攻击会导致数据库压力过大。

解决方法
#

1、使用Redis替换mybatis缓存,即使查询出来的数据为null,也会存入缓存

2、使用布隆过滤器,对一些不存在的条件进行过滤

缓存击穿
#

缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力

解决方法
#

1、设置热点数据缓存永不过期

2、利用代理或装饰模式,对mybatis源码进行切入,对数据库查询进行加锁

3、使用mybatis的拦截器,对源码中的query方法进行加锁

缓存雪崩
#

缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是, 缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。

解决方法
#

1、缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。

2、如果缓存数据库是分布式部署,将热点数据均匀分布在不同的缓存数据库中。

3、设置热点数据永远不过期

Mybatis - 点击查看当前系列文章
§ 1、Mybatis 「 当前文章 」