切面监听mybatis-plus mapper 实现自动更新缓存

应用场景

    由于公司业务存在大量数据需要做筛选查询,使用drools作为内存筛选器,需要将所有数据放到内存进行筛选,当数据达到一个量级时就不适用直接从数据库查询,效率极低;

    于是我牵头设计了Redis一级缓存,和本地内存二级缓存的缓存模型,二级缓存使用guava包里的Cache 实现不多说了,这里主要介绍一下Redis缓存;

    为了保证缓存的实时性,我选择在对数据进行操作的微服务上进行切面监听其对应的mapper。如果是update, insert 就进行缓存的更新,每个用户的数据使用单独的hash 存在Redis里面。

具体实现

 首先我定义了一个自定义注解,可以扩展至任何实体类的缓存处理上;

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
public @interface RedisAutoUpdate {

}

SqlHelper工具类 获取sql类型

package com.knx.organization.dao.util;

import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;

/**
 * Mybatis - 获取Mybatis查询sql工具
 *
 */
public class KnxSqlHelper {

    /**
     * 获取sql 类型
     * @param session
     * @param namespace
     * @return
     */
    public static String getSqlCommandType(SqlSession session, String namespace) {
        Configuration configuration = session.getConfiguration();
        MappedStatement mappedStatement = configuration.getMappedStatement(namespace);
        SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
        return sqlCommandType.name();
    }

}

然后对注解切面获取对应的mapper 代理切面:

package com.knx.organization.aspect;

import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;

/**
 * @author joker
 * 自定缓存
 * @date 2021/6/9
 */
@Aspect
@Component
@Slf4j
public class MapperAspect {


    @Autowired
    //我们自己定义的RedisUtil  推荐使用RedisTemplate
    OrgRedisUtil orgRedisUtil;

    @Autowired
    SqlSessionFactory sqlSessionFactory;

    @Autowired
    SqlSession sqlSession;

    private static String INSERT_STRING = "insert";

    private static String UPDATE_STRING = "update";


    @Pointcut("execution(* com.***.***.dao.mapper.*.*(..))")
    public void sql() {
    }

    @After("sql()")
    public void after(JoinPoint pjp) throws Throwable {

        //获取mapper 代理
        Object target = pjp.getTarget();
        //获取代理的mapper
        Type[] type = target.getClass().getGenericInterfaces();
        Class ct1 = Class.forName(type[0].getTypeName());
        //拿到mapper上的注解
        Annotation[] declaredAnnotations = ct1.getDeclaredAnnotations();
        //获取mapper 对应的实体名
        String mapperName = ct1.getGenericInterfaces()[0].getTypeName();

        String[] names = mapperName.split("<");
        //去掉末尾 有个 ">"
        String entityName = names[names.length - 1].substring(0, names[names.length - 1].length() - 1);
        for (int i = 0; i < declaredAnnotations.length; i++) {
            if (declaredAnnotations[i].annotationType().equals(RedisAutoUpdate.class)) {
                updateCache(pjp, ct1, entityName);
            }
        }


    }
    // 判断是update,insert 具体更新缓存的方法
    private void updateCache(JoinPoint pjp, Class ct, String entityName){
        Class entityClass = null;
        try {
            entityClass = Class.forName(entityName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
//获取sql 类型
        String sqlCommandType = KnxSqlHelper.getSqlCommandType(sqlSession, ct.getName() + "." + pjp.getSignature().getName());

        if (sqlCommandType.toLowerCase().startsWith(INSERT_STRING) || sqlCommandType.toLowerCase().startsWith(UPDATE_STRING)) {
            //  拿到入参里面的对象id 进行更新 为保证数据准确性 我选择拿到id 再从数据查询更新缓存
            Object[] args = pjp.getArgs();
            List<String> ids = Lists.newArrayList();
            for (int i1 = 0; i1 < args.length; i1++) {
                //批量修改 更新缓存
                if (args[i1] instanceof Collection) {
                    List<BaseModel> list = (List<BaseModel>) args[i1];
                    list.forEach(e -> ids.add(e.getId()));
                }
                //更新单条
                if (args[i1].getClass().getName().equals(entityName)) {
                    BaseModel baseModel = (BaseModel) args[i1];
                    ids.add(baseModel.getId());
                }

            }
// 最后 根据 ids更新缓存 TODO 根据你自己的业务场景 更新对应实体id 的缓存 就不贴我的实现了
           

        }
    }

}
  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值