Quartz + Tablesaw 报表统计
场景
在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("\n");
}
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("\n");
content = sb.toString();
}
}
return content;
}
Quartz + Tablesaw 报表统计的更多相关文章
- bzoj1058: [ZJOI2007]报表统计
set.操作:insert(u,v)在u后面插入v,若u后面已插入过,在插入过的后面插入.mingap求出序列两两之间差值的最小值.minsortgap求出排序后的序列两两之间的最小值.用multis ...
- BZOJ 1058: [ZJOI2007]报表统计( 链表 + set )
这种题用数据结构怎么写都能AC吧...按1~N弄个链表然后每次插入时就更新答案, 用set维护就可以了... --------------------------------------------- ...
- [补档][ZJOI2007] 报表统计
[ZJOI2007] 报表统计 题目 传送门 小Q的妈妈是一个出纳,经常需要做一些统计报表的工作.今天是妈妈的生日,小Q希望可以帮妈妈分担一些工作,作为她的生日礼物之一. 经过仔细观察,小Q发现统计一 ...
- 【BZOJ1058】【ZJOI2007】报表统计(链表,堆,Splay)
[BZOJ1058][ZJOI2007]报表统计 题面 题目描述 Q的妈妈是一个出纳,经常需要做一些统计报表的工作.今天是妈妈的生日,小Q希望可以帮妈妈分担一些工作,作为她的生日礼物之一. 经过仔细观 ...
- BZOJ_1058_[ZJOI2007]报表统计_STL
BZOJ_1058_[ZJOI2007]报表统计_STL Description 小Q的妈妈是一个出纳,经常需要做一些统计报表的工作.今天是妈妈的生日,小Q希望可以帮妈妈分担一些工 作,作为她的生日礼 ...
- bzoj 1058: [ZJOI2007]报表统计 (Treap)
链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1058 题面; 1058: [ZJOI2007]报表统计 Time Limit: 15 Sec ...
- 基于Fusioncharts的报表统计
先了解fusioncharts插件,fusioncharts是一款基于XML和flash的报表组件,支持Java.PHP.AngularJS等等开发语言,所以,开发出来,加入swf文件,就可以出现动态 ...
- 洛谷 P1110 [ZJOI2007]报表统计 解题报告
P1110 [ZJOI2007]报表统计 题目描述 \(Q\)的妈妈是一个出纳,经常需要做一些统计报表的工作.今天是妈妈的生日,小\(Q\)希望可以帮妈妈分担一些工作,作为她的生日礼物之一. 经过仔细 ...
- bzoj P1058 [ZJOI2007]报表统计——solution
1058: [ZJOI2007]报表统计 Time Limit: 15 Sec Memory Limit: 162 MB Submit: 4099 Solved: 1390 [Submit][St ...
随机推荐
- Python-函数-Day4
1.函数 1.1.集合 主要作用: 去重 关系测试, 交集\差集\并集\反向(对称)差集 a = {1,2,3,4} b ={3,4,5,6} a {1, 2, 3, 4} type(a) <c ...
- python当中的 可迭代对象 迭代器
学习python有一段时间了,在学习过程中遇到很多难理解的东西,做一下总结,希望能对其他朋友有一些帮助. 完全是个人理解,难免有错,欢迎其他大神朋友们批评指正. 1 迭代 什么是迭代呢??我们可以这样 ...
- VMware虚拟机安装
学习Linux系统最好的方式就是在自己的虚拟机上安装Linux:接下来就给大家简单介绍一下VMware虚拟机的安装以及Linux的安装:VMware虚拟机只是为了更好的学习Linux: ...
- 【第二十篇】C#微信H5支付 非微信内浏览器H5支付 浏览器微信支付
微信开发者文档 微信H5支付官方文档 请阅读清楚 最起码把所有参数看一遍 这个地方也可以看看 微信案例 http://wxpay.wxutil.com/mch/pay/h5.v2.php,请在微 ...
- mysql 千万级数据查询效率实践,分析 mysql查询优化实践--本文只做了一部分,仅供参考
数据量, 1300万的表加上112万的表 注意: 本文只做了部分优化,并不全面,仅供参考, 欢迎指点. 请移步tim查看,因为写的时候在tim写的,粘贴过来截图有问题,就直接上链接了. https ...
- 用JavaScript实现动态省市县三级联动
- 音频增益响度分析 ReplayGain 附完整C代码示例
人们所熟知的图像方面的3A算法有: AF自动对焦(Automatic Focus)自动对焦即调节摄像头焦距自动得到清晰的图像的过程 AE自动曝光(Automatic Exposure)自动曝光的是为了 ...
- .Net中集合排序还可以这么玩
背景: public class StockQuantity { public StockQuantity(string status, DateTime dateTime, int quantity ...
- SpringMVC 自定义类型转换器
先准备一个JavaBean(Employee) 一个Handler(SpringMVCTest) 一个converters(EmployeeConverter) 要实现的输入一个字符串转换成一个emp ...
- xcode7,AFN不能使用的问题
今天手贱立刻升级了Xcode7,结果AFN报错,且不能用了,解决办法如下 第一步:升级AFN到2.6.0 完成之后,运行,结果请求都失败,提示 The resource could not be lo ...