├── settings.gradle ├── doc ├── assets │ ├── RootModel.png │ ├── images_1.png │ ├── images_10.png │ ├── images_11.png │ ├── images_4.png │ ├── images_5.png │ ├── images_6.png │ ├── images_7.png │ ├── images_8.png │ └── images_9.png ├── images.md ├── README.md ├── plugin │ ├── changeNotes.md │ └── description.md └── upgrade-2.0.0.md ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitee ├── ISSUE_TEMPLATE.zh-CN.md └── PULL_REQUEST_TEMPLATE.zh-CN.md ├── .github ├── ISSUE_TEMPLATE.zh-CN.md ├── PULL_REQUEST_TEMPLATE.zh-CN.md └── workflows │ ├── gradle.yml │ └── upload-plugin.yml ├── src ├── main │ ├── resources │ │ ├── velocity.properties │ │ ├── init.properties │ │ ├── templates │ │ │ ├── default │ │ │ │ ├── beetl │ │ │ │ │ └── java │ │ │ │ │ │ ├── Mapper.xml.btl │ │ │ │ │ │ ├── Service.java.btl │ │ │ │ │ │ ├── Repository.java.btl │ │ │ │ │ │ ├── ServiceImpl.java.btl │ │ │ │ │ │ └── Entity.java.btl │ │ │ │ ├── freemarker │ │ │ │ │ └── java │ │ │ │ │ │ ├── Mapper.xml.ftl │ │ │ │ │ │ ├── Service.java.ftl │ │ │ │ │ │ ├── Repository.java.ftl │ │ │ │ │ │ ├── ServiceImpl.java.ftl │ │ │ │ │ │ ├── EntityVo.java.ftl │ │ │ │ │ │ ├── EntityQuery.java.ftl │ │ │ │ │ │ ├── Entity.java.ftl │ │ │ │ │ │ └── EntityForm.java.ftl │ │ │ │ └── velocity │ │ │ │ │ └── java │ │ │ │ │ ├── Mapper.xml.vm │ │ │ │ │ ├── Service.java.vm │ │ │ │ │ ├── Repository.java.vm │ │ │ │ │ ├── ServiceImpl.java.vm │ │ │ │ │ └── Entity.java.vm │ │ │ └── README.md │ │ ├── i18n │ │ │ ├── language.properties │ │ │ └── language_en.properties │ │ ├── syncFiles.txt │ │ ├── META-INF │ │ │ ├── plugin.xml │ │ │ └── pluginIcon.svg │ │ └── config.yml │ └── java │ │ ├── com │ │ └── github │ │ │ └── houkunlin │ │ │ ├── ui │ │ │ └── win │ │ │ │ ├── IWindows.java │ │ │ │ ├── table │ │ │ │ ├── ComboBoxTableCellEditor.java │ │ │ │ ├── ColumnSpec.java │ │ │ │ ├── TableDecorator.java │ │ │ │ ├── PlaceholderTableCellRenderer.java │ │ │ │ ├── EditorTableCellEditor.java │ │ │ │ ├── FileTypeTableDecorator.java │ │ │ │ └── GenericTableModel.java │ │ │ │ ├── TableSetting.form │ │ │ │ ├── tree │ │ │ │ ├── CheckBoxTreeLabel.java │ │ │ │ ├── CheckBoxTreeNodeSelectionListener.java │ │ │ │ ├── CheckBoxTreeCellRenderer.java │ │ │ │ └── CheckBoxTreeNode.java │ │ │ │ ├── SelectTemplate.form │ │ │ │ ├── TextFieldDocumentUtil.java │ │ │ │ ├── TableSetting.java │ │ │ │ ├── TablePanel.java │ │ │ │ ├── BaseSetting.form │ │ │ │ ├── TablePanel.form │ │ │ │ ├── Main.form │ │ │ │ └── BaseSetting.java │ │ │ ├── config │ │ │ ├── Developer.java │ │ │ ├── ConfigVo.java │ │ │ ├── BeanTransform.java │ │ │ ├── Options.java │ │ │ ├── Settings.java │ │ │ └── ConfigService.java │ │ │ ├── vo │ │ │ ├── ITable.java │ │ │ ├── IName.java │ │ │ ├── IEntity.java │ │ │ ├── impl │ │ │ │ ├── EntityPackageInfo.java │ │ │ │ ├── EntityNameInfo.java │ │ │ │ ├── TableImpl.java │ │ │ │ ├── BaseTypeMap.java │ │ │ │ ├── FieldNameInfo.java │ │ │ │ ├── EntityPackage.java │ │ │ │ ├── EntityImpl.java │ │ │ │ ├── EntityName.java │ │ │ │ ├── RootModel.java │ │ │ │ ├── PrimaryInfo.java │ │ │ │ ├── TableColumnImpl.java │ │ │ │ └── EntityFieldImpl.java │ │ │ ├── ITypeMap.java │ │ │ ├── ITableColumn.java │ │ │ ├── IEntityField.java │ │ │ └── Variable.java │ │ │ ├── icon │ │ │ └── DatabaseIcons.java │ │ │ ├── template │ │ │ ├── ITemplateGenerator.java │ │ │ ├── TplType.java │ │ │ ├── beetl │ │ │ │ ├── BeetlErrorHandler.java │ │ │ │ └── BeetlTemplateGenerator.java │ │ │ ├── freemarker │ │ │ │ └── FreemarkerTemplateGenerator.java │ │ │ ├── velocity │ │ │ │ └── VelocityTemplateGenerator.java │ │ │ ├── TemplateGeneratorFactory.java │ │ │ └── AbstractScriptedTemplateGenerator.java │ │ │ ├── model │ │ │ ├── FileType.java │ │ │ ├── TableColumnType.java │ │ │ ├── SaveFilePath.java │ │ │ └── JTableModel.java │ │ │ ├── message │ │ │ └── Bundles.java │ │ │ ├── util │ │ │ ├── IO.java │ │ │ ├── SyncResources.java │ │ │ ├── YamlUtils.java │ │ │ ├── ScriptManager.java │ │ │ └── Generator.java │ │ │ ├── action │ │ │ └── MainAction.java │ │ │ └── task │ │ │ └── GeneratorTask.java │ │ ├── velocity_implicit.vm │ │ └── freemarker_implicit.ftl └── test │ └── java │ └── com │ └── houkunlin │ └── TempCode.java ├── .gitattributes ├── .editorconfig ├── .gitignore ├── templates ├── jdbc-type.ftl ├── jdbc-typescript-type.ftl └── MyBatisMapper.xml.ftl ├── README.md ├── gradlew.bat ├── RootModel.uml └── gradlew /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = "Database Generator" 2 | -------------------------------------------------------------------------------- /doc/assets/RootModel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houkunlin/Database-Generator/HEAD/doc/assets/RootModel.png -------------------------------------------------------------------------------- /doc/assets/images_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houkunlin/Database-Generator/HEAD/doc/assets/images_1.png -------------------------------------------------------------------------------- /doc/assets/images_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houkunlin/Database-Generator/HEAD/doc/assets/images_10.png -------------------------------------------------------------------------------- /doc/assets/images_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houkunlin/Database-Generator/HEAD/doc/assets/images_11.png -------------------------------------------------------------------------------- /doc/assets/images_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houkunlin/Database-Generator/HEAD/doc/assets/images_4.png -------------------------------------------------------------------------------- /doc/assets/images_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houkunlin/Database-Generator/HEAD/doc/assets/images_5.png -------------------------------------------------------------------------------- /doc/assets/images_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houkunlin/Database-Generator/HEAD/doc/assets/images_6.png -------------------------------------------------------------------------------- /doc/assets/images_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houkunlin/Database-Generator/HEAD/doc/assets/images_7.png -------------------------------------------------------------------------------- /doc/assets/images_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houkunlin/Database-Generator/HEAD/doc/assets/images_8.png -------------------------------------------------------------------------------- /doc/assets/images_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houkunlin/Database-Generator/HEAD/doc/assets/images_9.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houkunlin/Database-Generator/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /.gitee/ISSUE_TEMPLATE.zh-CN.md: -------------------------------------------------------------------------------- 1 | ### 该问题是怎么引起的? 2 | 3 | 4 | 5 | ### 重现步骤 6 | 7 | 8 | 9 | ### 报错信息 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.zh-CN.md: -------------------------------------------------------------------------------- 1 | ### 该问题是怎么引起的? 2 | 3 | 4 | 5 | ### 重现步骤 6 | 7 | 8 | 9 | ### 报错信息 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md: -------------------------------------------------------------------------------- 1 | ### 相关的Issue 2 | 3 | 4 | ### 原因(目的、解决的问题等) 5 | 6 | 7 | ### 描述(做了什么,变更了什么) 8 | 9 | 10 | ### 测试用例 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.zh-CN.md: -------------------------------------------------------------------------------- 1 | ### 相关的Issue 2 | 3 | 4 | ### 原因(目的、解决的问题等) 5 | 6 | 7 | ### 描述(做了什么,变更了什么) 8 | 9 | 10 | ### 测试用例 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/main/resources/velocity.properties: -------------------------------------------------------------------------------- 1 | resource.default_encoding=utf-8 2 | output.encoding=utf-8 3 | resource.loaders=file 4 | runtime.log.logsystem.class=org.apache.velocity.runtime.log.NullLogChute,org.apache.velocity.runtime.log.JdkLogChute 5 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /src/main/resources/init.properties: -------------------------------------------------------------------------------- 1 | plugin.info=删除该文件可重新生成所有模板文件。删除当前目录可重新生成所有模板文件 2 | plugin.author=HouKunLin 3 | plugin.email=houkunlin@aliyun.com 4 | plugin.github=https://github.com/houkunlin/Database-Generator 5 | plugin.gitee=https://gitee.com/houkunlin/Database-Generator 6 | -------------------------------------------------------------------------------- /src/main/resources/templates/default/beetl/java/Mapper.xml.btl: -------------------------------------------------------------------------------- 1 | ${@gen.setType("xml")} 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/main/resources/templates/default/freemarker/java/Mapper.xml.ftl: -------------------------------------------------------------------------------- 1 | ${gen.setType("xml")} 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/main/resources/templates/default/velocity/java/Mapper.xml.vm: -------------------------------------------------------------------------------- 1 | ${gen.setType("xml")} 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /doc/images.md: -------------------------------------------------------------------------------- 1 | # 插件截图 2 | 3 | 4 | ![](assets/images_1.png) 5 | 6 | ![](assets/images_4.png) 7 | 8 | ![](assets/images_5.png) 9 | 10 | ![](assets/images_6.png) 11 | 12 | ![](assets/images_7.png) 13 | 14 | ![](assets/images_8.png) 15 | 16 | ![](assets/images_9.png) 17 | 18 | ![](assets/images_10.png) 19 | 20 | ![](assets/images_11.png) -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/ui/win/IWindows.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.ui.win; 2 | 3 | import javax.swing.*; 4 | 5 | /** 6 | * 内容窗口 7 | * 8 | * @author HouKunLin 9 | */ 10 | public interface IWindows { 11 | /** 12 | * 获取内容面板 13 | * 14 | * @return 面板 15 | */ 16 | JPanel getContent(); 17 | } 18 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # All text files should have the "lf" (Unix) line endings 2 | * text eol=lf 3 | # windows cmd should have the "crlf" (Win32) line endings 4 | *.cmd eol=crlf 5 | 6 | # Denote all files that are truly binary and should not be modified. 7 | *.png binary 8 | *.jpg binary 9 | *.jar binary 10 | *.ttf binary 11 | *.jks binary 12 | *.gif binary 13 | *.mp4 binary 14 | 15 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/config/Developer.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.config; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * 开发者信息 7 | * 8 | * @author HouKunLin 9 | */ 10 | @Data 11 | public class Developer { 12 | /** 13 | * 开发者姓名 14 | */ 15 | private String author; 16 | /** 17 | * 开发者电子邮件 18 | */ 19 | private String email; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/vo/ITable.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.vo; 2 | 3 | /** 4 | * 数据库表信息 5 | * 6 | * @author HouKunLin 7 | */ 8 | public interface ITable { 9 | /** 10 | * 获得数据库表名称 11 | * 12 | * @return 数据库表名称 13 | */ 14 | String getName(); 15 | 16 | /** 17 | * 获得数据库表注释内容 18 | * 19 | * @return 注释内容 20 | */ 21 | String getComment(); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/icon/DatabaseIcons.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.icon; 2 | 3 | import com.intellij.openapi.util.IconLoader; 4 | 5 | import javax.swing.*; 6 | 7 | /** 8 | * @author tanqi 9 | */ 10 | public interface DatabaseIcons { 11 | 12 | Icon REFRESH_DARK = IconLoader.getIcon("actions/refresh_dark.svg", DatabaseIcons.class); 13 | 14 | Icon REFRESH = IconLoader.getIcon("actions/refresh.svg", DatabaseIcons.class); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 4 7 | indent_style = space 8 | insert_final_newline = true 9 | max_line_length = 120 10 | tab_width = 4 11 | 12 | [*.yml] 13 | indent_size = 2 14 | 15 | [{*.bash,*.bats,*.dash,*.ksh,*.mksh,*.sh,*.zsh,.bash_aliases,.bash_logout,.bash_profile,.bashrc,.profile}] 16 | indent_size = 2 17 | tab_width = 2 18 | 19 | [{*.har,*.jsb2,*.jsb3,.babelrc,.eslintrc,.stylelintrc,bowerrc,jest.config}] 20 | indent_size = 2 21 | -------------------------------------------------------------------------------- /src/main/resources/templates/default/beetl/java/Service.java.btl: -------------------------------------------------------------------------------- 1 | ${@gen.setType("service")} 2 | package ${entity.packages.service}; 3 | 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | import ${entity.packages.entity.full}; 7 | import java.util.List; 8 | 9 | /** 10 | * Service:${entity.comment} 11 | * 12 | * @author ${developer.author} 13 | */ 14 | public interface ${entity.name.service} extends IService<${entity.name.entity}> { 15 | String CACHE_NAME = "${table.name}"; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/resources/templates/default/velocity/java/Service.java.vm: -------------------------------------------------------------------------------- 1 | ${gen.setType("service")} 2 | package ${entity.packages.service}; 3 | 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | import ${entity.packages.entity.full}; 7 | import java.util.List; 8 | 9 | /** 10 | * Service:${entity.comment} 11 | * 12 | * @author ${developer.author} 13 | */ 14 | public interface ${entity.name.service} extends IService<${entity.name.entity}> { 15 | String CACHE_NAME = "${table.name}"; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/resources/templates/default/freemarker/java/Service.java.ftl: -------------------------------------------------------------------------------- 1 | ${gen.setType("service")} 2 | package ${entity.packages.service}; 3 | 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | import ${entity.packages.entity.full}; 7 | import java.util.List; 8 | 9 | /** 10 | * Service:${entity.comment} 11 | * 12 | * @author ${developer.author} 13 | */ 14 | public interface ${entity.name.service} extends IService<${entity.name.entity}> { 15 | String CACHE_NAME = "${table.name}"; 16 | } 17 | -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | # 插件文档 2 | 3 | - [代码模板升级2.0.0指南](upgrade-2.0.0.md) 4 | 5 | - [代码模板变量说明文档](template-document.md) 6 | - [Groovy 脚本使用说明](groovy-scripts.md) 7 | - [插件截图](images.md) 8 | - [更新日志](changeNotes.md) 9 | - 插件说明文档(Jetbrains插件说明) 10 | - [插件说明](plugin/description.md) 11 | - [插件更新日志](plugin/changeNotes.md) 12 | 13 | - 涵盖所有模板变量使用的模板文件 [all-variable.ftl](https://github.com/houkunlin/Database-Generator/blob/master/src/main/resources/templates/all-variable.ftl) 以及对应的输出结果示例 [all-variable.md](all-variable.md) 14 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/vo/IName.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.vo; 2 | 3 | /** 4 | * 名称处理 5 | * 6 | * @author HouKunLin 7 | */ 8 | public interface IName { 9 | 10 | /** 11 | * 获得名称 12 | * 13 | * @return 名称(驼峰形式,首字母小写) 14 | */ 15 | default String getFirstLower() { 16 | return toString(); 17 | } 18 | 19 | /** 20 | * 获得名称 21 | * 22 | * @return 名称(驼峰形式,首字母大写) 23 | */ 24 | default String getFirstUpper() { 25 | return toString(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/resources/templates/default/beetl/java/Repository.java.btl: -------------------------------------------------------------------------------- 1 | ${@gen.setType("dao")} 2 | package ${entity.packages.dao}; 3 | 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import org.apache.ibatis.annotations.CacheNamespace; 6 | import org.springframework.stereotype.Repository; 7 | 8 | import ${entity.packages.entity.full}; 9 | 10 | /** 11 | * 存储库:${entity.comment} 12 | * 13 | * @author ${developer.author} 14 | */ 15 | @Repository 16 | @CacheNamespace 17 | public interface ${entity.name.dao} extends BaseMapper<${entity.name.entity}> { 18 | } 19 | -------------------------------------------------------------------------------- /src/main/resources/templates/default/velocity/java/Repository.java.vm: -------------------------------------------------------------------------------- 1 | ${gen.setType("dao")} 2 | package ${entity.packages.dao}; 3 | 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import org.apache.ibatis.annotations.CacheNamespace; 6 | import org.springframework.stereotype.Repository; 7 | 8 | import ${entity.packages.entity.full}; 9 | 10 | /** 11 | * 存储库:${entity.comment} 12 | * 13 | * @author ${developer.author} 14 | */ 15 | @Repository 16 | @CacheNamespace 17 | public interface ${entity.name.dao} extends BaseMapper<${entity.name.entity}> { 18 | } 19 | -------------------------------------------------------------------------------- /src/main/resources/templates/default/freemarker/java/Repository.java.ftl: -------------------------------------------------------------------------------- 1 | ${gen.setType("dao")} 2 | package ${entity.packages.dao}; 3 | 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import org.apache.ibatis.annotations.CacheNamespace; 6 | import org.springframework.stereotype.Repository; 7 | 8 | import ${entity.packages.entity.full}; 9 | 10 | /** 11 | * 存储库:${entity.comment} 12 | * 13 | * @author ${developer.author} 14 | */ 15 | @Repository 16 | @CacheNamespace 17 | public interface ${entity.name.dao} extends BaseMapper<${entity.name.entity}> { 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/vo/IEntity.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.vo; 2 | 3 | import com.github.houkunlin.vo.impl.EntityPackage; 4 | 5 | /** 6 | * 实体类信息 7 | * 8 | * @author HouKunLin 9 | */ 10 | public interface IEntity { 11 | /** 12 | * 获得实体类对象名称(不含后缀) 13 | * 14 | * @return 实体类对象名称(不含后缀,驼峰形式,首字母大写) 15 | */ 16 | IName getName(); 17 | 18 | /** 19 | * 获得实体对象注释内容 20 | * 21 | * @return 注释内容 22 | */ 23 | String getComment(); 24 | 25 | /** 26 | * 获得需要导入的包列表(一般情况下是在实体对象中使用),该方法只返回包的完整名称。 27 | * 28 | * @return 包列表 29 | */ 30 | EntityPackage getPackages(); 31 | } 32 | -------------------------------------------------------------------------------- /src/main/resources/i18n/language.properties: -------------------------------------------------------------------------------- 1 | # == 2 | panel.developer.title=开发信息 3 | panel.developer.name=姓名: 4 | panel.developer.email=邮箱: 5 | # == 6 | dir.project=项目路径: 7 | dir.source=代码路径: 8 | dir.resources=资源路径: 9 | # == 10 | btn.finish=完成,生成代码 11 | # == 12 | table.table-name=表名: 13 | table.entity-name=实体名: 14 | table.entity-comment=注释 : 15 | table.uri=URI : 16 | # == 17 | fileTypes.title=文件类型 18 | fileTypes.type=类型 19 | fileTypes.suffix=后缀 20 | fileTypes.package=包名 21 | fileTypes.ext=拓展名 22 | fileTypes.ext.placeholder=.java 23 | fileTypes.path=存储路径 24 | fileTypes.path.placeholder=默认为存储路径 25 | fileTypes.override=允许覆盖 26 | ## 27 | table-decorator.not-null=表格数据模型不能为空 28 | 29 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/vo/impl/EntityPackageInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.vo.impl; 2 | 3 | import com.github.houkunlin.vo.IName; 4 | import lombok.Getter; 5 | 6 | /** 7 | * 实体类包信息 8 | * 9 | * @author HouKunLin 10 | */ 11 | @Getter 12 | public class EntityPackageInfo { 13 | /** 14 | * 实体类包名(不含实体类名称) 15 | */ 16 | private final String pack; 17 | /** 18 | * 实体类完整包路径(含实体类名称) 19 | */ 20 | private final String full; 21 | 22 | public EntityPackageInfo(String pack, IName entityName) { 23 | this.pack = pack; 24 | this.full = String.format("%s.%s", pack, entityName); 25 | } 26 | 27 | @Override 28 | public String toString() { 29 | return pack; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/resources/i18n/language_en.properties: -------------------------------------------------------------------------------- 1 | # == 2 | panel.developer.title=Developer Info 3 | panel.developer.name=Name : 4 | panel.developer.email=Email : 5 | # == 6 | dir.project=Project Path : 7 | dir.source=Source Path : 8 | dir.resources=Resources Path : 9 | # == 10 | btn.finish=Generate Code 11 | # == 12 | table.table-name=Table : 13 | table.entity-name=Entity : 14 | table.entity-comment=Comment : 15 | table.uri=URI : 16 | # == 17 | fileTypes.title=File Types 18 | fileTypes.type=Type 19 | fileTypes.suffix=Suffix 20 | fileTypes.package=Package Name 21 | fileTypes.ext=Extension 22 | fileTypes.path=Path 23 | fileTypes.path.placeholder=Defaults to source path 24 | fileTypes.ext.placeholder=.java 25 | fileTypes.override=Override 26 | ## == 27 | table-decorator.not-null=Table Model is required 28 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/config/ConfigVo.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.config; 2 | 3 | import com.github.houkunlin.model.TableColumnType; 4 | import lombok.Data; 5 | 6 | @Data 7 | public class ConfigVo { 8 | private static ConfigVo INSTANCE; 9 | private Developer developer; 10 | private Options options; 11 | private Settings settings; 12 | private TableColumnType[] types; 13 | 14 | public static ConfigVo getInstance() { 15 | if (INSTANCE == null) { 16 | INSTANCE = new ConfigVo(); 17 | INSTANCE.developer = new Developer(); 18 | INSTANCE.options = new Options(); 19 | INSTANCE.settings = new Settings(); 20 | INSTANCE.types = new TableColumnType[0]; 21 | } 22 | return INSTANCE; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/vo/impl/EntityNameInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.vo.impl; 2 | 3 | import com.github.houkunlin.vo.IName; 4 | import com.google.common.base.CaseFormat; 5 | import lombok.Getter; 6 | 7 | /** 8 | * 实体类名称信息对象 9 | * 10 | * @author HouKunLin 11 | */ 12 | @Getter 13 | public class EntityNameInfo implements IName { 14 | private final String value; 15 | private final String firstUpper; 16 | private final String firstLower; 17 | 18 | public EntityNameInfo(String entityName, String suffix) { 19 | this.value = entityName + suffix; 20 | this.firstLower = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, value); 21 | this.firstUpper = value; 22 | } 23 | 24 | @Override 25 | public String toString() { 26 | return value; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/vo/impl/TableImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.vo.impl; 2 | 3 | import com.github.houkunlin.vo.ITable; 4 | import com.intellij.database.psi.DbTable; 5 | import lombok.EqualsAndHashCode; 6 | import lombok.Getter; 7 | import lombok.ToString; 8 | 9 | import java.util.Objects; 10 | 11 | /** 12 | * 数据库表信息 13 | * 14 | * @author HouKunLin 15 | */ 16 | @Getter 17 | public class TableImpl implements ITable { 18 | /** 19 | * 数据库表的原始对象 20 | */ 21 | @ToString.Exclude 22 | @EqualsAndHashCode.Exclude 23 | private final DbTable dbTable; 24 | 25 | private final String name; 26 | private final String comment; 27 | 28 | public TableImpl(DbTable dbTable) { 29 | this.dbTable = dbTable; 30 | this.name = dbTable.getName(); 31 | this.comment = Objects.toString(dbTable.getComment(), ""); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/config/BeanTransform.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.config; 2 | 3 | import org.mapstruct.Mapper; 4 | import org.mapstruct.MappingTarget; 5 | import org.mapstruct.ReportingPolicy; 6 | 7 | /** 8 | * 对象转换 9 | * 10 | * @author HouKunLin 11 | */ 12 | @Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE) 13 | public interface BeanTransform { 14 | /** 15 | * 对象转换 16 | * 17 | * @param source 原始对象 18 | * @param to target 19 | */ 20 | void copyTo(Developer source, @MappingTarget Developer to); 21 | 22 | /** 23 | * 对象转换 24 | * 25 | * @param source 原始对象 26 | * @param to target 27 | */ 28 | void copyTo(Options source, @MappingTarget Options to); 29 | 30 | /** 31 | * 对象转换 32 | * 33 | * @param source 原始对象 34 | * @param to target 35 | */ 36 | void copyTo(Settings source, @MappingTarget Settings to); 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/com/houkunlin/TempCode.java: -------------------------------------------------------------------------------- 1 | package com.houkunlin; 2 | 3 | import com.intellij.ide.plugins.PluginManager; 4 | import com.intellij.ide.plugins.PluginManagerCore; 5 | import com.intellij.openapi.application.PathManager; 6 | import com.intellij.openapi.extensions.PluginId; 7 | 8 | public class TempCode { 9 | public void test() { 10 | // 获得插件信息 11 | PluginManagerCore.getPlugin(PluginId.getId("com.github.houkunlin.database.generator")); 12 | // 获得所有插件列表 13 | PluginManager.getPlugins(); 14 | // 获得IDEA项目配置路径 15 | PathManager.getConfigPath(); 16 | 17 | // TODO 把 ContextUtils/ReadJsonConfig/SyncResources 整合到一个工具对象中,重构代码 18 | /* 19 | * TODO 把代码模板文件放到插件的临时文件(PathManager.getConfigPath() + "extensions/${pluginId}")路径中, 20 | * 这样可以多个项目共用同一套配置、代码模板,而不用在不同项目直接复制来保持代码模板一致 21 | */ 22 | /* 23 | * TODO 同时兼容原来的项目路径下的配置文件,优先获取这些配置 24 | */ 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/resources/templates/default/beetl/java/ServiceImpl.java.btl: -------------------------------------------------------------------------------- 1 | ${@gen.setType("serviceImpl")} 2 | package ${entity.packages.serviceImpl}; 3 | 4 | import ${entity.packages.entity.full}; 5 | import ${entity.packages.dao.full}; 6 | import ${entity.packages.service.full}; 7 | 8 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 9 | import org.springframework.cache.annotation.CacheConfig; 10 | import org.springframework.stereotype.Service; 11 | import org.springframework.transaction.annotation.Transactional; 12 | import java.util.List; 13 | import lombok.AllArgsConstructor; 14 | 15 | /** 16 | * Service:${entity.comment} 17 | * 18 | * @author ${developer.author} 19 | */ 20 | @CacheConfig(cacheNames = {${entity.name.serviceImpl}.CACHE_NAME}) 21 | @Transactional(rollbackFor = Exception.class) 22 | @Service 23 | @AllArgsConstructor 24 | public class ${entity.name.serviceImpl} extends ServiceImpl<${entity.name.dao}, ${entity.name.entity}> implements ${entity.name.service} { 25 | } 26 | -------------------------------------------------------------------------------- /src/main/resources/templates/default/velocity/java/ServiceImpl.java.vm: -------------------------------------------------------------------------------- 1 | ${gen.setType("serviceImpl")} 2 | package ${entity.packages.serviceImpl}; 3 | 4 | import ${entity.packages.entity.full}; 5 | import ${entity.packages.dao.full}; 6 | import ${entity.packages.service.full}; 7 | 8 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 9 | import org.springframework.cache.annotation.CacheConfig; 10 | import org.springframework.stereotype.Service; 11 | import org.springframework.transaction.annotation.Transactional; 12 | import java.util.List; 13 | import lombok.AllArgsConstructor; 14 | 15 | /** 16 | * Service:${entity.comment} 17 | * 18 | * @author ${developer.author} 19 | */ 20 | @CacheConfig(cacheNames = {${entity.name.serviceImpl}.CACHE_NAME}) 21 | @Transactional(rollbackFor = Exception.class) 22 | @Service 23 | @AllArgsConstructor 24 | public class ${entity.name.serviceImpl} extends ServiceImpl<${entity.name.dao}, ${entity.name.entity}> implements ${entity.name.service} { 25 | } 26 | -------------------------------------------------------------------------------- /src/main/resources/templates/default/freemarker/java/ServiceImpl.java.ftl: -------------------------------------------------------------------------------- 1 | ${gen.setType("serviceImpl")} 2 | package ${entity.packages.serviceImpl}; 3 | 4 | import ${entity.packages.entity.full}; 5 | import ${entity.packages.dao.full}; 6 | import ${entity.packages.service.full}; 7 | 8 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 9 | import org.springframework.cache.annotation.CacheConfig; 10 | import org.springframework.stereotype.Service; 11 | import org.springframework.transaction.annotation.Transactional; 12 | import java.util.List; 13 | import lombok.AllArgsConstructor; 14 | 15 | /** 16 | * Service:${entity.comment} 17 | * 18 | * @author ${developer.author} 19 | */ 20 | @CacheConfig(cacheNames = {${entity.name.serviceImpl}.CACHE_NAME}) 21 | @Transactional(rollbackFor = Exception.class) 22 | @Service 23 | @AllArgsConstructor 24 | public class ${entity.name.serviceImpl} extends ServiceImpl<${entity.name.dao}, ${entity.name.entity}> implements ${entity.name.service} { 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/template/ITemplateGenerator.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.template; 2 | 3 | import java.io.File; 4 | import java.util.Map; 5 | 6 | /** 7 | * 用于标识模板生成器的接口 8 | * 9 | * @author daiwenzh5 10 | */ 11 | public interface ITemplateGenerator { 12 | 13 | /** 14 | * 指定模板文件名,并生成文件 15 | * 16 | * @param templateName 模板文件名 17 | * @param context 上下文 18 | * @return 生成的文件内容 19 | * @throws Exception 可能出现的异常 20 | */ 21 | String generate(String templateName, Map context) throws Exception; 22 | 23 | /** 24 | * 指定模板内容,并生成文件 25 | * 26 | *

直接通过传入模板字符串渲染。

27 | * 28 | * @param templateContent 模板内容 29 | * @param context 上下文 30 | * @return 生成的文件内容 31 | * @throws Exception 可能出现的异常 32 | */ 33 | String generateInline(String templateContent, Map context) throws Exception; 34 | 35 | /** 36 | * 获取工作空间 37 | * 38 | * @return 工作空间 39 | */ 40 | File getWorkspace(); 41 | } 42 | -------------------------------------------------------------------------------- /src/main/resources/syncFiles.txt: -------------------------------------------------------------------------------- 1 | config.yml 2 | templates/all-variable.ftl 3 | templates/default/beetl/java/Entity.java.btl 4 | templates/default/beetl/java/Mapper.xml.btl 5 | templates/default/beetl/java/Repository.java.btl 6 | templates/default/beetl/java/Service.java.btl 7 | templates/default/beetl/java/ServiceImpl.java.btl 8 | templates/default/freemarker/java/Entity.java.ftl 9 | templates/default/freemarker/java/EntityForm.java.ftl 10 | templates/default/freemarker/java/EntityQuery.java.ftl 11 | templates/default/freemarker/java/EntityVo.java.ftl 12 | templates/default/freemarker/java/Mapper.xml.ftl 13 | templates/default/freemarker/java/Repository.java.ftl 14 | templates/default/freemarker/java/Service.java.ftl 15 | templates/default/freemarker/java/ServiceImpl.java.ftl 16 | templates/default/velocity/java/Entity.java.vm 17 | templates/default/velocity/java/Mapper.xml.vm 18 | templates/default/velocity/java/Repository.java.vm 19 | templates/default/velocity/java/Service.java.vm 20 | templates/default/velocity/java/ServiceImpl.java.vm 21 | templates/README.md -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/model/FileType.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.model; 2 | 3 | import lombok.*; 4 | import lombok.experimental.FieldDefaults; 5 | 6 | import static lombok.AccessLevel.PRIVATE; 7 | 8 | /** 9 | * 文件类型 10 | *

解决诸如entity、dao、service等不同代码层之间配置信息散乱的问题,使用统一的结构描述,以支持自定义类型的添加。

11 | * 12 | * @author daiwenzh5 13 | * @since 2025/4/26 14 | */ 15 | @Getter 16 | @Setter 17 | @ToString 18 | @AllArgsConstructor(staticName = "of") 19 | @FieldDefaults(level = PRIVATE) 20 | @NoArgsConstructor 21 | public class FileType { 22 | String type; 23 | String suffix; 24 | String packageName; 25 | String ext; 26 | String path; 27 | boolean override; 28 | 29 | /** 30 | * 创建文件类型 31 | * 32 | * @param type 文件类型 33 | * @param suffix 后缀 34 | * @param packageName 包名 35 | * @return 文件类型 36 | */ 37 | public static FileType of(String type, String suffix, String packageName) { 38 | return new FileType(type, suffix, packageName, null, null, true); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/resources/templates/default/beetl/java/Entity.java.btl: -------------------------------------------------------------------------------- 1 | ${@gen.setType("entity")} 2 | package ${entity.packages.entity}; 3 | 4 | import com.baomidou.mybatisplus.annotation.*; 5 | 6 | ${entity.packages} 7 | 8 | import java.io.Serializable; 9 | import lombok.AllArgsConstructor; 10 | import lombok.Builder; 11 | import lombok.Data; 12 | import lombok.NoArgsConstructor; 13 | 14 | /** 15 | * 实体类:${entity.comment}<% if(strutil.length(table.comment) > 0 && entity.comment != table.comment){%> (${table.comment})<%}%> 16 | * 17 | * @author ${developer.author} 18 | */ 19 | @Data 20 | @Builder 21 | @NoArgsConstructor 22 | @AllArgsConstructor 23 | @TableName("${table.name}") 24 | public class ${entity.name.entity} implements Serializable { 25 | <% 26 | for (field in fields) { 27 | if(field.selected){ 28 | %> 29 | /** 30 | * ${field.comment} 31 | */ 32 | <% if(field.primaryKey){%> 33 | @TableId(type = IdType.ASSIGN_ID) 34 | <%}%> 35 | private ${field.typeName} ${field.name}; 36 | <% }} %> 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/message/Bundles.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.message; 2 | 3 | import com.intellij.AbstractBundle; 4 | import org.jetbrains.annotations.Nls; 5 | import org.jetbrains.annotations.NonNls; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jetbrains.annotations.PropertyKey; 8 | 9 | /** 10 | * 国际化工具 11 | * 12 | * @author daiwenzh5 13 | * @since 2.8.4 14 | */ 15 | public class Bundles extends AbstractBundle { 16 | 17 | @NonNls 18 | private static final String BUNDLE = "i18n.language"; 19 | 20 | private static final Bundles INSTANCE = new Bundles(); 21 | 22 | private Bundles() { 23 | super(BUNDLE); 24 | } 25 | 26 | /** 27 | * 获取国际化信息 28 | * 29 | * @param key 键 30 | * @param params 参数 31 | * @return 国际化信息 32 | */ 33 | @NotNull 34 | public static @Nls String message(@NotNull @PropertyKey( 35 | resourceBundle = BUNDLE 36 | ) String key, @NotNull Object... params) { 37 | return INSTANCE.getMessage(key, params); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/ui/win/table/ComboBoxTableCellEditor.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.ui.win.table; 2 | 3 | import com.intellij.openapi.ui.ComboBox; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | import javax.swing.*; 7 | import java.awt.event.MouseEvent; 8 | import java.util.EventObject; 9 | 10 | /** 11 | * 单元格下拉输入框 12 | * 13 | * @author daiwenzh5 14 | * @since 2.8.4 15 | */ 16 | public class ComboBoxTableCellEditor extends DefaultCellEditor { 17 | 18 | @SafeVarargs 19 | public ComboBoxTableCellEditor(T... items) { 20 | super(createComboBox(items)); 21 | } 22 | 23 | private static @NotNull ComboBox createComboBox(T[] items) { 24 | var comboBox = new ComboBox<>(items); 25 | comboBox.setEditable(true); 26 | return comboBox; 27 | } 28 | 29 | @Override 30 | public boolean isCellEditable(EventObject e) { 31 | if (e instanceof MouseEvent mouseEvent) { 32 | return mouseEvent.getClickCount() >= 2; 33 | } 34 | return false; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/template/TplType.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.template; 2 | 3 | import org.apache.commons.io.FilenameUtils; 4 | 5 | import java.io.File; 6 | 7 | /** 8 | * 代码模板类型 9 | * 10 | * @author HouKunLin 11 | */ 12 | public enum TplType { 13 | /** 14 | * 未知模板 15 | */ 16 | NONE, 17 | /** 18 | * Freemarker 模板 19 | */ 20 | FREEMARKER, 21 | /** 22 | * BEETL 模板 23 | */ 24 | BEETL, 25 | /** 26 | * VELOCITY 模板 27 | */ 28 | VELOCITY, 29 | ; 30 | 31 | public static TplType create(File file) { 32 | return create(file.getName()); 33 | } 34 | 35 | public static TplType create(String file) { 36 | String extension = FilenameUtils.getExtension(file); 37 | if (extension == null) { 38 | return NONE; 39 | } 40 | return switch (extension) { 41 | case "ftl" -> FREEMARKER; 42 | case "vm" -> VELOCITY; 43 | case "btl" -> BEETL; 44 | default -> NONE; 45 | }; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/ui/win/TableSetting.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/config/Options.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.config; 2 | 3 | import com.google.common.base.CaseFormat; 4 | import lombok.Data; 5 | 6 | /** 7 | * 构建参数 8 | * 9 | * @author HouKunLin 10 | */ 11 | @Data 12 | public class Options { 13 | /** 14 | * 数据库字段风格类型 15 | */ 16 | private int dbFieldStyleType = 0; 17 | 18 | /** 19 | * 记住上次选择的模板 20 | */ 21 | private boolean retainLastSelectionTemplates = true; 22 | 23 | public CaseFormat obtainCaseFormat() { 24 | if (dbFieldStyleType == 0) { 25 | return CaseFormat.LOWER_UNDERSCORE; 26 | } else if (dbFieldStyleType == 1) { 27 | return CaseFormat.UPPER_UNDERSCORE; 28 | } else if (dbFieldStyleType == 2) { 29 | return CaseFormat.LOWER_CAMEL; 30 | } else if (dbFieldStyleType == 3) { 31 | return CaseFormat.UPPER_CAMEL; 32 | // } else if (dbFieldStyleType == 4) { 33 | // return CaseFormat.LOWER_HYPHEN; 34 | } else { 35 | return CaseFormat.LOWER_UNDERSCORE; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/ui/win/tree/CheckBoxTreeLabel.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.ui.win.tree; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | import javax.swing.*; 7 | import javax.swing.plaf.ColorUIResource; 8 | import java.awt.*; 9 | 10 | /** 11 | * 复选框的树形节点展示label 12 | * 13 | * @author HouKunLin 14 | */ 15 | public class CheckBoxTreeLabel extends JLabel { 16 | @Setter 17 | @Getter 18 | private boolean selected; 19 | @Setter 20 | @Getter 21 | private boolean hasFocus; 22 | 23 | public CheckBoxTreeLabel() { 24 | } 25 | 26 | @Override 27 | public void setBackground(Color color) { 28 | if (color instanceof ColorUIResource) 29 | color = null; 30 | super.setBackground(color); 31 | } 32 | 33 | @Override 34 | public Dimension getPreferredSize() { 35 | Dimension retDimension = super.getPreferredSize(); 36 | if (retDimension != null) { 37 | retDimension = new Dimension(retDimension.width + 3, retDimension.height); 38 | } 39 | return retDimension; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /doc/plugin/changeNotes.md: -------------------------------------------------------------------------------- 1 | - **2025-05-23: 2.9.0** 2 | - ZH-CN 3 | - feat: 新增自定义文件类型支持 [@daiwenzh5](https://github.com/daiwenzh5) 4 | - 删除了一些 settings 的变量用法,仅保留 settings.projectPath/settings.javaPath/settings.resourcesPath 5 | 6 | - **2025-04-18: 2.8.3** 7 | - ZH-CN 8 | - fix: 配置面板“项目路径”会丢失和没有自动刷新的问题 [@daiwenzh5](https://github.com/daiwenzh5) 9 | - feat: “记住选择模板”选项 [@daiwenzh5](https://github.com/daiwenzh5) 10 | - feat: 在模板中支持调用groovy脚本定义的方法 [@daiwenzh5](https://github.com/daiwenzh5) 11 | 12 | - **2024-08-19: 2.8.2** 13 | 14 | - 升级 beetl 到 3.17.0.RELEASE 15 | - 移除 hutool-core 依赖,使用 mapstruct 来转换对象 16 | - 修改 StringUtils 工具类过时用法 17 | - 升级 JDK 17 / Gradle 8.10 18 | 19 | - **2023-08-15: 2.8.1** 20 | 21 | - 升级 beetl 到 3.15.8.RELEASE 22 | - 升级 hutool-core 到 5.8.21 23 | - 升级 snakeyaml 到 2.1 24 | - 重构构建脚本 25 | - 增加一个选项来配置数据库的字段风格 26 | 27 | 28 | Full Log | 完整日志 29 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/ui/win/tree/CheckBoxTreeNodeSelectionListener.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.ui.win.tree; 2 | 3 | import javax.swing.*; 4 | import javax.swing.tree.DefaultTreeModel; 5 | import javax.swing.tree.TreePath; 6 | import java.awt.event.MouseAdapter; 7 | import java.awt.event.MouseEvent; 8 | 9 | /** 10 | * 复选框树形节点鼠标点击监听事件 11 | * 12 | * @author HouKunLin 13 | */ 14 | public class CheckBoxTreeNodeSelectionListener extends MouseAdapter { 15 | @Override 16 | public void mouseClicked(MouseEvent event) { 17 | JTree tree = (JTree) event.getSource(); 18 | int row = tree.getRowForLocation(event.getX(), event.getY()); 19 | TreePath path = tree.getPathForRow(row); 20 | if (path != null) { 21 | CheckBoxTreeNode node = (CheckBoxTreeNode) path.getLastPathComponent(); 22 | if (node != null) { 23 | boolean isSelected = !node.isSelected(); 24 | node.setSelected(isSelected); 25 | ((DefaultTreeModel) tree.getModel()).nodeStructureChanged(node); 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/vo/ITypeMap.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.vo; 2 | 3 | import com.github.houkunlin.config.Settings; 4 | import com.github.houkunlin.model.FileType; 5 | 6 | import java.util.Map; 7 | 8 | /** 9 | * 基于类型的映射接口 10 | * 11 | * @author daiwenzh5 12 | * @since 1.0 13 | */ 14 | public interface ITypeMap { 15 | 16 | /** 17 | * 根据类型获取映射对象 18 | * 19 | * @param type 类型 20 | * @return 结果 21 | */ 22 | T get(String type); 23 | 24 | /** 25 | * 根据设置信息初始化 26 | * 27 | * @param settings 设置信息 28 | */ 29 | default void initMore(Settings settings, V value) { 30 | var map = getMap(); 31 | map.clear(); 32 | settings.getFileTypes() 33 | .forEach(item -> map.put(item.getType(), this.mapping(item, value))); 34 | } 35 | 36 | /** 37 | * 映射 38 | * 39 | * @param fileType 文件类型 40 | * @return 结果 41 | */ 42 | T mapping(FileType fileType, V value); 43 | 44 | /** 45 | * 获取映射集 46 | * 47 | * @return 映射集 48 | */ 49 | Map getMap(); 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/vo/ITableColumn.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.vo; 2 | 3 | import com.intellij.database.model.DataType; 4 | 5 | /** 6 | * 数据库表字段信息(数据库表列对象信息) 7 | * 8 | * @author HouKunLin 9 | */ 10 | public interface ITableColumn { 11 | /** 12 | * 获得字段名称 13 | * 14 | * @return 字段名称 15 | */ 16 | String getName(); 17 | 18 | /** 19 | * 获得字段注释内容 20 | * 21 | * @return 注释内容 22 | */ 23 | String getComment(); 24 | 25 | /** 26 | * 获得字段类型名称(短名称) 27 | * 28 | * @return 字段类型名称(短名称) 29 | */ 30 | String getTypeName(); 31 | 32 | /** 33 | * 获得数据库的字段数据类型(IDEA内置对象) 34 | * 35 | * @return 字段类型信息 36 | */ 37 | DataType getDataType(); 38 | 39 | /** 40 | * 获得字段类型名称(长名称、完整名称) 41 | * 42 | * @return 字段类型名称(长名称、完整名称) 43 | */ 44 | String getFullTypeName(); 45 | 46 | /** 47 | * 获取是否是主键字段 48 | * 49 | * @return 是否是主键 50 | */ 51 | boolean isPrimaryKey(); 52 | 53 | /** 54 | * 获取是否选中该字段 55 | * 56 | * @return 是否选中该字段 57 | */ 58 | boolean isSelected(); 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/vo/IEntityField.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.vo; 2 | 3 | import com.intellij.database.model.DataType; 4 | 5 | /** 6 | * 实体类字段信息 7 | * 8 | * @author HouKunLin 9 | */ 10 | public interface IEntityField { 11 | /** 12 | * 获得字段名称(变量名称) 13 | * 14 | * @return 字段名称(驼峰形式,首字母小写) 15 | */ 16 | IName getName(); 17 | 18 | /** 19 | * 获得字段注释内容 20 | * 21 | * @return 注释内容 22 | */ 23 | String getComment(); 24 | 25 | /** 26 | * 获得字段类型名称(短名称) 27 | * 28 | * @return 字段类型名称(短名称) 29 | */ 30 | String getTypeName(); 31 | 32 | /** 33 | * 获得数据库的字段数据类型(IDEA内置对象) 34 | * 35 | * @return 字段类型信息 36 | */ 37 | DataType getDataType(); 38 | 39 | /** 40 | * 获得字段类型名称(长名称、完整名称) 41 | * 42 | * @return 字段类型名称(长名称、完整名称) 43 | */ 44 | String getFullTypeName(); 45 | 46 | /** 47 | * 获取是否是主键字段 48 | * 49 | * @return 是否是主键 50 | */ 51 | boolean isPrimaryKey(); 52 | 53 | /** 54 | * 获取是否选中该字段 55 | * 56 | * @return 是否选中该字段 57 | */ 58 | boolean isSelected(); 59 | } 60 | -------------------------------------------------------------------------------- /src/main/resources/templates/default/velocity/java/Entity.java.vm: -------------------------------------------------------------------------------- 1 | ${gen.setType("entity")} 2 | package ${entity.packages.entity}; 3 | 4 | import com.baomidou.mybatisplus.annotation.*; 5 | 6 | ${entity.packages} 7 | 8 | import java.io.Serializable; 9 | import lombok.AllArgsConstructor; 10 | import lombok.Builder; 11 | import lombok.Data; 12 | import lombok.NoArgsConstructor; 13 | 14 | /** 15 | * 实体类:${entity.comment}#if($table.comment.trim.length gt 0 && $entity.comment != $table.comment) (${table.comment})#end 16 | * 17 | * @author ${developer.author} 18 | */ 19 | @Data 20 | @Builder 21 | @NoArgsConstructor 22 | @AllArgsConstructor 23 | @TableName("${table.name}") 24 | public class ${entity.name.entity} implements Serializable{ 25 | #foreach($field in $fields) 26 | #if($field.selected) 27 | /** 28 | * ${field.comment} 29 | #if($field.column.comment.trim.length gt 0 && $field.comment != $field.column.comment) *

数据库字段说明:${field.column.comment}

#end 30 | */ 31 | #if ($field.primaryKey) 32 | @TableId(type = IdType.ASSIGN_ID) 33 | #end 34 | private ${field.typeName} ${field.name}; 35 | #end 36 | #end 37 | } 38 | -------------------------------------------------------------------------------- /src/main/resources/templates/default/freemarker/java/EntityVo.java.ftl: -------------------------------------------------------------------------------- 1 | ${gen.setFilename("${entity.name}Vo.java")} 2 | ${gen.setFilepath("${settings.javaPath}/${entity.packages.entity}/")} 3 | package ${entity.packages.entity}; 4 | 5 | ${entity.packages} 6 | 7 | import io.swagger.annotations.ApiModel; 8 | import io.swagger.annotations.ApiModelProperty; 9 | import lombok.AllArgsConstructor; 10 | import lombok.Builder; 11 | import lombok.Data; 12 | import lombok.NoArgsConstructor; 13 | import java.io.Serializable; 14 | 15 | /** 16 | * 视图对象:${entity.comment}<#if table.comment?trim?length gt 0 && entity.comment != table.comment> (${table.comment}) 17 | * 18 | * @author ${developer.author} 19 | */ 20 | @ApiModel("视图对象:${entity.comment}") 21 | @Data 22 | @Builder 23 | @NoArgsConstructor 24 | @AllArgsConstructor 25 | public class ${entity.name}Vo implements Serializable { 26 | <#list fields as field> 27 | <#if field.selected> 28 | /** 29 | * ${field.comment} 30 | <#if field.column.comment?trim?length gt 0 && field.comment != field.column.comment> *

数据库字段说明:${field.column.comment}

31 | */ 32 | @ApiModelProperty("${field.comment}") 33 | private ${field.typeName} ${field.name}; 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | Database Generator 3 | 侯坤林 4 | 5 | 7 | com.intellij.modules.lang 8 | com.intellij.modules.platform 9 | com.intellij.modules.java 10 | com.intellij.database 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/ui/win/SelectTemplate.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/vo/impl/BaseTypeMap.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.vo.impl; 2 | 3 | import com.github.houkunlin.config.Settings; 4 | import com.github.houkunlin.model.FileType; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import java.util.function.Function; 9 | 10 | /** 11 | * 一个基础的类型映射,子类必须显示调用{@link #init(Settings, Function)} 进行初始化。 12 | * 13 | * @author daiwenzh5 14 | */ 15 | public abstract class BaseTypeMap { 16 | 17 | private final Map map = new HashMap<>(); 18 | 19 | private boolean initialized = false; 20 | 21 | /** 22 | * 根据类型获取值 23 | * 24 | * @param type 类型 25 | * @return 映射值 26 | */ 27 | public final T get(String type) { 28 | if (!initialized) { 29 | throw new IllegalStateException("请先调用 init() 方法初始化"); 30 | } 31 | return map.get(type); 32 | } 33 | 34 | /** 35 | * 初始化类型映射 36 | * 37 | * @param settings 设置信息 38 | * @param mapping 映射器 39 | */ 40 | protected void init(Settings settings, Function mapping) { 41 | map.clear(); 42 | settings.getFileTypes() 43 | .forEach(item -> map.put(item.getType(), mapping.apply(item))); 44 | this.initialized = true; 45 | } 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/vo/impl/FieldNameInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.vo.impl; 2 | 3 | import com.github.houkunlin.config.Options; 4 | import com.github.houkunlin.vo.IName; 5 | import com.google.common.base.CaseFormat; 6 | import com.intellij.database.model.DasColumn; 7 | import lombok.Getter; 8 | 9 | /** 10 | * Java字段名称对象 11 | * 12 | * @author HouKunLin 13 | */ 14 | @Getter 15 | public class FieldNameInfo implements IName { 16 | private final String value; 17 | private final String firstUpper; 18 | private final String firstLower; 19 | 20 | /** 21 | * @param firstLower 首字母小写 22 | */ 23 | public FieldNameInfo(String firstLower) { 24 | this.value = firstLower; 25 | this.firstUpper = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, value); 26 | this.firstLower = firstLower; 27 | } 28 | 29 | public FieldNameInfo(DasColumn dbColumn, Options options) { 30 | // 把减号替换成下划线 31 | this.value = options.obtainCaseFormat().to(CaseFormat.LOWER_CAMEL, dbColumn.getName().replaceAll("-", "_")); 32 | this.firstUpper = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, value); 33 | this.firstLower = value; 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return value; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/template/beetl/BeetlErrorHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.template.beetl; 2 | 3 | import org.beetl.core.ConsoleErrorHandler; 4 | import org.beetl.core.GroupTemplate; 5 | import org.beetl.core.exception.BeetlException; 6 | import org.beetl.core.io.NoLockStringWriter; 7 | 8 | import java.io.IOException; 9 | import java.io.Writer; 10 | 11 | /** 12 | * Beetl 模板的异常处理器 13 | * 14 | * @author HouKunLin 15 | */ 16 | public class BeetlErrorHandler extends ConsoleErrorHandler { 17 | @Override 18 | public void processException(BeetlException ex, GroupTemplate groupTemplate, Writer writer) { 19 | Writer w = new NoLockStringWriter(); 20 | super.processException(ex, groupTemplate, w); 21 | throw new RuntimeException(w.toString(), ex); 22 | } 23 | 24 | @Override 25 | protected void println(Writer w, String msg) { 26 | append(w, msg, "\n"); 27 | super.println(w, msg); 28 | } 29 | 30 | @Override 31 | protected void print(Writer w, String msg) { 32 | append(w, msg); 33 | super.print(w, msg); 34 | } 35 | 36 | private void append(Writer w, String... strings) { 37 | try { 38 | for (String string : strings) { 39 | w.append(string); 40 | } 41 | } catch (IOException ignored) { 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/vo/impl/EntityPackage.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.vo.impl; 2 | 3 | import com.github.houkunlin.config.Settings; 4 | import lombok.Getter; 5 | 6 | import java.util.HashSet; 7 | import java.util.stream.Collectors; 8 | 9 | /** 10 | * 实体类对象的包信息 11 | * 12 | * @author HouKunLin 13 | */ 14 | @Getter 15 | public class EntityPackage extends BaseTypeMap { 16 | /** 17 | * 实体类字段所需要导入的包列表 18 | */ 19 | private final HashSet list = new HashSet<>(); 20 | private String toString = ""; 21 | 22 | public void add(String fullPackageName) { 23 | if (fullPackageName.startsWith("java.lang.")) { 24 | return; 25 | } 26 | list.add(fullPackageName); 27 | } 28 | 29 | 30 | public void initMore(Settings settings, EntityName entityName) { 31 | init(settings, item -> new EntityPackageInfo(item.getPackageName(), entityName.get(item.getType()))); 32 | } 33 | 34 | 35 | public void clear() { 36 | list.clear(); 37 | toString = ""; 38 | } 39 | 40 | @Override 41 | public String toString() { 42 | if (toString == null || toString.isBlank()) { 43 | toString = list.stream().map(item -> String.format("import %s;\n", item)).collect(Collectors.joining()); 44 | } 45 | return toString; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/resources/templates/default/freemarker/java/EntityQuery.java.ftl: -------------------------------------------------------------------------------- 1 | ${gen.setFilename("${entity.name}Query.java")} 2 | ${gen.setFilepath("${settings.javaPath}/${entity.packages.entity}/")} 3 | package ${entity.packages.entity}; 4 | 5 | ${entity.packages} 6 | 7 | import io.swagger.annotations.ApiModel; 8 | import io.swagger.annotations.ApiModelProperty; 9 | import lombok.AllArgsConstructor; 10 | import lombok.Builder; 11 | import lombok.Data; 12 | import lombok.NoArgsConstructor; 13 | 14 | /** 15 | * 查询对象:${entity.comment}<#if table.comment?trim?length gt 0 && entity.comment != table.comment> (${table.comment}) 16 | * 17 | * @author ${developer.author} 18 | */ 19 | @ApiModel("查询对象:${entity.comment}") 20 | @Data 21 | @Builder 22 | @NoArgsConstructor 23 | @AllArgsConstructor 24 | public class ${entity.name}Query { 25 | <#list fields as field> 26 | <#if field.selected> 27 | <#if field.name?starts_with("created") || field.name?starts_with("updated") || field.name?starts_with("deleted") > 28 | <#else> 29 | /** 30 | * ${field.comment} 31 | <#if field.column.comment?trim?length gt 0 && field.comment != field.column.comment> *

数据库字段说明:${field.column.comment}

32 | */ 33 | @ApiModelProperty("${field.comment}") 34 | private ${field.typeName} ${field.name}; 35 | 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/resources/templates/default/freemarker/java/Entity.java.ftl: -------------------------------------------------------------------------------- 1 | ${gen.setType("entity")} 2 | package ${entity.packages.entity}; 3 | 4 | import com.baomidou.mybatisplus.annotation.*; 5 | 6 | ${entity.packages} 7 | 8 | import java.io.Serializable; 9 | import lombok.AllArgsConstructor; 10 | import lombok.Builder; 11 | import lombok.Data; 12 | import lombok.NoArgsConstructor; 13 | 14 | /** 15 | * 实体类:${entity.comment}<#if table.comment?trim?length gt 0 && entity.comment != table.comment> (${table.comment}) 16 | * 17 | * @author ${developer.author} 18 | */ 19 | @Data 20 | @Builder 21 | @NoArgsConstructor 22 | @AllArgsConstructor 23 | @TableName("${table.name}") 24 | public class ${entity.name.entity} implements Serializable { 25 | <#list fields as field> 26 | <#if field.selected> 27 | /** 28 | * ${field.comment} 29 | <#if field.column.comment?trim?length gt 0 && field.comment != field.column.comment> *

数据库字段说明:${field.column.comment}

30 | */ 31 | <#if field.primaryKey> 32 | @TableId(type = IdType.ASSIGN_ID) 33 | 34 | <#if field.name?starts_with("created") || field.name?starts_with("updated") || field.name?starts_with("deleted")> 35 | @TableField(insertStrategy = FieldStrategy.NEVER, updateStrategy = FieldStrategy.NEVER) 36 | 37 | private ${field.typeName} ${field.name}; 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/ui/win/TextFieldDocumentUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.ui.win; 2 | 3 | import com.intellij.openapi.editor.Document; 4 | import com.intellij.ui.EditorTextField; 5 | 6 | import javax.swing.text.JTextComponent; 7 | import java.util.function.Consumer; 8 | 9 | /** 10 | * 输入框修改内容工具 11 | * 12 | * @author HouKunLin 13 | */ 14 | public class TextFieldDocumentUtil { 15 | 16 | /** 17 | * 更新配置信息的值 18 | * 19 | * @param document 文档 20 | * @param component 输入框组件 21 | * @param setValue 设置配置信息的set方法 22 | * @return 是否成功 23 | */ 24 | public static boolean updateSettingValue(Document document, EditorTextField component, Consumer setValue) { 25 | if (document == component.getDocument()) { 26 | setValue.accept(component.getText()); 27 | return true; 28 | } 29 | return false; 30 | } 31 | 32 | /** 33 | * 更新配置信息的值 34 | * 35 | * @param document 文档 36 | * @param component 输入框组件 37 | * @param setValue 设置配置信息的set方法 38 | * @return 是否成功 39 | */ 40 | public static boolean updateSettingValue(javax.swing.text.Document document, JTextComponent component, Consumer setValue) { 41 | if (document == component.getDocument()) { 42 | setValue.accept(component.getText()); 43 | return true; 44 | } 45 | return false; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/vo/impl/EntityImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.vo.impl; 2 | 3 | import com.github.houkunlin.config.Options; 4 | import com.github.houkunlin.config.Settings; 5 | import com.github.houkunlin.vo.IEntity; 6 | import com.intellij.database.psi.DbTable; 7 | import lombok.Getter; 8 | import lombok.Setter; 9 | 10 | import java.util.Objects; 11 | import java.util.Set; 12 | 13 | /** 14 | * 实体类信息 15 | * 16 | * @author HouKunLin 17 | */ 18 | @Getter 19 | public class EntityImpl implements IEntity { 20 | private final EntityPackage packages = new EntityPackage(); 21 | private EntityName name; 22 | @Setter 23 | private String comment; 24 | @Setter 25 | private String uri; 26 | 27 | public EntityImpl(DbTable dbTable, Options options) { 28 | this.comment = Objects.toString(dbTable.getComment(), ""); 29 | this.name = new EntityName(dbTable, options); 30 | } 31 | 32 | public void setName(String name) { 33 | this.name = new EntityName(name); 34 | } 35 | 36 | /** 37 | * 初始化更多的信息 38 | * 39 | * @param fullTypeNames 字段类型名称列表 40 | * @param settings 设置信息对象(用来初始化包名信息) 41 | */ 42 | public void initMore(Set fullTypeNames, Settings settings) { 43 | packages.clear(); 44 | fullTypeNames.forEach(packages::add); 45 | name.initMore(settings); 46 | packages.initMore(settings, name); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/vo/Variable.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.vo; 2 | 3 | import lombok.Getter; 4 | 5 | /** 6 | * 模板传递给程序的变量信息 7 | * 8 | * @author HouKunLin 9 | */ 10 | public class Variable { 11 | @Getter 12 | public static String filename; 13 | @Getter 14 | public static String filepath; 15 | @Getter 16 | public static String type; 17 | 18 | private static Variable instance; 19 | 20 | private Variable() { 21 | } 22 | 23 | public synchronized static Variable getInstance() { 24 | if (instance == null) { 25 | instance = new Variable(); 26 | } 27 | resetVariables(); 28 | return instance; 29 | } 30 | 31 | public static void resetVariables() { 32 | filename = null; 33 | filepath = null; 34 | type = null; 35 | } 36 | 37 | public static void setFilename(String filename) { 38 | Variable.filename = filename; 39 | } 40 | 41 | public static void setFilepath(String filepath) { 42 | Variable.filepath = filepath; 43 | } 44 | 45 | public static void setType(String type) { 46 | Variable.type = type; 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | return "Variable{" + 52 | "filename='" + filename + '\'' + 53 | ", filepath='" + filepath + '\'' + 54 | ", type='" + type + '\'' + 55 | '}'; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Java template 3 | # Compiled class file 4 | *.class 5 | 6 | # Log file 7 | *.log 8 | 9 | # BlueJ files 10 | *.ctxt 11 | 12 | # Mobile Tools for Java (J2ME) 13 | .mtj.tmp/ 14 | 15 | # Package Files # 16 | *.jar 17 | *.war 18 | *.nar 19 | *.ear 20 | *.zip 21 | *.tar.gz 22 | *.rar 23 | 24 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 25 | hs_err_pid* 26 | 27 | ### JetBrains template 28 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 29 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 30 | 31 | # User-specific stuff 32 | .idea/ 33 | *.iml 34 | 35 | # CMake 36 | cmake-build-*/ 37 | 38 | # File-based project format 39 | *.iws 40 | 41 | # IntelliJ 42 | out/ 43 | 44 | # mpeltonen/sbt-idea plugin 45 | .idea_modules/ 46 | 47 | # JIRA plugin 48 | atlassian-ide-plugin.xml 49 | 50 | # Crashlytics plugin (for Android Studio and IntelliJ) 51 | com_crashlytics_export_strings.xml 52 | crashlytics.properties 53 | crashlytics-build.properties 54 | fabric.properties 55 | 56 | ### Gradle template 57 | .gradle 58 | gradle.properties 59 | /build/ 60 | 61 | # Ignore Gradle GUI config 62 | gradle-app.setting 63 | 64 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 65 | !gradle-wrapper.jar 66 | 67 | # Cache of project 68 | .gradletasknamecache 69 | 70 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 71 | # gradle/wrapper/gradle-wrapper.properties 72 | 73 | idea-sandbox/ 74 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/vo/impl/EntityName.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.vo.impl; 2 | 3 | import com.github.houkunlin.config.Options; 4 | import com.github.houkunlin.config.Settings; 5 | import com.github.houkunlin.vo.IName; 6 | import com.google.common.base.CaseFormat; 7 | import com.intellij.database.psi.DbTable; 8 | import lombok.Getter; 9 | 10 | /** 11 | * 实体类名称对象。提供方便直接获取 Entity、Service、ServiceImpl、Dao、Controller 的对象完整名称 12 | * 13 | * @author HouKunLin 14 | */ 15 | @Getter 16 | public class EntityName extends BaseTypeMap implements IName { 17 | private final String value; 18 | private final String firstUpper; 19 | private final String firstLower; 20 | 21 | public EntityName(DbTable dbTable, Options options) { 22 | // 把减号替换成下划线 23 | this.value = options.obtainCaseFormat().to(CaseFormat.UPPER_CAMEL, dbTable.getName().replaceAll("-", "_")); 24 | this.firstUpper = value; 25 | this.firstLower = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, value); 26 | } 27 | 28 | public EntityName(String name) { 29 | this.value = name; 30 | this.firstUpper = value; 31 | this.firstLower = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, value); 32 | } 33 | 34 | @Override 35 | public String toString() { 36 | return value; 37 | } 38 | 39 | public void initMore(Settings settings) { 40 | init(settings, item -> new EntityNameInfo(value, item.getSuffix())); 41 | } 42 | 43 | private IName build(String suffix) { 44 | return new EntityNameInfo(value, suffix); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/ui/win/table/ColumnSpec.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.ui.win.table; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | 7 | import javax.swing.table.TableCellEditor; 8 | import java.util.function.BiConsumer; 9 | import java.util.function.Function; 10 | 11 | /** 12 | * 通用的列信息 13 | * 14 | * @author daiwenzh5 15 | * @since 2.8.4 16 | */ 17 | @Getter 18 | @AllArgsConstructor(access = AccessLevel.PRIVATE) 19 | public class ColumnSpec { 20 | private final String name; 21 | private final Class type; 22 | private final Function getter; 23 | private final BiConsumer setter; 24 | private boolean editable; 25 | private TableCellEditor cellEditor; 26 | private String placeholder; 27 | private int width; 28 | 29 | 30 | public static ColumnSpec of(String name, Class type, Function getter, BiConsumer setter) { 31 | return new ColumnSpec<>(name, type, getter, setter, true, null, null, 0); 32 | } 33 | 34 | public ColumnSpec withEditable(boolean editable) { 35 | this.editable = editable; 36 | return this; 37 | } 38 | 39 | public ColumnSpec withCellEditor(TableCellEditor cellEditor) { 40 | this.cellEditor = cellEditor; 41 | return this; 42 | } 43 | 44 | public ColumnSpec withPlaceholder(String placeholder) { 45 | this.placeholder = placeholder; 46 | return this; 47 | } 48 | 49 | public ColumnSpec withWidth(int width) { 50 | this.width = width; 51 | return this; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/vo/impl/RootModel.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.vo.impl; 2 | 3 | import com.github.houkunlin.config.Options; 4 | import com.github.houkunlin.config.Settings; 5 | import com.github.houkunlin.vo.IEntityField; 6 | import com.github.houkunlin.vo.ITable; 7 | import com.github.houkunlin.vo.ITableColumn; 8 | import com.intellij.database.psi.DbTable; 9 | import lombok.Getter; 10 | 11 | import java.util.List; 12 | import java.util.Set; 13 | import java.util.stream.Collectors; 14 | 15 | /** 16 | * 完整的类型信息 17 | * 18 | * @author HouKunLin 19 | */ 20 | @Getter 21 | public class RootModel { 22 | /** 23 | * 实体对象信息 24 | */ 25 | private final EntityImpl entity; 26 | /** 27 | * 实体对象字段列表 28 | */ 29 | private final List fields; 30 | /** 31 | * 数据库表信息 32 | */ 33 | private final ITable table; 34 | /** 35 | * 数据库表字段列表 36 | */ 37 | private final List columns; 38 | 39 | private final PrimaryInfo primary; 40 | 41 | public RootModel(DbTable dbTable, List fields, List columns, Options options) { 42 | this.table = new TableImpl(dbTable); 43 | this.entity = new EntityImpl(dbTable, options); 44 | this.fields = fields; 45 | this.columns = columns; 46 | this.primary = new PrimaryInfo(fields); 47 | } 48 | 49 | public EntityImpl getEntity(Settings settings) { 50 | Set fullTypeNames = fields.stream().map(IEntityField::getFullTypeName).collect(Collectors.toSet()); 51 | entity.initMore(fullTypeNames, settings); 52 | return entity; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/model/TableColumnType.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.model; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.List; 6 | import java.util.regex.Matcher; 7 | import java.util.regex.Pattern; 8 | 9 | /** 10 | * 字段类型数据 11 | * 12 | * @author HouKunLin 13 | */ 14 | @Data 15 | public class TableColumnType { 16 | public static final TableColumnType DEFAULT = new TableColumnType(true); 17 | /** 18 | * 数据库对应的类型 19 | */ 20 | private List dbTypes; 21 | /** 22 | * 短名称 23 | */ 24 | private String shortName; 25 | /** 26 | * 长名称 27 | */ 28 | private String longName; 29 | /** 30 | * 是否是默认的类型 31 | */ 32 | private Boolean isDefault = false; 33 | 34 | public TableColumnType() { 35 | } 36 | 37 | private TableColumnType(boolean isDefault) { 38 | this.isDefault = isDefault; 39 | if (isDefault) { 40 | this.shortName = "Object"; 41 | this.longName = "java.lang.Object"; 42 | } 43 | } 44 | 45 | /** 46 | * 判断一个数据库类型是否在这个对象里面 47 | * 48 | * @param dbType 数据库字段类 49 | * @return 判断结果 50 | */ 51 | public boolean at(String dbType) { 52 | if (dbTypes == null) { 53 | return false; 54 | } 55 | if (dbTypes.contains(dbType)) { 56 | return true; 57 | } 58 | for (String type : dbTypes) { 59 | Matcher matcher = Pattern.compile(type).matcher(dbType); 60 | if (matcher.find()) { 61 | return true; 62 | } 63 | } 64 | return false; 65 | } 66 | 67 | public boolean isDefault() { 68 | return isDefault != null && isDefault; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/vo/impl/PrimaryInfo.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.vo.impl; 2 | 3 | import com.github.houkunlin.vo.IEntityField; 4 | import com.github.houkunlin.vo.ITableColumn; 5 | import lombok.Getter; 6 | 7 | import java.util.List; 8 | import java.util.stream.Collectors; 9 | 10 | /** 11 | * 主键信息 12 | * 13 | * @author HouKunLin 14 | */ 15 | @Getter 16 | public class PrimaryInfo { 17 | /** 18 | * 主键的Java字段对象 19 | */ 20 | private final IEntityField field; 21 | 22 | /** 23 | * 主键的数据库字段对象 24 | */ 25 | private final ITableColumn column; 26 | 27 | /** 28 | * 实体对象字段列表(主键列表) 29 | */ 30 | private final List fields; 31 | 32 | /** 33 | * 数据库表字段列表(主键列表) 34 | */ 35 | private final List columns; 36 | 37 | public PrimaryInfo(List fields) { 38 | List collect = fields.stream().filter(EntityFieldImpl::isPrimaryKey).collect(Collectors.toList()); 39 | EntityFieldImpl primaryField; 40 | if (collect.isEmpty()) { 41 | // 没有主键对象,创建一个默认的主键对象 42 | primaryField = EntityFieldImpl.primaryField("id", "String", "java.lang.String", "主键ID"); 43 | TableColumnImpl tableColumn = TableColumnImpl.primaryColumn("id", "varchar", "varchar(255)", "主键ID"); 44 | primaryField.setColumn(tableColumn); 45 | tableColumn.setField(primaryField); 46 | } else { 47 | primaryField = collect.get(0); 48 | } 49 | this.field = primaryField; 50 | this.column = primaryField.getColumn(); 51 | this.fields = collect; 52 | this.columns = collect.stream().map(EntityFieldImpl::getColumn).collect(Collectors.toList()); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/template/freemarker/FreemarkerTemplateGenerator.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.template.freemarker; 2 | 3 | import com.github.houkunlin.template.AbstractScriptedTemplateGenerator; 4 | import freemarker.template.Configuration; 5 | import freemarker.template.Template; 6 | 7 | import java.io.File; 8 | import java.io.IOException; 9 | import java.io.StringReader; 10 | import java.io.StringWriter; 11 | import java.util.Map; 12 | 13 | /** 14 | * @author daiwenzh5 15 | * @since 1.0 16 | */ 17 | public class FreemarkerTemplateGenerator extends AbstractScriptedTemplateGenerator { 18 | 19 | private final Configuration configuration; 20 | 21 | public FreemarkerTemplateGenerator(File workspace) throws IOException { 22 | super(workspace); 23 | // 把freemarker的jar包添加到工程中 24 | // 创建一个Configuration对象 25 | configuration = new Configuration(Configuration.VERSION_2_3_30); 26 | // 设置config的默认字符集。一般是utf-8 27 | configuration.setDefaultEncoding("utf-8"); 28 | configuration.setDirectoryForTemplateLoading(getTemplateDir()); 29 | } 30 | 31 | @Override 32 | protected String doGenerate(String templateName, Map context) throws Exception { 33 | var template = configuration.getTemplate(templateName); 34 | var out = new StringWriter(); 35 | template.process(context, out); 36 | return out.toString(); 37 | } 38 | 39 | @Override 40 | protected String doGenerateInline(String templateContent, Map context) throws Exception { 41 | var template = new Template(null, new StringReader(templateContent), configuration); 42 | var out = new StringWriter(); 43 | template.process(context, out); 44 | return out.toString(); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/ui/win/table/TableDecorator.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.ui.win.table; 2 | 3 | import com.github.houkunlin.message.Bundles; 4 | import com.intellij.ui.ToolbarDecorator; 5 | import com.intellij.ui.table.JBTable; 6 | import lombok.Getter; 7 | 8 | import javax.swing.*; 9 | import java.util.List; 10 | import java.util.Objects; 11 | import java.util.function.BiConsumer; 12 | 13 | /** 14 | * 通用的表格装饰器 15 | * 16 | * @author daiwenzh5 17 | * @since 2.8.4 18 | */ 19 | @SuppressWarnings("unchecked") 20 | public class TableDecorator> { 21 | 22 | protected final JBTable table = new JBTable(); 23 | 24 | @Getter 25 | private GenericTableModel model; 26 | 27 | public final T setModel(GenericTableModel model) { 28 | Objects.requireNonNull(model, Bundles.message("table-decorator.not-null")); 29 | this.model = model.bindTable(table); 30 | return getSelf(); 31 | } 32 | 33 | /** 34 | * 添加工具类 35 | * 36 | * @param configuration 配置 37 | * @return 带工具栏的表格面板 38 | */ 39 | public final JPanel applyToolbar(BiConsumer configuration) { 40 | var decorator = ToolbarDecorator.createDecorator(table); 41 | configuration.accept(getSelf(), decorator); 42 | return decorator.createPanel(); 43 | } 44 | 45 | /** 46 | * 重置表格数据 47 | */ 48 | public void reset() { 49 | this.model.clear(); 50 | } 51 | 52 | /** 53 | * 重置表格数据,并添加数据 54 | * 55 | * @param data 表格数据 56 | */ 57 | public void reset(List data) { 58 | this.model 59 | .clear(false) 60 | .addRows(data); 61 | } 62 | 63 | private T getSelf() { 64 | return (T) this; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/template/velocity/VelocityTemplateGenerator.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.template.velocity; 2 | 3 | import com.github.houkunlin.template.AbstractScriptedTemplateGenerator; 4 | import com.github.houkunlin.util.IO; 5 | import org.apache.velocity.VelocityContext; 6 | import org.apache.velocity.app.VelocityEngine; 7 | 8 | import java.io.File; 9 | import java.io.IOException; 10 | import java.io.StringWriter; 11 | import java.util.Map; 12 | import java.util.Properties; 13 | 14 | /** 15 | * 基于velocity的模板生成器 16 | * 17 | * @author daiwenzh5 18 | * @since 1.0 19 | */ 20 | public class VelocityTemplateGenerator extends AbstractScriptedTemplateGenerator { 21 | 22 | private final VelocityEngine engine; 23 | 24 | public VelocityTemplateGenerator(File workspace) throws IOException { 25 | super(workspace); 26 | var properties = new Properties(); 27 | properties.load(IO.getInputStream("velocity.properties")); 28 | engine = new VelocityEngine(properties); 29 | engine.setProperty("resource.loader.file.path", getTemplateDir().getAbsolutePath()); 30 | engine.init(); 31 | } 32 | 33 | @Override 34 | protected String doGenerate(String templateName, Map context_) { 35 | var context = new VelocityContext(context_); 36 | var template = engine.getTemplate(templateName); 37 | var out = new StringWriter(); 38 | template.merge(context, out); 39 | return out.toString(); 40 | } 41 | 42 | @Override 43 | protected String doGenerateInline(String templateContent, Map context_) { 44 | var context = new VelocityContext(context_); 45 | var out = new StringWriter(); 46 | engine.evaluate(context, out, "", templateContent); 47 | return out.toString(); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /templates/jdbc-type.ftl: -------------------------------------------------------------------------------- 1 | <#-- MyBatis 官方的JdbcType列表:https://mybatis.org/mybatis-3/apidocs/reference/org/apache/ibatis/type/JdbcType.html --> 2 | <#assign JdbcTypes = { 3 | "ARRAY":"ARRAY", 4 | "BIGINT":"BIGINT", 5 | "BINARY":"BINARY", 6 | "BIT":"BIT", 7 | "BLOB":"BLOB", 8 | "BOOLEAN":"BOOLEAN", 9 | "CHAR":"CHAR", 10 | "CLOB":"CLOB", 11 | "CURSOR":"CURSOR", 12 | "DATALINK":"DATALINK", 13 | "DATE":"DATE", 14 | "DATETIMEOFFSET":"DATETIMEOFFSET", 15 | "DECIMAL":"DECIMAL", 16 | "DISTINCT":"DISTINCT", 17 | "DOUBLE":"DOUBLE", 18 | "FLOAT":"FLOAT", 19 | "INTEGER":"INTEGER", 20 | "JAVA_OBJECT":"JAVA_OBJECT", 21 | "LONGNVARCHAR":"LONGNVARCHAR", 22 | "LONGVARBINARY":"LONGVARBINARY", 23 | "LONGVARCHAR":"LONGVARCHAR", 24 | "NCHAR":"NCHAR", 25 | "NCLOB":"NCLOB", 26 | "NULL":"NULL", 27 | "NUMERIC":"NUMERIC", 28 | "NVARCHAR":"NVARCHAR", 29 | "REAL":"REAL", 30 | "REF":"REF", 31 | "ROWID":"ROWID", 32 | "SMALLINT":"SMALLINT", 33 | "SQLXML":"SQLXML", 34 | "STRUCT":"STRUCT", 35 | "TIME":"TIME", 36 | "TIMESTAMP":"TIMESTAMP", 37 | "TINYINT":"TINYINT", 38 | "UNDEFINED":"UNDEFINED", 39 | "VARBINARY":"VARBINARY", 40 | "VARCHAR":"VARCHAR" 41 | } /> 42 | <#-- 部分未考虑到的类型映射 --> 43 | <#assign OtherTypes = { 44 | "DATETIME":"TIMESTAMP", 45 | "INT":"INTEGER", 46 | "TINYINT":"INTEGER", 47 | "MEDIUMINT":"INTEGER", 48 | "SMALLINT":"INTEGER", 49 | "YEAR":"INTEGER", 50 | "MULTIPOINT":"INTEGER", 51 | "POINT":"INTEGER", 52 | "TEXT":"LONGVARCHAR", 53 | "LONGTEXT":"LONGVARCHAR", 54 | "MEDIUMTEXT":"VARCHAR", 55 | "MULTILINESTRING":"VARCHAR", 56 | "TINYTEXT":"VARCHAR", 57 | "LINESTRING":"VARCHAR", 58 | "LONGBLOB":"BLOB", 59 | "MEDIUMBLOB":"BLOB", 60 | "TINYBLOB":"BLOB" 61 | } /> 62 | <#-- 获取JdbcType信息 --> 63 | <#function jdbcType column> 64 | <#assign type = column.typeName?replace(" unsigned", "")?upper_case /> 65 | <#if JdbcTypes[type]??> 66 | <#return JdbcTypes[type]> 67 | 68 | <#if OtherTypes[type]??> 69 | <#return OtherTypes[type]> 70 | 71 | <#return 'OTHER'> 72 | -------------------------------------------------------------------------------- /templates/jdbc-typescript-type.ftl: -------------------------------------------------------------------------------- 1 | <#-- MyBatis 官方的JdbcType列表:https://mybatis.org/mybatis-3/apidocs/reference/org/apache/ibatis/type/JdbcType.html --> 2 | <#assign JdbcTypeToTypeScriptTypes = { 3 | "ARRAY":"any", 4 | "BIGINT":"bigint", 5 | "BINARY":"any", 6 | "BIT":"boolean", 7 | "BLOB":"any", 8 | "BOOLEAN":"boolean", 9 | "CHAR":"string", 10 | "CLOB":"any", 11 | "CURSOR":"any", 12 | "DATALINK":"any", 13 | "DATE":"Date", 14 | "DATETIMEOFFSET":"any", 15 | "DECIMAL":"number", 16 | "DISTINCT":"any", 17 | "DOUBLE":"number", 18 | "FLOAT":"number", 19 | "INTEGER":"number", 20 | "JAVA_OBJECT":"any", 21 | "LONGNVARCHAR":"string", 22 | "LONGVARBINARY":"any", 23 | "LONGVARCHAR":"number", 24 | "NCHAR":"string", 25 | "NCLOB":"any", 26 | "NULL":"any", 27 | "NUMERIC":"number", 28 | "NVARCHAR":"string", 29 | "REAL":"any", 30 | "REF":"any", 31 | "ROWID":"string", 32 | "SMALLINT":"number", 33 | "SQLXML":"any", 34 | "STRUCT":"any", 35 | "TIME":"Date", 36 | "TIMESTAMP":"Date", 37 | "TINYINT":"number", 38 | "UNDEFINED":"any", 39 | "VARBINARY":"any", 40 | "VARCHAR":"string" 41 | } /> 42 | <#-- 部分未考虑到的类型映射 --> 43 | <#assign OtherJdbcTypeToTypeScriptTypes = { 44 | "DATETIME":"Date", 45 | "INT":"number", 46 | "TINYINT":"number", 47 | "MEDIUMINT":"number", 48 | "SMALLINT":"number", 49 | "YEAR":"number", 50 | "MULTIPOINT":"number", 51 | "POINT":"number", 52 | "TEXT":"string", 53 | "LONGTEXT":"string", 54 | "MEDIUMTEXT":"string", 55 | "MULTILINESTRING":"string", 56 | "TINYTEXT":"string", 57 | "LINESTRING":"string", 58 | "LONGBLOB":"any", 59 | "MEDIUMBLOB":"any", 60 | "TINYBLOB":"any" 61 | } /> 62 | <#-- 获取 JdbcType 对应的 TypeScript 类型信息 --> 63 | <#function getTypeScriptType column> 64 | <#assign type = column.typeName?replace(" unsigned", "")?upper_case /> 65 | <#if JdbcTypeToTypeScriptTypes[type]??> 66 | <#return JdbcTypeToTypeScriptTypes[type]> 67 | 68 | <#if OtherJdbcTypeToTypeScriptTypes[type]??> 69 | <#return OtherJdbcTypeToTypeScriptTypes[type]> 70 | 71 | <#return 'any'> 72 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/pluginIcon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/ui/win/TableSetting.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.ui.win; 2 | 3 | import com.github.houkunlin.config.Options; 4 | import com.github.houkunlin.util.PluginUtils; 5 | import com.github.houkunlin.vo.impl.RootModel; 6 | import com.intellij.database.psi.DbTable; 7 | import com.intellij.psi.PsiElement; 8 | import lombok.Data; 9 | 10 | import javax.swing.*; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | /** 15 | * 数据库表配置面板(涵盖多个数据库表的内容) 16 | * 17 | * @author HouKunLin 18 | */ 19 | @Data 20 | public class TableSetting implements IWindows { 21 | /** 22 | * 面板:顶级内容面板 23 | */ 24 | private JPanel content; 25 | /** 26 | * 标签:包含多个数据库表的标签界面 27 | */ 28 | private JTabbedPane tableTabbedPane; 29 | private PsiElement[] psiElements; 30 | private Options options; 31 | private List tablePanels = new ArrayList<>(); 32 | 33 | public TableSetting(PsiElement[] psiElements, Options options) { 34 | this.psiElements = psiElements; 35 | this.options = options; 36 | this.reset(); 37 | } 38 | 39 | /** 40 | * 重置配置面板 41 | */ 42 | public void reset() { 43 | tableTabbedPane.removeAll(); 44 | tablePanels.clear(); 45 | // 确保即时加载types信息 46 | PluginUtils.resetColumnTypes(); 47 | for (PsiElement psiElement : psiElements) { 48 | if (psiElement instanceof DbTable dbTable) { 49 | TablePanel tablePanel = new TablePanel(dbTable, options); 50 | tableTabbedPane.addTab(dbTable.getName(), tablePanel.getContent()); 51 | tablePanels.add(tablePanel); 52 | } 53 | } 54 | } 55 | 56 | public List getRootModels() { 57 | List rootModels = new ArrayList<>(); 58 | for (TablePanel tablePanel : tablePanels) { 59 | rootModels.add(tablePanel.toModel()); 60 | } 61 | return rootModels; 62 | } 63 | 64 | @Override 65 | public JPanel getContent() { 66 | return content; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/util/IO.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.util; 2 | 3 | import com.intellij.openapi.diagnostic.Logger; 4 | 5 | import java.io.ByteArrayOutputStream; 6 | import java.io.Closeable; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | 10 | public class IO { 11 | private static final Logger log = Logger.getInstance(IO.class); 12 | /** 13 | * 获取资源文件的输入流 14 | * 15 | * @param resources 资源文件 16 | * @return 文件输入流 17 | */ 18 | public static InputStream getInputStream(String resources) { 19 | return IO.class.getClassLoader().getResourceAsStream(resources); 20 | } 21 | 22 | /** 23 | * 读取插件内部 Classpath 资源文件内容信息 24 | * 25 | * @param resources 资源路径 26 | * @return 文件内容 27 | */ 28 | public static String readResources(String resources) { 29 | return read(getInputStream(resources)); 30 | } 31 | 32 | /** 33 | * 从输入流中读取字符串内容 34 | * 35 | * @param inputStream 输入流 36 | * @return 字符串内容 37 | */ 38 | public static String read(InputStream inputStream) { 39 | if (inputStream == null) { 40 | return ""; 41 | } 42 | byte[] bytes = new byte[1024]; 43 | int len; 44 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 45 | try { 46 | while ((len = inputStream.read(bytes)) != -1) { 47 | outputStream.write(bytes, 0, len); 48 | } 49 | outputStream.flush(); 50 | return outputStream.toString(); 51 | } catch (IOException e) { 52 | log.error("读取文件失败", e); 53 | } finally { 54 | close(outputStream, inputStream); 55 | } 56 | return ""; 57 | } 58 | 59 | /** 60 | * 关闭输入、输出流 61 | * 62 | * @param closeables 可关闭流对象 63 | */ 64 | public static void close(Closeable... closeables) { 65 | for (Closeable closeable : closeables) { 66 | try { 67 | closeable.close(); 68 | } catch (IOException ignore) { 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Java CI with Gradle](https://github.com/houkunlin/Database-Generator/workflows/Java%20CI%20with%20Gradle/badge.svg) 2 | 3 | # Database Generator 4 | 5 | > 一个依赖 IDEA DatabaseTools 的代码生成器,通过数据库表结构生成相应的Java代码,插件提供一套简单的增删查改代码模板,也可以新增自定义模板来生成前端代码或其他相关的代码。 6 | 7 | 8 | 9 | 插件初次运行时会在当前目录下创建 `generator` 目录用来存放插件所需要的信息,其中 `generator/templates` 目录中存放了所需要生成的代码模板文件。 10 | 11 | 本插件支持 `beetl`/`freemarker`/`velocity` 三种模板引擎的代码模板,通过文件后缀(`btl`/`ftl`/`vm`)来选择使用什么模板引擎渲染,自定义模板请阅读 [模板变量文档](./doc/template-document.md) 了解相关变量内容后再进行自定义开发。 12 | 13 | 14 | 15 | [更新日志](./doc/changeNotes.md) | [模板变量说明](./doc/template-document.md) | [旧版模板升级2.0.0插件版本指南](./doc/upgrade-2.0.0.md) | [插件截图](./doc/images.md) | [所有变量使用 all-variable.ftl](https://github.com/houkunlin/Database-Generator/blob/master/src/main/resources/templates/all-variable.ftl) | [渲染输出结果示例 all-variable.md](doc/all-variable.md) 16 | 17 | 18 | 19 | 默认提供三种模板引擎(`beetl`/`freemarker`/`velocity`)的代码模板(SpringBoot+MyBatis-Plus+自定义工具),可选择保留其中一种模板引擎的代码模板,然后根据自己的需求对代码模板进行修改,阅读 [模板变量说明](./doc/template-document.md) 了解模板变量的具体内容。 20 | 21 | 支持在模板中使用自定义的Groovy脚本,需要在模板父级目录下创建 `scripts` 文件夹,然后放置 `*.groovy` 文件即可。阅读 [Groovy脚本使用文档](./doc/groovy-scripts.md) 了解如何使用 Groovy 脚本。 22 | ```text 23 | ${工作空间} 24 | |-templates 25 | |-scripts 26 | |--*.groovy 27 | ``` 28 | ## 代码模板文件在IDEA中存放的位置 29 | 30 | - 代码模板文件默认放到:`Scratches and Consoles/Extensions` (中文:草稿文件和控制台/扩展/Database Generator) 31 | - 同时支持以下模板路径:`${project.dir}/.idea/generator/templates` 和 `${project.dir}/generator/templates` 32 | 33 | 34 | 35 | ## 注意事项 36 | 37 | **版本 `v2.7.0` 对配置文件产生了破坏性变更,由原来的 `config/*.json` JSON格式配置文件改为 `config.yml` YAML配置文件** 38 | 39 | **在旧版本升级后,原来的 `config/*.json` 配置文件将失效,请参照 `src/main/resources/config.yml` 文件重制你自定义的配置文件** 40 | 41 | **也可以删除本地的 `init.properties` 文件然后重新生成配置文件,但请注意备份您的自定义模板文件** 42 | 43 | 44 | 45 | ## 截图 46 | 47 | ![](./doc/assets/images_1.png) 48 | 49 | ![](./doc/assets/images_8.png) 50 | 51 | ![](./doc/assets/images_9.png) 52 | 53 | ![](./doc/assets/images_10.png) 54 | 55 | ![](./doc/assets/images_11.png) 56 | 57 | 58 | 59 | 60 | 61 | ## 参考代码 62 | - better-mybatis-generator https://github.com/kmaster/better-mybatis-generator 63 | - EasyCode https://github.com/makejavas/EasyCode 64 | 65 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/config/Settings.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.config; 2 | 3 | import com.github.houkunlin.model.FileType; 4 | import com.google.common.collect.Lists; 5 | import lombok.Data; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * 设置信息 11 | * 12 | * @author HouKunLin 13 | */ 14 | @Data 15 | public class Settings { 16 | /** 17 | * 项目路径 18 | */ 19 | private String projectPath; 20 | /** 21 | * Java代码路径 22 | */ 23 | private String javaPath = "src/main/java"; 24 | /** 25 | * 资源文件路径 26 | */ 27 | private String resourcesPath = "src/main/resources"; 28 | /** 29 | * 文件类型 30 | */ 31 | private List fileTypes; 32 | 33 | public String getResourcesPathAt(String filename) { 34 | return resourcesPath + "/" + filename; 35 | } 36 | 37 | public String getJavaPathAt(String filename) { 38 | return javaPath + "/" + filename; 39 | } 40 | 41 | /** 42 | * 获取文件路径 43 | * 44 | * @param path_ 路径 45 | * @param filename 文件名 46 | * @return 文件路径 47 | * @since 2.8.4 48 | */ 49 | public String getPathAt(String path_, String filename) { 50 | var path = (path_ == null || path_.isBlank()) ? javaPath : path_; 51 | return path + "/" + filename; 52 | } 53 | 54 | public List getFileTypes() { 55 | if (fileTypes == null) { 56 | initFileTypes(); 57 | } 58 | return fileTypes; 59 | } 60 | 61 | private void initFileTypes() { 62 | fileTypes = Lists.newArrayList( 63 | FileType.of("entity", "Entity", "com.example.entity", ".java", "src/main/java", true), 64 | FileType.of("dao", "Repository", "com.example.repository", ".java", "src/main/java", true), 65 | FileType.of("service", "Service", "com.example.service", ".java", "src/main/java", true), 66 | FileType.of("serviceImpl", "ServiceImpl", "com.example.service.impl", ".java", "src/main/java", true), 67 | FileType.of("controller", "Controller", "com.example.controller", ".java", "src/main/java", true), 68 | FileType.of("xml", "Mapper", "mapper", ".xml", "src/main/resources", true) 69 | ); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/action/MainAction.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.action; 2 | 3 | import com.github.houkunlin.config.ConfigService; 4 | import com.github.houkunlin.ui.win.Main; 5 | import com.github.houkunlin.util.PluginUtils; 6 | import com.intellij.database.psi.DbTable; 7 | import com.intellij.openapi.actionSystem.AnAction; 8 | import com.intellij.openapi.actionSystem.AnActionEvent; 9 | import com.intellij.openapi.actionSystem.LangDataKeys; 10 | import com.intellij.openapi.actionSystem.PlatformDataKeys; 11 | import com.intellij.openapi.project.Project; 12 | import com.intellij.openapi.ui.Messages; 13 | import com.intellij.psi.PsiElement; 14 | 15 | /** 16 | * 操作入口 17 | * 18 | * @author HouKunLin 19 | */ 20 | public class MainAction extends AnAction { 21 | 22 | /** 23 | * 在 Database 面板中右键打开插件主页面 24 | * 25 | * @param actionEvent 操作对象 26 | */ 27 | @Override 28 | public void actionPerformed(AnActionEvent actionEvent) { 29 | PsiElement[] psiElements = actionEvent.getData(LangDataKeys.PSI_ELEMENT_ARRAY); 30 | if (psiElements == null || psiElements.length == 0) { 31 | Messages.showWarningDialog("请至少选择一张表", "通知"); 32 | return; 33 | } 34 | boolean isOk = false; 35 | for (PsiElement psiElement : psiElements) { 36 | if (psiElement instanceof DbTable) { 37 | isOk = true; 38 | break; 39 | } 40 | } 41 | if (!isOk) { 42 | Messages.showWarningDialog("请至少选择一张表", "通知"); 43 | return; 44 | } 45 | Project project = actionEvent.getData(PlatformDataKeys.PROJECT); 46 | if (project == null) { 47 | Messages.showErrorDialog("无法获取到当前项目的 Project 对象", "错误"); 48 | return; 49 | } 50 | PluginUtils.setProject(project); 51 | PluginUtils.syncResources(project); 52 | ConfigService configService = ConfigService.getInstance(project); 53 | if (configService == null) { 54 | Messages.showWarningDialog("初始化配置信息失败,但并不影响继续使用!", "错误"); 55 | configService = new ConfigService(); 56 | } 57 | PluginUtils.resetColumnTypes(); 58 | new Main(project, psiElements, configService); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/ui/win/table/PlaceholderTableCellRenderer.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.ui.win.table; 2 | 3 | import javax.swing.*; 4 | import javax.swing.table.DefaultTableCellRenderer; 5 | import java.awt.*; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** 10 | * 单元格占位符渲染器,允许配置多尔个列的占位符 11 | * 12 | * @author daiwenzh5 13 | * @since 2.8.4 14 | */ 15 | public class PlaceholderTableCellRenderer extends DefaultTableCellRenderer { 16 | 17 | private final Map placeholders = new HashMap<>(); 18 | 19 | private final String defaultPlaceholder; 20 | 21 | public PlaceholderTableCellRenderer() { 22 | this(""); 23 | } 24 | 25 | public PlaceholderTableCellRenderer(String defaultPlaceholder) { 26 | this.defaultPlaceholder = defaultPlaceholder; 27 | } 28 | 29 | @Override 30 | public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 31 | var component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 32 | if (component instanceof JLabel label) { 33 | resetLabelText(value, label, column); 34 | } 35 | return component; 36 | } 37 | 38 | private void resetLabelText(Object value, JLabel label, int column) { 39 | if (value == null || value.toString() 40 | .trim() 41 | .isEmpty()) { 42 | label.setText(placeholders.getOrDefault(column, defaultPlaceholder)); 43 | // label.setForeground(UIManager.getColor("TextField.inactiveForeground")); 44 | label.setForeground(UIManager.getColor("Component.infoForeground")); 45 | } else { 46 | label.setText(value.toString()); 47 | label.setForeground(UIManager.getColor("TextField.foreground")); 48 | } 49 | } 50 | 51 | /** 52 | * 设置单个列的占位符 53 | * 54 | * @param column 列序号 55 | * @param placeholder 占位符 56 | * @return 当前对象 57 | */ 58 | public PlaceholderTableCellRenderer setColumn(int column, String placeholder) { 59 | placeholders.put(column, placeholder); 60 | return this; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/util/SyncResources.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.util; 2 | 3 | import com.intellij.openapi.project.Project; 4 | import com.intellij.util.ExceptionUtil; 5 | import lombok.Data; 6 | import lombok.RequiredArgsConstructor; 7 | 8 | import java.io.File; 9 | import java.io.InputStream; 10 | 11 | /** 12 | * 同步插件资源到项目路径 13 | * 14 | * @author HouKunLin 15 | */ 16 | @Data 17 | @RequiredArgsConstructor 18 | public class SyncResources implements Runnable { 19 | /** 20 | * 插件初始化文件 21 | */ 22 | private static final String INIT_FILENAME = "init.properties"; 23 | private final Project project; 24 | 25 | /** 26 | * 复制插件内部的模板文件到项目路径中 27 | */ 28 | @Override 29 | public void run() { 30 | File initFile = PluginUtils.getExtensionPluginDirFile(INIT_FILENAME); 31 | boolean initFileExists = initFile.exists(); 32 | if (initFileExists) { 33 | // 不再强制覆盖 初始化文件 34 | return; 35 | } 36 | try { 37 | // 只处理不存在的初始化文件 38 | String content = IO.readResources(INIT_FILENAME); 39 | FileUtils.copyFile(project, initFile, content, false); 40 | } catch (Throwable e) { 41 | ExceptionUtil.rethrow(new RuntimeException("同步插件init文件到本地出现错误:\r\n" + e.getMessage(), e)); 42 | } 43 | try { 44 | syncFiles(); 45 | } catch (Throwable e) { 46 | ExceptionUtil.rethrow(new RuntimeException("同步插件配置文件到本地出现错误:\r\n" + e.getMessage(), e)); 47 | } 48 | 49 | PluginUtils.refreshWorkspace(); 50 | } 51 | 52 | /** 53 | * 复制插件内部的代码模板到项目路径中 54 | * 55 | */ 56 | private void syncFiles() { 57 | String syncFiles = IO.readResources("syncFiles.txt"); 58 | String[] split = syncFiles.split("\n"); 59 | for (String filePath : split) { 60 | if (filePath == null || filePath.isBlank()) { 61 | continue; 62 | } 63 | InputStream inputStream = IO.getInputStream(filePath); 64 | if (inputStream == null) { 65 | continue; 66 | } 67 | String content = IO.read(inputStream); 68 | 69 | FileUtils.copyFile(project, PluginUtils.getExtensionPluginDirFile(filePath), content, true); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/resources/templates/README.md: -------------------------------------------------------------------------------- 1 | # README.md 2 | 3 | ## ZH-CN 4 | 5 | **注意:请不要信任及使用来源不明、未经您工作团队安全审查的脚本及模板文件!!!**
6 | **注意:请不要信任及使用来源不明、未经您工作团队安全审查的脚本及模板文件!!!**
7 | **注意:请不要信任及使用来源不明、未经您工作团队安全审查的脚本及模板文件!!!**
8 | 9 | 本插件默认提供三种模板引擎的参照模板代码,在正式使用时,请选择想要使用的一种模板引擎的模板代码保留,然后删除其他模板引擎的模板代码。 10 | 11 | 代码模板变量参数信息请查看 [模板变量用法](https://github.com/houkunlin/Database-Generator/blob/master/doc/template-document.md) 12 | 13 | Groovy 脚本使用详情请查看 [Groovy 脚本使用说明](https://github.com/houkunlin/Database-Generator/blob/master/doc/groovy-scripts.md) 14 | 15 | 2.5.0 版本默认会在 `Scratches and Consoles - Extensions` 路径下创建代码模板和配置文件,不会在 `${project.dir}/generator` 存放相关文件。 16 | 17 | 支持以下路径的代码模板: 18 | 19 | 1. `${project.dir}/.idea/generator/templates` 20 | 2. `${project.dir}/generator/templates` 21 | 3. `Scratches and Consoles - Extensions/Plugin/generator/templates` 22 | 23 | ## EN 24 | 25 | **Note: Please do not trust or use scripts and template files from unknown sources that have not been reviewed by your work team!!!**
26 | **Note: Please do not trust or use scripts and template files from unknown sources that have not been reviewed by your work team!!!**
27 | **Note: Please do not trust or use scripts and template files from unknown sources that have not been reviewed by your work team!!!**
28 | 29 | This plug-in provides reference template codes for three template engines by default. When officially used, please select the template code of one template engine you want to use to keep, and then delete the template codes of other template engines. 30 | 31 | Please check the variable parameter information of the code template [Template variable usage](https://github.com/houkunlin/Database-Generator/blob/master/doc/template-document.md) 32 | 33 | For more information about using Groovy scripts, see [Groovy Script Instructions](https://github.com/houkunlin/Database-Generator/blob/master/doc/groovy-scripts.md) 34 | 35 | The 2.5.0 version will create code templates and configuration files in the `Scratches and Consoles - Extensions` path by default, and will not store related files in `${project.dir}/generator`. 36 | 37 | Support the code templates of the following paths: 38 | 39 | 1. `${project.dir}/.idea/generator/templates` 40 | 2. `${project.dir}/generator/templates` 41 | 3. `Scratches and Consoles - Extensions/Plugin/generator/templates` 42 | -------------------------------------------------------------------------------- /src/main/resources/templates/default/freemarker/java/EntityForm.java.ftl: -------------------------------------------------------------------------------- 1 | ${gen.setFilename("${entity.name}Form.java")} 2 | ${gen.setFilepath("${settings.javaPath}/${entity.packages.entity}/")} 3 | package ${entity.packages.entity}; 4 | 5 | import com.baomidou.mybatisplus.annotation.*; 6 | 7 | ${entity.packages} 8 | 9 | import io.swagger.annotations.ApiModel; 10 | import io.swagger.annotations.ApiModelProperty; 11 | import javax.persistence.*; 12 | import java.io.Serializable; 13 | import lombok.AllArgsConstructor; 14 | import lombok.Builder; 15 | import lombok.Data; 16 | import lombok.NoArgsConstructor; 17 | import org.hibernate.validator.constraints.Length; 18 | import org.springline.web.mvc.SpringlineCommand; 19 | import javax.validation.constraints.NotBlank; 20 | 21 | /** 22 | * 表单对象:${entity.comment}<#if table.comment?trim?length gt 0 && entity.comment != table.comment> (${table.comment}) 23 | * 24 | * @author ${developer.author} 25 | */ 26 | @ApiModel("表单对象:${entity.comment}") 27 | @Data 28 | @Builder 29 | @NoArgsConstructor 30 | @AllArgsConstructor 31 | public class ${entity.name}Form implements Serializable { 32 | <#list fields as field> 33 | <#if field.selected> 34 | <#if field.name?starts_with("created") || field.name?starts_with("updated") || field.name?starts_with("deleted") > 35 | <#else> 36 | /** 37 | * ${field.comment} 38 | <#if field.column.comment?trim?length gt 0 && field.comment != field.column.comment> *

数据库字段说明:${field.column.comment}

39 | */ 40 | <#if field.typeName == "String"> 41 | <#if field.primaryKey> 42 | @Length(max = ${field.dataType.length}, message = "${field.comment} 在 ${field.dataType.length} 个字符以内") 43 | <#else> 44 | <#if field.name == "remark"> 45 | @Length(max = ${field.dataType.length}, message = "${field.comment} 在 ${field.dataType.length} 个字符以内") 46 | <#else> 47 | @Length(max = ${field.dataType.length}, message = "${field.comment} 在 ${field.dataType.length} 个字符以内") 48 | @NotBlank(message = "${field.comment} 不能为空") 49 | 50 | 51 | 52 | @ApiModelProperty("${field.comment}") 53 | private ${field.typeName} ${field.name}; 54 | 55 | 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/ui/win/TablePanel.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.ui.win; 2 | 3 | import com.github.houkunlin.config.Options; 4 | import com.github.houkunlin.model.JTableModel; 5 | import com.github.houkunlin.vo.impl.EntityImpl; 6 | import com.github.houkunlin.vo.impl.RootModel; 7 | import com.intellij.database.psi.DbTable; 8 | import lombok.Data; 9 | 10 | import javax.swing.*; 11 | import java.util.Objects; 12 | 13 | /** 14 | * 数据库表面板(对应了单个表的配置信息) 15 | * 16 | * @author HouKunLin 17 | */ 18 | @Data 19 | public class TablePanel implements IWindows { 20 | /** 21 | * 数据库表对象 22 | */ 23 | private final DbTable dbTable; 24 | /** 25 | * 面板:顶级内容面板 26 | */ 27 | private JPanel content; 28 | /** 29 | * 输入框:数据库表名 30 | */ 31 | private JTextField tableNameField; 32 | /** 33 | * 输入框:Entity 名称 34 | */ 35 | private JTextField entityNameField; 36 | /** 37 | * 输入框:Entity 注释 38 | */ 39 | private JTextField commentField; 40 | /** 41 | * 表格:数据库表的字段信息 42 | */ 43 | private JTable table1; 44 | private JTextField uriField; 45 | /** 46 | * 界面表格对象的数据模型 47 | */ 48 | private JTableModel model; 49 | /** 50 | * 生成代码时需要用到的model对象 51 | */ 52 | private RootModel rootModel; 53 | private Options options; 54 | 55 | public TablePanel(DbTable dbTable, Options options) { 56 | this.dbTable = dbTable; 57 | this.options = options; 58 | 59 | model = new JTableModel(table1, dbTable, options); 60 | toModel(); 61 | } 62 | 63 | public RootModel toModel() { 64 | if (rootModel == null) { 65 | rootModel = new RootModel(dbTable, model.getFieldImpls(), model.getColumnImpls(), options); 66 | tableNameField.setText(rootModel.getTable().getName()); 67 | entityNameField.setText(String.valueOf(rootModel.getEntity().getName())); 68 | commentField.setText(rootModel.getTable().getComment()); 69 | uriField.setText(rootModel.getTable().getName().replace("_", "-")); 70 | return rootModel; 71 | } 72 | EntityImpl entity = rootModel.getEntity(); 73 | entity.setName(entityNameField.getText()); 74 | entity.setComment(Objects.toString(commentField.getText(), "")); 75 | entity.setUri(Objects.toString(uriField.getText(), "")); 76 | return rootModel; 77 | } 78 | 79 | @Override 80 | public JPanel getContent() { 81 | return content; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Gradle 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle 3 | 4 | name: Java CI with Gradle 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up JDK 17 20 | uses: actions/setup-java@v1 21 | with: 22 | java-version: 17 23 | - name: Grant execute permission for gradlew 24 | run: chmod +x gradlew 25 | - name: Generator Plugin Doc Markdown Text 26 | env: # Or as an environment variable 27 | intellijPublishToken: ${{ secrets.INTELLIJPUBLISHTOKEN }} 28 | # 不打包 Kotlin 标准依赖:https://kotlinlang.org/docs/reference/using-gradle.html#dependency-on-the-standard-library 29 | run: ./gradlew markdownToHtml 30 | - name: Build Plugin with Gradle 31 | env: # Or as an environment variable 32 | intellijPublishToken: ${{ secrets.INTELLIJPUBLISHTOKEN }} 33 | # 不打包 Kotlin 标准依赖:https://kotlinlang.org/docs/reference/using-gradle.html#dependency-on-the-standard-library 34 | run: ./gradlew buildPlugin -Pkotlin.stdlib.default.dependency=false 35 | - name: Rename 36 | run: mv build/distributions/*.zip build/distributions/plugin.zip 37 | - name: Unpack Plugin Zip 38 | run: unzip -d tmp/ build/distributions/plugin.zip 39 | - uses: actions/upload-artifact@v4 40 | with: 41 | # Name of the artifact to upload. 42 | # Optional. Default is 'artifact' 43 | name: Database_Generator 44 | # A file, directory or wildcard pattern that describes what to upload 45 | # Required. 46 | path: tmp/**/* 47 | # The desired behavior if no files are found using the provided path. 48 | # Available Options: 49 | # warn: Output a warning but do not fail the action 50 | # error: Fail the action with an error message 51 | # ignore: Do not output any warnings or errors, the action does not fail 52 | # Optional. Default is 'warn' 53 | if-no-files-found: warn 54 | # Duration after which artifact will expire in days. 0 means using default retention. 55 | # Minimum 1 day. 56 | # Maximum 90 days unless changed from the repository settings page. 57 | # Optional. Defaults to repository settings. 58 | retention-days: 7 59 | -------------------------------------------------------------------------------- /src/main/java/velocity_implicit.vm: -------------------------------------------------------------------------------- 1 | #* @implicitly included *# 2 | #* @vtlvariable name="date" type="org.joda.time.DateTime" *# 3 | #* @vtlvariable name="gen" type="com.github.houkunlin.vo.Variable.static" *# 4 | #* @vtlvariable name="settings" type="com.github.houkunlin.config.Settings" *# 5 | #* @vtlvariable name="settings.projectPath" type="java.lang.String" *# 6 | #* @vtlvariable name="settings.javaPath" type="java.lang.String" *# 7 | #* @vtlvariable name="settings.resourcesPath" type="java.lang.String" *# 8 | #* @vtlvariable name="developer" type="com.github.houkunlin.config.Developer" *# 9 | #* @vtlvariable name="developer.author" type="java.lang.String" *# 10 | #* @vtlvariable name="developer.email" type="java.lang.String" *# 11 | #* @vtlvariable name="entity" type="com.github.houkunlin.vo.impl.EntityImpl" *# 12 | #* @vtlvariable name="entity.name" type="java.util.Map" *# 13 | #* @vtlvariable name="entity.name.entity" type="com.github.houkunlin.vo.impl.EntityName" *# 14 | #* @vtlvariable name="entity.name.service" type="com.github.houkunlin.vo.impl.EntityName" *# 15 | #* @vtlvariable name="entity.name.serviceImpl" type="com.github.houkunlin.vo.impl.EntityName" *# 16 | #* @vtlvariable name="entity.name.dao" type="com.github.houkunlin.vo.impl.EntityName" *# 17 | #* @vtlvariable name="entity.name.controller" type="com.github.houkunlin.vo.impl.EntityName" *# 18 | #* @vtlvariable name="entity.name.xml" type="com.github.houkunlin.vo.impl.EntityName" *# 19 | #* @vtlvariable name="entity.packages" type="com.github.houkunlin.vo.impl.EntityPackage" *# 20 | #* @vtlvariable name="entity.packages.entity" type="com.github.houkunlin.vo.impl.EntityPackageInfo" *# 21 | #* @vtlvariable name="entity.packages.service" type="com.github.houkunlin.vo.impl.EntityPackageInfo" *# 22 | #* @vtlvariable name="entity.packages.serviceImpl" type="com.github.houkunlin.vo.impl.EntityPackageInfo" *# 23 | #* @vtlvariable name="entity.packages.dao" type="com.github.houkunlin.vo.impl.EntityPackageInfo" *# 24 | #* @vtlvariable name="entity.packages.controller" type="com.github.houkunlin.vo.impl.EntityPackageInfo" *# 25 | #* @vtlvariable name="entity.packages.xml" type="com.github.houkunlin.vo.impl.EntityPackageInfo" *# 26 | #* @vtlvariable name="table" type="com.github.houkunlin.vo.impl.TableImpl" *# 27 | #* @vtlvariable name="primary" type="com.github.houkunlin.vo.impl.PrimaryInfo" *# 28 | #* @vtlvariable name="fields" type="java.util.List" *# 29 | #* @vtlvariable name="columns" type="java.util.List" *# 30 | -------------------------------------------------------------------------------- /src/main/java/freemarker_implicit.ftl: -------------------------------------------------------------------------------- 1 | [#ftl] 2 | [#-- @implicitly included --] 3 | [#-- @ftlvariable name="date" type="org.joda.time.DateTime" --] 4 | [#-- @ftlvariable name="gen" type="com.github.houkunlin.vo.Variable.static" --] 5 | [#-- @ftlvariable name="settings" type="com.github.houkunlin.config.Settings" --] 6 | [#-- @ftlvariable name="settings.projectPath" type="java.lang.String" --] 7 | [#-- @ftlvariable name="settings.javaPath" type="java.lang.String" --] 8 | [#-- @ftlvariable name="settings.resourcesPath" type="java.lang.String" --] 9 | [#-- @ftlvariable name="developer" type="com.github.houkunlin.config.Developer" --] 10 | [#-- @ftlvariable name="developer.author" type="java.lang.String" --] 11 | [#-- @ftlvariable name="developer.email" type="java.lang.String" --] 12 | [#-- @ftlvariable name="entity" type="com.github.houkunlin.vo.impl.EntityImpl" --] 13 | [#-- @ftlvariable name="entity.name" type="java.util.Map" --] 14 | [#-- @ftlvariable name="entity.name.entity" type="com.github.houkunlin.vo.impl.EntityName" --] 15 | [#-- @ftlvariable name="entity.name.service" type="com.github.houkunlin.vo.impl.EntityName" --] 16 | [#-- @ftlvariable name="entity.name.serviceImpl" type="com.github.houkunlin.vo.impl.EntityName" --] 17 | [#-- @ftlvariable name="entity.name.dao" type="com.github.houkunlin.vo.impl.EntityName" --] 18 | [#-- @ftlvariable name="entity.name.controller" type="com.github.houkunlin.vo.impl.EntityName" --] 19 | [#-- @ftlvariable name="entity.name.xml" type="com.github.houkunlin.vo.impl.EntityName" --] 20 | [#-- @ftlvariable name="entity.packages" type="com.github.houkunlin.vo.impl.EntityPackage" --] 21 | [#-- @ftlvariable name="entity.packages.entity" type="com.github.houkunlin.vo.impl.EntityPackageInfo" --] 22 | [#-- @ftlvariable name="entity.packages.service" type="com.github.houkunlin.vo.impl.EntityPackageInfo" --] 23 | [#-- @ftlvariable name="entity.packages.serviceImpl" type="com.github.houkunlin.vo.impl.EntityPackageInfo" --] 24 | [#-- @ftlvariable name="entity.packages.dao" type="com.github.houkunlin.vo.impl.EntityPackageInfo" --] 25 | [#-- @ftlvariable name="entity.packages.controller" type="com.github.houkunlin.vo.impl.EntityPackageInfo" --] 26 | [#-- @ftlvariable name="entity.packages.xml" type="com.github.houkunlin.vo.impl.EntityPackageInfo" --] 27 | [#-- @ftlvariable name="table" type="com.github.houkunlin.vo.impl.TableImpl" --] 28 | [#-- @ftlvariable name="primary" type="com.github.houkunlin.vo.impl.PrimaryInfo" --] 29 | [#-- @ftlvariable name="fields" type="java.util.List" --] 30 | [#-- @ftlvariable name="columns" type="java.util.List" --] 31 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/vo/impl/TableColumnImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.vo.impl; 2 | 3 | import com.github.houkunlin.vo.IEntityField; 4 | import com.github.houkunlin.vo.ITableColumn; 5 | import com.intellij.database.model.DasColumn; 6 | import com.intellij.database.model.DataType; 7 | import com.intellij.database.util.DasUtil; 8 | import com.intellij.util.ReflectionUtil; 9 | import lombok.EqualsAndHashCode; 10 | import lombok.Getter; 11 | import lombok.Setter; 12 | import lombok.ToString; 13 | 14 | import java.util.Objects; 15 | 16 | /** 17 | * 数据库表字段信息(数据库表列对象信息) 18 | * 19 | * @author HouKunLin 20 | */ 21 | @Getter 22 | public class TableColumnImpl implements ITableColumn { 23 | /** 24 | * 数据库表的原始字段对象 25 | */ 26 | @ToString.Exclude 27 | @EqualsAndHashCode.Exclude 28 | private final DasColumn dbColumn; 29 | private final String name; 30 | private final String comment; 31 | private final String typeName; 32 | private final DataType dataType; 33 | private final String fullTypeName; 34 | private final boolean primaryKey; 35 | @Setter 36 | @ToString.Exclude 37 | @EqualsAndHashCode.Exclude 38 | private IEntityField field; 39 | @Setter 40 | private boolean selected; 41 | 42 | private TableColumnImpl(String name, String typeName, String fullTypeName, String comment, boolean primaryKey, boolean selected) { 43 | this.dbColumn = null; 44 | this.name = name; 45 | this.typeName = typeName; 46 | this.dataType = null; 47 | this.fullTypeName = fullTypeName; 48 | this.comment = comment; 49 | this.primaryKey = primaryKey; 50 | this.selected = selected; 51 | } 52 | 53 | public TableColumnImpl(DasColumn dbColumn) { 54 | this.dbColumn = dbColumn; 55 | this.name = dbColumn.getName(); 56 | this.dataType = dbColumn.getDasType().toDataType(); 57 | this.fullTypeName = dataType.getSpecification(); 58 | this.typeName = ReflectionUtil.getField(DataType.class, dataType, String.class, "typeName"); 59 | this.comment = Objects.toString(dbColumn.getComment(), ""); 60 | this.primaryKey = DasUtil.isPrimary(dbColumn); 61 | this.selected = true; 62 | } 63 | 64 | /** 65 | * 创建主键字段对象 66 | * 67 | * @param name 字段名称 68 | * @param typeName 字段类型 69 | * @param fullTypeName 字段完整类型 70 | * @param comment 字段注释 71 | * @return 字段对象 72 | */ 73 | public static TableColumnImpl primaryColumn(String name, String typeName, String fullTypeName, String comment) { 74 | return new TableColumnImpl(name, typeName, fullTypeName, comment, true, true); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/template/beetl/BeetlTemplateGenerator.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.template.beetl; 2 | 3 | import com.github.houkunlin.template.AbstractScriptedTemplateGenerator; 4 | import com.github.houkunlin.util.ScriptManager; 5 | import org.beetl.core.Configuration; 6 | import org.beetl.core.GroupTemplate; 7 | import org.beetl.core.ResourceLoader; 8 | import org.beetl.core.resource.FileResourceLoader; 9 | import org.beetl.core.resource.StringTemplateResourceLoader; 10 | 11 | import java.io.File; 12 | import java.io.IOException; 13 | import java.util.Map; 14 | 15 | /** 16 | * @author daiwenzh5 17 | * @since 1.0 18 | */ 19 | public class BeetlTemplateGenerator extends AbstractScriptedTemplateGenerator { 20 | 21 | private final GroupTemplate groupTemplateString; 22 | private final GroupTemplate groupTemplateFile; 23 | 24 | public BeetlTemplateGenerator(File workspace) throws IOException { 25 | super(workspace); 26 | //初始化代码 27 | var configuration = Configuration.defaultConfiguration(); 28 | configuration.setNativeCall(true); 29 | configuration.setNativeSecurity("org.beetl.core.DefaultNativeSecurityManager"); 30 | groupTemplateString = createGroupTemplate(new StringTemplateResourceLoader(), configuration); 31 | groupTemplateFile = createGroupTemplate(new FileResourceLoader(getTemplateDir().getAbsolutePath()), configuration); 32 | 33 | } 34 | 35 | private GroupTemplate createGroupTemplate(ResourceLoader loader, Configuration configuration) { 36 | var groupTemplate = new GroupTemplate(loader, configuration, GroupTemplate.class.getClassLoader()); 37 | groupTemplate.setErrorHandler(new BeetlErrorHandler()); 38 | return groupTemplate; 39 | } 40 | 41 | @Override 42 | protected String doGenerate(String templateName, Map context) { 43 | //获取模板 44 | var template = groupTemplateFile.getTemplate(templateName); 45 | template.binding(context); 46 | //渲染结果 47 | return template.render(); 48 | } 49 | 50 | @Override 51 | protected String doGenerateInline(String templateContent, Map context) { 52 | //获取模板 53 | var template = groupTemplateString.getTemplate(templateContent); 54 | template.binding(context); 55 | //渲染结果 56 | return template.render(); 57 | } 58 | 59 | @Override 60 | protected void registerScriptManager(Map context, ScriptManager scriptManager) { 61 | scriptManager.forEach(this::registerScriptMethods); 62 | } 63 | 64 | private void registerScriptMethods(String name, Object script) { 65 | groupTemplateFile.registerFunctionPackage(ScriptManager.NAMESPACE + name, script); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/model/SaveFilePath.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.model; 2 | 3 | import com.github.houkunlin.config.Settings; 4 | import com.github.houkunlin.vo.Variable; 5 | import com.github.houkunlin.vo.impl.RootModel; 6 | import com.intellij.openapi.util.text.StringUtil; 7 | import lombok.Getter; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | /** 11 | * 保存文件信息 12 | * 13 | * @author HouKunLin 14 | */ 15 | public class SaveFilePath { 16 | 17 | /** 18 | * 模板文件的 type 类型 19 | */ 20 | private final String type; 21 | /** 22 | * 模板文件保存路径 23 | */ 24 | private String toString; 25 | 26 | @Getter 27 | private final boolean override; 28 | 29 | public SaveFilePath(String filename, String filepath, boolean override) { 30 | String name = getValue(Variable.filename, filename); 31 | String path = getValue(Variable.filepath, filepath); 32 | type = Variable.type; 33 | toString = (path.replace(".", "/") + "/" + name); 34 | toString = toString.replace("\\", "/").replaceAll("/+", "/"); 35 | this.override = override; 36 | Variable.resetVariables(); 37 | } 38 | 39 | public static SaveFilePath create(RootModel rootModel, Settings settings) { 40 | var entityName = String.valueOf(rootModel.getEntity().getName()); 41 | if (Variable.type == null) { 42 | return createTemp(entityName, settings); 43 | } 44 | return settings.getFileTypes() 45 | .stream() 46 | .filter(item -> item.getType() 47 | .equals(Variable.type)) 48 | .map(item -> createSaveFilePath(settings, entityName, item)) 49 | .findFirst() 50 | .orElseGet(() -> createTemp(entityName, settings)); 51 | } 52 | 53 | private static @NotNull SaveFilePath createSaveFilePath(Settings settings, String entityName, FileType item) { 54 | var filename = entityName + item.getSuffix() + StringUtil.defaultIfEmpty(item.getExt(), ".java"); 55 | var relativePath = settings.getPathAt(item.getPath(), item.getPackageName()); 56 | return new SaveFilePath(filename, relativePath, item.isOverride()); 57 | } 58 | 59 | private String getValue(String tempValue, String defaultValue) { 60 | if (tempValue != null) { 61 | return tempValue; 62 | } else { 63 | return defaultValue; 64 | } 65 | } 66 | 67 | @Override 68 | public String toString() { 69 | return toString; 70 | } 71 | 72 | public static SaveFilePath createTemp(String filename, Settings settings) { 73 | return new SaveFilePath(filename, settings.getResourcesPathAt("temp"), true); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/template/TemplateGeneratorFactory.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.template; 2 | 3 | import com.github.houkunlin.template.beetl.BeetlTemplateGenerator; 4 | import com.github.houkunlin.template.freemarker.FreemarkerTemplateGenerator; 5 | import com.github.houkunlin.template.velocity.VelocityTemplateGenerator; 6 | import com.github.houkunlin.util.PluginUtils; 7 | import com.google.common.collect.HashBasedTable; 8 | import com.google.common.collect.Table; 9 | 10 | import java.io.File; 11 | import java.io.IOException; 12 | 13 | /** 14 | * 模板生成器工厂 15 | * 16 | * @author daiwenzh5 17 | * @since 1.0 18 | */ 19 | public class TemplateGeneratorFactory { 20 | 21 | private static final Table cache = HashBasedTable.create(); 22 | 23 | /** 24 | * 创建模板生成器 25 | * 26 | * @param templateFile 模板文件 27 | * @return 模板生成器 28 | */ 29 | public static ITemplateGenerator create(File templateFile) { 30 | var workspace = getTemplateWorkspace(templateFile); 31 | var workspacePath = workspace.getAbsolutePath(); 32 | var type = TplType.create(templateFile); 33 | var value = cache.get(workspacePath, type); 34 | if (value != null) { 35 | return value; 36 | } 37 | var generator = create(workspace, type); 38 | cache.put(workspacePath, type, generator); 39 | return generator; 40 | } 41 | 42 | private static ITemplateGenerator create(File workspace, TplType type) { 43 | try { 44 | return switch (type) { 45 | case BEETL -> new BeetlTemplateGenerator(workspace); 46 | case FREEMARKER -> new FreemarkerTemplateGenerator(workspace); 47 | case VELOCITY -> new VelocityTemplateGenerator(workspace); 48 | default -> null; 49 | }; 50 | } catch (IOException e) { 51 | throw new RuntimeException("创建 Root 模板处理器失败:" + workspace.getAbsolutePath() + "\r\n" + e.getMessage(), e); 52 | } 53 | } 54 | 55 | private static File getTemplateWorkspace(File templateFile) { 56 | var absolutePath = templateFile.getAbsolutePath(); 57 | var file = PluginUtils.getProjectWorkspacePluginDir(); 58 | if (absolutePath.startsWith(file.getAbsolutePath())) { 59 | return file; 60 | } 61 | file = PluginUtils.getProjectPluginDir(); 62 | if (absolutePath.startsWith(file.getAbsolutePath())) { 63 | return file; 64 | } 65 | file = PluginUtils.getExtensionPluginDir(); 66 | if (absolutePath.startsWith(file.getAbsolutePath())) { 67 | return file; 68 | } 69 | throw new RuntimeException("无法找到代码模板文件在插件中的根路径:" + templateFile.getAbsolutePath()); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/template/AbstractScriptedTemplateGenerator.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.template; 2 | 3 | import com.github.houkunlin.util.PluginUtils; 4 | import com.github.houkunlin.util.ScriptManager; 5 | import lombok.Getter; 6 | 7 | import java.io.File; 8 | import java.util.Map; 9 | 10 | /** 11 | * 可执行脚本的模板生成器 12 | * 13 | * @author daiwenzh5 14 | * @since 1.0 15 | */ 16 | @Getter 17 | public abstract class AbstractScriptedTemplateGenerator implements ITemplateGenerator { 18 | 19 | /** 20 | * 工作空间 21 | */ 22 | private final File workspace; 23 | 24 | /** 25 | * 模板目录 26 | */ 27 | private final File templateDir; 28 | 29 | public AbstractScriptedTemplateGenerator(File workspace) { 30 | this.workspace = workspace; 31 | this.templateDir = workspace.toPath() 32 | .resolve(PluginUtils.TEMPLATE_DIR) 33 | .toFile(); 34 | } 35 | 36 | @Override 37 | public final String generate(String templateName, Map context) throws Exception { 38 | var scriptManager = ScriptManager.of(workspace.toPath() 39 | .resolve(ScriptManager.DIR), true); 40 | registerScriptManager(context, scriptManager); 41 | return doGenerate(templateName, context); 42 | } 43 | 44 | @Override 45 | public final String generateInline(String templateContent, Map context) throws Exception { 46 | var scriptManager = ScriptManager.of(workspace.toPath() 47 | .resolve(ScriptManager.DIR), true); 48 | registerScriptManager(context, scriptManager); 49 | return doGenerateInline(templateContent, context); 50 | } 51 | 52 | /** 53 | * 将{@link ScriptManager}注册到模板引擎的上下文中 54 | * 55 | * @param context 上下文 56 | * @param scriptManager 脚本管理器 57 | */ 58 | protected void registerScriptManager(Map context, ScriptManager scriptManager) { 59 | context.put(ScriptManager.VARIABLE, scriptManager); 60 | } 61 | 62 | /** 63 | * 根据传入的模板名称,读取模板,并执行生成逻辑 64 | * 65 | * @param templateName 模板名称 66 | * @param context 上下文 67 | * @return 生成的代码 68 | * @throws Exception 可能出现的异常 69 | */ 70 | protected abstract String doGenerate(String templateName, Map context) throws Exception; 71 | 72 | /** 73 | * 根据传入的模板内容,直接执行生成逻辑 74 | * 75 | * @param templateContent 模板内容 76 | * @param context 上下文 77 | * @return 生成的代码 78 | * @throws Exception 可能出现的异常 79 | */ 80 | protected abstract String doGenerateInline(String templateContent, Map context) throws Exception; 81 | 82 | } 83 | -------------------------------------------------------------------------------- /doc/upgrade-2.0.0.md: -------------------------------------------------------------------------------- 1 | # 模板升级到 2.x 指南 2 | 3 | 本次发布涉及到目标变量重构,并且不兼容旧版本的模板信息,因此需要针对代码模板进行升级。 4 | 5 | 本文仅涉及部分内容,详细的新版模板变量用法请查看 [模板变量用法](./template-document.md) 6 | 7 | 8 | 9 | 旧版的 `` 指令,改为直接调用 `${gen.setType("TYPE")}` 方法,详细说明请查看模板变量 `gen` 变量说明。 10 | 11 | 12 | 13 | ## 对象名称 14 | 15 | |旧的|新的| 16 | |---|---| 17 | |Entity对象名称
`<#assign entityClass = "${table.entityName}${settings.entitySuffix}" />`|使用`${entity.name.entity}`| 18 | |Service对象名称
`<#assign serviceClass = "${table.entityName}${settings.serviceSuffix}" />`|使用`${entity.name.service}`| 19 | |`<#assign serviceImplClass = "${table.entityName}${settings.serviceSuffix}Impl" />`|使用`${entity.name.serviceImpl}`| 20 | |`<#assign daoClass = "${table.entityName}${settings.daoSuffix}" />`|使用`${entity.name.dao}`| 21 | |`<#assign controllerClass = "${table.entityName}${settings.controllerSuffix}" />`|使用`${entity.name.controller}`| 22 | |`<#assign serviceVar = "${table.entityVar}${settings.serviceSuffix}" />`|使用`${entity.name.service.firstLower}`| 23 | 24 | 25 | 26 | ## 包名 27 | 28 | |旧的|新的| 29 | |---|---| 30 | |`${settings.entityPackage}`|`${entity.packages.entity}`| 31 | |`${settings.dao}`|`${entity.packages.dao}`| 32 | |`${settings.servicePackage}`|`${entity.packages.service}`| 33 | |`${settings.servicePackage}.impl`|`${entity.packages.serviceImpl}`| 34 | |`${settings.controllerPackage}`|`${entity.packages.controller}`| 35 | |---|---| 36 | |`import ${settings.entityPackage}.${entityClass}`|`import ${entity.packages.entity.full}`| 37 | |`import ${settings.servicePackage}.${serviceClass}`|`import ${entity.packages.service.full}`| 38 | |`import ${settings.daoPackage}.${daoClass}`|`import ${entity.packages.dao.full}`| 39 | |---|---| 40 | |实体类导入包|`${entity.packages}`| 41 | 42 | 43 | 44 | ## 实体类 45 | 46 | | 旧的 | 新的 | 47 | | ---- | ---- | 48 | |导入包 `<#list table.getPackages() as package>import ${package};`| `${entity.packages}`| 49 | |实体类名称`${table.entityName}`| `${entity.name}` 不含后缀 | 50 | |实体类名称`${table.entityName}${settings.entitySuffix}`| `${entity.name.entity}`含后缀 | 51 | |数据库表名`${table.tableName}`|`${table.name}`| 52 | |Entity注释`${table.comment}`|`${entity.comment}`| 53 | |Entity字段`<#list table.columns as col>`|`<#list fields as field>`| 54 | |获取Entity字段类型`${col.columnType.shortName}`|`${field.typeName}`| 55 | |获取Entity字段名`${col.fieldName}`|`${field.name}`| 56 | |获取Entity字段注释`${col.comment}`|`${field.comment}`| 57 | |Entity字段首字母大写`${col.fieldMethod}`|`${field.name.firstUpper}`| 58 | 59 | 60 | 61 | ## 其他 62 | 63 | 在 1.x 版本中有一个 `${table.getPrimaryColumn().fieldMethod}` 获取主键字段信息,在 2.0.0 中未引入该功能,在 2.1.0 中重新引入了该功能,具体用法为 `${primary.field.name.firstUpper}` 通过 `primary` 变量来获取到主键的信息,详细用法请查看模板变量说明文档。 64 | 65 | 66 | 67 | 在 1.x 版本中可在 `table.columns` 中获取到数据库字段、Java字段的字段名称,在 2.0.0 版本中未引入改功能,在 2.1.0 中重新引入了该功能,具体用法为 `${field.column.name}` 从Java字段获取该字段对应的数据库字段对象的字段名称,或者 `${column.field.name}` 从数据库字段获取该字段对应的Java字段对象的字段名称。 -------------------------------------------------------------------------------- /doc/plugin/description.md: -------------------------------------------------------------------------------- 1 | [Github](https://github.com/houkunlin/Database-Generator) | [Gitee](https://gitee.com/houkunlin/Database-Generator) | Document 2 | 3 | **English (Google Translate)** / 中文 4 | 5 | A code generator that relies on the IDEA Database tool to generate the corresponding Java code through the database table structure. The plug-in provides a simple set of add, delete, and modify code templates. You can also add custom templates to generate front-end code or other related code. 6 | 7 | Provide `all-variable.ftl` template file to illustrate all variable usage and corresponding results. 8 | 9 | This plugin supports `beetl`/`freemarker`/`velocity` code templates of three template engines. You can choose which template engine to use for the file suffix (`btl`/`ftl`/`vm`). For custom templates, please Read Template Variable Document to understand the contents of related variables before custom development. 10 | 11 | This plug-in supports calling the methods in the Groovy script defined by yourself in the template file, please refer to the Groovy Script Instructions for details 。 12 | 13 | **Please check the Code Template Upgrade Guide, and check the detailed writing Template Variable Document, or check out the code template that covers the use of all variables all-variable.ftl** 14 | 15 |
16 | 17 | **中文** / English 18 | 19 | 一个依赖 IDEA Database 工具的代码生成器,通过数据库表结构生成相应的Java代码,插件提供一套简单的增删查改代码模板,也可以新增自定义模板来生成前端代码或其他相关的代码。 20 | 21 | 提供 `all-variable.ftl` 模板文件来说明所有的变量使用和对应结果。 22 | 23 | 本插件支持 `beetl`/`freemarker`/`velocity` 三种模板引擎的代码模板,通过文件后缀(`btl`/`ftl`/`vm`)来选择使用什么模板引擎渲染,自定义模板请阅读 模板变量文档 了解相关变量内容后再进行自定义开发。 24 | 25 | 本插件支持在模板文件中调用自己定义好的 Groovy 脚本中的方法,详细用法请参考 Groovy 脚本使用说明 。 26 | 27 | **请查看 代码模板升级指南 ,和查看详细的编写 模板变量文档 ,或者查看涵盖所有变量使用的代码模板 all-variable.ftl** 28 | 29 |
30 | Author: HouKunLin
31 | Email: houkunlin@aliyun.com 32 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/ui/win/tree/CheckBoxTreeCellRenderer.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.ui.win.tree; 2 | 3 | import javax.swing.*; 4 | import javax.swing.tree.TreeCellRenderer; 5 | import java.awt.*; 6 | 7 | /** 8 | * 复选框的树形渲染 9 | * 10 | * @author HouKunLin 11 | */ 12 | public class CheckBoxTreeCellRenderer extends JPanel implements TreeCellRenderer { 13 | protected final JCheckBox check; 14 | protected final CheckBoxTreeLabel label; 15 | 16 | public CheckBoxTreeCellRenderer() { 17 | setLayout(null); 18 | add(check = new JCheckBox()); 19 | add(label = new CheckBoxTreeLabel()); 20 | check.setBackground(UIManager.getColor("Tree.textBackground")); 21 | check.setOpaque(false); 22 | label.setForeground(UIManager.getColor("Tree.textForeground")); 23 | } 24 | 25 | /** 26 | * 返回的是一个JPanel对象,该对象中包含一个JCheckBox对象 27 | * 和一个JLabel对象。并且根据每个结点是否被选中来决定JCheckBox 28 | * 是否被选中。 29 | */ 30 | @Override 31 | public Component getTreeCellRendererComponent(JTree tree, Object value, 32 | boolean selected, boolean expanded, boolean leaf, int row, 33 | boolean hasFocus) { 34 | String stringValue = tree.convertValueToText(value, selected, expanded, leaf, row, hasFocus); 35 | setEnabled(tree.isEnabled()); 36 | check.setSelected(((CheckBoxTreeNode) value).isSelected()); 37 | label.setFont(tree.getFont()); 38 | label.setText(stringValue); 39 | label.setSelected(selected); 40 | label.setHasFocus(hasFocus); 41 | /*if (leaf) 42 | label.setIcon(UIManager.getIcon("Tree.leafIcon")); 43 | else if (expanded) 44 | label.setIcon(UIManager.getIcon("Tree.openIcon")); 45 | else 46 | label.setIcon(UIManager.getIcon("Tree.closedIcon"));*/ 47 | label.setOpaque(false); 48 | this.setOpaque(false); 49 | return this; 50 | } 51 | 52 | @Override 53 | public Dimension getPreferredSize() { 54 | Dimension dCheck = check.getPreferredSize(); 55 | Dimension dLabel = label.getPreferredSize(); 56 | return new Dimension(dCheck.width + dLabel.width, Math.max(dCheck.height, dLabel.height)); 57 | } 58 | 59 | @Override 60 | public void doLayout() { 61 | Dimension dCheck = check.getPreferredSize(); 62 | Dimension dLabel = label.getPreferredSize(); 63 | int yCheck = 0; 64 | int yLabel = 0; 65 | if (dCheck.height < dLabel.height) { 66 | yCheck = (dLabel.height - dCheck.height) / 2; 67 | } else { 68 | yLabel = (dCheck.height - dLabel.height) / 2; 69 | } 70 | check.setLocation(0, yCheck); 71 | check.setBounds(0, yCheck, dCheck.width, dCheck.height); 72 | label.setLocation(dCheck.width, yLabel); 73 | label.setBounds(dCheck.width, yLabel, dLabel.width, dLabel.height); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/ui/win/table/EditorTableCellEditor.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.ui.win.table; 2 | 3 | import com.intellij.ide.highlighter.JavaFileType; 4 | import com.intellij.openapi.editor.Document; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.psi.JavaCodeFragment; 7 | import com.intellij.psi.JavaCodeFragmentFactory; 8 | import com.intellij.psi.PsiDocumentManager; 9 | import com.intellij.ui.EditorTextField; 10 | import com.intellij.util.ui.AbstractTableCellEditor; 11 | 12 | import javax.swing.*; 13 | import java.awt.*; 14 | import java.awt.event.FocusEvent; 15 | import java.awt.event.FocusListener; 16 | import java.awt.event.MouseEvent; 17 | import java.util.EventObject; 18 | import java.util.Objects; 19 | 20 | /** 21 | * 支持自动完成包名的表格编辑器 22 | * 23 | * @author daiwenzh5 24 | * @since 2.8.4 25 | */ 26 | public class EditorTableCellEditor extends AbstractTableCellEditor { 27 | 28 | private final EditorTextField editorTextField; 29 | 30 | /** 31 | * 获取焦点时的单元格值 32 | */ 33 | private String cellValue; 34 | 35 | public EditorTableCellEditor(Project project) { 36 | this.editorTextField = createEditorTextField(project); 37 | // 当获取到焦点时,尝试将输入框的值设置为单元格值 38 | this.editorTextField.addFocusListener(new FocusListener() { 39 | @Override 40 | public void focusGained(FocusEvent e) { 41 | if (editorTextField.getText() 42 | .equals(cellValue)) { 43 | return; 44 | } 45 | editorTextField.setText(Objects.toString(cellValue, "")); 46 | } 47 | 48 | @Override 49 | public void focusLost(FocusEvent e) { 50 | 51 | } 52 | }); 53 | } 54 | 55 | @Override 56 | public boolean isCellEditable(EventObject e) { 57 | if (e instanceof MouseEvent mouseEvent) { 58 | return mouseEvent.getClickCount() >= 2; 59 | } 60 | return false; 61 | } 62 | 63 | @Override 64 | public Object getCellEditorValue() { 65 | return editorTextField.getText(); 66 | } 67 | 68 | @Override 69 | public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { 70 | // 记录单元格的值 71 | this.cellValue = value.toString(); 72 | return editorTextField; 73 | } 74 | 75 | private EditorTextField createEditorTextField(Project project) { 76 | // https://jetbrains.org/intellij/sdk/docs/user_interface_components/editor_components.html 77 | JavaCodeFragment code = JavaCodeFragmentFactory.getInstance(project) 78 | .createReferenceCodeFragment("", null, true, false); 79 | Document document = PsiDocumentManager.getInstance(project) 80 | .getDocument(code); 81 | JavaFileType fileType = JavaFileType.INSTANCE; 82 | return new EditorTextField(document, project, fileType); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/resources/config.yml: -------------------------------------------------------------------------------- 1 | developer: 2 | author: '' 3 | email: '' 4 | options: 5 | overrideJava: true 6 | overrideXml: true 7 | overrideOther: true 8 | settings: 9 | javaPath: src/main/java 10 | resourcesPath: src/main/resources 11 | fileTypes: 12 | - type: entity 13 | suffix: Entity 14 | packageName: com.example.entity 15 | ext: .java 16 | path: src/main/java 17 | override: true 18 | - type: dao 19 | suffix: Repository 20 | packageName: com.example.repository 21 | ext: .java 22 | path: src/main/java 23 | override: true 24 | - type: service 25 | suffix: Service 26 | packageName: com.example.service 27 | ext: .java 28 | path: src/main/java 29 | override: true 30 | - type: serviceImpl 31 | suffix: ServiceImpl 32 | packageName: com.example.service.impl 33 | ext: .java 34 | path: src/main/java 35 | override: true 36 | - type: controller 37 | suffix: Controller 38 | packageName: com.example.controller 39 | ext: .java 40 | path: src/main/java 41 | override: true 42 | - type: xml 43 | suffix: Mapper 44 | packageName: mapper 45 | ext: .xml 46 | path: src/main/resources 47 | override: true 48 | types: 49 | - shortName: Object 50 | longName: java.lang.Object 51 | isDefault: true 52 | dbTypes: [] 53 | - shortName: Boolean 54 | longName: java.lang.Boolean 55 | dbTypes: 56 | - ^bit$ 57 | - ^bool # Postgresql 58 | - ^boolean # Postgresql 59 | - shortName: Long 60 | longName: java.lang.Long 61 | dbTypes: 62 | - ^bigint 63 | - shortName: Integer 64 | longName: java.lang.Integer 65 | dbTypes: 66 | - ^tinyint 67 | - ^smallint 68 | - ^mediumint 69 | - ^integer # Postgresql 70 | - ^interval # Postgresql 71 | - ^int 72 | - shortName: String 73 | longName: java.lang.String 74 | dbTypes: 75 | - ^char 76 | - ^varchar 77 | - ^text 78 | - ^longtext 79 | - ^tinytext 80 | - ^cidr # Postgresql 81 | - ^inet # Postgresql 82 | - ^macaddr # Postgresql 83 | - ^path # Postgresql 84 | - ^uuid # Postgresql 85 | - ^xml # Postgresql 86 | - shortName: BigDecimal 87 | longName: java.math.BigDecimal 88 | dbTypes: 89 | - ^decimal 90 | - ^numeric 91 | - ^money # Postgresql 92 | - shortName: Float 93 | longName: java.lang.Float 94 | dbTypes: 95 | - ^float 96 | - ^real # Postgresql 97 | - shortName: Double 98 | longName: java.lang.Double 99 | dbTypes: 100 | - ^double 101 | - shortName: LocalDateTime 102 | longName: java.time.LocalDateTime 103 | dbTypes: 104 | - ^datetime 105 | - ^timestamp 106 | - shortName: LocalDate 107 | longName: java.time.LocalDate 108 | dbTypes: 109 | - ^date 110 | - shortName: LocalTime 111 | longName: java.time.LocalTime 112 | dbTypes: 113 | - ^time 114 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/vo/impl/EntityFieldImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.vo.impl; 2 | 3 | import com.github.houkunlin.config.Options; 4 | import com.github.houkunlin.model.TableColumnType; 5 | import com.github.houkunlin.util.PluginUtils; 6 | import com.github.houkunlin.vo.IEntityField; 7 | import com.github.houkunlin.vo.ITableColumn; 8 | import com.intellij.database.model.DasColumn; 9 | import com.intellij.database.model.DataType; 10 | import com.intellij.database.util.DasUtil; 11 | import lombok.EqualsAndHashCode; 12 | import lombok.Getter; 13 | import lombok.Setter; 14 | import lombok.ToString; 15 | 16 | import java.util.Objects; 17 | 18 | /** 19 | * 实体类字段信息 20 | * 21 | * @author HouKunLin 22 | */ 23 | @Getter 24 | public class EntityFieldImpl implements IEntityField { 25 | private final FieldNameInfo name; 26 | private final String typeName; 27 | private final DataType dataType; 28 | private final String fullTypeName; 29 | private final boolean primaryKey; 30 | @Setter 31 | @ToString.Exclude 32 | @EqualsAndHashCode.Exclude 33 | private ITableColumn column; 34 | @Setter 35 | private String comment; 36 | @Setter 37 | private boolean selected; 38 | 39 | private EntityFieldImpl(FieldNameInfo name, String typeName, String fullTypeName, boolean primaryKey, String comment, boolean selected) { 40 | this.name = name; 41 | this.typeName = typeName; 42 | this.dataType = null; 43 | this.fullTypeName = fullTypeName; 44 | this.primaryKey = primaryKey; 45 | this.comment = comment; 46 | this.selected = selected; 47 | } 48 | 49 | public EntityFieldImpl(DasColumn dbColumn, Options options) { 50 | this.name = new FieldNameInfo(dbColumn, options); 51 | this.dataType = dbColumn.getDasType().toDataType(); 52 | // 获取完整的数据库类型,如:varchar(255),char(1) 53 | var columnType = type(dataType.toString()); 54 | this.typeName = columnType.getShortName(); 55 | this.fullTypeName = columnType.getLongName(); 56 | this.comment = Objects.toString(dbColumn.getComment(), ""); 57 | this.primaryKey = DasUtil.isPrimary(dbColumn); 58 | this.selected = true; 59 | } 60 | 61 | /** 62 | * 创建主键字段对象 63 | * 64 | * @param name 字段名称 65 | * @param typeName 字段类型 66 | * @param fullTypeName 字段完整类型 67 | * @param comment 字段注释 68 | * @return 字段对象 69 | */ 70 | public static EntityFieldImpl primaryField(String name, String typeName, String fullTypeName, String comment) { 71 | FieldNameInfo fieldNameInfo = new FieldNameInfo(name); 72 | return new EntityFieldImpl(fieldNameInfo, typeName, fullTypeName, true, comment, true); 73 | } 74 | 75 | public TableColumnType type(String dbType) { 76 | TableColumnType[] columnTypes = PluginUtils.getColumnTypes(); 77 | if (dbType == null) { 78 | return TableColumnType.DEFAULT; 79 | } 80 | if (columnTypes == null) { 81 | return TableColumnType.DEFAULT; 82 | } 83 | for (TableColumnType columnType : columnTypes) { 84 | if (columnType.at(dbType)) { 85 | return columnType; 86 | } 87 | } 88 | for (TableColumnType columnType : columnTypes) { 89 | if (columnType.isDefault()) { 90 | return columnType; 91 | } 92 | } 93 | return columnTypes.length > 0 ? columnTypes[0] : TableColumnType.DEFAULT; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/util/YamlUtils.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.util; 2 | 3 | import lombok.experimental.UtilityClass; 4 | import org.yaml.snakeyaml.LoaderOptions; 5 | import org.yaml.snakeyaml.Yaml; 6 | import org.yaml.snakeyaml.constructor.Constructor; 7 | import org.yaml.snakeyaml.introspector.Property; 8 | import org.yaml.snakeyaml.introspector.PropertyUtils; 9 | 10 | import java.io.InputStream; 11 | import java.io.Reader; 12 | import java.util.Map; 13 | import java.util.Optional; 14 | import java.util.concurrent.ConcurrentHashMap; 15 | import java.util.function.Function; 16 | 17 | /** 18 | * yaml工具 19 | * @author daiwenzh5 20 | * @since 2.8.4 21 | */ 22 | @UtilityClass 23 | public class YamlUtils { 24 | 25 | private final static Map cache = new ConcurrentHashMap<>(); 26 | 27 | /** 28 | * 从输入流中读取yaml文件,并转换为指定对象 29 | * 30 | * @param input 输入流 31 | * @param clazz 类 32 | * @param 对象类型 33 | * @return 对象 34 | */ 35 | public Optional load(InputStream input, Class clazz) { 36 | return load(clazz, yaml -> yaml.loadAs(input, clazz)); 37 | } 38 | 39 | /** 40 | * 从阅读字符流中读取yaml文件,并转换为指定对象 41 | * 42 | * @param input 阅读字符流 43 | * @param clazz 类 44 | * @param 对象类型 45 | * @return 对象 46 | */ 47 | public Optional load(Reader input, Class clazz) { 48 | return load(clazz, yaml -> yaml.loadAs(input, clazz)); 49 | } 50 | 51 | /** 52 | * 从字符串中读取yaml文件,并转换为指定对象 53 | * 54 | * @param input 输入字符串 55 | * @param clazz 类 56 | * @param 对象类型 57 | * @return 对象 58 | */ 59 | public Optional load(String input, Class clazz) { 60 | return load(clazz, yaml -> yaml.loadAs(input, clazz)); 61 | } 62 | 63 | private Optional load(Class clazz, Function loader) { 64 | try { 65 | var yaml = cache.computeIfAbsent(clazz.getName(), k -> buildYamlInstance(clazz)); 66 | return Optional.ofNullable(loader.apply(yaml)); 67 | } catch (Exception e) { 68 | return Optional.empty(); 69 | } 70 | } 71 | 72 | private Yaml buildYamlInstance(Class clazz) { 73 | var constructor = new Constructor(clazz, new LoaderOptions()); 74 | constructor.setPropertyUtils(new PropertyUtils() { 75 | @Override 76 | public Property getProperty(Class type, String name) { 77 | if (name.indexOf('-') > -1) { 78 | name = camelize(name); 79 | } 80 | // 关键代码 忽略yaml中无法在类中找到属性的字段 81 | setSkipMissingProperties(true); 82 | return super.getProperty(type, name); 83 | } 84 | }); 85 | return new Yaml(constructor); 86 | } 87 | 88 | private static String camelize(String input) { 89 | for (int i = 0; i < input.length(); i++) { 90 | if (input.charAt(i) == '-') { 91 | input = input.substring(0, i) + input.substring(i + 1, i + 2) 92 | .toUpperCase() + input.substring(i + 2); 93 | } 94 | if (input.charAt(i) == ' ') { 95 | input = input.substring(0, i) + input.substring(i + 1, i + 2) 96 | .toUpperCase() + input.substring(i + 2); 97 | } 98 | } 99 | return input; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/ui/win/BaseSetting.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 |
87 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/task/GeneratorTask.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.task; 2 | 3 | import com.github.houkunlin.util.FileUtils; 4 | import com.github.houkunlin.util.Generator; 5 | import com.github.houkunlin.util.PluginUtils; 6 | import com.github.houkunlin.vo.impl.RootModel; 7 | import com.intellij.openapi.progress.ProgressIndicator; 8 | import com.intellij.openapi.progress.Task; 9 | import com.intellij.openapi.project.DumbService; 10 | import com.intellij.openapi.project.Project; 11 | import com.intellij.openapi.ui.Messages; 12 | import com.intellij.psi.PsiFile; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | import javax.swing.*; 16 | import java.io.File; 17 | import java.util.List; 18 | 19 | /** 20 | * @author HouKunLin 21 | */ 22 | public class GeneratorTask extends Task.Modal { 23 | private final Project project; 24 | private final JFrame windows; 25 | private final Generator generator; 26 | private final List templates; 27 | private final List rootModels; 28 | 29 | public GeneratorTask(@NotNull Project project, JFrame windows, Generator generator, List templates, List rootModels) { 30 | super(project, "生成代码", false); 31 | this.project = project; 32 | this.windows = windows; 33 | this.generator = generator; 34 | this.templates = templates; 35 | this.rootModels = rootModels; 36 | } 37 | 38 | @Override 39 | public void run(@NotNull ProgressIndicator indicator) { 40 | indicator.setIndeterminate(false); 41 | 42 | generator(indicator); 43 | } 44 | 45 | public void generator(ProgressIndicator indicator) { 46 | indicator.setText("正在生成代码 ......"); 47 | 48 | indicator.setFraction(0.0); 49 | 50 | int modelSize = rootModels.size(); 51 | int templateSize = templates.size(); 52 | 53 | int countInt = modelSize * templateSize; 54 | double count = (countInt) * 1.0; 55 | for (int i = 0; i < modelSize; i++) { 56 | RootModel rootModel = rootModels.get(i); 57 | int start = i * templateSize; 58 | generator.generator(rootModel, templates, (integer, filename) -> { 59 | int index = start + integer + 1; 60 | indicator.setText2(String.format("[%s/%s] [%s] --> %s", 61 | index, countInt, rootModel.getTable().getName(), filename)); 62 | indicator.setFraction(index / count); 63 | }); 64 | } 65 | indicator.setText("生成代码完毕!"); 66 | indicator.setText2("正在执行收尾工作,请稍候 ......"); 67 | 68 | indicator.setFraction(1.0); 69 | indicator.setIndeterminate(true); 70 | PluginUtils.refreshProject(); 71 | } 72 | 73 | @Override 74 | public void onSuccess() { 75 | super.onSuccess(); 76 | windows.dispose(); 77 | DumbService dumbService = DumbService.getInstance(project); 78 | if (dumbService.isDumb()) { 79 | dumbService.showDumbModeNotification("正在等待其他任务完成后才能进行格式化代码操作 ..."); 80 | } 81 | dumbService.smartInvokeLater(() -> { 82 | List saveFiles = generator.getSaveFiles(); 83 | if (!saveFiles.isEmpty()) { 84 | FileUtils.getInstance().reformatCode(project, saveFiles.toArray(new PsiFile[0])); 85 | } 86 | }); 87 | Messages.showInfoMessage(String.format("代码构建完毕,涉及 %s 个数据库表和 %s 个模板文件,总共生成 %s 个文件。", rootModels.size(), templates.size(), generator.getSaveFiles().size()), "完成"); 88 | } 89 | 90 | @Override 91 | public void onThrowable(@NotNull Throwable error) { 92 | super.onThrowable(error); 93 | Messages.showErrorDialog("代码生成失败,当前插件 2.x 版本不兼容旧版的代码模板,请升级代码模板,代码模板升级指南请查看插件介绍。\n\n" + error.getMessage(), "生成代码失败"); 94 | windows.setVisible(true); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /.github/workflows/upload-plugin.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Gradle 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle 3 | 4 | name: Upload Plugin 5 | 6 | on: 7 | push: 8 | tags: [ v* ] 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Set up JDK 17 18 | uses: actions/setup-java@v1 19 | with: 20 | java-version: 17 21 | - name: Grant execute permission for gradlew 22 | run: chmod +x gradlew 23 | - name: Generator Plugin Doc Markdown Text 24 | env: # Or as an environment variable 25 | intellijPublishToken: ${{ secrets.INTELLIJPUBLISHTOKEN }} 26 | # 不打包 Kotlin 标准依赖:https://kotlinlang.org/docs/reference/using-gradle.html#dependency-on-the-standard-library 27 | run: ./gradlew markdownToHtml 28 | - name: Build Plugin with Gradle 29 | env: # Or as an environment variable 30 | intellijPublishToken: ${{ secrets.INTELLIJPUBLISHTOKEN }} 31 | # 不打包 Kotlin 标准依赖:https://kotlinlang.org/docs/reference/using-gradle.html#dependency-on-the-standard-library 32 | run: ./gradlew buildPlugin -Pkotlin.stdlib.default.dependency=false 33 | - name: Publish Plugin with Gradle 34 | env: # Or as an environment variable 35 | intellijPublishToken: ${{ secrets.INTELLIJPUBLISHTOKEN }} 36 | # 不打包 Kotlin 标准依赖:https://kotlinlang.org/docs/reference/using-gradle.html#dependency-on-the-standard-library 37 | run: ./gradlew publishPlugin -Pkotlin.stdlib.default.dependency=false 38 | - name: Rename 39 | run: mv build/distributions/*.zip build/distributions/plugin.zip 40 | - name: Create Release 41 | id: create_release 42 | uses: actions/create-release@v1 43 | env: 44 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 45 | with: 46 | tag_name: ${{ github.ref }} 47 | release_name: Release ${{ github.ref }} 48 | draft: false 49 | prerelease: false 50 | - name: Upload Release Asset 51 | id: upload-release-asset 52 | uses: actions/upload-release-asset@v1 53 | env: 54 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 55 | with: 56 | # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps 57 | upload_url: ${{ steps.create_release.outputs.upload_url }} 58 | asset_path: build/distributions/plugin.zip 59 | asset_name: Database_Generator.zip 60 | asset_content_type: application/zip 61 | - name: Unpack Plugin Zip 62 | run: unzip -d tmp/ build/distributions/plugin.zip 63 | - uses: actions/upload-artifact@v4 64 | with: 65 | # Name of the artifact to upload. 66 | # Optional. Default is 'artifact' 67 | name: Database_Generator 68 | # A file, directory or wildcard pattern that describes what to upload 69 | # Required. 70 | path: tmp/**/* 71 | # The desired behavior if no files are found using the provided path. 72 | # Available Options: 73 | # warn: Output a warning but do not fail the action 74 | # error: Fail the action with an error message 75 | # ignore: Do not output any warnings or errors, the action does not fail 76 | # Optional. Default is 'warn' 77 | if-no-files-found: warn 78 | # Duration after which artifact will expire in days. 0 means using default retention. 79 | # Minimum 1 day. 80 | # Maximum 90 days unless changed from the repository settings page. 81 | # Optional. Defaults to repository settings. 82 | retention-days: 7 83 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/ui/win/TablePanel.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 |
93 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/util/ScriptManager.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.util; 2 | 3 | import com.intellij.openapi.diagnostic.Logger; 4 | import groovy.lang.GroovyClassLoader; 5 | 6 | import java.io.IOException; 7 | import java.nio.file.Files; 8 | import java.nio.file.Path; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | import java.util.WeakHashMap; 12 | import java.util.function.BiConsumer; 13 | 14 | /** 15 | * 脚本管理器 16 | * 17 | * @author daiwenzh5 18 | * @since 1.0 19 | */ 20 | public class ScriptManager { 21 | 22 | private final static Logger logger = Logger.getInstance(ScriptManager.class); 23 | 24 | /** 25 | * 脚本目录 26 | */ 27 | public final static String DIR = "scripts"; 28 | 29 | /** 30 | * 变量名 31 | */ 32 | public final static String VARIABLE = DIR; 33 | 34 | /** 35 | * 命名空间 36 | */ 37 | public final static String NAMESPACE = VARIABLE + "."; 38 | 39 | 40 | /** 41 | * 缓存,确保相同的脚本目录,只加载一次 42 | */ 43 | private final static WeakHashMap cache = new WeakHashMap<>(); 44 | private final Map scripts = new HashMap<>(); 45 | private static final ScriptManager EMPTY = new ScriptManager(); 46 | private final Path scriptDir; 47 | private final GroovyClassLoader groovyClassLoader = new GroovyClassLoader(); 48 | 49 | private ScriptManager() { 50 | this.scriptDir = null; 51 | } 52 | 53 | private ScriptManager(Path scriptDir) { 54 | if (!Files.exists(scriptDir)) { 55 | logger.warn("脚本: " + scriptDir + "不存在"); 56 | this.scriptDir = null; 57 | return; 58 | } 59 | this.scriptDir = scriptDir; 60 | scan(); 61 | } 62 | 63 | 64 | /** 65 | * 根据脚本目录获取脚本管理器,如果目录不存在,则返回一个空的脚本管理器。对于相同目录,优先从内存在获取。 66 | * 67 | * @param scriptDir 脚本目录 68 | * @param reload 是否重新加载 69 | * @return 一个脚本管理器 70 | */ 71 | public static ScriptManager of(Path scriptDir, boolean reload) { 72 | if (scriptDir == null) { 73 | return EMPTY; 74 | } 75 | var scriptManager = cache.computeIfAbsent(scriptDir.toString(), key -> new ScriptManager(scriptDir)); 76 | if (reload) { 77 | scriptManager.scan(); 78 | } 79 | return scriptManager; 80 | } 81 | 82 | 83 | private void scan() { 84 | if (scriptDir == null) { 85 | return; 86 | } 87 | // 扫描脚本目录,加载所有.groovy文件 88 | try (var files = Files.walk(scriptDir)) { 89 | files.filter(path -> path.toString() 90 | .endsWith(".groovy")) 91 | .forEach(this::registerScript); 92 | } catch (IOException ignored) { 93 | 94 | } 95 | 96 | } 97 | 98 | private void registerScript(Path path) { 99 | try { 100 | var file = path.toFile(); 101 | Class parsedClass = groovyClassLoader.parseClass(file); 102 | var fileName = file.getName(); 103 | var instance = parsedClass.getDeclaredConstructor() 104 | .newInstance(); 105 | logger.info("load script: " + scriptDir.relativize(path)); 106 | scripts.put(fileName.replace(".groovy", ""), instance); 107 | } catch (Exception e) { 108 | throw new RuntimeException("Failed to load script: " + path, e); 109 | } 110 | } 111 | 112 | public Object get(String fileName) { 113 | return scripts.get(fileName); 114 | } 115 | 116 | /** 117 | * 遍历所有方法 118 | * 119 | * @param action 操作 120 | */ 121 | public void forEach(BiConsumer action) { 122 | scripts.forEach(action); 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/config/ConfigService.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.config; 2 | 3 | import com.github.houkunlin.util.PluginUtils; 4 | import com.intellij.openapi.components.PersistentStateComponent; 5 | import com.intellij.openapi.components.State; 6 | import com.intellij.openapi.components.Storage; 7 | import com.intellij.openapi.project.Project; 8 | import com.intellij.util.xmlb.XmlSerializerUtil; 9 | import lombok.Data; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | import org.mapstruct.factory.Mappers; 13 | 14 | import java.util.ArrayList; 15 | import java.util.Collections; 16 | import java.util.List; 17 | 18 | /** 19 | * 配置 Service 20 | * 21 | * @author HouKunLin 22 | */ 23 | @Data 24 | @State(name = "com.github.houkunlin.database.generator.config.ConfigService", 25 | defaultStateAsResource = true, 26 | storages = {@Storage("database-generator-config.xml")}) 27 | public class ConfigService implements PersistentStateComponent { 28 | public static final BeanTransform BEAN_TRANSFORM = Mappers.getMapper(BeanTransform.class); 29 | private Developer developer; 30 | private Options options; 31 | private Settings settings; 32 | private final List lastSelectionTemplates = new ArrayList<>(); 33 | 34 | public ConfigService() { 35 | ConfigVo configVo = PluginUtils.loadConfig(); 36 | developer = configVo.getDeveloper(); 37 | options = configVo.getOptions(); 38 | settings = configVo.getSettings(); 39 | assert developer != null; 40 | assert options != null; 41 | assert settings != null; 42 | } 43 | 44 | /** 45 | * 重新从配置文件中读取配置信息 46 | */ 47 | public synchronized void reset(){ 48 | ConfigVo configVo = PluginUtils.loadConfig(); 49 | Developer developer = configVo.getDeveloper(); 50 | Options options = configVo.getOptions(); 51 | Settings settings = configVo.getSettings(); 52 | assert developer != null; 53 | assert options != null; 54 | assert settings != null; 55 | if (settings.getProjectPath() == null) { 56 | settings.setProjectPath(PluginUtils.getProjectPath().normalize().toString()); 57 | } 58 | this.lastSelectionTemplates.clear(); 59 | BEAN_TRANSFORM.copyTo(developer, this.developer); 60 | BEAN_TRANSFORM.copyTo(options, this.options); 61 | BEAN_TRANSFORM.copyTo(settings, this.settings); 62 | } 63 | 64 | @Nullable 65 | public static ConfigService getInstance(Project project) { 66 | return project.getService(ConfigService.class); 67 | } 68 | 69 | @Nullable 70 | @Override 71 | public ConfigService getState() { 72 | return this; 73 | } 74 | 75 | @Override 76 | public void loadState(@NotNull ConfigService state) { 77 | XmlSerializerUtil.copyBean(state, this); 78 | } 79 | 80 | /** 81 | * 设置上次选择的模板列表,仅当启用 "记住上次选择的模板" 选项时,才生效 82 | * 83 | * @param lastSelectionTemplates 上次选择的模板列表 84 | */ 85 | public void setLastSelectionTemplates(List lastSelectionTemplates) { 86 | if (this.options == null || !this.options.isRetainLastSelectionTemplates()) { 87 | return; 88 | } 89 | this.lastSelectionTemplates.clear(); 90 | this.lastSelectionTemplates.addAll(lastSelectionTemplates); 91 | } 92 | 93 | /** 94 | * 获取上次选择的模板列表, 当未启用 "记住上次选择的模板" 选项时,直接返回空列表 95 | * @return 上次选择的模板列表 96 | */ 97 | public List getLastSelectionTemplates() { 98 | if (this.options == null || !this.options.isRetainLastSelectionTemplates()) { 99 | return Collections.emptyList(); 100 | } 101 | return this.lastSelectionTemplates; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /templates/MyBatisMapper.xml.ftl: -------------------------------------------------------------------------------- 1 | <#include "jdbc-type.ftl"> 2 | ${gen.setType("xml")}<#-- 生成 MyBatis 的 Mapper.xml 内容代码模板 --> 3 | 4 | 5 | 6 | 7 | <#list columns as column> 8 | <#-- ${column.name}:${column.typeName} --> 9 | <#if column.primaryKey> 10 | 11 | <#else> 12 | 13 | 14 | 15 | 16 | 17 | <#list columns as column>${column.name}<#if column?has_next>, 18 | 19 | 25 | 26 | delete from ${table.name} 27 | where ${primary.column.name} = ${r'#'}{${primary.field.name},jdbcType=${jdbcType(primary.column)}} 28 | 29 | 31 | insert into ${table.name} ( 32 | <#list columns as column>${column.name}<#if column?has_next>, 33 | ) 34 | values ( 35 | <#list columns as column>${r'#'}{${column.field.name},jdbcType=${jdbcType(column)}}<#if column?has_next>, 36 | ) 37 | 38 | 40 | insert into ${table.name} 41 | 42 | <#list columns as column> 43 | 44 | ${column.name}, 45 | 46 | 47 | 48 | 49 | <#list columns as column> 50 | 51 | ${r'#'}{${column.field.name},jdbcType=${jdbcType(column)}}, 52 | 53 | 54 | 55 | 56 | 57 | update ${table.name} 58 | 59 | <#list columns as column> 60 | 61 | ${column.name} = ${r'#'}{${column.field.name},jdbcType=${jdbcType(column)}}, 62 | 63 | 64 | 65 | where ${primary.column.name} = ${r'#'}{${primary.field.name},jdbcType=${jdbcType(primary.column)}} 66 | 67 | 68 | update ${table.name} 69 | set 70 | <#list columns as column> 71 | ${column.name} = ${r'#'}{${column.field.name},jdbcType=${jdbcType(column)}}<#if column?has_next>, 72 | 73 | where ${primary.column.name} = ${r'#'}{${primary.field.name},jdbcType=${jdbcType(primary.column)}} 74 | 75 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/ui/win/Main.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 |
99 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/ui/win/table/FileTypeTableDecorator.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.ui.win.table; 2 | 3 | import com.github.houkunlin.config.Settings; 4 | import com.github.houkunlin.message.Bundles; 5 | import com.github.houkunlin.model.FileType; 6 | import com.github.houkunlin.util.PluginUtils; 7 | import com.intellij.openapi.actionSystem.ActionToolbarPosition; 8 | import com.intellij.openapi.project.Project; 9 | import com.intellij.ui.ToolbarDecorator; 10 | import com.intellij.util.ObjectUtils; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | import javax.swing.*; 14 | 15 | /** 16 | * 文件类型表格 17 | * 18 | * @author daiwenzh5 19 | * @since 2.8.4 20 | */ 21 | public class FileTypeTableDecorator extends TableDecorator { 22 | 23 | private FileTypeTableDecorator() { 24 | } 25 | 26 | public static JPanel create(Project project, Settings settings) { 27 | return new FileTypeTableDecorator() 28 | .setModel(createTableModel(project, settings)) 29 | .applyToolbar((tableDecorator, toolbarDecorator) -> configurationToolbar(settings, tableDecorator, toolbarDecorator)); 30 | } 31 | 32 | private static void configurationToolbar(Settings settings, FileTypeTableDecorator tableDecorator, ToolbarDecorator toolbarDecorator) { 33 | toolbarDecorator 34 | .setToolbarPosition(ActionToolbarPosition.TOP) 35 | .setAddAction(btn -> tableDecorator.getModel() 36 | // 添加新行并设置默认值 37 | .addRows(FileType.of("", "", "", ".java", settings.getJavaPath(), true))) 38 | .setRemoveAction(btn -> { 39 | if (tableDecorator.table.getSelectedRows().length == 0) { 40 | return; 41 | } 42 | var selectedRows = tableDecorator.table.getSelectedRows(); 43 | tableDecorator.getModel() 44 | .removeRows(selectedRows); 45 | tableDecorator.table.clearSelection(); 46 | }); 47 | } 48 | 49 | private static @NotNull GenericTableModel createTableModel(Project project, Settings settings) { 50 | var editorTableCellEditor = new EditorTableCellEditor(project); 51 | var extTableCellEditor = new ComboBoxTableCellEditor<>(".java", ".kt", ".xml"); 52 | var javaPath = ObjectUtils.notNull(settings.getJavaPath(), "src/main/java"); 53 | var pathTableCellEditor = new ComboBoxTableCellEditor<>( 54 | javaPath, 55 | javaPath.replace("java", "kotlin"), 56 | settings.getResourcesPath() 57 | ); 58 | return new GenericTableModel<>(settings.getFileTypes()) 59 | .addColumn(ColumnSpec.of(Bundles.message("fileTypes.type"), String.class, FileType::getType, FileType::setType)) 60 | .addColumn(ColumnSpec.of(Bundles.message("fileTypes.suffix"), String.class, FileType::getSuffix, FileType::setSuffix)) 61 | .addColumn(ColumnSpec.of(Bundles.message("fileTypes.package"), String.class, FileType::getPackageName, FileType::setPackageName) 62 | .withWidth(120) 63 | .withCellEditor(editorTableCellEditor)) 64 | .addColumn(ColumnSpec.of(Bundles.message("fileTypes.ext"), String.class, FileType::getExt, FileType::setExt) 65 | .withCellEditor(extTableCellEditor) 66 | .withPlaceholder(Bundles.message("fileTypes.ext.placeholder")) 67 | .withWidth(50)) 68 | .addColumn(ColumnSpec.of(Bundles.message("fileTypes.path"), String.class, FileType::getPath, FileType::setPath) 69 | .withCellEditor(pathTableCellEditor) 70 | .withPlaceholder(Bundles.message("fileTypes.path.placeholder")) 71 | .withWidth(200)) 72 | .addColumn(ColumnSpec.of(Bundles.message("fileTypes.override"), Boolean.class, FileType::isOverride, FileType::setOverride) 73 | .withWidth(30)); 74 | } 75 | 76 | @Override 77 | public void reset() { 78 | var fileTypes = PluginUtils.loadConfig() 79 | .getSettings() 80 | .getFileTypes(); 81 | this.reset(fileTypes); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/ui/win/tree/CheckBoxTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.ui.win.tree; 2 | 3 | import lombok.Getter; 4 | 5 | import javax.swing.tree.DefaultMutableTreeNode; 6 | import javax.swing.tree.TreeNode; 7 | import java.io.File; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * 拥有复选框的树形节点 13 | * 14 | * @author HouKunLin 15 | */ 16 | @Getter 17 | public class CheckBoxTreeNode extends DefaultMutableTreeNode { 18 | protected boolean selected; 19 | 20 | public CheckBoxTreeNode() { 21 | this(null); 22 | } 23 | 24 | public CheckBoxTreeNode(Object userObject) { 25 | this(userObject, true, false); 26 | } 27 | 28 | public CheckBoxTreeNode(Object userObject, boolean allowsChildren, boolean selected) { 29 | super(userObject, allowsChildren); 30 | this.selected = selected; 31 | } 32 | 33 | public void setSelected(boolean selected) { 34 | this.selected = selected; 35 | if (selected) { 36 | // 如果选中,则将其所有的子结点都选中 37 | if (children != null) { 38 | for (Object obj : children) { 39 | CheckBoxTreeNode node = (CheckBoxTreeNode) obj; 40 | if (!node.isSelected()) { 41 | node.setSelected(true); 42 | } 43 | } 44 | } 45 | // 向上检查,如果父结点的所有子结点都被选中,那么将父结点也选中 46 | CheckBoxTreeNode pNode = (CheckBoxTreeNode) parent; 47 | // 开始检查pNode的所有子节点是否都被选中 48 | if (pNode != null) { 49 | int index = 0; 50 | for (; index < pNode.children.size(); ++index) { 51 | CheckBoxTreeNode pChildNode = (CheckBoxTreeNode) pNode.children.get(index); 52 | if (!pChildNode.isSelected()) { 53 | break; 54 | } 55 | } 56 | 57 | /*表明pNode所有子结点都已经选中,则选中父结点, 58 | 该方法是一个递归方法,因此在此不需要进行迭代,因为 59 | 当选中父结点后,父结点本身会向上检查的。*/ 60 | 61 | if (index == pNode.children.size()) { 62 | if (!pNode.isSelected()) { 63 | pNode.setSelected(true); 64 | } 65 | } 66 | } 67 | } else { 68 | 69 | /* 如果是取消父结点导致子结点取消,那么此时所有的子结点都应该是选择上的; 70 | 否则就是子结点取消导致父结点取消,然后父结点取消导致需要取消子结点,但 71 | 是这时候是不需要取消子结点的。*/ 72 | 73 | if (children != null) { 74 | int index = 0; 75 | for (; index < children.size(); ++index) { 76 | CheckBoxTreeNode childNode = (CheckBoxTreeNode) children.get(index); 77 | if (!childNode.isSelected()) { 78 | break; 79 | } 80 | } 81 | // 从上向下取消的时候 82 | if (index == children.size()) { 83 | for (javax.swing.tree.TreeNode child : children) { 84 | CheckBoxTreeNode node = (CheckBoxTreeNode) child; 85 | if (node.isSelected()) { 86 | node.setSelected(false); 87 | } 88 | } 89 | } 90 | } 91 | 92 | // 向上取消,只要存在一个子节点不是选上的,那么父节点就不应该被选上。 93 | //这一块注释之后,子节点的取消不会影响父节点的勾选,为实现取消子节点,单独只选择父节点自己查看 94 | CheckBoxTreeNode pNode = (CheckBoxTreeNode) parent; 95 | if (pNode != null && pNode.isSelected()) { 96 | pNode.setSelected(false); 97 | } 98 | } 99 | } 100 | 101 | public List getAllSelectNodes() { 102 | List list = new ArrayList<>(); 103 | if (selected) { 104 | list.add(this); 105 | } 106 | if (children != null) { 107 | for (TreeNode child : children) { 108 | if (child instanceof CheckBoxTreeNode node) { 109 | list.addAll(node.getAllSelectNodes()); 110 | } 111 | } 112 | } 113 | return list; 114 | } 115 | 116 | @Override 117 | public String toString() { 118 | if (userObject == null) { 119 | return ""; 120 | } 121 | if (userObject instanceof File) { 122 | return ((File) userObject).getName(); 123 | } else { 124 | return userObject.toString(); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /RootModel.uml: -------------------------------------------------------------------------------- 1 | 2 | 3 | JAVA 4 | com.github.houkunlin.vo.impl.RootModel 5 | 6 | com.github.houkunlin.vo.impl.EntityFieldImpl 7 | com.github.houkunlin.vo.impl.EntityNameInfo 8 | com.github.houkunlin.vo.impl.TableColumnImpl 9 | com.github.houkunlin.vo.impl.EntityImpl 10 | com.github.houkunlin.vo.impl.RootModel 11 | com.github.houkunlin.vo.impl.PrimaryInfo 12 | com.github.houkunlin.vo.impl.FieldNameInfo 13 | com.github.houkunlin.vo.impl.EntityName 14 | com.github.houkunlin.vo.impl.TableImpl 15 | com.github.houkunlin.vo.impl.EntityPackageInfo 16 | com.github.houkunlin.vo.impl.EntityPackage 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | com.github.houkunlin.vo.impl.PrimaryInfo 76 | 77 | 78 | Fields 79 | 80 | All 81 | private 82 | 83 | 84 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/util/Generator.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.util; 2 | 3 | import com.github.houkunlin.config.Developer; 4 | import com.github.houkunlin.config.Options; 5 | import com.github.houkunlin.config.Settings; 6 | import com.github.houkunlin.model.SaveFilePath; 7 | import com.github.houkunlin.template.ITemplateGenerator; 8 | import com.github.houkunlin.template.TemplateGeneratorFactory; 9 | import com.github.houkunlin.vo.Variable; 10 | import com.github.houkunlin.vo.impl.RootModel; 11 | import com.intellij.openapi.project.Project; 12 | import com.intellij.psi.PsiFile; 13 | import com.intellij.util.ExceptionUtil; 14 | import lombok.Data; 15 | import org.joda.time.DateTime; 16 | 17 | import java.io.File; 18 | import java.util.ArrayList; 19 | import java.util.HashMap; 20 | import java.util.List; 21 | import java.util.Map; 22 | import java.util.function.BiConsumer; 23 | 24 | /** 25 | * 代码生成工具 26 | * 27 | * @author HouKunLin 28 | */ 29 | @Data 30 | public class Generator { 31 | private final Settings settings; 32 | private final Options options; 33 | private final Map context; 34 | private final List saveFiles; 35 | private final Project project; 36 | 37 | public Generator(Project project, Settings settings, Options options, Developer developer) { 38 | this.project = project; 39 | this.settings = settings; 40 | this.options = options; 41 | this.saveFiles = new ArrayList<>(); 42 | this.context = new HashMap<>(8); 43 | context.put("settings", settings); 44 | context.put("developer", developer); 45 | context.put("gen", Variable.getInstance()); 46 | context.put("date", DateTime.now()); 47 | } 48 | 49 | /** 50 | * 执行生成代码任务 51 | */ 52 | public void generator(RootModel rootModel, List templateFiles, BiConsumer progress) { 53 | if (rootModel == null || templateFiles == null || templateFiles.isEmpty()) { 54 | return; 55 | } 56 | loadContext(rootModel); 57 | for (int i = 0; i < templateFiles.size(); i++) { 58 | var templateFile = templateFiles.get(i); 59 | var generator = TemplateGeneratorFactory.create(templateFile); 60 | var workspace = generator.getWorkspace(); 61 | var templateFilename = FileUtils.relativePath(workspace, templateFile) 62 | .replaceFirst(PluginUtils.TEMPLATE_DIR, ""); 63 | if (progress != null) { 64 | progress.accept(i, templateFilename); 65 | } 66 | // 重置内容,方便使用默认配置 67 | Variable.resetVariables(); 68 | generatorTemplateFile(rootModel, generator, templateFile, templateFilename); 69 | } 70 | } 71 | 72 | private void loadContext(RootModel rootModel) { 73 | context.put("table", rootModel.getTable()); 74 | context.put("columns", rootModel.getColumns()); 75 | context.put("entity", rootModel.getEntity(settings)); 76 | context.put("fields", rootModel.getFields()); 77 | context.put("primary", rootModel.getPrimary()); 78 | } 79 | 80 | private void generatorTemplateFile(RootModel rootModel, ITemplateGenerator generator, File templateFile, String templateFilename) { 81 | try { 82 | var result = generator.generate(templateFilename, context); 83 | if (result == null || result.isBlank()) { 84 | // 不保存空内容的文件 85 | return; 86 | } 87 | var savePsiFile = getSavePsiFile(rootModel, templateFile, result); 88 | if (savePsiFile != null && !saveFiles.contains(savePsiFile)) { 89 | saveFiles.add(savePsiFile); 90 | } 91 | } catch (Throwable e) { 92 | ExceptionUtil.rethrow(new RuntimeException("解析错误:" + templateFile.getAbsolutePath() + "\r\n" + e.getMessage(), e)); 93 | } 94 | } 95 | 96 | private PsiFile getSavePsiFile(RootModel rootModel, File templateFile, String result) { 97 | var saveFilePath = getSaveFilePath(rootModel, templateFile); 98 | var saveFile = new File(settings.getProjectPath(), String.valueOf(saveFilePath)); 99 | return FileUtils.getInstance() 100 | .saveFileContent(project, saveFile, result, saveFilePath.isOverride()); 101 | } 102 | 103 | private SaveFilePath getSaveFilePath(RootModel rootModel, File templateFile) { 104 | if (Variable.type == null) { 105 | return SaveFilePath.createTemp(templateFile.getName(), settings); 106 | } 107 | return SaveFilePath.create(rootModel, settings); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/model/JTableModel.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.model; 2 | 3 | import com.github.houkunlin.config.Options; 4 | import com.github.houkunlin.vo.impl.EntityFieldImpl; 5 | import com.github.houkunlin.vo.impl.TableColumnImpl; 6 | import com.google.common.collect.HashBasedTable; 7 | import com.google.common.collect.Table; 8 | import com.intellij.database.model.DasColumn; 9 | import com.intellij.database.psi.DbTable; 10 | import com.intellij.database.util.DasUtil; 11 | import com.intellij.util.containers.JBIterable; 12 | import lombok.Data; 13 | import lombok.EqualsAndHashCode; 14 | 15 | import javax.swing.*; 16 | import javax.swing.table.AbstractTableModel; 17 | import javax.swing.table.TableColumn; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | /** 22 | * 表格列名信息 23 | * 24 | * @author HouKunLin 25 | */ 26 | @Data 27 | @EqualsAndHashCode(callSuper = true) 28 | public class JTableModel extends AbstractTableModel { 29 | private final List fieldImpls = new ArrayList<>(); 30 | private final List columnImpls = new ArrayList<>(); 31 | 32 | Table table = HashBasedTable.create(); 33 | String[] names = {"选中", "DB列名", "Java字段", "DB类型", "Java类型", "注释"}; 34 | Boolean[] editable = {true, false, false, false, false, true}; 35 | 36 | public JTableModel(JTable columnTable, DbTable dbTable, Options options) { 37 | initTableContent(dbTable, options); 38 | columnTable.setModel(this); 39 | setColumnSelected(columnTable); 40 | } 41 | 42 | private void setColumnSelected(JTable columnTable) { 43 | TableColumn column = columnTable.getColumnModel().getColumn(0); 44 | column.setCellEditor(new DefaultCellEditor(new JCheckBox())); 45 | int width = 30; 46 | column.setMaxWidth(width); 47 | column.setMinWidth(width); 48 | } 49 | 50 | private void initTableContent(DbTable dbTable, Options options) { 51 | // ((DbTableImpl) psiElements[0]).getDelegate().getDasChildren(ObjectKind.COLUMN).get(3) 52 | // ((DbTableImpl)((DbColumnImpl) psiElement).getTable()).getDelegate().getDasChildren(ObjectKind.COLUMN).get(5).getName() 53 | // ((MysqlImplModel.Table) delegate).getKeys().myElements.get(0).getColNames() 54 | // ((MysqlImplModel.Table) delegate).getPrimaryKey().getColNames() 55 | int rowIndex = -1; 56 | JBIterable columns = DasUtil.getColumns(dbTable); 57 | for (DasColumn column : columns) { 58 | EntityFieldImpl entityField = new EntityFieldImpl(column, options); 59 | TableColumnImpl tableColumn = new TableColumnImpl(column); 60 | entityField.setColumn(tableColumn); 61 | tableColumn.setField(entityField); 62 | fieldImpls.add(entityField); 63 | columnImpls.add(tableColumn); 64 | 65 | int colIndex = -1; 66 | ++rowIndex; 67 | // 第0列选中复选框 68 | table.put(rowIndex, ++colIndex, true); 69 | // 第1列:DB列名 70 | table.put(rowIndex, ++colIndex, tableColumn.getName()); 71 | // 第2列:Java字段 72 | table.put(rowIndex, ++colIndex, entityField.getName()); 73 | // 第3列:DB类型 74 | table.put(rowIndex, ++colIndex, tableColumn.getFullTypeName()); 75 | // 第4列:Java类型 76 | table.put(rowIndex, ++colIndex, entityField.getTypeName()); 77 | // 第5列:注释 78 | table.put(rowIndex, ++colIndex, entityField.getComment()); 79 | } 80 | } 81 | 82 | @Override 83 | public int getRowCount() { 84 | return table.rowKeySet().size(); 85 | } 86 | 87 | @Override 88 | public int getColumnCount() { 89 | return table.columnKeySet().size(); 90 | } 91 | 92 | @Override 93 | public Object getValueAt(int rowIndex, int columnIndex) { 94 | return table.get(rowIndex, columnIndex); 95 | } 96 | 97 | @Override 98 | public String getColumnName(int column) { 99 | return names[column]; 100 | } 101 | 102 | @Override 103 | public Class getColumnClass(int columnIndex) { 104 | return getValueAt(0, columnIndex).getClass(); 105 | } 106 | 107 | @Override 108 | public boolean isCellEditable(int row, int column) { 109 | return editable[column]; 110 | } 111 | 112 | @Override 113 | public void setValueAt(Object aValue, int rowIndex, int columnIndex) { 114 | if (columnIndex == 0) { 115 | fieldImpls.get(rowIndex).setSelected((Boolean) aValue); 116 | columnImpls.get(rowIndex).setSelected((Boolean) aValue); 117 | update(aValue, rowIndex, columnIndex); 118 | } else if (columnIndex == names.length - 1) { 119 | fieldImpls.get(rowIndex).setComment(String.valueOf(aValue)); 120 | update(aValue, rowIndex, columnIndex); 121 | } 122 | } 123 | 124 | private void update(Object aValue, int rowIndex, int columnIndex) { 125 | table.put(rowIndex, columnIndex, aValue); 126 | fireTableCellUpdated(rowIndex, columnIndex); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/ui/win/BaseSetting.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.ui.win; 2 | 3 | import com.github.houkunlin.config.Developer; 4 | import com.github.houkunlin.config.Options; 5 | import com.github.houkunlin.config.Settings; 6 | import com.github.houkunlin.ui.win.table.FileTypeTableDecorator; 7 | import com.google.common.collect.Maps; 8 | import com.intellij.openapi.project.Project; 9 | 10 | import javax.swing.*; 11 | import javax.swing.event.DocumentEvent; 12 | import javax.swing.event.DocumentListener; 13 | import javax.swing.text.JTextComponent; 14 | import java.awt.*; 15 | import java.awt.event.ItemEvent; 16 | import java.util.Map; 17 | import java.util.Set; 18 | import java.util.function.Consumer; 19 | 20 | /** 21 | * 基础设置窗口 22 | * 23 | * @author HouKunLin 24 | */ 25 | public class BaseSetting implements IWindows { 26 | private final Project project; 27 | /** 28 | * 配置对象:设置信息 29 | */ 30 | private final Settings settings; 31 | /** 32 | * 配置对象:是否覆盖文件 33 | */ 34 | private final Options options; 35 | /** 36 | * 配置对象:开发者信息 37 | */ 38 | private final Developer developer; 39 | /** 40 | * 输入框:开发者姓名 41 | */ 42 | private JTextField authorField; 43 | /** 44 | * 输入框:电子邮件 45 | */ 46 | private JTextField emailField; 47 | /** 48 | * 面板:顶级页面面板 49 | */ 50 | private JPanel content; 51 | /** 52 | * 下拉框:数据库字段风格类型 53 | */ 54 | private JComboBox databaseFieldStyleType; 55 | 56 | /** 57 | * 复选:是否记住上次的模板 58 | */ 59 | private JCheckBox retainLastSelectionTemplates; 60 | private JPanel fileTypeTablePanel; 61 | private final Runnable noteReset; 62 | 63 | public BaseSetting(Project project, Settings settings, Developer developer, Options options, Runnable noteReset) { 64 | this.project = project; 65 | this.settings = settings; 66 | this.developer = developer; 67 | this.options = options; 68 | this.noteReset = noteReset; 69 | initFileTypeTable(); 70 | initDatabaseFieldStyle(); 71 | initConfig(); 72 | 73 | /* 普通输入框的输入事件监听 */ 74 | class TextFieldDocumentListener implements DocumentListener { 75 | /** 76 | * 存放 setValue 与 输入框 的关系 77 | */ 78 | private final Map, JTextComponent> map = Maps.newHashMap(); 79 | 80 | public TextFieldDocumentListener() { 81 | map.put(developer::setAuthor, authorField); 82 | map.put(developer::setEmail, emailField); 83 | } 84 | 85 | @Override 86 | public void insertUpdate(DocumentEvent e) { 87 | documentChanged(e); 88 | } 89 | 90 | @Override 91 | public void removeUpdate(DocumentEvent e) { 92 | documentChanged(e); 93 | } 94 | 95 | @Override 96 | public void changedUpdate(DocumentEvent e) { 97 | } 98 | 99 | /** 100 | * swing 输入框组件内容更改事件 101 | * 102 | * @param e 事件 103 | */ 104 | public void documentChanged(DocumentEvent e) { 105 | javax.swing.text.Document document = e.getDocument(); 106 | Set, JTextComponent>> entries = map.entrySet(); 107 | for (Map.Entry, JTextComponent> entry : entries) { 108 | if (TextFieldDocumentUtil.updateSettingValue(document, entry.getValue(), entry.getKey())) { 109 | break; 110 | } 111 | } 112 | } 113 | } 114 | 115 | TextFieldDocumentListener textFieldDocumentListener = new TextFieldDocumentListener(); 116 | authorField.getDocument().addDocumentListener(textFieldDocumentListener); 117 | emailField.getDocument().addDocumentListener(textFieldDocumentListener); 118 | 119 | 120 | } 121 | 122 | private void initFileTypeTable() { 123 | fileTypeTablePanel.add(FileTypeTableDecorator.create(project, settings), BorderLayout.CENTER); 124 | } 125 | 126 | private void initDatabaseFieldStyle() { 127 | databaseFieldStyleType.addItem("下划线(LOWER_UNDERSCORE)和连接符(LOWER_HYPHEN)"); 128 | databaseFieldStyleType.addItem("下划线(UPPER_UNDERSCORE)和连接符(LOWER_HYPHEN)"); 129 | databaseFieldStyleType.addItem("小驼峰(LOWER_CAMEL)"); 130 | databaseFieldStyleType.addItem("大坨峰(UPPER_CAMEL)"); 131 | // databaseFieldStyleType.addItem("连接符(LOWER_HYPHEN)"); 132 | databaseFieldStyleType.addItemListener(e -> { 133 | if (e.getStateChange() != ItemEvent.SELECTED) { 134 | return; 135 | } 136 | options.setDbFieldStyleType(databaseFieldStyleType.getSelectedIndex()); 137 | this.noteReset.run(); 138 | }); 139 | } 140 | 141 | /** 142 | * 初始化开发者信息的输入框内容 143 | */ 144 | public void initConfig() { 145 | authorField.setText(developer.getAuthor()); 146 | emailField.setText(developer.getEmail()); 147 | databaseFieldStyleType.setSelectedIndex(options.getDbFieldStyleType()); 148 | retainLastSelectionTemplates.setSelected(options.isRetainLastSelectionTemplates()); 149 | } 150 | 151 | @Override 152 | public JPanel getContent() { 153 | return content; 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/main/java/com/github/houkunlin/ui/win/table/GenericTableModel.java: -------------------------------------------------------------------------------- 1 | package com.github.houkunlin.ui.win.table; 2 | 3 | import lombok.Getter; 4 | 5 | import javax.swing.*; 6 | import javax.swing.table.AbstractTableModel; 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.List; 10 | import java.util.function.BiConsumer; 11 | 12 | /** 13 | * 通用的表格模型 14 | * 15 | * @author daiwenzh5 16 | * @since 2.8.4 17 | */ 18 | @SuppressWarnings({"UnusedReturnValue"}) 19 | public class GenericTableModel extends AbstractTableModel { 20 | 21 | private final List> columns = new ArrayList<>(); 22 | 23 | @Getter 24 | private final List data; 25 | 26 | public GenericTableModel(List data) { 27 | this.data = data; 28 | } 29 | 30 | /** 31 | * 添加行数据 32 | * 33 | * @param items 行数据 34 | * @return 当前表格模型 35 | */ 36 | @SafeVarargs 37 | public final GenericTableModel addRows(T... items) { 38 | return addRows(List.of(items)); 39 | } 40 | 41 | /** 42 | * 添加行数据 43 | * @param items 行数据 44 | * @return 当前表格模型 45 | */ 46 | public final GenericTableModel addRows(List items) { 47 | if (items == null || items.isEmpty()) { 48 | return this; 49 | } 50 | var startRow = data.size(); 51 | data.addAll(items); 52 | var endRow = data.size() - 1; 53 | fireTableRowsInserted(startRow, endRow); 54 | return this; 55 | } 56 | 57 | /** 58 | * 根据行号删除数据 59 | * 60 | * @param rows 行号 61 | * @return 当前表格模型 62 | */ 63 | public final GenericTableModel removeRows(int... rows) { 64 | // 将rows按降序排序 65 | Arrays.sort(rows); 66 | for (var i = rows.length - 1; i >= 0; i--) { 67 | this.data.remove(rows[i]); 68 | } 69 | fireTableDataChanged(); 70 | return this; 71 | } 72 | 73 | /** 74 | * 添加列 75 | * 76 | * @param column 列信息 77 | * @return 当前表格模型 78 | */ 79 | public final GenericTableModel addColumn(ColumnSpec column) { 80 | columns.add(column); 81 | return this; 82 | } 83 | 84 | /** 85 | * 绑定表格 86 | * 87 | * @param table 表格 88 | * @return 当前表格模型 89 | */ 90 | public final GenericTableModel bindTable(JTable table) { 91 | table.setModel(this); 92 | PlaceholderTableCellRenderer placeholderTableCellRenderer = null; 93 | for (int i = 0; i < columns.size(); i++) { 94 | var columnSpec = columns.get(i); 95 | var tableColumn = table.getColumnModel() 96 | .getColumn(i); 97 | if (columnSpec.getCellEditor() != null) { 98 | tableColumn.setCellEditor(columnSpec.getCellEditor()); 99 | } 100 | if (columnSpec.getPlaceholder() != null && !columnSpec.getPlaceholder() 101 | .isBlank()) { 102 | if (placeholderTableCellRenderer == null) { 103 | placeholderTableCellRenderer = new PlaceholderTableCellRenderer(); 104 | } 105 | tableColumn.setCellRenderer(placeholderTableCellRenderer.setColumn(i, columnSpec.getPlaceholder())); 106 | } 107 | if (columnSpec.getWidth() > 0) { 108 | tableColumn.setPreferredWidth(columnSpec.getWidth()); 109 | } 110 | } 111 | return this; 112 | } 113 | 114 | @Override 115 | public final int getRowCount() { 116 | return data.size(); 117 | } 118 | 119 | @Override 120 | public final int getColumnCount() { 121 | return columns.size(); 122 | } 123 | 124 | @Override 125 | public final Object getValueAt(int row, int col) { 126 | T item = data.get(row); 127 | return columns.get(col) 128 | .getGetter() 129 | .apply(item); 130 | } 131 | 132 | @Override 133 | @SuppressWarnings("unchecked") 134 | public final void setValueAt(Object value, int row, int col) { 135 | var item = data.get(row); 136 | //noinspection rawtypes 137 | final BiConsumer setter = columns.get(col) 138 | .getSetter(); 139 | setter.accept(item, value); 140 | } 141 | 142 | @Override 143 | public final String getColumnName(int column) { 144 | return columns.get(column) 145 | .getName(); 146 | } 147 | 148 | @Override 149 | public final Class getColumnClass(int columnIndex) { 150 | return columns.get(columnIndex) 151 | .getType(); 152 | } 153 | 154 | @Override 155 | public boolean isCellEditable(int row, int column) { 156 | return columns.get(column) 157 | .isEditable(); 158 | } 159 | 160 | /** 161 | * 清空数据 162 | * 163 | * @return 当前表格模型 164 | */ 165 | public GenericTableModel clear() { 166 | return clear(true); 167 | } 168 | 169 | /** 170 | * 清空数据 171 | * 172 | * @param fireTableDataChanged 是否触发表格数据改变事件 173 | * @return 当前表格模型 174 | */ 175 | public GenericTableModel clear(boolean fireTableDataChanged) { 176 | if (!this.data.isEmpty()) { 177 | data.clear(); 178 | if (fireTableDataChanged) { 179 | fireTableDataChanged(); 180 | } 181 | } 182 | return this; 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | --------------------------------------------------------------------------------