SpringBoot连接池druid&&redis实现缓存处理

目录

一、概要分析

二、集成连接池durid

一、连接池分类

 二、集成步骤

1、导入pom.xml

 2、更改yml文件

 三、测试

 三、集成集成缓存redis

   1、导入依赖

 2、更改yml文件

  3、写好配置类

CrossConfiguration:

RedisConfiguration:

3.1 、实现类中使用@cacheable注解开启缓存

​ 3.2、keyGenerator方法的使用

3.3、Template的使用

 4、写好帮助类

RedisUtil:


一、概要分析

在日常敲代码的过程中,代码繁多以及运行速度慢,为了提高代码运行速度,优化代码性能,将提供一下两种方法来解决

二、集成连接池durid

一、连接池分类

C3P0:是一个开放源代码的JDBC连接池,它在lib目录中与Hibernate一起发布,包括了实现jdbc3和jdbc2扩展规范说明的Connection 和Statement 池的DataSources 对象。

Proxool:是一个Java SQL Driver驱动程序,提供了对选择的其它类型的驱动程序的连接池封装。可以非常简单的移植到现存的代码中,完全可配置,快速、成熟、健壮。可以透明地为现存的JDBC驱动程序增加连接池功能。

Jakarta DBCP:DBCP是一个依赖Jakartacommons-pool对象池机制的数据库连接池。DBCP可以直接的在应用程序中使用。

BoneCP:是一个快速、开源的数据库连接池。帮用户管理数据连接,让应用程序能更快速地访问数据库。比C3P0/DBCP连接池速度快25倍。

Druid:Druid不仅是一个数据库连接池,还包含一个ProxyDriver(代理程序)、一系列内置的JDBC组件库、一个SQLParser。支持所有JDBC兼容的数据库,包括Oracle、MySql、Derby、Postgresql、SQL Server、H2等(Druid是一个关系型数据库连接池,是阿里巴巴的一个开源项目,Druid在监控,可扩展性,稳定性和性能方面具有比较明显的优势.通过Druid提供的监控功能,可以实时观察数据库连接池和SQL查询的工作情况.使用Druid在一定程度上可以提高数据库的访问技能.)

 二、集成步骤

1、新建模块

  2、导入pom依赖

<dependency>    
<groupId>com.alibaba</groupId>    
<artifactId>druid-spring-boot-starter</artifactId>    
<version>1.2.8</version></dependency>

3、更改yml文件

server:
  port: 8080
spring:
  datasource:
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/crm?useSSL=false&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&allowPublicKeyRetrieval=true
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      initial-size: 5                                       # 初始化大小
      min-idle: 10                                          # 最小连接数
      max-active: 20                                        # 最大连接数
      max-wait: 60000                                       # 获取连接时的最大等待时间
      min-evictable-idle-time-millis: 300000                # 一个连接在池中最小生存的时间,单位是毫秒
      time-between-eviction-runs-millis: 60000              # 多久才进行一次检测需要关闭的空闲连接,单位是毫秒
      filters: stat                                         # 配置扩展插件:stat-监控统计,log4j-日志,wall-防火墙(防止SQL注入),去掉后,监控界面的sql无法统计   ,wall
      validation-query: SELECT 1                            # 检测连接是否有效的 SQL语句,为空时以下三个配置均无效
      test-on-borrow: true                                  # 申请连接时执行validationQuery检测连接是否有效,默认true,开启后会降低性能
      test-on-return: true                                  # 归还连接时执行validationQuery检测连接是否有效,默认false,开启后会降低性能
      test-while-idle: true                                 # 申请连接时如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效,默认false,建议开启,不影响性能
      stat-view-servlet:
        enabled: true                                       # 是否开启 StatViewServlet
        allow: 127.0.0.1                                    # 访问监控页面 白名单,默认127.0.0.1
        deny: 192.168.56.1                                  # 访问监控页面 黑名单
        login-username: admin                               # 访问监控页面 登陆账号
        login-password: 123                                 # 访问监控页面 登陆密码
      filter:
        stat:
          enabled: true                                     # 是否开启 FilterStat,默认true
          log-slow-sql: true                                # 是否开启 慢SQL 记录,默认false
          slow-sql-millis: 5000                             # 慢 SQL 的标准,默认 3000,单位:毫秒
          merge-sql: false                                  # 合并多个连接池的监控数据,默认false
    redis:
      database: 0         #数据库索引
      host: 127.20.10.5     #主机位置
      port: 6379          #端口
      password: 123456           #密码
      jedis:
        pool:
          max-active: 8   #最大连接数
          max-wait: -1    #最大阻塞等待时间(负数表示没限制)
          max-idle: 8     #最大空闲
          min-idle: 0     #最小空闲
      timeout: 10000      #连接超时时间
logging:
  level:
    com.zxy.code.mapper: debug

4、写实体类 

package com.zxy.code.pojo;
 
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
 
/**
 * 学生表	student
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class Student {
    /**
     * 学生编号 主键 自增
     */
    private Long stuId;
 
    /**
     * 学生名字
     */
    private String stuName;
 
    /**
     * 学生电话
     */
    private String stuPhone;
 
    /**
     * 学生班级
     */
    private Long stuClass;
 
    /**
     * 学生地址
     */
    private String stuAddress;
}

5、mapper层 

package com.zxy.code.mapper;
 
import com.zxy.code.pojo.Student;
import org.springframework.stereotype.Repository;
import tk.mybatis.mapper.common.Mapper;
 
//让当前类成为一个bean,被spring容器所管理
@Repository
public interface StudentMapper extends Mapper<Student> {
 
}

6、启动类进行管理mapper

Springboot05Application.java类:

package com.zxy.code;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import tk.mybatis.spring.annotation.MapperScan;
 
@SpringBootApplication
@MapperScan("com.zxy.code.mapper")
//开启切面的自动代理
@EnableAspectJAutoProxy
//开启事务管理器
@EnableTransactionManagement
public class Springboot05Application {
 
    public static void main(String[] args) {
        SpringApplication.run(Springboot05Application.class, args);
    }
 
}

7、service层

导入PageBean分页与PageAspect切面

PageBean分页:

package com.zxy.code.util;
 
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
 
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
 

@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageBean {
 
    private int total;
    private int page = 1;
    private int rows = 5;
    private boolean pagination = true;
    private String url;
    private Map<String, String[]> ms;
 
    public void setMs(Map<String, String[]> ms) {
        this.ms = ms;
    }
 
    public int calcStartIndex() {
        return (page - 1) * rows;
    }
 
    public int calcMaxPage() {
        return total % rows == 0 ? total / rows : total / rows + 1;
    }
 
    public int nextPage() {
        return page + 1 > calcMaxPage() ? calcMaxPage() : page + 1;
    }
 
    public int prevPage() {
        return page - 1 > 1 ? page - 1 : 1;
    }
 
    public void setRequest(HttpServletRequest req) {
        setUrl(req.getRequestURL().toString());
        setMs(req.getParameterMap());
        String page = req.getParameter("page");
        if (page == null) {
            setPage(1);
        } else {
            setPage(Integer.parseInt(page));
        }
        String rows = req.getParameter("rows");
        if (rows == null) {
            setRows(5);
        } else {
            setRows(Integer.parseInt(rows));
        }
        String pagination = req.getParameter("pagination");
        if (pagination != null && "false".equals(pagination)) {
            setPagination(false);
        }
    }
 
}

PageAspect切面:

package com.zxy.code.aspect;
 
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.zxy.code.util.PageBean;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;


//让此类成为一个bean
@Component
@Aspect
@Slf4j
public class PageAspect {
 
    @Around(value = "execution(* *..*Service.*Pager(..))")
    public Object invoke(ProceedingJoinPoint point) throws Throwable {
        PageBean pageBean = null;
        for (Object e : point.getArgs()) {
            if (e instanceof PageBean) {
                pageBean = (PageBean) e;
                break;
            }
        }
        if (pageBean != null && pageBean.isPagination()) {
            PageHelper.startPage(pageBean.getPage(), pageBean.getRows());
        }
        Object obj = point.proceed(point.getArgs());
        if (obj != null) {
            if (obj instanceof Page) {
                Page page = (Page) obj;
                PageInfo pageInfo = new PageInfo(page);
                pageBean.setTotal(new Long(pageInfo.getTotal()).intValue());
                return pageInfo.getList();
            }
        }
        return obj;
    }
 
}

接口StudentService.java:

package com.zxy.code.service;
 
import com.zxy.code.pojo.Student;
import com.zxy.code.util.PageBean;
 
import java.util.List;
 
public interface StudentService {
 
    List<Student> findPager(PageBean pageBean);
 
}

 实现接口StudentServiceImpl:

package com.zxy.code.service;
 
import com.zxy.code.mapper.StudentMapper;
import com.zxy.code.pojo.Student;
import com.zxy.code.util.PageBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
import java.util.List;
 
@Service
public class StudentServiceImpl implements StudentService {
 
    @Autowired
    private StudentMapper mapper;
 
    @Override
    public List<Student> findPager(PageBean pageBean) {
        return mapper.selectAll();
    }
 
}

 8、测试

Springboot05ApplicationTests :

package com.zxy.code;
 
import com.zxy.code.service.StudentService;
import com.zxy.code.util.PageBean;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
 
@SpringBootTest
class Springboot05ApplicationTests {
 
    @Autowired
    private StudentService service;
 
    @Test
    void contextLoads() {
        PageBean pageBean=new PageBean();
        service.findPager(pageBean).forEach(System.out::println);
    }
 
}

效果

数据查询出来后,下一步如果想知道查询了那些语句,那么这里就需要用到数据源连接池监控“监控地址(http://localhost:8080/druid/login.html) ”这里就可以看见你查询那些语句,和执行时间多久。好处是使用Druid在一定程度上可以提高数据库的访问技能.,方便对数据进行整理。

分页插件yml 

pagehelper : # https://pagehelper.github.io/docs/howtouse
# 是否启用分页合理化。如果启用 , page<1 , 会自动查询第一页的数据
# page>maxPages , 自动查询最后一页数据
# 不启用的 , 以上两种情况都会返回空数据
reasonable : true
# 分页插件会从查询方法的参数值中 , 自动根据上面 params 配置的字段中取值 , 查找到合适的值时就会
自动分页
supportMethodsArguments : true
# 默认值为 false, 当该参数设置为 true , 如果 pageSize=0 或者 RowBounds.limit = 0
会查询出全部的结果
page-size-zero : true
# 指定数据库 , 不指定的话会默认自动检测数据库类型
helper-dialect : mysq

 三、集成集成缓存redis

   1、导入依赖

<dependency>    
<groupId>org.springframework.boot</groupId>    
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

 2、更改yml文件

redis:
  database: 0         #数据库索引
  host:      #主机位置
  port:           #端口
  password:            #密码
  jedis:
    pool:
      max-active: 8   #最大连接数
      max-wait: -1    #最大阻塞等待时间(负数表示没限制)
      max-idle: 8     #最大空闲
      min-idle: 0     #最小空闲
  timeout: 10000      #连接超时时间

3、controller层

package com.lv.code.controller;
 
import com.lv.code.service.StudentService;
import com.lv.code.util.PageBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
import javax.servlet.http.HttpServletRequest;
 
//输出json数据
@RestController
//访问路径
@RequestMapping("stu")
public class StudentController {
 
    @Autowired
    private StudentService service;
 
    @GetMapping("/all")
    public Object index(HttpServletRequest request){
        PageBean pageBean=new PageBean();
        pageBean.setRequest(request);
        return service.findPager(pageBean);
    }
 
}

  4、写好配置类

CrossConfiguration:

package com.zxy.code.conf;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;


/**
 * @author zjjt
 */
@Configuration
public class CrossConfiguration extends WebMvcConfigurerAdapter {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry
                /*可以跨域的路径*/
                .addMapping("/**")
                /*可以跨域的ip*/
                .allowedOrigins("*")
                /*可以跨域的方法*/
                .allowedMethods("*")
                /*设置预检请求多就失效*/
                .maxAge(6000)
                /*允许携带的头部*/
                .allowedHeaders("*");
    }

}

RedisConfiguration:

package com.zxy.code.conf;

import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
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.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.ClassUtils;

import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.time.Duration;

/**
 * @author zjjt
 */
@Configuration
@EnableCaching //开启缓存
public class RedisConfiguration extends CachingConfigurerSupport {

    //@Bean 相当于 @Component,只不过Bean可以写在方法上面,内在跟Component是一样的
    @Bean
    @Primary
    CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .computePrefixWith(cacheName -> cacheName + ":-cache-:")
                /*设置缓存过期时间*/
                .entryTtl(Duration.ofHours(1))
                /*禁用缓存空值,不缓存null校验*/
                .disableCachingNullValues()
                /*设置CacheManager的值序列化方式为json序列化,可使用加入@Class属性*/
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(
                        new GenericJackson2JsonRedisSerializer()
                ));
        /*使用RedisCacheConfiguration创建RedisCacheManager*/
        RedisCacheManager manager = RedisCacheManager.builder(factory)
                .cacheDefaults(cacheConfiguration)
                .build();
        return manager;
    }

    @Bean
    @Primary
    public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        redisTemplate.setConnectionFactory(factory);
        RedisSerializer stringSerializer = new StringRedisSerializer();
        /* key序列化 */
        redisTemplate.setKeySerializer(stringSerializer);
        /* value序列化 */
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        /* Hash key序列化 */
        redisTemplate.setHashKeySerializer(stringSerializer);
        /* Hash value序列化 */
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    @Bean
    @Primary
    @Override
    public KeyGenerator keyGenerator() {
        return (Object target, Method method, Object... params) -> {
            final int NO_PARAM_KEY = 0;
            final int NULL_PARAM_KEY = 53;
            StringBuilder key = new StringBuilder();
            /* Class.Method: */
            key.append(target.getClass().getSimpleName())
                    .append(".")
                    .append(method.getName())
                    .append(":");
            if (params.length == 0) {
                return key.append(NO_PARAM_KEY).toString();
            }
            int count = 0;
            for (Object param : params) {
                /* 参数之间用,进行分隔 */
                if (0 != count) {
                    key.append(',');
                }
                if (param == null) {
                    key.append(NULL_PARAM_KEY);
                } else if (ClassUtils.isPrimitiveArray(param.getClass())) {
                    int length = Array.getLength(param);
                    for (int i = 0; i < length; i++) {
                        key.append(Array.get(param, i));
                        key.append(',');
                    }
                } else if (ClassUtils.isPrimitiveOrWrapper(param.getClass()) || param instanceof String) {
                    key.append(param);
                } else {
                    /*JavaBean一定要重写hashCode和equals*/
                    key.append(param.hashCode());
                }
                count++;
            }
            return key.toString();
        };
    }

}

cachemManager缓存管理器

redisTemplate直接用它操作redis里面的内容 

keyGenerator 主键生成器

3.1 、实现类中使用@cacheable注解开启缓存

  @Cacheable(cacheNames = "aa",cacheManager = "cacheManager")    //用来声明方法是
    public List<Student> findPager(PageBean pageBean){
        return  mapper.selectAll();
    }

cacheNames是缓存的名字用来做分类的,查看缓存时方便,可以在类上进行注解,表示所有的方法的缓存名字都为指定名字

cacheManager是缓存管理器

第一次运行会出现sql语句,然后数据会进入redis缓存中

  当修改cacheManager时

 redis

 3.2、keyGenerator方法的使用

 

 redis:

key用来标识方法的结果,由键去找值,找不到进数据库,要去缓存用户信息,我们一般把用户的id作为键

spel表达式使用#取值

例如

 @Override
    @Cacheable(key ="#pageBean.page")    //用来声明方法是
    public List<Student> findPager(PageBean pageBean){
        return  mapper.selectAll();
    }
cachePut用来更新缓存,用来做修改

cacheEvict用来删除缓存,通过键key

3.3、Template的使用

 4、写好帮助类

RedisUtil:

package com.zxy.code.util;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.redis.RedisSystemException;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.connection.ReturnType;
import org.springframework.data.redis.connection.jedis.JedisConnection;
import org.springframework.data.redis.connection.lettuce.LettuceConnection;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;

/**
 * Redis工具类
 * <p>
 * 声明: 此工具只简单包装了redisTemplate的大部分常用的api,没有包装redisTemplate所有的api
 * 如果对此工具类中的功能不太满意,或对StringRedisTemplate提供的api不太满意,
 * 那么可自行实现相应的{@link StringRedisTemplate}类中的对应execute方法,以达
 * 到自己想要的效果; 至于如何实现,则可参考源码或{@link LockOps}中的方法
 * <p>
 * 注: 此工具类依赖spring-boot-starter-data-redis类库
 * 注: 更多javadoc细节,可详见{@link RedisOperations}
 * <p>
 * 统一说明一: 方法中的key、 value都不能为null
 * 统一说明二: 不能跨数据类型进行操作,否者会操作失败/操作报错
 * 如: 向一个String类型的做Hash操作,会失败/报错......等等
 */
@Slf4j
@Component
@SuppressWarnings("unused")
public class RedisUtil implements ApplicationContextAware {

    /**
     * 使用StringRedisTemplate(,其是RedisTemplate的定制化升级)
     */
    private static StringRedisTemplate redisTemplate;

    private static final ObjectMapper mapper = new ObjectMapper();

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        RedisUtil.redisTemplate = applicationContext.getBean(StringRedisTemplate.class);
    }

    /**
     * key相关操作
     */
    public static class KeyOps {

        /**
         * 根据key,删除redis中的对应key-value
         * <p>
         * 注: 若删除失败,则返回false
         * <p>
         * 若redis中,不存在该key,那么返回的也是false
         * 所以,不能因为返回了false,就认为redis中一定还存
         * 在该key对应的key-value
         *
         * @param key 要删除的key
         * @return 删除是否成功
         */
        public static Boolean delete(String key) {
            log.info("delete(...) => key -> {}", key);
            // 返回值只可能为true/false,不可能为null
            Boolean result =
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值