Java|在 IDEA 里自动生成 MyBatis 模板代码

简介: 基于 MyBatis 开发的项目,新增数据库表以后,总是需要编写对应的 Entity、Mapper 和 Service 等等 Class 的代码,这些都是重复的工作,我们可以想一些办法来自动生成这些代码。

背景

基于 MyBatis 开发的项目,新增数据库表以后,总是需要编写对应的 Entity、Mapper 和 Service 等等 Class 的代码,这些都是重复的工作,我们可以想一些办法来自动生成这些代码。

方案

一种可选的方案是使用 MyBatis Generator,官方支持,常见需求一般也都能满足。但是它的配置文件比较繁琐,如果有一些项目相关的个性化需求,不一定很好处理。

这里介绍另外一种我觉得更为简便灵活的方法。

近几年版本的 IDEA 里已经自带了 Database Tools and SQL 插件,可以连接数据库进行常用的操作,并且,它还自带了数据库表对应 POJO 类的代码生成器:在 Database 面板里配置好数据源以后,右键表名,依次选择 Scripted Extensions、Generate POJOs.groovy,选择生成路径后,即可生成对应的 Entity 类。

图片

既然能够生成 Entity,那么我们可以基于它进行修改,让它一次性生成我们需要的 Entity、Mapper 和 Service。

需求

基于项目情况,我们对生成的代码有如下要求:

  1. Entity 需要继承指定基类,数据库表的公共字段放在基类里;

  2. Mapper、Service 和 ServiceImpl 分别需要实现指定的类继承关系;

  3. Entity、Mapper 和 Service 需要自动放在对应的子包下。

以 t_promotion_channel 表为例,指定该表和对应的代码目录之后,生成的目录结构如下:

.
├── entity
│   └── PromotionChannel.java
├── mapper
│   └── PromotionChannelMapper.java
└── service
    ├── PromotionChannelService.java
    └── impl
        └── PromotionChannelServiceImpl.java
AI 代码解读

需要生成的代码如下:

entity/PromotionChannel.java

package com.test.data.promotion.entity;

import com.test.common.base.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.persistence.Table;

/**
 * @author mazhuang
 */
@EqualsAndHashCode(callSuper = true)
@Data
@Table(name = "t_promotion_channel")
public class PromotionChannel extends BaseEntity {

    private static final long serialVersionUID = 5495175453870776988L;
    /**
     * 用户ID
    */
    private Long fkUserId;

    /**
     * 渠道名称
    */
    private String channelName;

}
AI 代码解读

mapper/PromotionChannelMapper.java

package com.test.data.promotion.mapper;

import com.test.common.base.BaseMapper;
import com.test.data.promotion.entity.PromotionChannel;

/**
 * @author mazhuang
 */
public interface PromotionChannelMapper extends BaseMapper<PromotionChannel> {

}
AI 代码解读

service/PromotionChannelService.java

package com.test.data.promotion.service;

import com.test.common.base.BaseService;
import com.test.data.promotion.entity.PromotionChannel;

/**
 * @author mazhuang
 */
public interface PromotionChannelService extends BaseService<PromotionChannel> {

}
AI 代码解读

service/impl/PromotionChannelServiceImpl.java

package com.test.data.promotion.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import com.test.common.base.BaseServiceImpl;
import com.test.data.promotion.entity.PromotionChannel;
import com.test.data.promotion.mapper.PromotionChannelMapper;
import com.test.data.promotion.service.PromotionChannelService;

/**
 * @author mazhuang
 */
@Slf4j
@Service
public class PromotionChannelServiceImpl extends BaseServiceImpl<PromotionChannelMapper, PromotionChannel> implements PromotionChannelService {

}
AI 代码解读

实现

右键一个数据库表,依次选择 Scripted Extensions、Go to Scripts Directory,进入生成的脚本目录,找到 Generate POJOs.groovy,复制一份,重命名为 Generate MyBatis Code.groovy,然后修改内容如下:

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

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

typeMapping = [
        (~/(?i)int/)                      : "Long",
        (~/(?i)float|double|decimal|real/): "BigDecimal",
        (~/(?i)datetime|timestamp/)       : "java.util.Date",
        (~/(?i)date/)                     : "java.sql.Date",
        (~/(?i)time/)                     : "java.sql.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 className = javaName(table.getName().replaceFirst('t_', ''), true)
    def fields = calcFields(table)

    dirPath = dir.getAbsolutePath()
    packageName = calcPackageName(dirPath)

    // Generate POJO
    new File(dirPath + File.separator + "entity", className + ".java").withPrintWriter("utf-8") { out -> generateEntity(out, table.getName(), className, fields, packageName) }

    // Generate Mapper
    new File(dirPath + File.separator + "mapper", className + "Mapper.java").withPrintWriter("utf-8") { out -> generateMapper(out, className, packageName) }

    // Generate Service
    new File(dirPath + File.separator + "service", className + "Service.java").withPrintWriter("utf-8") { out -> generateService(out, className, packageName) }

    // Generate ServiceImpl
    new File(dirPath + File.separator + "service" + File.separator + "impl", className + "ServiceImpl.java").withPrintWriter("utf-8") { out -> generateServiceImpl(out, className, packageName) }
}

static def generateEntity(out, tableName, className, fields, packageName) {
    out.println "package $packageName" + ".entity;"
    out.println ""
    out.println "import com.test.common.base.BaseEntity;"
    out.println "import lombok.Data;"
    out.println "import lombok.EqualsAndHashCode;"
    out.println "import javax.persistence.Table;"
    out.println ""
    out.println "/**\n * @author mazhuang\n */"
    out.println "@EqualsAndHashCode(callSuper = true)"
    out.println "@Data"
    out.println "@Table(name = \"$tableName\")"
    out.println "public class $className extends BaseEntity {"
    out.println ""

    def baseEntityFields = ['pkid', 'addedBy', 'addedTime', 'lastModifiedBy', 'lastModifiedTime', 'valid']
    fields.each() {
        if (baseEntityFields.contains(it.name)) {
            return
        }
        if (it.annos != "") out.println "  ${it.annos}"
        if (it.comment != null) out.println "    /**\n     * ${it.comment}\n    */"
        out.println "    private ${it.type}${it.name};\n"
    }

    out.println "}"
}

static def generateMapper(out, className, packageName) {
    out.println "package $packageName" + ".mapper;"
    out.println ""

    out.println "import com.test.common.base.BaseMapper;"
    out.println "import $packageName" + ".entity.$className;"
    out.println ""

    out.println "/**\n * @author mazhuang\n */"
    out.println "public interface $className" + "Mapper extends BaseMapper<$className> {"
    out.println ""
    out.println "}"
}

static def generateService(out, className, packageName) {
    out.println "package $packageName" + ".service;"
    out.println ""

    out.println "import com.test.common.base.BaseService;"
    out.println "import $packageName" + ".entity.$className;"
    out.println ""

    out.println "/**\n * @author mazhuang\n */"
    out.println "public interface $className" + "Service extends BaseService<$className> {"
    out.println ""
    out.println "}"
}

static def generateServiceImpl(out, className, packageName) {
    out.println "package $packageName" + ".service.impl;"
    out.println ""

    out.println "import lombok.extern.slf4j.Slf4j;"
    out.println "import org.springframework.stereotype.Service;"
    out.println "import com.test.common.base.BaseServiceImpl;"
    out.println "import $packageName" + ".entity.$className;"
    out.println "import $packageName" + ".mapper.$className" + "Mapper;"
    out.println "import $packageName" + ".service.$className" + "Service;"
    out.println ""

    out.println "/**\n * @author mazhuang\n */"
    out.println "@Slf4j"
    out.println "@Service"
    out.println "public class $className" + "ServiceImpl extends BaseServiceImpl<$className" + "Mapper, $className> implements $className" + "Service {"
    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,
                               comment: col.getComment(), // 注释
                               default: col.getDefault(), // 默认值
                               annos  : ""]]

    }
}

static def calcPackageName(dirPath) {
    def startPos = dirPath.indexOf('com')
    return dirPath.substring(startPos).replaceAll(File.separator, ".")
}

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]
}
AI 代码解读

大功告成,现在右键一个数据库表,依次选择 Scripted Extensions、Generate MyBatis Code.groovy,在弹出的目录选择框里选择想要放置代码的目录,即可生成期望的模板代码了。

后续如果有一些个性化的代码生成需求,可以根据实际情况修改、新增脚本来完成。

其它

本文代码生成器脚本已上传至 GitHub,仓库地址:https://github.com/mzlogin/code-generator ,以后如果有更新,或者新的代码生成脚本,也会放在这个仓库里。

目录
打赏
0
6
6
0
117
分享
相关文章
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
200 96
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
JVM实战—1.Java代码的运行原理
本文介绍了Java代码的运行机制、JVM类加载机制、JVM内存区域及其作用、垃圾回收机制,并汇总了一些常见问题。
JVM实战—1.Java代码的运行原理
|
20天前
|
Java代码结构解析:类、方法、主函数(1分钟解剖室)
### Java代码结构简介 掌握Java代码结构如同拥有程序世界的建筑蓝图,类、方法和主函数构成“黄金三角”。类是独立的容器,承载成员变量和方法;方法实现特定功能,参数控制输入环境;主函数是程序入口。常见错误包括类名与文件名不匹配、忘记static修饰符和花括号未闭合。通过实战案例学习电商系统、游戏角色控制和物联网设备监控,理解类的作用、方法类型和主函数任务,避免典型错误,逐步提升编程能力。 **脑图速记法**:类如太空站,方法即舱段;main是发射台,static不能换;文件名对仗,括号要成双;参数是坐标,void不返航。
45 5
Java使用MyBatis-Plus的OR
通过MyBatis-Plus的条件构造器,Java开发者可以方便地进行复杂的查询条件组合,包括AND和OR条件的灵活使用。熟练掌握这些技巧,可以显著提升开发效率和代码可读性。
90 20
【潜意识Java】MyBatis中的动态SQL灵活、高效的数据库查询以及深度总结
本文详细介绍了MyBatis中的动态SQL功能,涵盖其背景、应用场景及实现方式。
186 6
如何在 Java 代码中使用 JSqlParser 解析复杂的 SQL 语句?
大家好,我是 V 哥。JSqlParser 是一个用于解析 SQL 语句的 Java 库,可将 SQL 解析为 Java 对象树,支持多种 SQL 类型(如 `SELECT`、`INSERT` 等)。它适用于 SQL 分析、修改、生成和验证等场景。通过 Maven 或 Gradle 安装后,可以方便地在 Java 代码中使用。
543 11
Java后端开发-使用springboot进行Mybatis连接数据库步骤
本文介绍了使用Java和IDEA进行数据库操作的详细步骤,涵盖从数据库准备到测试类编写及运行的全过程。主要内容包括: 1. **数据库准备**:创建数据库和表。 2. **查询数据库**:验证数据库是否可用。 3. **IDEA代码配置**:构建实体类并配置数据库连接。 4. **测试类编写**:编写并运行测试类以确保一切正常。
106 2
【潜意识Java】深入理解MyBatis的Mapper层,以及让数据访问更高效的详细分析
深入理解MyBatis的Mapper层,以及让数据访问更高效的详细分析
125 1
【潜意识Java】深入理解MyBatis,从基础到高级的深度细节应用
本文详细介绍了MyBatis,一个轻量级的Java持久化框架。内容涵盖MyBatis的基本概念、配置与环境搭建、基础操作(如创建实体类、Mapper接口及映射文件)以及CRUD操作的实现。此外,还深入探讨了高级特性,包括动态SQL和缓存机制。通过代码示例,帮助开发者更好地掌握MyBatis的使用技巧,提升数据库操作效率。总结部分强调了MyBatis的优势及其在实际开发中的应用价值。
45 1
利用 Java 代码获取淘宝关键字 API 接口
在数字化商业时代,精准把握市场动态与消费者需求是企业成功的关键。淘宝作为中国最大的电商平台之一,其海量数据中蕴含丰富的商业洞察。本文介绍如何通过Java代码高效、合规地获取淘宝关键字API接口数据,帮助商家优化产品布局、制定营销策略。主要内容包括: 1. **淘宝关键字API的价值**:洞察用户需求、优化产品标题与详情、制定营销策略。 2. **获取API接口的步骤**:注册账号、申请权限、搭建Java开发环境、编写调用代码、解析响应数据。 3. **注意事项**:遵守法律法规与平台规则,处理API调用限制。 通过这些步骤,商家可以在激烈的市场竞争中脱颖而出。
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等