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

December 17, 2023
测试
测试
测试
测试
9 分钟阅读

程序员最大的毛病可能就是懒,因为懒所以做出了许许多多提高自己工作效率的工具. 起因于我是商业开发,既然是商业项目避免不了各种数据统计,虽然公司有专门的数据平台,但是应对一些临时性需求还是免不了开发人员去导出一些数据.每一次有需求来我都是写一个复杂的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即可.

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
}

总结

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

继续阅读

更多来自我们博客的帖子

如何安装 BuddyPress
由 测试 December 17, 2023
经过差不多一年的开发,BuddyPress 这个基于 WordPress Mu 的 SNS 插件正式版终于发布了。BuddyPress...
阅读更多
Filter如何工作
由 测试 December 17, 2023
在 web.xml...
阅读更多
如何理解CGAffineTransform
由 测试 December 17, 2023
CGAffineTransform A structure for holding an affine transformation matrix. ...
阅读更多