造个小轮子——实体类和JPA接口的逆向生成(基于Groovy)

原创
2019/07/02 10:20
阅读数 1.9K

今天记录一个关于逆向生成代码的工作日志,工作密度还算高吧,但是挡不住弟弟写这篇博客。

Version Control:Gradle

DB:postgresql

ORM:spring-boot-starter-data-jpa(Java Persistence API)

Language:Groovy

先说好版本

spring-boot version: 2.0.5.RELEASE
//jpa
compile("org.springframework.boot:spring-boot-starter-data-jpa") {
    exclude module: 'log4j-slf4j-impl'
}
compile group: 'org.postgresql', name: 'postgresql', version: '42.2.5'

执行结果

Show You The Code

import com.intellij.database.model.DasTable
import com.intellij.database.util.Case
import com.intellij.database.util.DasUtil

import java.text.SimpleDateFormat

/*
 * 适用对于PostgreSQL的逆向生成
 * 生成的实体类基于lombok
 * Available context bindings:
 *   SELECTION   Iterable<DasObject>
 *   PROJECT     project
 *   FILES       files helper
 */

// 包名
packageName = "com.sample;"
// 数据库类型 对应 实体类中的类型
typeMapping = [
        (~/(?i)bool/)                     : "Boolean",
        (~/(?i)jsonb/)                    : "String", // 由于我的postgre中有jsonb类型的字段,这里我用了方言映射成了String
        (~/(?i)bigint/)                   : "Long",
        (~/(?i)integer/)                  : "Integer",
        (~/(?i)smallint/)                 : "Short",
        (~/(?i)float|double|decimal|real/): "Double",
        (~/(?i)datetime|timestamp/)       : "Timestamp",
        (~/(?i)date/)                     : "Date",
        (~/(?i)time/)                     : "Time",
        (~/(?i)/)                         : "String"
]

// 选择目标目录
FILES.chooseDirectoryAndSave("Choose directory", "Choose where to store generated files") { dir ->
    SELECTION.filter { it instanceof DasTable }.each { generate(it, dir) }
}

def generate(table, dir) {
    def tableName = table.getName()
    def className = javaName(tableName, true)

    // 去掉不规范表名后的s
    if (className[-1] == "s") {
        className = className[0..-2]
    }

    def fields = calcFields(table)
    // 实体类目标目录
    new File(dir, className + ".java").withPrintWriter { out -> generateEntity(out, tableName, className, fields) }

    // jpa-api目标目录
    def file = new File(dir.getPath() + "/api/")
    file.mkdir()
    PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(new File(file, className + "Repository.java")), "UTF-8"))
    printWriter.withPrintWriter {out -> generateAPI(out, className, fields)}
}

// 生成实体类
def generateEntity(out, tableName, className, fields) {
    out.println "package $packageName"
    out.println ""
    out.println "import com.fasterxml.jackson.annotation.JsonIgnoreProperties;"
    out.println "import org.hibernate.annotations.*;"
    out.println "import javax.persistence.*;"
    out.println "import javax.persistence.Entity;"
    out.println "import javax.persistence.Table;"
    out.println "import lombok.Data;"
    out.println "import java.io.Serializable;"
    def hasJSON = false
    def importArray = []
    fields.each() {
        if (it.name == "profile") {
            out.println "import com.o2o2.base.dialect.StringJsonUserType;"
            hasJSON = true
        }
        if (it.type == "Date" && !importArray.any { ipt -> ipt == "Date" }) {
            out.println "import java.sql.Date;"
            importArray << "Date"
        }
        if (it.type == "Timestamp" && !importArray.any { ipt -> ipt == "Timestamp" }) {
            out.println "import java.sql.Timestamp;"
            importArray << "Timestamp"
        }
        if (it.type == "Time" && !importArray.any { ipt -> ipt == "Time" }) {
            out.println "import java.sql.Time;"
            importArray << "Time"
        }
    }
    out.println ""
    out.println "/**\n" +
            " * @Desprication: Reverse generated from the database java entity.\n" +
            " * @Author: LENNON\n" +
            " * @Date: "+ new SimpleDateFormat("yyyy-MM-dd").format(new Date()) + " \n" +
            " * @Modify By: \n" +
            " */"
    out.println ""
    if (hasJSON) {
        out.println "@TypeDefs({@TypeDef(name = \"StringJsonObject\", typeClass = StringJsonUserType.class)})"
    }
    out.println "@JsonIgnoreProperties({\"hibernateLazyInitializer\", \"handler\"})"
    out.println "@DynamicInsert"
    out.println "@DynamicUpdate"
    out.println "@Entity"
    out.println "@Table(name = \"$tableName\", schema = \"public\", catalog = \"daimler_o2o\")"
    out.println "@Data"
    out.println "public class $className implements Serializable {"
    out.println ""
    fields.each() {
        if (it.annos != "") {
            out.println "  ${it.annos}"
        }
        if (it.name == "id") {
            out.println "  @Id"
            out.println "  @Column(nullable = false)"
        }
        if (it.name == "profile") {
            out.println "  @Type(type = \"StringJsonObject\")"
        }
        out.println "  private ${it.type} ${it.name};"
    }
    out.println ""
    out.println "}"
}

// 生成jpa-api
def generateAPI(out, className, fields) {
    def apiName = className + "Repository"
    out.println "package $packageName"
    out.println ""
    out.println "/* Please manually import your entity class. */"
    out.println "import com.o2o2.db.dao.BaseRepository;"
    out.println "import org.springframework.stereotype.Repository;"
    def pkType = "Long"
    fields.each() {
        if (it.name == "id") {
            pkType = it.type
        }
    }
    out.println ""
    out.println "/**\n" +
            " * @Desprication: Reverse generated from the database jpa-api.\n" +
            " * @Author:  LENNON\n" +
            " * @Date: "+ new SimpleDateFormat("yyyy-MM-dd").format(new Date()) + " \n" +
            " * @Modify By: \n" +
            " */"
    out.println ""
    out.println "@Repository"
    out.println "public interface $apiName extends BaseRepository<$className, $pkType> {"
    out.println ""
    out.println "   /* Declare your interface here. */"
    out.println ""
    out.println "}"
}

// 小驼峰命名的转换,类型的映射,以及注解的判断
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
        fields += [[
                           name : javaName(col.getName(), false),
                           type : typeStr,
                           annos: ""
                   ]]
    }
}

def javaName(str, capitalize) {
    def s = com.intellij.psi.codeStyle.NameUtil.splitNameIntoWords(str)
            .collect { Case.LOWER.apply(it).capitalize() }
            .join("")
            .replaceAll(/[^\p{javaJavaIdentifierPart}[_]]/, "_")
    capitalize || s.length() == 1 ? s : Case.LOWER.apply(s[0]) + s[1..-1]
}

设置数据源

右键表 -> Scriped Extensisions -> Generate POJOs.groovy

其他的就交给你之前造的小轮子吧,古德拉客~

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
0 收藏
0
分享
返回顶部
顶部