quartz+tablesaw报表统计

devywb devywb     2022-10-11     458

关键词:

场景

在12 月份做的报表功能中,直接从 ES 查询一个月的数据。当数据量特别大时,查询速度会非常缓慢甚至查询失败。解决方案是使用定时任务,在每天凌晨指定时间自动查询前一天的数据,然后写入 CSV 文件中,每天追加。生成报表文件时,就不用再查询 ES,而是读取 CSV 文件,统计一个月每天数据的总和。

一、定时任务

定时任务使用的是 Quartz 框架。

Quartz 是什么

Quartz 是一个开源的作业调度框架,由 java 编写,在.NET 平台为 Quartz.Net,通过 Quart 可以快速完成任务调度的工作。

Quartz 使用场景

如定时发送邮件、定时统计数据生成报表等等

在项目中使用 Quartz

添加依赖

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.0</version>
</dependency>

spring-quartz.xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
    <!-- 要调用的工作类 -->
    <bean id="quartzJob" class="com.devywb.quartzJob"></bean>
    <!-- 定义调用的对象和方法 -->
    <bean id="jobTask" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <!-- 调用的类 -->
        <property name="targetObject">
            <ref bean="quartzJob"></ref>
        </property>
        <!-- 调用的方法 -->
        <property name="targetMethod">
            <value>work</value>
        </property>
    </bean>
    
    <!-- 定义触发的时间 -->
    <bean id="doTime" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
      <property name="jobDetail">
                <ref bean="jobTask"/>
      </property>
      <!-- cron表达式 -->
      <property name="cronExpression">
        <!-- 每隔20秒钟执行一次 -->
        <!-- <value>*/20 * * * * ?</value> -->
        <!-- 每天凌晨五点执行 -->
        <value>0 0 5 * * ?</value>
      </property>
    </bean>
    
     <!-- 总管理类 -->
     <bean id="startQuertz" lazy-init="false" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
         <property name="triggers">
             <list>
                 <ref bean="doTime"/>
             </list>
         </property>
     </bean>
</beans>

二、Tablesaw

Tablesaw是一套内存内数据表,其中包含多种数据工具与面向列的存储格式。其设计思路认为没人会面向小型任务执行分布式分析,而大家可以在单一服务器上对200万行级别的表进行交互。
大家能够利用Tablesaw执行各种规则,从而检查显示布局、数据优先级或者针对数据显示及交互向特定用户提供扩展控制范围。在它的帮助下,我们可以利用RDBMS与CSV文件导入数据,添加及删除列,执行映射与规约操作或者将表保存在经过压缩的列式存储格式当中。

添加依赖

<!-- https://mvnrepository.com/artifact/tech.tablesaw/tablesaw-core-->
<dependency>
    <groupId>tech.tablesaw</groupId>
    <artifactId>tablesaw-core</artifactId>
    <version>0.11.2</version>
    <exclusions>
        <exclusion>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-math3</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.0</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-math3 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-math3</artifactId>
    <version>3.0</version>
</dependency>

在项目中添加 Tablesaw 的依赖后,项目无法启动,原因是 commons-lang3 和 commons-math3 两个依赖的版本太高了。后来采用的解决方法是排除依赖再另外引入低版本依赖。

常用 API

// 读取 csv 文件
Table table = Table.read().csv(String filePath);

// 获取所有列名
List<String> columnNames = table.columnNames();

// sum
table.sum("列名").get();

// 排序
table.sortDescendingOn("列名"); // 降序
table.sortAscendingOn("列名"); // 升序

// 分组
table.groupBy(columns);

// 前多少条
table.first(nRows)

// 生成 saw 文件
Table table = Table.read().csv(contents, tableName);
table.save(folder);

// 读取 saw 文件
Table table = Table.readTable(tableNameAndPath)

踩过的坑

写入数据至 CSV 文件时,当数据中某个字段包含分隔符或其他特殊符号时,程序会报错。下面列出两种解决方案。

第一种,使用第三方库,例如 openCSV,它底层对特殊符号做了处理;

第二种,手动处理字段中的特殊符号;

/**
 * list 转 CSV 字符串
 * @param header
 * @param data
 * @return
 */
public static String list2CsvStr(Map<String, String> header, List<Map<String, Object>> data) {
    String content = "";
    if (header != null && header.size() > 0) {
        if (data != null && data.size() > 0) {
            StringBuilder sb = new StringBuilder();
            for (Map<String, Object> map : data) {
                Set<Entry<String, String>> entrySet = header.entrySet();
                int i = 0;
                for (Entry<String, String> entry : entrySet) {
                    String key = entry.getKey();
                    if(i > 0) sb.append(SEPARATOR); // 不是第一列时,添加分隔符
                    if(map.containsKey(key)) {
                        Object value = map.get(key);
                        String valueStr = value != null ? value.toString() : "";
                        if(valueStr.contains(SEPARATOR)) { // 如果数据包含分割符
                            if(valueStr.contains(""")) { // 如果字段中包含双引号,替换成两个
                                valueStr.replace(""", """");
                            }
                            // 如果字段包含分割符,则用双引号括起来
                            valueStr = """ + valueStr +""";
                        }
                        sb.append(valueStr);
                    } else {
                        sb.append("");
                    }
                    i ++ ;
                }
                sb.append("
");
            }
            content = sb.toString();
        }
    }
    return content;
}

/**
 * map 转 CSV 字符串
 * @param header
 * @param data
 * @return
 */
public static String map2CsvStr(Map<String, String> header, Map<String, Object> data) {
    String content = "";
    if (header != null && header.size() > 0) {
        if (data != null && data.size() > 0) {
            StringBuilder sb = new StringBuilder();
            int i = 0;
            Set<Entry<String, String>> entrySet = header.entrySet();
            for (Entry<String, String> entry : entrySet) {
                String key = entry.getKey();
                if(i > 0) sb.append(SEPARATOR);
                if(data.containsKey(key)) {
                    Object value = data.get(key);
                    String valueStr = value != null ? value.toString() : "";
                    if(valueStr.contains(SEPARATOR)) { // 如果数据包含分割符
                        if(valueStr.contains(""")) { // 如果字段中包含双引号,替换成两个
                            valueStr.replace(""", """");
                        }
                        // 如果字段包含分割符,则用双引号括起来
                        valueStr = """ + valueStr +""";
                    }
                    sb.append(valueStr);
                } else {
                    sb.append("");
                }
                i ++;
            }
            sb.append("
");
            content = sb.toString();
        }
    }
    return content;
}

spring整合quartz实现动态定时器

...用方法不同<!--定时器--> <dependency><groupId>org.quartz-scheduler</groupId><artifactId>quartz< 查看详情

quartz怎么设置多任务

用quartz实现多任务动态加载Hudson报表系统二期结束了,这次新增了邮件定制功能,实现此功能的核心在于quartz框架。Quartz是什么Quartz是一个用Java编写的任务调度框架,任务调度是什么,举例说明:比如我们需要在每个星期四下... 查看详情

超级好用的java数据可视化库:tablesaw

...合刚学习完Java语言基础的人群,跟着本文可了解和使用Tablesaw项目。示例均在Windows操作系统下演示本文作者:HelloGitHub-秦人HelloGitHub推出的《讲解开源项目》系列,今天给大家带来一款基于Java语言的数据可视化库开源项目——Ta... 查看详情

quartz简介

...直接使用自定义线程的方法处理,开发会很具有挑战性。Quartz提供的功能让开发者可以应对绝大多数任务调度的功能需求。Quartz基础结构  Quartz对任务调度的领域问题进行了高度抽象,提出了调度器、任务和触发器这3个核心... 查看详情

zabbix报表统计

在运维进行容量规划的时候,经常需要根据不同维度展现不同业务域的资源使用情况,zabbix的报表展现是很明显的短板。作为一款监控软件,报表统计是很重要的功能,没办法,只好自己写一个php页面。 查看详情

javasql编辑器动态报表数据库备份还原quartz定时任务调度自定义表单java图片爬虫

650)this.width=650;"width="600"height="309"class="zoom"id="aimg_pPLjl"src="https://img.alicdn.com/imgextra/i1/332189337/TB2BndqoXXXXXbpXXXXXXXXXXXX_!!332189337.png?t=1461910434000"border="0"/>获取【下载地址】 查看详情

bzoj1058:[zjoi2007]报表统计

1058:[ZJOI2007]报表统计TimeLimit:15Sec MemoryLimit:162MBSubmit:3844 Solved:1309[Submit][Status][Discuss]Description  小Q的妈妈是一个出纳,经常需要做一些统计报表的工作。今天是妈妈的生日,小Q希望可以帮妈妈分担一些工作,作为她的生... 查看详情

bzoj1058[zjoi2007]报表统计

[ZJOI2007]报表统计TimeLimit: 15Sec  MemoryLimit: 162MBSubmit: 3932  Solved: 1334[Submit][Status][Discuss]Description  小Q的妈妈是一个出纳,经常需要做一些统计报表的工作。今天是妈妈的生日,小Q希望可以帮妈妈分担 查看详情

stimulsoft报表操作笔记:统计

一、引言  报表大家应该都知道是什么,简单来说就是用表格、图表等格式来动态显示数据。现在web系统中很多需要使用到报表统计、打印功能等,将所需用到的数据绑定到指定的位置,然后分类汇总,这样查看起来更清晰,... 查看详情

javasql编辑器动态报表数据库备份还原quartz定时任务调度自定义表单ssm

A调用摄像头拍照,自定义裁剪编辑头像,头像图片色度调节B集成代码生成器[正反双向](单表、主表、明细表、树形表,快速开发利器)+快速表单构建器freemaker模版技术,0个代码不用写,生成完整的一个模块,带页面、建表sql脚... 查看详情

bzojp1058[zjoi2007]报表统计——solution

1058:[ZJOI2007]报表统计TimeLimit: 15Sec  MemoryLimit: 162MBSubmit: 4099  Solved: 1390[Submit][Status][Discuss]Description  小Q的妈妈是一个出纳,经常需要做一些统计报表的工作。今天是妈妈的生日,小Q希望可以 查看详情

《电商系统后台统计报表模块》需求分析与设计的课程小结

《电商系统后台统计报表模块》需求分析与设计的课程小结1)分工情况介绍,小组分工合作情况介绍张顺程-选题,功能分析,建模2)选题讨论电商后台的统计报表也是非常重要的一个功能模块,一个功能齐全且具有良好显示效... 查看详情

质量数据统计报表

质量数据报表统计维度:1.虚实散打率(80%)2.慢查询(1s,5s,30s,60s)3.Bugreopen状态4.迭代中每个开发头上的bug数+severity。5.迭代中测试提出的bug数+severity。6.每个Story中对应的bug数。7.SonarBlocker,Critical,Major+Miner+Info-对应的项目数。8.... 查看详情

javasql编辑器动态报表数据库备份还原quartz定时任务调度自定义表单java图片爬虫

650)this.width=650;"width="600"height="488"class="zoom"id="aimg_d778z"src="https://wx2.sinaimg.cn/mw690/005spsLRgy1fj1wt1cad3j30vj0poadp.jpg"border="0"/> 官网http://www.fhadmin.org/A代码编辑器,在线模版编辑,仿开 查看详情

javasql编辑器动态报表数据库备份还原quartz定时任务调度自定义表单java图片爬虫

650)this.width=650;"width="600"height="488"class="zoom"id="aimg_P2qnR"src="https://wx2.sinaimg.cn/mw690/005spsLRgy1fj1wt1cad3j30vj0poadp.jpg"border="0"/>官网http://www.fhadmin.org/A代码编辑器,在线模版编辑,仿开发工具编辑器 查看详情

javasql编辑器动态报表数据库备份还原quartz定时任务调度自定义表单java图片

A代码编辑器,在线模版编辑,仿开发工具编辑器,pdf在线预览,文件转换编码B集成代码生成器[正反双向](单表、主表、明细表、树形表,快速开发利器)+快速表单构建器freemaker模版技术,0个代码不用写,生成完整的一个模块,... 查看详情

javasql编辑器动态报表数据库备份还原quartz定时任务调度自定义表单java图片爬虫

650)this.width=650;"width="600"height="488"class="zoom"id="aimg_O2bDB"src="https://wx2.sinaimg.cn/mw690/005spsLRgy1fj1wt1cad3j30vj0poadp.jpg"border="0"/> A代码编辑器,在线模版编辑,仿开发工具编辑器,pdf在线预览,文件转换编码B集成代 查看详情

javasql编辑器动态报表数据库备份还原quartz定时任务调度自定义表单java图片爬虫

650)this.width=650;"width="600"height="488"class="zoom"id="aimg_kgn3E"src="https://wx2.sinaimg.cn/mw690/005spsLRgy1fj1wt1cad3j30vj0poadp.jpg"border="0"/> A代码编辑器,在线模版编辑,仿开发工具编辑器,pdf在线预览,文件转换编码B集成代 查看详情