结合Groovy脚本在IDEA可以为所欲为——使用数据库表生成实体类和表结构JSON

发布于:2025-07-03 ⋅ 阅读:(17) ⋅ 点赞:(0)

生成MyBatisPlus实体

在I DEA 编辑器,双击 shift 键在 Generate POJOs.groovy 文件,同级目录下新建一个文件MyBatisPlus pojo.groovy,将下面的代码粘贴进去即可,有问题可以自己改,在IDEA自带数据库工具的数据库表上右键执行

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 java.time.LocalDate
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter

/*
 * Available context bindings:
 * SELECTION Iterable<DasObject>
 * PROJECT  project
 * FILES  files helper
 */
packageName = "com.monkeyhi.xxx"

// 此处指定对应的类型映射,可按需修改,目前tinyint如果要映射到自定义枚举类型,只能手动修改
typeMapping = [
        (~/(?i)tinyint|smallint|mediumint/)      : "Integer",
        (~/(?i)bool|bit/)                        : "Boole",
        (~/(?i)int/)                             : "Integer",
        (~/(?i)float|double|decimal|real|number/): "BigDecimal",
        (~/(?i)datetime|timestamp/)              : "LocalDateTime",
        (~/(?i)date/)                            : "LocalDate",
        (~/(?i)time/)                            : "LocalDateTime",
        (~/(?i)blob|mediumblob|binary|bfile|clob|raw|image/): "Byte[]",
        (~/(?i)text|mediumtext/)                 : "String",
        (~/(?i)/)                                : "String",

]

// 上面用到类和它的导入路径的之间的映射
importMap = [
        "BigDecimal" : "java.math.BigDecimal",
        "LocalDate" : "java.time.LocalDate",
        "LocalDateTime": "java.time.LocalDateTime",
]

// 导入路径列表,下面引用的时候会去重,也可以直接声明成一个 HashSet
importList = []

// 弹出选择文件的对话框
FILES.chooseDirectoryAndSave("选择文件夹", "选择存储位置") { dir ->
    SELECTION.filter { it instanceof DasTable && it.getKind() == ObjectKind.TABLE }.each { generate(it, dir) }
}

def generate(table, dir) {
    def className = javaName(table.getName(), true)
    def fields = calcFields(table)
    packageName = getPackageName(dir)
    new PrintWriter(new OutputStreamWriter(new FileOutputStream(new File(dir, className + ".java")), "utf-8")).withPrintWriter { out -> generate(out, className, fields, table) }
}

def calcFields(table) {
    def pkSet = new HashSet<String>()
    def prKey = DasUtil.getPrimaryKey(table);
    if (prKey != null) {
        def keyRef = prKey.getColumnsRef();
        if (keyRef != null) {
            def it = keyRef.iterate();
            while (it.hasNext()) {
                pkSet.add(it.next())
            }
        }
    }

    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
        if (importMap.containsKey(typeStr)) {
            importList.add(importMap.get(typeStr))
        }
        fields += [[
                           colName : col.getName(),
                           name : javaName(col.getName(), false),
                           type : typeStr,
                           comment: col.getComment(),
                           isKey: pkSet.contains(col.getName()),//判读字段是否是主健
                           annos : ""
                   ]]
    }
}

def isNotEmpty(content) {
    return content != null && content.toString().trim().length() > 0
}

// 获取包所在文件夹路径
def getPackageName(dir) {
    return dir.toString().replaceAll("\\\\", ".").replaceAll("/", ".").replaceAll("^.*src(\\.main\\.java\\.)?", "")
}

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]
}

static String genSerialID() {
    return "    private static final long serialVersionUID = " + Math.abs(new Random().nextLong()) + "L;"
}


// 从这里开始,拼实体类的具体逻辑代码
def generate(out, className, fields, table) {
    out.println """
package ${packageName};

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import com.baomidou.mybatisplus.annotation.*;
import java.io.Serializable;
${importList.unique().collect { "import " + it + ";" }.join("\n")}

/**
 * ${table.getComment()?:""}
 *
 * @author monkeyhi
 * @since ${LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))}
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("${table.getName()}")
public class $className implements Serializable {

${genSerialID()}
${fields.collect {"""
    /**
     * ${it.comment?:''}
     */
    ${it.isKey ? "@TableId(value = \"${it.colName}\", type = IdType.AUTO)" :
            "@TableField(\"${it.colName}\")"}
    @ApiModelProperty(\"${it.comment?:''}\")
    private ${it.type} ${it.name};
""" }.join("")}
}
"""
}

生成的实体如下:


package com.xxx..entity;

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import com.baomidou.mybatisplus.annotation.*;
import java.io.Serializable;


/**
 * 请求-单位关联表
 *
 * @author monkeyhi
 * @since 2025-07-01 15:26
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("tab_xxx_ddd")
public class TabXxxDdd implements Serializable {

    private static final long serialVersionUID = 615020304229774329L;

    /**
     * 主键
     */
    @TableId(value = "ID", type = IdType.AUTO)
    @ApiModelProperty("主键")
    private String id;

    /**
     * 请求id
     */
    @TableField("QQ_ID")
    @ApiModelProperty("请求id")
    private String qqId;

    /**
     * 单位id
     */
    @TableField("DW_ID")
    @ApiModelProperty("单位id")
    private String dwId;

}

生成表结构JSON

也有可以生成json,然后自己可以用 javaScript 在浏览器控制台输出相关功能代码,,同上新建一个文件 Generate tableJson.groovy

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 java.time.LocalDate

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

// 此处指定对应的类型映射,可按需修改,目前tinyint如果要映射到自定义枚举类型,只能手动修改
typeMapping = [
        (~/(?i)tinyint|smallint|mediumint/)      : "Integer",
        (~/(?i)bool|bit/)                        : "Boole",
        (~/(?i)int/)                             : "Integer",
        (~/(?i)float|double|decimal|real|number/): "BigDecimal",
        (~/(?i)datetime|timestamp/)              : "LocalDateTime",
        (~/(?i)date/)                            : "LocalDate",
        (~/(?i)time/)                            : "LocalDateTime",
        (~/(?i)blob|mediumblob|binary|bfile|clob|raw|image/): "Byte[]",
        (~/(?i)text|mediumtext/)                 : "String",
        (~/(?i)/)                                : "String",

]

// 上面用到类和它的导入路径的之间的映射
importMap = [
        "BigDecimal" : "java.math.BigDecimal",
        "LocalDate" : "java.time.LocalDate",
        "LocalDateTime": "java.time.LocalDateTime",
]

// 导入路径列表,下面引用的时候会去重,也可以直接声明成一个 HashSet
importList = []

// 弹出选择文件的对话框
FILES.chooseDirectoryAndSave("选择文件夹", "选择存储位置") { dir ->
    SELECTION.filter { it instanceof DasTable && it.getKind() == ObjectKind.TABLE }.each { generate(it, dir) }
}

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

    def entityInfo = [
            dir: dir, // 获取工程的目录
            package : getPackageName(dir), // 获取包名
            name: className, // 获取类名
            tableName: table.getName(),
            tableComment: table.getComment()?:"",
            fieldName: [], // 获取所有字段名
            pkField: [], // 获取id字段
            field: [], // 字段
            fieldAll: [], // 全部字段
    ]
    // 填充field
    calcFields(table, entityInfo)

    new PrintWriter(new OutputStreamWriter(new FileOutputStream(new File(dir, className + ".json")), "utf-8")).withPrintWriter { out -> generate(out, entityInfo, table) }
}

def calcFields(table, entityInfo) {
    def pkSet = new HashSet<String>()
    def prKey = DasUtil.getPrimaryKey(table);
    if (prKey != null) {
        def keyRef = prKey.getColumnsRef();
        if (keyRef != null) {
            def it = keyRef.iterate();
            while (it.hasNext()) {
                pkSet.add(it.next())
            }
        }
    }

    entityInfo.fieldAll = 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 typeStrFull = "java.lang" + "." + typeStr
        if (importMap.containsKey(typeStr)) {
            def matchType = importMap.get(typeStr)
            importList.add(matchType)
            // 有映射关系,使用映射关系
            typeStrFull = matchType
        }
        def ifPk = pkSet.contains(col.getName())
        // def ifPk = DasUtil.isPrimary(col)

        fields += [[
                           name       : javaName(col.getName(), false), // 默认驼峰命名法
                           capitalName: javaName(col.getName(), true), // 首字母大写
                           dbName     : col.getName(), // 数据库的名字
                           type       : typeStr, // 类型名称
                           typeFull   : typeStrFull, // 类型全称
                           comment    : col.getComment() ?: "", // 字段注释
                           ifPk       : ifPk
                   ]]
    }

    // 填充pk
    entityInfo.pkField = entityInfo.fieldAll.findAll { it -> it.ifPk}
    // 填充filed
    entityInfo.field = entityInfo.fieldAll.findAll { it -> !it.ifPk}
    // 填充fieldName
    entityInfo.fieldName = entityInfo.fieldAll.collect { it.name }
}

def isNotEmpty(content) {
    return content != null && content.toString().trim().length() > 0
}

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]
}

// 获取包所在文件夹路径
def getPackageName(dir) {
    return dir.toString().replaceAll("\\\\", ".").replaceAll("/", ".").replaceAll("^.*src(\\.main\\.java\\.)?", "") + ";"
}


// 从这里开始,拼实的具体逻辑代码
def generate(out, entityInfo, table) {
    out.print """
{
    "dir": "${entityInfo.dir}",
    "package" : "${entityInfo.package}",
    "name": "${entityInfo.name}",
    "tableName": "${entityInfo.tableName}",
    "tableComment": "${entityInfo.tableComment}",
    "fieldName": [
        ${entityInfo.fieldName.collect { '"' + it + '"' }.join(", ")}
    ],
    "pkField": [${entityInfo.pkField.collect {"""
        {
            "name": "${it.name}",
            "capitalName": "${it.capitalName}",
            "dbName": "${it.dbName}",
            "type": "${it.type}",
            "typeFull": "${it.typeFull}",
            "comment": "${it.comment}",
            "ifPk": ${it.ifPk}
        }"""}.join(",")}
    ],
    "field": [${entityInfo.field.collect {"""
        {
            "name": "${it.name}",
            "capitalName": "${it.capitalName}",
            "dbName": "${it.dbName}",
            "type": "${it.type}",
            "typeFull": "${it.typeFull}",
            "comment": "${it.comment}",
            "ifPk": ${it.ifPk}
        }"""}.join(",")}
    ],
    "fieldAll": [${entityInfo.fieldAll.collect {"""
        {
            "name": "${it.name}",
            "capitalName": "${it.capitalName}",
            "dbName": "${it.dbName}",
            "type": "${it.type}",
            "typeFull": "${it.typeFull}",
            "comment": "${it.comment}",
            "ifPk": ${it.ifPk}
        }"""}.join(",")}
    ]
}
"""
}

生成的 JSON 格式如下


{
    "dir": "/Users/monkeyhi/work/workspace/test-server/src/main/java/com/xxx/entity",
    "package" : "com.xxx.entity;",
    "name": "TabXxxDdd",
    "tableName": "tab_xxx_ddd",
    "tableComment": "请求-单位关联表",
    "fieldName": [
        "id", "qqId", "dwId"
    ],
    "pkField": [
        {
            "name": "id",
            "capitalName": "Id",
            "dbName": "ID",
            "type": "String",
            "typeFull": "java.lang.String",
            "comment": "主键",
            "ifPk": true
        }
    ],
    "field": [
        {
            "name": "qqId",
            "capitalName": "QqId",
            "dbName": "QQ_ID",
            "type": "String",
            "typeFull": "java.lang.String",
            "comment": "请求id",
            "ifPk": false
        },
        {
            "name": "dwId",
            "capitalName": "DwId",
            "dbName": "DW_ID",
            "type": "String",
            "typeFull": "java.lang.String",
            "comment": "单位id",
            "ifPk": false
        }
    ],
    "fieldAll": [
        {
            "name": "id",
            "capitalName": "Id",
            "dbName": "ID",
            "type": "String",
            "typeFull": "java.lang.String",
            "comment": "主键",
            "ifPk": true
        },
        {
            "name": "qqId",
            "capitalName": "QqId",
            "dbName": "QQ_ID",
            "type": "String",
            "typeFull": "java.lang.String",
            "comment": "请求id",
            "ifPk": false
        },
        {
            "name": "dwId",
            "capitalName": "DwId",
            "dbName": "DW_ID",
            "type": "String",
            "typeFull": "java.lang.String",
            "comment": "单位id",
            "ifPk": false
        }
    ]
}


常用的一些函数收集

参数是table的
table.getName() // 表名称
table.getComment() // 表注释
table:属性 name、comment

获取列
JBIterable<? extends DasColumn> = DasUtil.getColumns(table)
JBIterable<? extends DasForeignKey> = DasUtil.getForeignKeys(table)
JBIterable<? extends DasIndex> = DasUtil.getIndices(table)
JBIterable<? extends DasTableKey> = DasUtil.getTableKeys(table)
DasTableKey = DasUtil.getPrimaryKey(table)

所有的key都可以获取列 JBIterable<? extends DasColumn> = key.getColumnsRef()
比如获取主键的所有列:
===
def pkSet = new HashSet<String>()
def prKey = DasUtil.getPrimaryKey(table);
if (prKey != null) {
    def keyRef = prKey.getColumnsRef();
    def it = keyRef.iterate();
    while (it.hasNext()) {
        pkSet.add(it.next())
    }
}
===
列可以获取到表
table = column.getTable()

判断列的属性
column:{name,comment,dataType:{size,scale,sizeUnit,jdbcType:未来将会被移除,vagueArg,suffix,schemaName,packageName,typeName}}
DasUtil.isPrimary(column)
DasUtil.isForeign(column)
DasUtil.isAuto(column)
DasUtil.isAutoGenerated(column)
DasUtil.isComputed(column)
DasUtil.hasAttribute(column, DasColumn.Attribute.PRIMARY_KEY)

dataType = column.getDataType()
dataType.getSpecification() = dataType.toString() = 'varchar(30)'
dataType.getLength() = dataType.getPrecision() = dataType.size = 30
dataType.getScale() = dataType.scale = 0
dataType.jdbcType = 12 = org.apache.ibatis.type.JdbcType.forCode(12) = org.apache.ibatis.type.JdbcType.VARCHAR

常量
DasColumn.Attribute 的枚举值 DasColumn.Attribute.PRIMARY_KEY 其key还可以取如下值:PRIMARY_KEY, CANDIDATE_KEY, FOREIGN_KEY, INDEX, AUTO_GENERATED, COMPUTED, VERSION