由需求而产生的一款db导出excel的工具

程序员最大的毛病可能就是懒,因为懒所以做出了许许多多提高自己工作效率的工具.
起因于我是商业开发,既然是商业项目避免不了各种数据统计,虽然公司有专门的数据平台,但是应对一些临时性需求还是免不了开发人员去导出一些数据.每一次有需求来我都是写一个复杂的sql,然后放到DataGrip中执行,利用其功能导出cvs,然而越来越多的需求该功能无法满足,比如导出组合表,也就是一个excel中有多个sheet表.那么应该这个需求我写了一个为自己的工具.

我理想中的工具#

1.简单模式使用sql查询直接导出
2.复杂模式可以定义一些复杂的bean,然后通过组合代码中自定义实现导出逻辑
3.可以自己定义表头,以及对应的数据处理,比如把时间戳转换为yyy-MM-dd hh:MM:ss这样的形式
4.支持一个excel中含有多个sheet
5.不需要很复杂的配置,因为自用,所以能约定俗成的地方就约定俗成.

语言的选择#

这个很随意了,我是选择自己最熟悉的语言,也就是Java.
同事听说我用Java写这种工具,强烈推荐我用py,但天生动态语言无感,可以说是反感,所以放弃.

实现#

DB连接: DBUtils
Excel: POI
具体过程很简单,代码逻辑也很清晰,这里只说下主要流程,详细的可以参考源码Github地址,另外由于个人使用,所以没有太多的校验和异常考虑.

easy-excel https://github.com/mrdear/easy-excel)

另外分享一个IDEA从数据库表生成对应Bean的脚本,使用方法自定义自己的extensions script即可.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import com.google.common.collect.Sets
import com.intellij.database.model.DasTable
import com.intellij.database.model.ObjectKind
import com.intellij.database.util.Case
import com.intellij.database.util.DasUtil
import org.apache.commons.lang3.StringUtils

/*
* Available context bindings:
* SELECTION Iterable<DasObject>
* PROJECT project
* FILES files helper
*/

typeMapping = [
(~/(?i)tinyint/) : "Integer",
(~/(?i)int/) : "Long",
(~/(?i)float|double|decimal|real/): "Double",
(~/(?i)date|datetime|timestamp/) : "java.util.Date",
(~/(?i)time/) : "java.sql.Time",
(~/(?i)/) : "String"
]

colTypeMapping = [
(~/(?i)tinyint/) : "TINYINT",
(~/(?i)int/) : "BIGINT",
(~/(?i)float|double|decimal|real/): "DECIMAL",
(~/(?i)datetime|timestamp/) : "TIMESTAMP",
(~/(?i)date/) : "TIMESTAMP",
(~/(?i)time/) : "TIMESTAMP",
(~/(?i)text/) : "LONGVARCHAR",
(~/(?i)/) : "VARCHAR"
]

FILES.chooseDirectoryAndSave("Choose directory", "Choose where to store generated files") { dir ->
SELECTION.filter { it instanceof DasTable && it.getKind() == ObjectKind.TABLE }.each {
generate(it, dir)
}
}


def generate(table, dir) {
def packageName = getPackageName(dir)
def className = javaName(table.getName(), true)
def fields = calcFields(table)
//创建相关目录 repository目录下的
def path = dir.toString()
//创建pojo与xml
new File(path + File.separator + className + ".java").withPrintWriter { out -> generatePojo(out,
packageName, className, fields) }

}
/**
* 生成POJO
* @param out
* @param packageName
* @param className
* @param fields
* @return
*/
def generatePojo(out, packageName, className, fields) {

out.println "package ${packageName};"
out.println ""
out.println "@Data"
out.println "public class ${className} {"
out.println ""
fields.each() {
// 输出注释
if (StringUtils.isNoneEmpty(it.comment)) {
out.println " /**"
out.println " * ${it.comment}"
out.println " */"
}
if (it.annos != "") out.println " ${it.annos}"
out.println " private ${it.type} ${it.name};"
}
out.println ""
out.println "}"
}

// ------------方法 ---------------

/**
* 拿到所有的字段
* @param table 数据库表
* @return 字段Object
*/
def calcFields(table) {
DasUtil.getColumns(table).reduce([]) { fields, col ->
def spec = Case.LOWER.apply(col.getDataType().getSpecification())
def typeStr = typeMapping.find { p, t -> p.matcher(spec).find() }.value
def colTypeStr = colTypeMapping.find { p, t -> p.matcher(spec).find() }.value
fields += [[
colName: col.getName(),
name : javaName(col.getName(), false),
type : typeStr,
colType: colTypeStr,
comment: col.getComment(),
annos : ""]]
}
}
/**
* 获取字段对应的Java类名称
*/
def javaName(str, capitalize) {
def s = str.split(/(?<=[^\p{IsLetter}])/).collect { Case.LOWER.apply(it).capitalize() }
.join("").replaceAll("_", "")
capitalize || s.length() == 1 ? s : Case.LOWER.apply(s[0]) + s[1..-1]
}

/**
* 获取包名称
* @param dir 实体类所在目录
* @return
*/
def getPackageName(dir) {
def target = dir.toString().replaceAll("/", ".").replaceAll("^.*src(\\.main\\.java\\.)?", "")

return target.charAt(0) == '.' ? target.substring(1) : target
}

总结#

本文的主要目的是表达迷茫的时候不知道自己该做什么,那么就从自己身边的需求开始,分析自己所遇到的痛点,然后用你喜欢的方式去解决这个痛点,那么这个过程就是你的进步.

Java学习记录--委派模型与类加载器
Mybatis源码分析(四)--TypeHandler的解析