springmybatis-redis整合

本文介绍了如何在Spring环境中结合MyBatis、Redis和AOP实现数据库查询的缓存优化。通过注解定义查询数据优先从Redis获取,若未命中再查询数据库,并将结果存入Redis,有效减少数据库压力。配置包括连接池、数据源、Mapper扫描、AOP切面等,同时展示了Service层的查询方法示例。
摘要由CSDN通过智能技术生成

导入相关依赖: spring-context        lombok        spring-jdbc (连接池)   spring-aspects(aop依赖) 

  mysql-connector-java(数据库依赖)        mybatis(mybatis依赖)  mybatis-spring        jedis(redis依赖)        fastjson(josn依赖) 

配置文件 配好  redis的ip和端口  还有连接池的配置最好用yml文件        

一个配置类config下AppConfig类用来配置对象       

@Configuration
//和value一起使用用来将配置文件信息填充到属性
@PropertySource(value = "classpath:redis.properties")
//开启mapper对象扫描
@MapperScan(basePackages = "com.powernode.mapper")
@ComponentScan(basePackages = {"com.powernode.service","com.powernode.aop"})
//引入表示开启AOP代理自动配置,如果配@EnableAspectJAutoProxy表示使用cglib进行代理对象的生成;设置@EnableAspectJAutoProxy(exposeProxy=true)表示通过aop框架暴露该代理对象,
// aopContext能够访问.从@EnableAspectJAutoProxy的定义可以看得出,
// 它引入AspectJAutoProxyRegister.class对象
// ,该对象是基于注解@EnableAspectJAutoProxy注册一个AnnotationAwareAspectJAutoProxyCreator,
// 该对象通过调用AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry)
// ;注册一个aop代理对象生成器。
@EnableAspectJAutoProxy
public class AppConfig {
    //配置文件信息
    @Value("${redis.host}")
    private String host;
    @Value("${redis.port}")
    private Integer port;

    @Value("${jdbc.driver}")
    private String driver;

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    /**
     *  自定义的连接池的配置信息
     * @return
     */
    @Bean
    public JedisPoolConfig jedisPoolConfig(){
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(13);
        jedisPoolConfig.setMaxIdle(13);
        jedisPoolConfig.setMinIdle(5);
        jedisPoolConfig.setTimeBetweenEvictionRunsMillis(3000);
        jedisPoolConfig.setMaxWaitMillis(2000);
        return jedisPoolConfig;
    }


    /**
     *  redis 连接池
     * @param jedisPoolConfig
     * @return
     */
    @Bean
    public JedisPool jedisPool(JedisPoolConfig jedisPoolConfig){
        JedisPool jedisPool = new JedisPool(jedisPoolConfig,host,port);
        return jedisPool;
    }

    /**
     * 创建了连接池
     * @return
     */
    @Bean
    public DataSource dataSource(){
        DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
        driverManagerDataSource.setDriverClassName(driver);
        driverManagerDataSource.setUrl(url);
        driverManagerDataSource.setUsername(username);
        driverManagerDataSource.setPassword(password);
        return driverManagerDataSource;
    }

    @Bean
    public   org.apache.ibatis.session.Configuration configuratio(){
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        configuration.setLogImpl(StdOutImpl.class);
        return configuration;
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource,org.apache.ibatis.session.Configuration configuration) throws MalformedURLException {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        // 设置数据源
        sqlSessionFactoryBean.setDataSource(dataSource);
        // 设置 日志
        sqlSessionFactoryBean.setConfiguration(configuration);
        // 设置类型别名
        sqlSessionFactoryBean.setTypeAliasesPackage("com.powernode.domain");
        // 设置映射文件
        ClassPathResource urlResource = new ClassPathResource("mapper/UserMapper.xml");
        sqlSessionFactoryBean.setMapperLocations(urlResource);
        return sqlSessionFactoryBean;
    }

 定义一个aop切片类   用来实现数据库查询前的一些前置后置处理(如已经查询过的数据放入缓存减少数据库压力)这里使用注解的方式

/**
 * 描述:
 *      查询数据 优先从redis 获取  获取到了 则直接返回数据  否则从数据库查询
 *      从redis 获取数据 根据key 获取
 * 作者: Mr.Z
 * 时间: xxx
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface GetCache {
    /**
     *  前缀
     * @return
     */
    String prefix() default "";

    /**
     *  获取数据的key
     *      如果参数类型是 对象 则 key 是 对象的中属性名
     *      如果参数是 基本类型 key 就是 参数名称
     *
     *
     * @return
     */
    String key() ;

    /**
     *  参数如果是对象时 参数类型
     * @return
     */
    String type() default "";

同目录下创建aop增强方法类

@Component
//标明这是一个增强类
@Aspect
public class CacheAspect {


    @Autowired
    //jedis子类
    private JedisPool jedisPool;

    /**
     *  使用了 Cache 注解 开启 AOP
     */
    //定义切点
    @Pointcut("@annotation(com.powernode.aop.GetCache)")
    public void point(){}

    /**
     *  环绕通知  :先从 redis 取  如果取不到则 从数据库取
     * @param pj
     * @return
     */
    //环绕方法  后面指代切面方法
    @Around("point()")
    //pj  目标方法
    public Object handlerCache(ProceedingJoinPoint pj) throws Throwable {
        System.out.println(" aop 代码执行了");
        // 最终的结果
        Object result = null;
        // 获取redis连接对象
        Jedis resource = jedisPool.getResource();
        // 目标类 对象
        Object target = pj.getTarget();


        //参数
        Object[] args = pj.getArgs();
        // 方法签名
        MethodSignature signature = (MethodSignature) pj.getSignature();
        // 结果类型
        Class returnType = signature.getReturnType();
        //代理对象
        Object aThis = pj.getThis();
        // 获取目标方法上的注解信息  prefix  key  type  等等信息
        // 方法名称
        String name = signature.getName();
        // 方法参数列表
        Class[] parameterTypes = signature.getParameterTypes();
        // 获取实现类中的方法
        Method method = target.getClass().getDeclaredMethod(name,parameterTypes);
        GetCache getCache = method.getAnnotation(GetCache.class);
        // 前缀
        String prefix = getCache.prefix();
        // key
        String key = getCache.key();
        // type
        String type = getCache.type();
        // 如果 type 是空  说明 key 是参数名   不是空 说明type 是 参数类型
        String keyValue = prefix + ":" ;
        if (StrUtil.isBlank(type)){
            // 方法参数
            LocalVariableTableParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
            // 参数名称
            String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
            List<String> parameters = Arrays.asList(parameterNames);
            // 参数名称的索引
            int i = parameters.indexOf(key);
            if (i == -1){
                // 没有指定的key  直接调用目标方法
                throw new RuntimeException("配置信息有误,没有参数名为:"+key);
            }
            Object paramValue = args[i];
            // redis 中的key
            keyValue = keyValue + paramValue.toString();
            // 字符串
            String dataString = resource.get(keyValue);
            // 如果从redis中没有获取到数据 则调用目标方法
            if (StrUtil.isBlank(dataString)){
                Object proceed = pj.proceed();
                // 将查询结果放入redis
                if (proceed != null){
                    resource.set(keyValue, JSON.toJSONString(proceed));
                }
                return proceed;
            }
            // 获取到了数据 则将数据转化为 目标类对象
            return JSON.parseObject(dataString,returnType);
        }
        // 传入的参数是 对象的情况
        Object obj = args[0];
        Class<?> paramClass = Class.forName(type);
        Field declaredField = paramClass.getDeclaredField(key);
        declaredField.setAccessible(true);
        Object  paramValue = declaredField.get(obj);
        // redis 中的key
        keyValue = keyValue + paramValue.toString();
        // 字符串
        String dataString = resource.get(keyValue);
        // 如果从redis中没有获取到数据 则调用目标方法
        if (StrUtil.isBlank(dataString)){
            Object proceed = pj.proceed();
            // 将查询结果放入redis
            if (proceed != null){
                resource.set(keyValue,JSON.toJSONString(proceed));
            }
            return proceed;
        }
        // 将连接对象放回 连接池
        jedisPool.returnResource(resource);
        // 获取到了数据 则将数据转化为 目标类对象
        return JSON.parseObject(dataString,returnType);
    }

service实现类    这里以id查询方法为例:

@Service
public class UserServiceImpl implements UserService{

    @Resource
    private UserMapper userMapper;

    @GetCache(prefix = "user",key="id",type = "com.powernode.domain.User")
    @Override
    public User select(User user) {
      userMapper.select(user.getId());
        return user;
    }

方法主类:  获取bean对象   拿到service接口对象  调方法即可查询     

其他mapper 和domin这里不做描述    

在第一次查询时  是查询数据库 第二次时  用的redis的缓存

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值