由需求而产生的一款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即可.

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
}

总结

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏个人随笔

C# 操作 access 数据库

随笔: (1)   命名空间             using System.Data.OleDb; (2)   连接字符串             priv...

2975
来自专栏非著名程序员

Android应用程序优化注意事项

? 我们在开发过程中,如果不注意性能的优化,代码的优化等等,可能会导致应用程序的卡顿和效率极慢,所以开发过程中,注意细节,注意代码的编写和变量,常量的使用,可...

20010
来自专栏Golang语言社区

游戏AI设计经验分享——行为树研究

http://bbs.gameres.com/thread_493700.html

1800
来自专栏Golang语言社区

游戏AI设计经验分享——行为树研究

http://bbs.gameres.com/thread_493700.html

2673
来自专栏Fundebug

JS的分号可以省掉吗?

摘要: JavaScript语言从设计之初就是考虑带分号的,使用不带分号的编码规则就要小心点啦。

1556
来自专栏ThoughtWorks

在项目中透明地引入特性开关

之前曾经推荐过崔立强的《使用功能开关更好地实现持续部署》,介绍Feature Toggle的实践。北京办公室的孟宇现在对这个问题有了新的思考,当我们抛却Spri...

4196
来自专栏Java架构师学习

一个今日头条的面试题——LRU原理和Redis实现

很久前参加过今日头条的面试,遇到一个题,目前半部分是如何实现 LRU,后半部分是 Redis 中如何实现 LRU。

5422
来自专栏落影的专栏

AUGraph结合RemoteI/O Unit与Mixer Unit

前言 相关文章: 使用VideoToolbox硬编码H.264 使用VideoToolbox硬解码H.264 使用AudioToolbox编码AAC 使...

5319
来自专栏IT笔记

JAVA工作三年面试(二)

这里讲述下第二家公司的面试,这是一家大型互联网公司,简称W,一般像博主这样的传统行业去跳到这种公司简直是要跪舔的节奏,所以从一开始就带着一份敬仰之情去面试。由于...

4427
来自专栏个人随笔

C# 操作 access 数据库

private staticstring connStr = @"Provider= Microsoft.Ace.OLEDB.12.0;...

45613

扫码关注云+社区

领取腾讯云代金券