MyBaits-Plus使用redis作为缓存的另外一种方法

文章写于2023-06-30,里面包的版本尽量都是用的当前日期下最新的,读者如果阅读的时间据此较远的话,应注意包和插件的版本.

不喜欢废话,直接放码过来.

一.引入包

需要引入的包有:reids,jedis,cache,thymeleaf,spring web,mysql,lombok

pom代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.flamelp</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>MP_Redis</name>
    <description>MP_Redis</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
<!--        reids,jedis,cache,thymeleaf,spring web,mysql,lombok-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
<!--mp代码生成器-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.3.1</version>
        </dependency>
<!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.3.1</version>
        </dependency>
<!--mybatis-plus生成代码,需要用到这个包的模板-->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity</artifactId>
            <version>1.7</version>
        </dependency>
<!--swagger引用-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>3.0.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
<!--        部分文件需要添加到target文件目录-->
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>mapper/*.xml</include>
                    <include>static/*.*</include>
                    <include>templates/*.html</include>
<!--                    如果不将yml文件添加到target目录,将会提示:url路径错误-->
                    <include>*.yml</include>
                </includes>
            </resource>
        </resources>
    </build>

</project>

二.编写redis

创建一个utils包,在包下面创建一个RedisConfig来,用于管理redis缓存.代码如下.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import redis.clients.jedis.JedisPoolConfig;

@Configuration
@EnableCaching//驱动缓存
public class RedisConfig extends CachingConfigurerSupport {
    @Bean("redisPoolConfig")
    public JedisPoolConfig poolConfig(){
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        //最大空闲数
        poolConfig.setMaxIdle(50);
        //最大连接数
        poolConfig.setMaxTotal(100);
        //最大等待时间,毫秒
        poolConfig.setMaxWaitMillis(200000);
        //poolConfig.setMaxWait(Duration.ofMillis(200000));
        return poolConfig;
    }

    @Bean("redisConnectionFactory")
    public RedisConnectionFactory redisConnectionFactory(@Autowired JedisPoolConfig jedisPoolConfig){
        RedisStandaloneConfiguration rsc = new RedisStandaloneConfiguration();
        //设置Redis服务器
        rsc.setHostName("127.0.0.1");
        //设置密码
        rsc.setPassword("123456");
        //端口
        rsc.setPort(6379);
        //获得默认连接池构造器
        JedisClientConfiguration.JedisClientConfigurationBuilder jpcb = JedisClientConfiguration.builder();
        //设置Redis连接池
        jpcb.usePooling().poolConfig(jedisPoolConfig);
        //获取构建器
        JedisClientConfiguration jedisClientConfiguration = jpcb.build();
        //创建工厂
        return new JedisConnectionFactory(rsc,jedisClientConfiguration);
    }

    /**
     * 配置redis数据存储模板
     * @param connectionFactory
     * @return
     */
    @Bean("redisTemplate")
    public RedisTemplate<String,Object> redisTemplate(@Autowired RedisConnectionFactory connectionFactory){
        //创建redis模板
        RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
        //字符串和JDK序列化器
        RedisSerializer<String> strSerializer = RedisSerializer.string();
        RedisSerializer<Object> jdkSerializer = RedisSerializer.java();
        //设置键值序列化器
        redisTemplate.setKeySerializer(strSerializer);
        redisTemplate.setValueSerializer(jdkSerializer);
        //设置好哈希字段和值序列化器
        redisTemplate.setHashKeySerializer(strSerializer);
        redisTemplate.setHashValueSerializer(jdkSerializer);
        //设置redisTemplate的连接工厂
        redisTemplate.setConnectionFactory(connectionFactory);
        return redisTemplate;
    }

    /**
     * 设置字符串连接模板
     * @param connectionFactory
     * @return
     */
    @Bean("stringRedisTemplate")
    public StringRedisTemplate stringRedisTemplate(@Autowired RedisConnectionFactory connectionFactory){
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
        stringRedisTemplate.setConnectionFactory(connectionFactory);
        return stringRedisTemplate;
    }

    /**
     * 初始化缓存管理器
     * @param redisConnectionFactory
     * @return
     */
    @Bean(name="redisCacheManager")
    public CacheManager initRedisCacheManager(@Autowired RedisConnectionFactory redisConnectionFactory){
        //获得redis默认配置
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        //创建redis缓存管理器
        RedisCacheManager cacheManager = RedisCacheManager.RedisCacheManagerBuilder
                .fromConnectionFactory(redisConnectionFactory)
                //定义缓存管理器名称和配置,以便后续使用
                .withCacheConfiguration("redisCacheManager",config)
                .build();
        return cacheManager;
    }
}

三.在spring boot项目的启动类的main方法中添加如下代码:

    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(RedisConfig.class);
        SpringApplication.run(MpRedisApplication.class, args);
    }

四.修改你的application.yml文件,除了基础配置之外,主要是在执行SQL语句的时候把SQL命令打印在控制台,看看SQL语句的执行情况,或者是否有读取redis的缓存.

#server
server:
  port: 8092
  servlet:
    context-path: /

#spring
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/XXXX
    username: xxxx
    password: xxxx


#mybatis-plus
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    #开启缓存
     cache-enabled: true

五.在你的service类的增删改查方法中加上以下标签就可以实现redis的缓存

@Cacheable,@CachePut,@CacheEvict

但是我们打算偷个懒,使用mybaits plus生成项目框架,所以还得做些其它事情.

六.mybatis plus自带的方法对redis的缓存实现不够友好,所以动手修改下几个父类,官方建议将缓存放在service层实现,所以就针对这一层下手.

mybatis plus 的service层的接口和类分别实现和继承了:IService,ServiceImpl.打算自定义父类和接口.所以有以下代码:

在utils包里创建IMyService接口,具体代码如下:

import com.baomidou.mybatisplus.extension.service.IService;

import java.io.Serializable;

public interface IMyService<T> extends IService<T> {
    T selectById(Serializable id);
    T add(T entity);
    boolean delete(Serializable id);
    T modify(T entity);
}
IMyService中自定义增删改查的方法,用这些方法来实现redis的缓存.请注意用上面的这几个方法实现数据的增删改查就会调用缓存,其它的不会使用缓存,如果读者需要对更多的方法实现缓存,在上面这个接口增加方法就行(当然还要最下面的这个类里面实现对应的方法.)

查询所有结果,按条件查询结果这些的结果,重复使用率不高对于缓存的意义不大,所以没有做缓存.

在utils包里在创建MyServiceImpl类,代码如下:

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;

import java.io.Serializable;

public class MyServiceImpl<M extends BaseMapper<T>, T> extends ServiceImpl<M,T> {

    /**
     * @Cacheable:先查缓存,缓存没有对应的key值在查找数据库,找到后再进行缓存.
     * value:redisCacheManager.缓存的名称,放在同一个缓存里面,通过key值进行区分方便管理.
     * key:缓存在redis中时以键值对的形式存在.根据key查找缓存的值,所以不同类型的缓存不能仅仅依靠id进行区分.
     * 这里用实体类名+_主键id作为key,确保key的唯一性.
     * #root.target对象为调用该方法的类,获得类名后(serviceImpl类),根据命名规则去掉'ServiceImpl'后就是实体类名.
     * unless = "#result==null" 当没有查询到结果时不进行缓存.
     */
    @Cacheable(value="redisCacheManager"
            ,key ="#root.target.getClass().getSimpleName().replaceAll('ServiceImpl','_')+#id"
            ,unless = "#result==null")
    public T selectById(Serializable id){
        return this.getById(id);
    }

    //@CachePut会调用方法,并将方法的结果进行缓存.
    @CachePut(value = "redisCacheManager"
            ,key="#root.target.getClass().getSimpleName().replaceAll('ServiceImpl','_')+#entity.id")
    public T add(T entity){
        this.save(entity);
        return entity;
    }

    //@CacheEvict删除对应的缓存,再执行方法
    @CacheEvict(value="redisCacheManager"
            ,key="#root.target.getClass().getSimpleName().replaceAll('ServiceImpl','_')+#id")
    public boolean delete(Serializable id){
        return this.removeById(id);
    }

    @CachePut(value = "redisCacheManager"
            ,key="#root.target.getClass().getSimpleName().replaceAll('ServiceImpl','_')+#entity.id")
    public T modify(T entity){
        this.updateById(entity);
        return entity;
    }
}

七.使用mybatis-plus生成项目框架

到这一步redis缓存的设置和mybatis-plus自定义父类就已经完成了.接下来编写MP的代码生成器.

还是在utils包里面创建CreateFrame类,用于生成MP代码框架.具体代码如下:

import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine;

import java.sql.Types;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class CreateFrame {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/XXXX";
        String username="root";
        String password = "XXXXX";

        String pkgPath = System.getProperty("user.dir")+"/src/main/java";
        String pkgXml = System.getProperty("user.dir")+"/src/main/resources/mapper";

        FastAutoGenerator.create(url, username, password)
                .globalConfig(builder -> {//全局配置
                    builder.author("flamelp. 仅用于教学使用") // 设置作者
                            .enableSwagger() // 开启 swagger 模式
                            .dateType(DateType.TIME_PACK)//使用time类型转换数据库中的时间
                            .commentDate("yyyy-MM-dd")//注释日期
                            .outputDir(pkgPath); // 指定输出目录
                })
                .dataSourceConfig(builder -> builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
                    int typeCode = metaInfo.getJdbcType().TYPE_CODE;
                    if (typeCode == Types.SMALLINT) {// 自定义类型转换
                        return DbColumnType.INTEGER;
                    }
                    return typeRegistry.getColumnType(metaInfo);

                }))
                .packageConfig(builder -> {//将包都放在启动类所在的包下面.
                    builder.parent("com.flamelp") // 设置父包名
                            .moduleName("demo") // 设置父包模块名
//                            .entity("entity")//entity包名
//                            .mapper("mapper")//dao包名
//                            .service("service")//service包名
//                            .service("service.impl")//service实体类包名
//                            .controller("controller")//controller包名
//                            .xml("mapper")//xml文件包名
                            .pathInfo(Collections.singletonMap(OutputFile.xml, pkgXml)); // 设置mapperXml生成路径
                })
                .strategyConfig(builder -> {
                    builder.addInclude(getTables("all")); // 设置需要生成的表名
                    //.addTablePrefix("t_"); // 设置过滤表前缀

                    //实体类配置
                    builder.build().entityBuilder()
                            .enableLombok()//设置Lombok注解
                            //.enableFileOverride()//覆盖已生成的文件,
                            .enableTableFieldAnnotation()//开启实体类字段注解
                            .naming(NamingStrategy.underline_to_camel)//数据库表名命名规则:下划线转驼峰
                            .columnNaming(NamingStrategy.no_change)//数据表字段命名规则:不处理
//                            .logicDeletePropertyName("deleted")//逻辑删除属性名(实体)
                    ;//数据进行逻辑删除

                    //controller配置策略
                    builder.build().controllerBuilder();

                    //service配置策略
                    builder.build().serviceBuilder()
                            .superServiceClass("com.flamelp.demo.utils.IMyService")//设置服务类的父类
                            .superServiceImplClass("com.flamelp.demo.utils.MyServiceImpl");//设置服务实现类的父类.

                    //Mapper(dao)配置
                    builder.build().mapperBuilder()
                            .enableMapperAnnotation()//设置mapper注解
                            .enableBaseResultMap()//启用 BaseResultMap 生成resultMap
                            //.superClass("MPJBaseMapper")//设置mapper类的父类
                            //.enableFileOverride()//覆盖已生成的文件,
                            .enableBaseColumnList()//启用 BaseColumnList,生成列的列表
//                            .cache(MybatisRedisCache.class)//开启缓存.
                    ;
                })
                // 使用Freemarker引擎模板,默认的是Velocity引擎模板,使用Velocity模版时,需要在pom文件中配置Velocity的引用
                .templateEngine(new VelocityTemplateEngine())
                .execute();
    }
    protected static List<String> getTables(String tables) {
        return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(","));
    }
}

八.运行CreateFrame类中的main方法,

搞定,收工,写到第八点感觉自己写了个八股文似的.上面代码的注释比较全,所以没有用过多的语言描述.

另外使用mysql数据库时数据库表采用"_"下划线分隔单词是个好的方法,因为mysql的表名全部给你转小写了,但是没关系MP会给你在实体类等类的命名中给你改成驼峰命名法.但是表的列名就没有必要使用下划线了,MySQL可以识别列名大小写.

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值