首页 > 其他分享 >业务代码中先处理业务最后存储数据

业务代码中先处理业务最后存储数据

时间:2024-10-29 11:31:03浏览次数:6  
标签:存储 return public 中先 业务 field TransacationalEntity entity class

背景说明:

在处理复杂业务的时候,特别是研发自测期间,经常会产生很多不必要的垃圾数据。

技术原理:

先将要存入数据库的数据放在缓存中,等所有业务代码执行完后,再统一保存;

代码如下:

@Slf4j
public class BaseService<M extends BaseMapper<T>, T> extends ServiceImpl<M, T> implements IService<T> {

    /**
     * 构建查询
     * @param e
     * @return
     */
    @SneakyThrows
    public <E extends Object> EntityWrapper<E> wrapperTable(E e, String tableName, Boolean like) {
        EntityWrapper<E> wrapper = new EntityWrapper<>();
        for (String field: JSON.parseObject(JSON.toJSONString(e)).keySet()) {
            Field ld = findField(e.getClass(), field);
            if(ld == null || !checkField(ld)) continue;
            String _s = columnName(ld);
            String m =null;
            if(_s.contains("_")){
                 m = StrUtil.toCamelCase("get_"+_s);
            }else {m=StrUtil.genGetter(_s);}

            Object o = e.getClass().getMethod(m).invoke(e);
            if(ObjectUtils.isEmpty(o)) continue;
            if(!ObjectUtils.isEmpty(tableName)) {
                _s = tableName+"."+_s;
            }
            if(like) wrapper.like(_s,o.toString());
            else wrapper.eq(_s , o);
        }
        return wrapper;
    }
    /**
     * 构建查询
     * @param e
     * @return
     */
    @SneakyThrows
    public <E extends Object> EntityWrapper<E> wrapper(E e, Boolean like) {
        return wrapperTable(e,null,like);
    }
    /**
     * 构建查询
     * @param e
     * @return
     */
    @SneakyThrows
    public <E extends Object> EntityWrapper<E> wrapper(E e) {
        return wrapperTable(e,null,false);
    }
    private boolean checkField(Field field) {
        if(!field.isAnnotationPresent(TableId.class) && !field.isAnnotationPresent(TableField.class)) return false;
        if(field.isAnnotationPresent(TableField.class) && !field.getAnnotation(TableField.class).exist()) return false;
        return true;
    }
    private Field findField(Class c,String field) {
        if(c == Object.class) return null;
        try {
            return c.getDeclaredField(field);
        } catch (NoSuchFieldException e) {
            return findField(c.getSuperclass(),field);
        }
    }

    private String columnName(Field field) {
        if(field.isAnnotationPresent(TableId.class) && ObjectUtils.isNotEmpty(field.getAnnotation(TableId.class).value())) return field.getAnnotation(TableId.class).value();
        if(field.isAnnotationPresent(TableField.class) && field.getAnnotation(TableField.class).exist() && ObjectUtils.isNotEmpty(field.getAnnotation(TableField.class).value())) return field.getAnnotation(TableField.class).value();
        return StrUtil.toUnderlineCase(field.getName());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean insertOrUpdate(T entity){
        if(entity instanceof TransacationalEntity) {
            //截流所有 TransacationalEntity 下的新增
            if(((TransacationalEntity) entity).getSqlStatus().equalsIgnoreCase("begin")) {
                return TransactionalUtils.add(this,((TransacationalEntity) entity));
            } else if(((TransacationalEntity) entity).getSqlStatus().equalsIgnoreCase("commit")) {
                return TransactionalUtils.commit(((TransacationalEntity) entity).getSqlVersion());
            } else {
                return super.insertOrUpdate(entity);
            }
        } else {
            return super.insertOrUpdate(entity);
        }
    }

    @SneakyThrows
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean insertOrUpdateBatch(List<T> entityList) {
        try {
            this.baseMapper.getClass().getMethod("insertOrUpdateBatch", List.class).invoke(this.baseMapper,entityList);
        } catch (NoSuchMethodException e) {
            log.warn("mapper类{},不存在方法{},替换为service执行",this.baseMapper.getClass(),"insertOrUpdateBatch");
            super.insertOrUpdateBatch(entityList);
        }
        return true;
    }


}
业务父类

其他方法可以忽略,都是懒得写xml但是版本又不允许升级,偷懒写的sql条件辅助工具。

主要要关注以下两个方法:

第一个,用于截流原来的保存方法,将要保存的数据存入缓存中。

@Transactional(rollbackFor = Exception.class)
    @Override
    public boolean insertOrUpdate(T entity){
        if(entity instanceof TransacationalEntity) {
            //截流所有 TransacationalEntity 下的新增
            if(((TransacationalEntity) entity).getSqlStatus().equalsIgnoreCase("begin")) {
                return TransactionalUtils.add(this,((TransacationalEntity) entity));
            } else if(((TransacationalEntity) entity).getSqlStatus().equalsIgnoreCase("commit")) {
                return TransactionalUtils.commit(((TransacationalEntity) entity).getSqlVersion());
            } else {
                return super.insertOrUpdate(entity);
            }
        } else {
            return super.insertOrUpdate(entity);
        }
    }

第二个,用于最后统一处理保存,我这里偷懒了,使用了代码生成器,所以xml中已经有了 insertOrupdataBatch.

@SneakyThrows
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean insertOrUpdateBatch(List<T> entityList) {
        try {
            this.baseMapper.getClass().getMethod("insertOrUpdateBatch", List.class).invoke(this.baseMapper,entityList);
        } catch (NoSuchMethodException e) {
            log.warn("mapper类{},不存在方法{},替换为service执行",this.baseMapper.getClass(),"insertOrUpdateBatch");
            super.insertOrUpdateBatch(entityList);
        }
        return true;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TransacationalEntity {

    /**
     * begin null commit
     */
    @TableField(exist = false)
    @ApiModelProperty(hidden = true)
    private String sqlStatus;

    /**
     * begin null commit
     */
    @TableField(exist = false)
    @ApiModelProperty(hidden = true)
    private String sqlVersion;
}
Pojo父类
public class TransactionalUtils {
    static volatile ConcurrentHashMap<String, ConcurrentHashMap<BaseService, CopyOnWriteArrayList<Object>>> cMap = new ConcurrentHashMap<>();
    static volatile ConcurrentHashMap<String,Long> tMap = new ConcurrentHashMap<>();
     public static <E extends TransacationalEntity> boolean add(BaseService service, E e) {
         long millis = System.currentTimeMillis();
         synchronized(service.getClass()) {
            ConcurrentHashMap<BaseService, CopyOnWriteArrayList<Object>> map = cMap
                    .getOrDefault(e.getSqlVersion(), new ConcurrentHashMap<>());
            CopyOnWriteArrayList<Object> list = map.getOrDefault(service, new CopyOnWriteArrayList<>());
            list.add(e);
            map.put(service, list);
            cMap.put(e.getSqlVersion(), map);
            tMap.put(e.getSqlVersion(),tMap.getOrDefault(e.getSqlVersion(), 0L)+System.currentTimeMillis()-millis) ;
        }
         return true;
    }

    @SneakyThrows
    public static boolean commit(String key) {
        long millis = System.currentTimeMillis();
        int size = 0;
        for (Map.Entry<BaseService, CopyOnWriteArrayList<Object>> entry : cMap.remove(key).entrySet()) {
            entry.getKey().insertOrUpdateBatch(entry.getValue());
            size+=entry.getValue().size();
        }
        System.out.println("批量业务入队总量" + size);
        System.out.println("批量业务入队耗时" + tMap.remove(key));
        System.out.println("批量业务提交耗时" + (System.currentTimeMillis()-millis));
        return true;
    }

}
核心工具类

以下是调用示例:

String tranId = IdUtil.simpleUUID();
        Map<String, Object> userMap = new HashMap();
        userMap.put("sqlStatus","begin");
        userMap.put("sqlVersion", tranId);
        propretyDye(beans,userMap);
        insertOrUpdate(Arrays.asList(beans));
        TransactionalUtils.commit(tranId);

先给要截流的数据加上版本号和标识,其实只要一个版本号就可以了,sqlStatus这个字段懒得删除了。

版本号主要用来在缓存中分组,隔离其他业务提交。

附带一个参数处理的小工具,有时候要重复的一层层赋值,太懒了,所以写个工具,让机器自己给我处理所有数据的值,用法如上面的示例,没仔细检查,偶尔会发生栈溢出,但是不影响,应该是项目的全局配置不够造成的:

/**
     * 属性染色
     * 如果对象 o 包含 propreties 的key,则设置key对应的值
     * 处理对象和对象中所有的子对象
     * @param o
     * @param propreties
     * @return
     */
    @SneakyThrows
    public static Object propretyDye(Object o, Map<String,Object> propreties) {
        if(o instanceof Collection) for (Object object : (Collection<?>) o) {
            propretyDye(object,propreties);
        }
        Class<?> aClass = o.getClass();
        while (aClass instanceof Object) {
            for (Field field : aClass.getDeclaredFields()) {
                field.setAccessible(true);
                if(propreties.containsKey(field.getName())) field.set(o,propreties.get(field.getName()));
                if(field.get(o) instanceof Collection) for (Object object : (Collection<?>) field.get(o)) {
                    propretyDye(object,propreties);
                }
                if(field.get(o) instanceof Object && BeanUtil.isBean(field.get(o).getClass())) propretyDye(field.get(o),propreties);
            }
            aClass = aClass.getSuperclass();
        }
        return o;
    }

 

标签:存储,return,public,中先,业务,field,TransacationalEntity,entity,class
From: https://www.cnblogs.com/tangzeqi/p/18512635

相关文章

  • 业务-页面卡顿问题总结
    问题:1、web3.1地图绘制1000个标签实时位置,每隔8~10S左右就会卡顿2S,间歇性地持续进行2、在解决问题1之后,页面开始运行比较流畅,运行半小时后,页面逐渐卡死 定位思路:问题1:通过chrome浏览器的performance功能,截取卡顿前后,发现JS引擎执行时间在卡顿时间段内占比很高,放大细节来看,......
  • 客户端存储 — IndexedDB 实现分页查询
    前言相信IndexedDB大家都有过了解,但是不一定每个人都有过实践,并且其中涉及到事务、游标等概念,会导致在初次使用时会有些不适应,那么本文会通过IndexedDB实现分页查询的形式进行实践,在开始之前,可以尝试思考一下浏览器的客户端存储你都了解哪些呢?其实客户端存储分为下面......
  • C/C++ 中有哪些基本数据类型?它们的存储大小和取值范围是多少?
      1.整型类型int存储大小:4字节(32位)取值范围:有符号(signedint):-2^(31)到2^(31)-1(即-2,147,483,648到2,147,483,647)short存储大小:2字节(16位)取值范围:有符号(signedshort):-2^(15)到2^(15)-1(即-32,768到32,767)long存储大小:通常为4字......
  • GaussDB事务存储组件
    事务存储组件云原生数据库支持透明多写,所有节点对等,每个计算节点都可以读写全部的数据页面,事务在本节点执行,没有分布式事务。每个计算节点都有Localbufferpool,采用Remotememorypool扩展计算节点的内存,在多个计算节点之间共享buffer地址,避免页面在多个计算节点之间传来传去。......
  • Java数据存储容器大全
    一、Collection接口及其子类List:列表,可以存储有序的、可重复的元素。ArrayList:基于数组实现的动态数组,可以动态增长和缩小。它提供了快速的随机访问,但在列表中间进行插入和删除操作可能较。LinkedList:基于双向链表实现的列表,具有高效的插入和删除操作(特别是在列表头部和......
  • <十七>Ceph 块存储理论与实践
    Ceph集群的检查可以简化为MON状态检查、OSD状态检查和PG状态检查。上一章节我们重点介绍了MON的状态和维护方法。本章节将重点介绍OSD状态和块存储常用命令。Tips:如果是故障排查,请在确保MON状态正常的情况下进行OSD和PG状态检查。Tips:下面的简单理解只是......
  • GaussDB数据库存储过程介绍
    @目录一、前言二、GaussDB中的定义三、存储过程的使用场景四、存储过程的使用优缺点五、存储过程的示例及示例解析1、GaussDB存储过程语法格式2、GaussDB存储过程语法示例3、存储过程的调用方法七、总结一、前言华为云数据库GaussDB是一款高性能、高安全性的云原生数据库,在数据......
  • 大华设备视频平台EasyCVR私有化视频平台云端录像、监控存储、回看、计划与配置功能全
    EasyCVR是TSINGSEE青犀视频在音视频流媒体技术和人工智能领域的深入研发成果,它以出色的视频处理、汇聚和融合能力,在构建视频监控系统方面表现出独特的优势。大华设备视频平台EasyCVR能够接入高清网络摄像机的RTSP直播流,并且支持多种其他直播流格式,例如RTMP、HTTP-FLV、HLS(M3U8)......
  • 大华设备视频平台EasyCVR私有化视频平台云端录像、监控存储、回看、计划与配置功能全
    EasyCVR是TSINGSEE青犀视频在音视频流媒体技术和人工智能领域的深入研发成果,它以出色的视频处理、汇聚和融合能力,在构建视频监控系统方面表现出独特的优势。大华设备视频平台EasyCVR能够接入高清网络摄像机的RTSP直播流,并且支持多种其他直播流格式,例如RTMP、HTTP-FLV、HLS(M3U8)......
  • 【数据库】数据库管理(下)存储过程 触发器 慢查询日志 备份与恢复
    文章目录存储过程参数控制语句触发器慢日志备份&恢复备份方法恢复方法导入导出数据存储过程数据库存储过程(StoredProcedure)是一组为了完成特定功能的SQL语句集合,这些语句被预先编译并保存在数据库中。存储过程可以接受输入参数、执行复杂的业务逻辑,并返回结果......