├── .gitignore ├── .travis.yml ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src ├── main ├── java │ └── io │ │ └── github │ │ └── gcdd1993 │ │ └── jpa │ │ └── codegen │ │ ├── CodeGenerator.java │ │ ├── config │ │ ├── CodeGeneratorConfig.java │ │ └── ModuleConfig.java │ │ ├── exception │ │ └── JpaCodegenException.java │ │ ├── metadata │ │ ├── AnnotationInfo.java │ │ ├── BaseClassInfo.java │ │ ├── BaseEntityParser.java │ │ ├── DefaultEntityInfoParser.java │ │ ├── EntityInfo.java │ │ ├── FieldInfo.java │ │ ├── IEntityParser.java │ │ └── IdInfo.java │ │ ├── render │ │ ├── DefaultRender.java │ │ ├── IRender.java │ │ ├── RenderingRequest.java │ │ └── RenderingResponse.java │ │ └── util │ │ ├── FreeMarkerUtils.java │ │ ├── ReflectUtils.java │ │ └── ResourceReader.java └── resources │ ├── example.properties │ └── template │ ├── spring1 │ ├── controller.ftl │ ├── form.ftl │ ├── repository.ftl │ └── service.ftl │ └── spring2 │ ├── controller.ftl │ ├── form.ftl │ ├── repository.ftl │ └── service.ftl └── test └── java └── ApplicationTest.java /.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 | ### Gradle template 28 | .gradle 29 | /build/ 30 | 31 | # Ignore Gradle GUI config 32 | gradle-app.setting 33 | 34 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 35 | !gradle-wrapper.jar 36 | 37 | # Cache of project 38 | .gradletasknamecache 39 | 40 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 41 | # gradle/wrapper/gradle-wrapper.properties 42 | 43 | ### JetBrains template 44 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 45 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 46 | 47 | # User-specific stuff 48 | .idea/**/workspace.xml 49 | .idea/**/tasks.xml 50 | .idea/**/usage.statistics.xml 51 | .idea/**/dictionaries 52 | .idea/**/shelf 53 | 54 | # Generated files 55 | .idea/**/contentModel.xml 56 | 57 | # Sensitive or high-churn files 58 | .idea/**/dataSources/ 59 | .idea/**/dataSources.ids 60 | .idea/**/dataSources.local.xml 61 | .idea/**/sqlDataSources.xml 62 | .idea/**/dynamic.xml 63 | .idea/**/uiDesigner.xml 64 | .idea/**/dbnavigator.xml 65 | 66 | # Gradle 67 | .idea/**/gradle.xml 68 | .idea/**/libraries 69 | 70 | # Gradle and Maven with auto-import 71 | # When using Gradle or Maven with auto-import, you should exclude module files, 72 | # since they will be recreated, and may cause churn. Uncomment if using 73 | # auto-import. 74 | # .idea/modules.xml 75 | # .idea/*.iml 76 | # .idea/modules 77 | # *.iml 78 | # *.ipr 79 | 80 | # CMake 81 | cmake-build-*/ 82 | 83 | # Mongo Explorer plugin 84 | .idea/**/mongoSettings.xml 85 | 86 | # File-based project format 87 | *.iws 88 | 89 | # IntelliJ 90 | out/ 91 | 92 | # mpeltonen/sbt-idea plugin 93 | .idea_modules/ 94 | 95 | # JIRA plugin 96 | atlassian-ide-plugin.xml 97 | 98 | # Cursive Clojure plugin 99 | .idea/replstate.xml 100 | 101 | # Crashlytics plugin (for Android Studio and IntelliJ) 102 | com_crashlytics_export_strings.xml 103 | crashlytics.properties 104 | crashlytics-build.properties 105 | fabric.properties 106 | 107 | # Editor-based Rest Client 108 | .idea/httpRequests 109 | 110 | # Android studio 3.1+ serialized cache file 111 | .idea/caches/build_file_checksums.ser 112 | 113 | /.idea/ 114 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | sudo: required 3 | dist: xenial 4 | jdk: 5 | - openjdk8 6 | branches: 7 | only: 8 | - master 9 | before_cache: 10 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 11 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ 12 | cache: 13 | directories: 14 | - $HOME/.gradle/caches/ 15 | - $HOME/.gradle/wrapper/ 16 | before_install: 17 | - chmod +x gradlew 18 | install: 19 | - ./gradlew jar 20 | script: 21 | - ./gradlew bintrayUpload -PbintrayUser=${bintray_user} -PbintrayKey=${bintray_key} -PdryRun=false -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Table of Contents 2 | ================= 3 | 4 | * [简介](#简介) 5 | * [SpringBoot使用示例](#springboot使用示例) 6 | * [如何使用](#如何使用) 7 | * [导入仓库](#导入仓库) 8 | * [配置代码生成器](#配置代码生成器) 9 | * [配置文件](#配置文件) 10 | * [编写代码模板](#编写代码模板) 11 | * [编写生成器入口](#编写生成器入口) 12 | * [如何编写模板?](#如何编写模板) 13 | * [基本信息](#基本信息) 14 | * [实体信息](#实体信息) 15 | * [自定义配置](#自定义配置) 16 | * [什么是模块?](#什么是模块) 17 | * [Plans](#plans) 18 | 19 | [![Build Status](https://travis-ci.org/gcdd1993/jpa-codegen.svg?branch=master)](https://travis-ci.org/gcdd1993/jpa-codegen) 20 | 21 | # 简介 22 | 23 | 这是一款基于`Freemarker`模板驱动的代码生成器。 24 | 25 | 依据现有的实体类代码,自动生成CRUD代码,解放双手,加快开发速度。 26 | 27 | 生成的代码包括但不仅限于(可以自定义生成模块) 28 | 29 | - Form表单代码 30 | - Repository代码 31 | - Service代码 32 | - Controller代码 33 | 34 | # SpringBoot使用示例 35 | 36 | 克隆[示例项目](https://github.com/gcdd1993/jpa-codegen-sample),体会解放双手的美妙感受! 37 | 38 | # 如何使用 39 | 40 | ## 导入仓库 41 | 42 | ```groovy 43 | maven { 44 | url 'https://dl.bintray.com/gcdd1993/maven' 45 | } 46 | dependencies { 47 | // jpa code generator 48 | testCompile 'io.github.gcdd1993:jpa-codegen:v1.0.2' 49 | testCompile 'org.freemarker:freemarker:2.3.28' 50 | } 51 | ``` 52 | 53 | ## 配置代码生成器 54 | 55 | ### 配置文件 56 | 57 | ```properties 58 | ## 作者 59 | author=gcdd1993 60 | ## 代码注释 61 | comments=code generated by jpa-codegen 62 | ## 是否覆盖原文件,除非特殊情况,不然请不要覆盖 63 | cover=false 64 | ## 代码模板目录 65 | template.dir=src/test/resources/template/ 66 | ## 实体类包名 Deprecated从v1.0.1开始从配置文件中移除 67 | - entity.package=com.maxtropy.sample.entity 68 | ## 实体类标识符 69 | entity.flag=entity 70 | ## 以下配置是模块配置(格式 模块名.配置名),必须在模板目录下提供与模块名相同的模板 71 | ## 生成的代码后缀 72 | repository.suffix=Repository 73 | ## 模板名称 74 | repository.template=repository.ftl 75 | ## 模块标识符 76 | repository.flag=entity.repo 77 | 78 | service.suffix=Service 79 | service.template=service.ftl 80 | service.flag=service 81 | form.suffix=Form 82 | form.template=form.ftl 83 | form.flag=form 84 | controller.suffix=Controller 85 | controller.template=controller.ftl 86 | controller.flag=web 87 | ``` 88 | 89 | 其中 90 | 91 | ```properties 92 | repository.suffix=Repository 93 | repository.template=repository.ftl 94 | repository.flag=entity.repo 95 | ``` 96 | 97 | 是模块配置,[什么是模块?](#什么是模块?) 98 | 99 | ## 编写代码模板 100 | 101 | 模板主要基于`Freemarker`,如`Spring Boot2.x`代码模板可以像下面这样 102 | 103 | ```java 104 | package ${packageName}; 105 | 106 | import ${entity.packageName}.${entity.className}; 107 | import org.springframework.data.jpa.repository.JpaRepository; 108 | import org.springframework.data.querydsl.QuerydslPredicateExecutor; 109 | <#list imports as import> 110 | import ${import}; 111 | 112 | 113 | /** 114 | * repository for ${entity.className} generated by jpa-codegen 115 | * ${comments} 116 | * 117 | * @author ${author} 118 | * Created On ${date}. 119 | */ 120 | public interface ${className} extends JpaRepository<${entity.className}, ${entity.id.className}>, QuerydslPredicateExecutor<${entity.className}> { 121 | } 122 | ``` 123 | 124 | - [Spring Boot 2.x模板]() 125 | 126 | - [如何编写模板?](#如何编写模板) 127 | 128 | ## 编写生成器入口 129 | 130 | 在test模块中编写生成器入口,如 131 | 132 | ```java 133 | public class Codegen { 134 | 135 | @Test 136 | public void generate() { 137 | new CodeGenerator("src/test/resources/codegen.properties") 138 | .registerRender("repository") 139 | .generate(); 140 | } 141 | 142 | } 143 | ``` 144 | 145 | 然后运行`generate()`,在项目目录下将会生成 146 | 147 | ![Imgur](https://i.imgur.com/WTg4qMx.png) 148 | 149 | ![Imgur](https://i.imgur.com/yJJ1d59.png) 150 | 151 | 生成的代码完全由模板以及实体类信息决定。 152 | 153 | # 如何编写模板? 154 | 155 | 模板完全基于`FreeMarker`以及实体类信息,`FreeMarker`参考[FreeMarker Docs]() 156 | 157 | 支持的元素定义如下 158 | 159 | ## 基本信息 160 | 161 | | Freemarker元素 | 解释 | 示例输出 | 162 | | ---------------- | ------------------------ | -------------------------------------------------------- | 163 | | `${ftlName}` | 模板名称 | `controller.ftl` | 164 | | `${ftlPath}` | 模板目录 | `src/main/resources/template/` | 165 | | `${savePath}` | 保存路径 | `src/main/resources/io/github/gcdd1993/controller` | 166 | | `${packageName}` | java文件包名 | `io.github.gcdd1993.controller` | 167 | | `${className}` | java文件类名 | `UserController` | 168 | | `${author}` | 作者 | `gaochen` | 169 | | `${date}` | 创建日期,默认为当前日期 | `2019/6/23` | 170 | | `${comments}` | 注释信息 | `generated by jpa-codegen` | 171 | | `${imports}` | java文件引入信息 | `org.springframework.beans.factory.annotation.Autowired` | 172 | 173 | ## 实体信息 174 | 175 | | Freemarker元素 | 解释 | 示例输出 | 176 | | ------------------------------------------ | ---------------------------------------- | ------------------ | 177 | | `${entity.className}` | 实体类名,`class.getSimpleName()` | User | 178 | | `${entity.packageName}` | 实体包名,`class.getPackage().getName()` | io.github.gcdd1993 | 179 | | `${entity.tableName}` | 实体表名,`@Table(name="")` | sys_user | 180 | | `${entity.id.className}` | 实体主键类名,`@Id`注释的字段的类名 | Integer | 181 | | `${entity.id.packageName}` | 实体主键包名,`@Id`注释的字段的包名 | java.lang | 182 | | `${entity.fields.className}` | 实体所有字段(只支持基本类型)类名 | String | 183 | | `${entity.fields.packageName}` | 实体所有字段(只支持基本类型)包名 | java.lang | 184 | | `${entity.fields.name}` | 实体所有字段(只支持基本类型)属性名 | name | 185 | | `${entity.fields.annotations.className}` | 实体所有字段注解的类名 | Id | 186 | | `${entity.fields.annotations.packageName}` | 实体所有字段注解的包名 | javax.persistence | 187 | 188 | ## 自定义配置 189 | 190 | 除了以上默认的信息之外,可能会有额外的信息需要填入生成的代码中,jpa-codegen提供直接将配置文件中的配置渲染到模板的能力。 191 | 192 | 例如在配置文件`autogen.properties`写下一行 193 | 194 | ```properties 195 | custom.additional.comment=this is additional comment 196 | ``` 197 | 198 | 在模板中可以使用`${otherParams.additional_comment}`获取到该配置。 199 | 200 | 要注意的是:自定义配置**使用`custom`开头**,后面的**配置会将.替换为_**作为`FreeMarker`模板的key,例如上述的`additional.comment`使用`${otherParams.additional_comment}`获取。 201 | 202 | # 什么是模块? 203 | 204 | 由于代码千变万化,为了尽可能的做到通用性,[jpa-codegen]()将每一种类型的代码抽象为模块,每一个模块将使用各自的模板,依照实体信息生成代码。 205 | 206 | 需要为模板配置一下信息: 207 | 208 | - repository.suffix=Repository 209 | 210 | 模块类名后缀,生成的类名规则由**实体类名+后缀构成** 211 | 212 | - repository.template=repository.ftl 213 | 214 | 模块使用的`Freemarker`模板 215 | 216 | - repository.flag=entity.repo 217 | 218 | 模块标识符,生成的代码包名由**实体类将实体标识符替换为模块标识符**来确认。 219 | 220 | 如 221 | 222 | - 实体包名:`io.github.gcdd1993.entity` 223 | - 实体标识符:`entity` 224 | - 模块标识符:`entity.repo` 225 | 226 | 则生成的`repository`代码包名为 --> `io.github.gcdd1993.entity.repo` 227 | 228 | # Plans 229 | 230 | - 编写gradle插件 231 | 232 | - 编写maven插件 -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | } 5 | dependencies { 6 | classpath 'com.novoda:bintray-release:0.9.1' 7 | } 8 | } 9 | 10 | plugins { 11 | id 'java' 12 | id "io.franzbecker.gradle-lombok" version "3.1.0" 13 | } 14 | 15 | apply plugin: 'com.novoda.bintray-release' 16 | 17 | group 'io.github.gcdd1993' 18 | version 'v1.0.2' 19 | 20 | sourceCompatibility = 1.8 21 | 22 | tasks.withType(JavaCompile) { 23 | options.encoding = "UTF-8" 24 | } 25 | 26 | javadoc { 27 | options.addStringOption('encoding', 'UTF-8') 28 | } 29 | 30 | repositories { 31 | maven() { 32 | url 'http://maven.aliyun.com/nexus/content/groups/public/' 33 | } 34 | mavenCentral() 35 | jcenter() 36 | } 37 | 38 | publish { 39 | userOrg = 'gcdd1993' 40 | groupId = 'io.github.gcdd1993' 41 | artifactId = 'jpa-codegen' 42 | publishVersion = project.version 43 | desc = 'A simple gradle plugins to generate jpa crud code' 44 | website = 'https://github.com/gcdd1993/jpa-codegen' 45 | } 46 | 47 | jar { 48 | enabled = true 49 | } 50 | 51 | dependencies { 52 | compile gradleApi() 53 | compile 'org.slf4j:slf4j-api:1.7.25' 54 | compile 'org.freemarker:freemarker:2.3.28' 55 | compile 'org.springframework.boot:spring-boot-starter-data-jpa:2.1.5.RELEASE' 56 | 57 | testCompile group: 'junit', name: 'junit', version: '4.12' 58 | } 59 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gcdd1993/jpa-codegen/3174e1c96044425db9742c3ea64d3d64194b0ea9/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS='"-Xmx64m"' 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS="-Xmx64m" 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'jpa-codegen' -------------------------------------------------------------------------------- /src/main/java/io/github/gcdd1993/jpa/codegen/CodeGenerator.java: -------------------------------------------------------------------------------- 1 | package io.github.gcdd1993.jpa.codegen; 2 | 3 | import io.github.gcdd1993.jpa.codegen.config.CodeGeneratorConfig; 4 | import io.github.gcdd1993.jpa.codegen.config.ModuleConfig; 5 | import io.github.gcdd1993.jpa.codegen.exception.JpaCodegenException; 6 | import io.github.gcdd1993.jpa.codegen.metadata.DefaultEntityInfoParser; 7 | import io.github.gcdd1993.jpa.codegen.metadata.EntityInfo; 8 | import io.github.gcdd1993.jpa.codegen.metadata.IEntityParser; 9 | import io.github.gcdd1993.jpa.codegen.render.DefaultRender; 10 | import io.github.gcdd1993.jpa.codegen.render.IRender; 11 | import io.github.gcdd1993.jpa.codegen.util.ReflectUtils; 12 | import lombok.extern.slf4j.Slf4j; 13 | 14 | import java.io.File; 15 | import java.io.FileInputStream; 16 | import java.io.IOException; 17 | import java.time.LocalDate; 18 | import java.time.format.DateTimeFormatter; 19 | import java.util.*; 20 | import java.util.stream.Collectors; 21 | 22 | /** 23 | * TODO 24 | * 25 | * @author gaochen 26 | * Created on 2019/6/18. 27 | */ 28 | @Slf4j 29 | public class CodeGenerator { 30 | 31 | private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy/MM/dd"); 32 | 33 | private static final String SRC_PATH = "src/main/"; 34 | 35 | private CodeGeneratorConfig config; 36 | 37 | private Properties properties = new Properties(); 38 | 39 | private List moduleList = new LinkedList<>(); 40 | 41 | private IEntityParser entityParser; 42 | 43 | private IRender render; 44 | 45 | public CodeGenerator(String configLocation) { 46 | try { 47 | properties.load(new FileInputStream(new File(configLocation))); 48 | 49 | config = new CodeGeneratorConfig(); 50 | 51 | config.setAuthor(properties.getProperty("author", System.getProperty("user.name"))); 52 | config.setComments(properties.getProperty("comments", "code generated by jpa codegen")); 53 | config.setDate(DATE_TIME_FORMATTER.format(LocalDate.now())); 54 | 55 | config.setFtlPath(properties.getProperty("template.dir", SRC_PATH + "resources/template/")); 56 | config.setCover(Boolean.parseBoolean(properties.getProperty("cover", "false"))); 57 | 58 | // io.github.gcdd1993.entity -> entity flag is entity 59 | config.setEntityFlag(properties.getProperty("entity.flag", "entity")); 60 | 61 | // custom other params 62 | Map otherParams = new HashMap<>(256); 63 | for (Object key : properties.keySet()) { 64 | String keyStr = key.toString(); 65 | if (keyStr.contains(".") && 66 | "custom".equals(keyStr.substring(0, keyStr.indexOf(".")))) { 67 | otherParams.put(keyStr.substring(keyStr.indexOf(".") + 1).replace(".", "_"), properties.getProperty(keyStr)); 68 | } 69 | } 70 | config.setOtherParams(otherParams); 71 | 72 | // 实体解析器 73 | entityParser = new DefaultEntityInfoParser(); 74 | 75 | // 渲染器 76 | render = new DefaultRender(config); 77 | 78 | log.info("init code generator success."); 79 | 80 | } catch (IOException e) { 81 | throw new JpaCodegenException("init code generator failed.", e); 82 | } 83 | } 84 | 85 | /** 86 | * 添加包下的所有实体 87 | * 88 | * @param packages 包名 89 | */ 90 | public CodeGenerator packInclude(String... packages) { 91 | for (String pack : packages) { 92 | config.getEntityClasses().addAll(ReflectUtils.getClassListByAnnotation(pack, javax.persistence.Entity.class)); 93 | } 94 | return this; 95 | } 96 | 97 | /** 98 | * 添加实体类,必须由{@link javax.persistence.Entity} 修饰 99 | * 100 | * @param classes 实体类 101 | */ 102 | public CodeGenerator clazzInclude(Class... classes) { 103 | for (Class clazz : classes) { 104 | config.getEntityClasses().add(clazz); 105 | } 106 | return this; 107 | } 108 | 109 | /** 110 | * 排除某些实体 111 | * 112 | * @param classes 要排除的实体,通常在{@link #packInclude(String...)}之后使用,排除一些特殊的类 113 | */ 114 | public CodeGenerator clazzExlude(Class... classes) { 115 | for (Class clazz : classes) { 116 | config.getEntityClasses().remove(clazz); 117 | } 118 | return this; 119 | } 120 | 121 | /** 122 | * 解析模块配置 123 | * 124 | * @param module 模块 125 | * @return 模块配置 126 | */ 127 | private ModuleConfig parseModuleConfig(String module) { 128 | ModuleConfig moduleConfig = new ModuleConfig(); 129 | moduleConfig.setClassNameSuffix(properties.getProperty(module + ".class.suffix", 130 | module.substring(0, 1).toUpperCase().concat(module.substring(1)))); 131 | moduleConfig.setFtlName(properties.getProperty(module + ".ftlName", module + ".ftl")); 132 | moduleConfig.setFlag(properties.getProperty(module + ".flag", module)); 133 | 134 | return moduleConfig; 135 | } 136 | 137 | public void generate() { 138 | List entityInfos = config.getEntityClasses().stream() 139 | .map(entityParser::parse) 140 | .filter(Objects::nonNull) 141 | .collect(Collectors.toList()); 142 | 143 | if (!entityInfos.isEmpty()) { 144 | log.info("find {} entity classes, now start generate code.", entityInfos.size()); 145 | 146 | entityInfos.forEach(entityInfo -> 147 | moduleList.forEach(module -> render.render(entityInfo, module))); 148 | } else { 149 | log.warn("find none entity class, please check your entity package is true."); 150 | } 151 | } 152 | 153 | /** 154 | * 注册渲染组件 155 | * 156 | * @param module 模块名 157 | * @return 代码生成器本身 158 | */ 159 | public CodeGenerator registerRender(String module) { 160 | ModuleConfig moduleConfig = parseModuleConfig(module); 161 | config.getModuleConfigMap().put(module, moduleConfig); 162 | moduleList.add(module); 163 | return this; 164 | } 165 | 166 | } 167 | -------------------------------------------------------------------------------- /src/main/java/io/github/gcdd1993/jpa/codegen/config/CodeGeneratorConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.gcdd1993.jpa.codegen.config; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | /** 11 | * TODO 12 | * 13 | * @author gaochen 14 | * Created on 2019/6/20. 15 | */ 16 | @Data 17 | public class CodeGeneratorConfig { 18 | private String entityFlag; 19 | private List> entityClasses = new ArrayList<>(256); 20 | private String ftlPath; 21 | private String author; 22 | private String date; 23 | private String comments; 24 | private boolean cover; 25 | 26 | private Map moduleConfigMap = new HashMap<>(); 27 | 28 | private Map otherParams; 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/io/github/gcdd1993/jpa/codegen/config/ModuleConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.gcdd1993.jpa.codegen.config; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * 模块名称 7 | * 8 | * @author gaochen 9 | * Created on 2019/6/20. 10 | */ 11 | @Data 12 | public class ModuleConfig { 13 | private String flag; 14 | private String packageName; 15 | private String classNameSuffix; 16 | private String ftlName; 17 | private String savePath; 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/io/github/gcdd1993/jpa/codegen/exception/JpaCodegenException.java: -------------------------------------------------------------------------------- 1 | package io.github.gcdd1993.jpa.codegen.exception; 2 | 3 | /** 4 | * exception when app running 5 | * 6 | * @author gaochen 7 | * Created on 2019/6/21. 8 | */ 9 | public class JpaCodegenException extends RuntimeException { 10 | public JpaCodegenException(String message) { 11 | super(message); 12 | } 13 | 14 | public JpaCodegenException(String message, Throwable cause) { 15 | super(message, cause); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/github/gcdd1993/jpa/codegen/metadata/AnnotationInfo.java: -------------------------------------------------------------------------------- 1 | package io.github.gcdd1993.jpa.codegen.metadata; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | 6 | /** 7 | * 注解信息 8 | * 9 | * @author gaochen 10 | * Created on 2019/6/21. 11 | */ 12 | @EqualsAndHashCode(callSuper = false) 13 | @Data 14 | public class AnnotationInfo extends BaseClassInfo { 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/github/gcdd1993/jpa/codegen/metadata/BaseClassInfo.java: -------------------------------------------------------------------------------- 1 | package io.github.gcdd1993.jpa.codegen.metadata; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * 类描述信息 7 | * 8 | * @author gaochen 9 | * Created on 2019/6/21. 10 | */ 11 | @Data 12 | public abstract class BaseClassInfo { 13 | 14 | /** 15 | * 类名 16 | */ 17 | protected String className; 18 | 19 | /** 20 | * 包名 21 | */ 22 | protected String packageName; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/io/github/gcdd1993/jpa/codegen/metadata/BaseEntityParser.java: -------------------------------------------------------------------------------- 1 | package io.github.gcdd1993.jpa.codegen.metadata; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * 默认的实体解析器 7 | * 8 | * @author gaochen 9 | * Created on 2019/6/21. 10 | */ 11 | public abstract class BaseEntityParser implements IEntityParser { 12 | 13 | @Override 14 | public final EntityInfo parse(Class clazz) { 15 | EntityInfo entityInfo = parseEntity(clazz); 16 | entityInfo.setFields(parseField(clazz)); 17 | entityInfo.setId(parseId(clazz)); 18 | return entityInfo; 19 | } 20 | 21 | /** 22 | * 解析实体信息 23 | * 24 | * @param clazz 指定实体类 25 | * @return 实体信息 26 | */ 27 | public abstract EntityInfo parseEntity(Class clazz); 28 | 29 | /** 30 | * 解析字段信息 31 | * 32 | * @param clazz 指定实体类 33 | * @return 字段信息 34 | */ 35 | public abstract List parseField(Class clazz); 36 | 37 | /** 38 | * 解析主键信息 39 | * 40 | * @param clazz 指定实体类 41 | * @return 主键信息 42 | */ 43 | public abstract IdInfo parseId(Class clazz); 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/io/github/gcdd1993/jpa/codegen/metadata/DefaultEntityInfoParser.java: -------------------------------------------------------------------------------- 1 | package io.github.gcdd1993.jpa.codegen.metadata; 2 | 3 | import io.github.gcdd1993.jpa.codegen.util.ReflectUtils; 4 | 5 | import javax.persistence.IdClass; 6 | import javax.persistence.Table; 7 | import java.lang.annotation.Annotation; 8 | import java.lang.reflect.Field; 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.List; 12 | import java.util.Optional; 13 | 14 | /** 15 | * 默认的实体解析器 16 | * 17 | * @author gaochen 18 | * Created on 2019/6/21. 19 | */ 20 | public class DefaultEntityInfoParser extends BaseEntityParser { 21 | 22 | /** 23 | * 主键注解 24 | */ 25 | private static final List> ID_CLASS_LIST = Arrays.asList(javax.persistence.Id.class, 26 | org.springframework.data.annotation.Id.class); 27 | 28 | @Override 29 | public EntityInfo parseEntity(Class clazz) { 30 | EntityInfo entityInfo = new EntityInfo(); 31 | entityInfo.setClassName(clazz.getSimpleName()); 32 | 33 | String packageName = clazz.getPackage().getName(); 34 | 35 | entityInfo.setPackageName(packageName); 36 | 37 | Table tableAnnotation = clazz.getAnnotation(javax.persistence.Table.class); 38 | if (tableAnnotation != null) { 39 | entityInfo.setTableName(tableAnnotation.name()); 40 | } 41 | return entityInfo; 42 | } 43 | 44 | @Override 45 | public List parseField(Class clazz) { 46 | List fields = new ArrayList<>(); 47 | Field[] declaredFields = clazz.getDeclaredFields(); 48 | for (Field f : declaredFields) { 49 | Package packageType = f.getType().getPackage(); 50 | boolean javaLangType = packageType == null || "java.lang".equals(packageType.getName()); 51 | boolean isId = false; 52 | for (Class idClass : ID_CLASS_LIST) { 53 | boolean hasAnnotation = ReflectUtils.hasAnnotation(f, idClass); 54 | // 跳过主键字段 55 | if (hasAnnotation) { 56 | isId = true; 57 | break; 58 | } 59 | } 60 | 61 | // 只解析出基本类型 62 | if (javaLangType && !isId) { 63 | FieldInfo fieldInfo = new FieldInfo(); 64 | fieldInfo.setClassName(f.getType().getSimpleName()); 65 | fieldInfo.setPackageName(f.getType().getTypeName()); 66 | 67 | fieldInfo.setName(f.getName()); 68 | 69 | // 字段注解 70 | Annotation[] fieldAnnotations = f.getAnnotations(); 71 | if (fieldAnnotations.length > 0) { 72 | List annotationInfos = new ArrayList<>(); 73 | 74 | for (Annotation annotation : fieldAnnotations) { 75 | AnnotationInfo annotationInfo = new AnnotationInfo(); 76 | annotationInfo.setClassName(annotation.annotationType().getSimpleName()); 77 | annotationInfo.setPackageName(annotation.annotationType().getPackage().getName()); 78 | } 79 | fieldInfo.setAnnotations(annotationInfos); 80 | } 81 | 82 | fields.add(fieldInfo); 83 | } 84 | } 85 | // try from super class 86 | Class maybeExists = clazz.getSuperclass(); 87 | if (!Object.class.equals(maybeExists)) { 88 | fields.addAll(parseField(maybeExists)); 89 | } 90 | return fields; 91 | } 92 | 93 | @Override 94 | public IdInfo parseId(Class clazz) { 95 | // 尝试从类注解获取主键信息 96 | IdClass idClassAnnotation = clazz.getAnnotation(javax.persistence.IdClass.class); 97 | if (idClassAnnotation != null) { 98 | IdInfo idInfo = new IdInfo(); 99 | idInfo.setClassName(idClassAnnotation.value().getSimpleName()); 100 | idInfo.setPackageName(idClassAnnotation.value().getTypeName()); 101 | return idInfo; 102 | } 103 | // 尝试获取第一个带主键注解的字段 104 | for (Class idClass : ID_CLASS_LIST) { 105 | Optional fieldByAnnotation = ReflectUtils.getFieldByAnnotation(clazz, idClass); 106 | if (fieldByAnnotation.isPresent()) { 107 | Field field = fieldByAnnotation.get(); 108 | IdInfo idInfo = new IdInfo(); 109 | idInfo.setClassName(field.getType().getSimpleName()); 110 | idInfo.setPackageName(field.getType().getTypeName()); 111 | return idInfo; 112 | } 113 | } 114 | // try from super class 115 | Class maybeExists = clazz.getSuperclass(); 116 | if (!Object.class.equals(maybeExists)) { 117 | return parseId(maybeExists); 118 | } 119 | return null; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/io/github/gcdd1993/jpa/codegen/metadata/EntityInfo.java: -------------------------------------------------------------------------------- 1 | package io.github.gcdd1993.jpa.codegen.metadata; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 实体信息 10 | * 11 | * @author gaochen 12 | * Created on 2019/6/18. 13 | */ 14 | @EqualsAndHashCode(callSuper = false) 15 | @Data 16 | public class EntityInfo extends BaseClassInfo { 17 | 18 | /** 19 | * 表名 20 | */ 21 | private String tableName; 22 | 23 | /** 24 | * 主键类名 25 | */ 26 | private IdInfo id; 27 | 28 | /** 29 | * 所有< 属性名, 类 > 30 | */ 31 | private List fields; 32 | 33 | /** 34 | * 注解 35 | */ 36 | private List annotations; 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/io/github/gcdd1993/jpa/codegen/metadata/FieldInfo.java: -------------------------------------------------------------------------------- 1 | package io.github.gcdd1993.jpa.codegen.metadata; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * TODO 10 | * 11 | * @author gaochen 12 | * Created on 2019/6/21. 13 | */ 14 | @EqualsAndHashCode(callSuper = false) 15 | @Data 16 | public class FieldInfo extends BaseClassInfo { 17 | 18 | /** 19 | * 字段名称 20 | */ 21 | private String name; 22 | 23 | /** 24 | * 字段上的注解 25 | */ 26 | private List annotations; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/io/github/gcdd1993/jpa/codegen/metadata/IEntityParser.java: -------------------------------------------------------------------------------- 1 | package io.github.gcdd1993.jpa.codegen.metadata; 2 | 3 | /** 4 | * 实体解析器 5 | * 6 | * @author gaochen 7 | * Created on 2019/6/21. 8 | */ 9 | public interface IEntityParser { 10 | 11 | /** 12 | * 将指定实体类解析为实体信息 13 | * 14 | * @param clazz 实体类 15 | * @return 实体信息 16 | */ 17 | EntityInfo parse(Class clazz); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/io/github/gcdd1993/jpa/codegen/metadata/IdInfo.java: -------------------------------------------------------------------------------- 1 | package io.github.gcdd1993.jpa.codegen.metadata; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | 6 | /** 7 | * 主键 8 | * 9 | * @author gaochen 10 | * Created on 2019/6/21. 11 | */ 12 | @EqualsAndHashCode(callSuper = false) 13 | @Data 14 | public class IdInfo extends BaseClassInfo { 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/github/gcdd1993/jpa/codegen/render/DefaultRender.java: -------------------------------------------------------------------------------- 1 | package io.github.gcdd1993.jpa.codegen.render; 2 | 3 | import io.github.gcdd1993.jpa.codegen.config.CodeGeneratorConfig; 4 | import io.github.gcdd1993.jpa.codegen.config.ModuleConfig; 5 | import io.github.gcdd1993.jpa.codegen.metadata.EntityInfo; 6 | import io.github.gcdd1993.jpa.codegen.util.FreeMarkerUtils; 7 | 8 | import java.util.*; 9 | 10 | /** 11 | * TODO 12 | * 13 | * @author gaochen 14 | * Created on 2019/6/20. 15 | */ 16 | public class DefaultRender implements IRender { 17 | 18 | private final CodeGeneratorConfig config; 19 | 20 | private Map lastRenderingResponseMap = new HashMap<>(); 21 | 22 | public DefaultRender(CodeGeneratorConfig config) { 23 | this.config = config; 24 | } 25 | 26 | @Override 27 | public final RenderingResponse render(EntityInfo entityInfo, String module) { 28 | RenderingRequest renderingRequest = new RenderingRequest(); 29 | 30 | renderingRequest.setLastRenderResponse(lastRenderingResponseMap); 31 | 32 | ModuleConfig moduleConfig = config.getModuleConfigMap().get(module); 33 | renderingRequest.setClassName(entityInfo.getClassName() + moduleConfig.getClassNameSuffix()); 34 | String packageName = entityInfo.getPackageName().replace(config.getEntityFlag(), moduleConfig.getFlag()); 35 | 36 | renderingRequest.setPackageName(packageName); 37 | renderingRequest.setSavePath("src/main/java/" + packageName.replace(".", "/") + "/"); 38 | renderingRequest.setFtlName(moduleConfig.getFtlName()); 39 | 40 | renderingRequest.setFtlPath(config.getFtlPath()); 41 | renderingRequest.setCover(config.isCover()); 42 | 43 | renderingRequest.setEntity(entityInfo); 44 | 45 | renderingRequest.setAuthor(config.getAuthor()); 46 | renderingRequest.setComments(config.getComments()); 47 | renderingRequest.setDate(config.getDate()); 48 | 49 | // fields ,只支持基本类型映射 50 | renderingRequest.setOtherParams(config.getOtherParams()); 51 | 52 | // check for other imports 53 | renderingRequest.setImports(checkImports(entityInfo)); 54 | 55 | // use freemarker to render code. 56 | RenderingResponse lastRenderingResponse = FreeMarkerUtils.process(renderingRequest); 57 | 58 | lastRenderingResponseMap.put(module, lastRenderingResponse); 59 | return lastRenderingResponse; 60 | } 61 | 62 | private List checkImports(EntityInfo entityInfo) { 63 | List imports = new ArrayList<>(); 64 | String packageName = entityInfo.getId().getPackageName(); 65 | if (!"java.lang".equals(packageName)) { 66 | imports.add(packageName); 67 | } 68 | return imports; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/io/github/gcdd1993/jpa/codegen/render/IRender.java: -------------------------------------------------------------------------------- 1 | package io.github.gcdd1993.jpa.codegen.render; 2 | 3 | import io.github.gcdd1993.jpa.codegen.metadata.EntityInfo; 4 | 5 | /** 6 | * TODO 7 | * 8 | * @author gaochen 9 | * Created on 2019/6/20. 10 | */ 11 | public interface IRender { 12 | 13 | /** 14 | * 渲染为java文件 15 | * 16 | * @param entityInfo 实体信息 17 | * @param module 模块名称 18 | * @return 渲染结果 19 | */ 20 | RenderingResponse render(EntityInfo entityInfo, String module); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/io/github/gcdd1993/jpa/codegen/render/RenderingRequest.java: -------------------------------------------------------------------------------- 1 | package io.github.gcdd1993.jpa.codegen.render; 2 | 3 | import io.github.gcdd1993.jpa.codegen.metadata.EntityInfo; 4 | import lombok.Data; 5 | 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | /** 10 | * 渲染请求 11 | * 12 | * @author gaochen 13 | * Created on 2019/6/20. 14 | */ 15 | @Data 16 | public class RenderingRequest { 17 | private String ftlName; 18 | private String ftlPath; 19 | private String savePath; 20 | private String packageName; 21 | private boolean cover; 22 | private String className; 23 | private String author; 24 | private String date; 25 | private String comments; 26 | private EntityInfo entity; 27 | private List imports; 28 | private Map lastRenderResponse; 29 | private Map otherParams; 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/github/gcdd1993/jpa/codegen/render/RenderingResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.gcdd1993.jpa.codegen.render; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * 渲染结果 7 | * 8 | * @author gaochen 9 | * Created on 2019/6/20. 10 | */ 11 | @Data 12 | public class RenderingResponse { 13 | private boolean success; 14 | private String errorMsg; 15 | private String ftlName; 16 | private String packageName; 17 | private String className; 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/io/github/gcdd1993/jpa/codegen/util/FreeMarkerUtils.java: -------------------------------------------------------------------------------- 1 | package io.github.gcdd1993.jpa.codegen.util; 2 | 3 | import freemarker.ext.beans.BeansWrapper; 4 | import freemarker.template.Configuration; 5 | import freemarker.template.Template; 6 | import freemarker.template.TemplateException; 7 | import freemarker.template.TemplateExceptionHandler; 8 | import io.github.gcdd1993.jpa.codegen.exception.JpaCodegenException; 9 | import io.github.gcdd1993.jpa.codegen.render.RenderingRequest; 10 | import io.github.gcdd1993.jpa.codegen.render.RenderingResponse; 11 | import lombok.experimental.UtilityClass; 12 | import lombok.extern.slf4j.Slf4j; 13 | 14 | import java.io.File; 15 | import java.io.IOException; 16 | import java.io.StringWriter; 17 | import java.io.Writer; 18 | import java.nio.file.Files; 19 | import java.nio.file.Path; 20 | import java.nio.file.Paths; 21 | 22 | /** 23 | * TODO 24 | * 25 | * @author gaochen 26 | * Created on 2019/6/20. 27 | */ 28 | @Slf4j 29 | @UtilityClass 30 | public class FreeMarkerUtils { 31 | 32 | private static Configuration configuration; 33 | 34 | static { 35 | configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS); 36 | configuration.setObjectWrapper(Configuration.getDefaultObjectWrapper(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS)); 37 | configuration.setDefaultEncoding("UTF-8"); 38 | } 39 | 40 | /** 41 | * 获取解析后的字符串 42 | * 43 | * @param renderingRequest 模板参数 44 | * @return 渲染结果 45 | */ 46 | private static String render(RenderingRequest renderingRequest) { 47 | try (Writer writer = new StringWriter()) { 48 | configuration.setDirectoryForTemplateLoading(new File(renderingRequest.getFtlPath())); 49 | configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); 50 | configuration.setObjectWrapper(new BeansWrapper(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS)); 51 | configuration.setWhitespaceStripping(true); 52 | 53 | Template template = configuration.getTemplate(renderingRequest.getFtlName()); 54 | template.process(renderingRequest, writer); 55 | return writer.toString(); 56 | } catch (TemplateException | IOException e) { 57 | throw new JpaCodegenException(String.format("render %s code source error.", renderingRequest.getEntity().getClassName()), e); 58 | } 59 | } 60 | 61 | /** 62 | * 获取解析后的值. 63 | * 64 | * @param renderingRequest 模板参数 65 | * @return 渲染结果 66 | */ 67 | public static RenderingResponse process(RenderingRequest renderingRequest) { 68 | RenderingResponse response = new RenderingResponse(); 69 | response.setClassName(renderingRequest.getClassName()); 70 | response.setPackageName(renderingRequest.getPackageName()); 71 | response.setFtlName(renderingRequest.getFtlName()); 72 | 73 | String code = render(renderingRequest); 74 | try { 75 | if (code != null) { 76 | saveToFile(code, renderingRequest.getSavePath(), renderingRequest.getClassName() + ".java", renderingRequest.isCover()); 77 | } 78 | response.setSuccess(true); 79 | } catch (IOException e) { 80 | throw new JpaCodegenException(String.format("render %s code source failed.", renderingRequest.getEntity().getClassName()), e); 81 | } 82 | return response; 83 | } 84 | 85 | private static void saveToFile(String code, String filePath, String fileName, boolean cover) throws IOException { 86 | String finalFileName = filePath + fileName; 87 | Path path = Paths.get(finalFileName); 88 | if (Files.exists(path)) { 89 | // check for cove 90 | if (!cover) { 91 | log.info("skip {} due to file exists.", fileName); 92 | return; 93 | } else { 94 | Files.delete(path); 95 | } 96 | } 97 | 98 | // check for dir 99 | Path dirPath = Paths.get(filePath); 100 | if (!Files.exists(dirPath)) { 101 | Files.createDirectories(dirPath); 102 | } 103 | 104 | Files.createFile(path); 105 | Files.write(path, code.getBytes()); 106 | 107 | log.debug("path: {}, code: {}", path, code); 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/io/github/gcdd1993/jpa/codegen/util/ReflectUtils.java: -------------------------------------------------------------------------------- 1 | package io.github.gcdd1993.jpa.codegen.util; 2 | 3 | import io.github.gcdd1993.jpa.codegen.exception.JpaCodegenException; 4 | import lombok.experimental.UtilityClass; 5 | import org.springframework.util.StringUtils; 6 | 7 | import java.io.File; 8 | import java.lang.annotation.Annotation; 9 | import java.lang.reflect.Field; 10 | import java.net.JarURLConnection; 11 | import java.net.URL; 12 | import java.util.ArrayList; 13 | import java.util.Enumeration; 14 | import java.util.List; 15 | import java.util.Optional; 16 | import java.util.jar.JarEntry; 17 | import java.util.jar.JarFile; 18 | 19 | /** 20 | * Java反射工具类 21 | * 22 | * @author gaochen 23 | * Created on 2019/6/19. 24 | */ 25 | @UtilityClass 26 | public class ReflectUtils { 27 | 28 | public static Optional getFieldByAnnotation(Class clazz, Class annotationType) { 29 | Field[] declaredFields = clazz.getDeclaredFields(); 30 | for (Field field : declaredFields) { 31 | Annotation[] annotations = field.getDeclaredAnnotations(); 32 | for (Annotation annotation : annotations) { 33 | if (annotation.annotationType().equals(annotationType)) { 34 | return Optional.of(field); 35 | } 36 | } 37 | } 38 | return Optional.empty(); 39 | } 40 | 41 | /** 42 | * 获取指定包名下的所有类(可根据注解进行过滤) 43 | * 44 | * @param packageName 包名 45 | * @param annotationClass 注解类 46 | * @return 类 47 | */ 48 | @SuppressWarnings("unchecked") 49 | public static List> getClassListByAnnotation(String packageName, Class annotationClass) { 50 | List> classList = new ArrayList<>(); 51 | try { 52 | Enumeration urls = Thread.currentThread().getContextClassLoader().getResources(packageName.replaceAll("\\.", "/")); 53 | while (urls.hasMoreElements()) { 54 | URL url = urls.nextElement(); 55 | if (url != null) { 56 | String protocol = url.getProtocol(); 57 | if ("file".equals(protocol)) { 58 | String packagePath = url.getPath(); 59 | addClassByAnnotation(classList, packagePath, packageName, annotationClass); 60 | } else if ("jar".equals(protocol)) { 61 | JarURLConnection urlConnection = (JarURLConnection) url.openConnection(); 62 | JarFile jarFile = urlConnection.getJarFile(); 63 | Enumeration jarEntries = jarFile.entries(); 64 | while (jarEntries.hasMoreElements()) { 65 | JarEntry jarEntry = jarEntries.nextElement(); 66 | String jarEntryName = jarEntry.getName(); 67 | if (jarEntryName.endsWith(".class")) { 68 | String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", "."); 69 | Class cls = Class.forName(className); 70 | if (cls.isAnnotationPresent(annotationClass)) { 71 | classList.add(cls); 72 | } 73 | } 74 | } 75 | } 76 | } 77 | } 78 | } catch (Exception e) { 79 | throw new JpaCodegenException(String.format("get class from package %s by annotation %s error", packageName, annotationClass.getSimpleName()), e); 80 | } 81 | return classList; 82 | } 83 | 84 | private static String getClassName(String packageName, String fileName) { 85 | String className = fileName.substring(0, fileName.lastIndexOf(".")); 86 | if (!StringUtils.isEmpty(packageName)) { 87 | className = packageName + "." + className; 88 | } 89 | return className; 90 | } 91 | 92 | private static String getSubPackagePath(String packagePath, String filePath) { 93 | String subPackagePath = filePath; 94 | if (!StringUtils.isEmpty(packagePath)) { 95 | subPackagePath = packagePath + "/" + subPackagePath; 96 | } 97 | return subPackagePath; 98 | } 99 | 100 | private static String getSubPackageName(String packageName, String filePath) { 101 | String subPackageName = filePath; 102 | if (!StringUtils.isEmpty(packageName)) { 103 | subPackageName = packageName + "." + subPackageName; 104 | } 105 | return subPackageName; 106 | } 107 | 108 | private static void addClassByAnnotation(List> classList, String packagePath, String packageName, Class annotationClass) { 109 | try { 110 | File[] files = getClassFiles(packagePath); 111 | if (files != null) { 112 | for (File file : files) { 113 | String fileName = file.getName(); 114 | if (file.isFile()) { 115 | String className = getClassName(packageName, fileName); 116 | Class cls = Class.forName(className); 117 | if (cls.isAnnotationPresent(annotationClass)) { 118 | classList.add(cls); 119 | } 120 | } else { 121 | String subPackagePath = getSubPackagePath(packagePath, fileName); 122 | String subPackageName = getSubPackageName(packageName, fileName); 123 | addClassByAnnotation(classList, subPackagePath, subPackageName, annotationClass); 124 | } 125 | } 126 | } 127 | } catch (Exception e) { 128 | throw new JpaCodegenException("add class by annotation error", e); 129 | } 130 | } 131 | 132 | private static File[] getClassFiles(String packagePath) { 133 | return new File(packagePath).listFiles(file -> (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory()); 134 | } 135 | 136 | public static boolean hasAnnotation(Field field, Class annotationType) { 137 | Annotation maybeExist = field.getAnnotation(annotationType); 138 | return maybeExist != null; 139 | } 140 | 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/io/github/gcdd1993/jpa/codegen/util/ResourceReader.java: -------------------------------------------------------------------------------- 1 | package io.github.gcdd1993.jpa.codegen.util; 2 | 3 | import lombok.experimental.UtilityClass; 4 | import lombok.extern.slf4j.Slf4j; 5 | 6 | import java.io.File; 7 | import java.io.FileInputStream; 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | 11 | /** 12 | * TODO 13 | * 14 | * @author gaochen 15 | * Created on 2019/6/21. 16 | */ 17 | @Slf4j 18 | @UtilityClass 19 | public class ResourceReader { 20 | 21 | public static InputStream getResourceAsStream(String path) throws IOException { 22 | InputStream classPathResource = ResourceReader.class.getClassLoader().getResourceAsStream(path); 23 | if (classPathResource != null) { 24 | return classPathResource; 25 | } 26 | return new FileInputStream(new File(path)); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/resources/example.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gcdd1993/jpa-codegen/3174e1c96044425db9742c3ea64d3d64194b0ea9/src/main/resources/example.properties -------------------------------------------------------------------------------- /src/main/resources/template/spring1/controller.ftl: -------------------------------------------------------------------------------- 1 | package ${packageName}; 2 | 3 | import ${entity.packageName}.${entity.className}; 4 | import ${lastRenderResponse.form.packageName}.${lastRenderResponse.form.className}; 5 | import ${lastRenderResponse.service.packageName}.${lastRenderResponse.service.className}; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.bind.annotation.*; 8 | 9 | import java.util.List; 10 | import java.util.Optional; 11 | 12 | /** 13 | * ${comments} 14 | * 15 | * @author ${author} 16 | * Created On ${date}. 17 | */ 18 | @RestController 19 | @RequestMapping("/${entity.className?uncap_first}") 20 | public class ${className} { 21 | 22 | @Autowired 23 | private ${lastRenderResponse.service.className} ${lastRenderResponse.service.className?uncap_first}; 24 | 25 | @PostMapping 26 | public ${entity.className} create(${lastRenderResponse.form.className} form) { 27 | return ${lastRenderResponse.service.className?uncap_first}.create(form); 28 | } 29 | 30 | @DeleteMapping 31 | public void delete(${entity.idClassName} id) { 32 | ${lastRenderResponse.service.className?uncap_first}.delete(id); 33 | } 34 | 35 | @PutMapping("/{id}") 36 | public Optional<${entity.className}> update(@RequestBody ${lastRenderResponse.form.className} form, 37 | @PathVariable("id") ${entity.idClassName} id) { 38 | return ${lastRenderResponse.service.className?uncap_first}.update(form, id); 39 | } 40 | 41 | @GetMapping("/{id}") 42 | public Optional<${entity.className}> get(${entity.idClassName} id) { 43 | return ${lastRenderResponse.service.className?uncap_first}.get(id); 44 | } 45 | 46 | @GetMapping 47 | public List<${entity.className}> list() { 48 | return ${lastRenderResponse.service.className?uncap_first}.list(); 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /src/main/resources/template/spring1/form.ftl: -------------------------------------------------------------------------------- 1 | package ${packageName}; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * ${comments} 7 | * 8 | * @author ${author} 9 | * Created On ${date}. 10 | */ 11 | @Data 12 | public class ${className} { 13 | 14 | <#list fields as field> 15 | private ${field.className} ${field.name}; 16 | 17 | 18 | } -------------------------------------------------------------------------------- /src/main/resources/template/spring1/repository.ftl: -------------------------------------------------------------------------------- 1 | package ${packageName}; 2 | 3 | import ${entity.packageName}.${entity.className}; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | <#if imports??> 6 | <#list imports as import> 7 | import ${import}; 8 | 9 | 10 | 11 | /** 12 | * ${comments} 13 | * 14 | * @author ${author} 15 | * Created On ${date}. 16 | */ 17 | public interface ${className} extends JpaRepository<${entity.className}, ${entity.idClassName}> { 18 | } -------------------------------------------------------------------------------- /src/main/resources/template/spring1/service.ftl: -------------------------------------------------------------------------------- 1 | package ${packageName}; 2 | 3 | import org.springframework.beans.BeanUtils; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.stereotype.Service; 6 | import ${entity.packageName}.${entity.className}; 7 | import ${lastRenderResponse.repository.packageName}.${lastRenderResponse.repository.className}; 8 | import ${lastRenderResponse.form.packageName}.${lastRenderResponse.form.className}; 9 | <#if imports??> 10 | <#list imports as import> 11 | import ${import}; 12 | 13 | 14 | 15 | import java.util.List; 16 | import java.util.Optional; 17 | 18 | /** 19 | * ${comments} 20 | * 21 | * @author ${author} 22 | * Created On ${date}. 23 | */ 24 | @Service 25 | public class ${className} { 26 | 27 | @Autowired 28 | private ${lastRenderResponse.repository.className} ${lastRenderResponse.repository.className?uncap_first}; 29 | 30 | public ${entity.className} create(${lastRenderResponse.form.className} form) { 31 | ${entity.className} ${entity.className?uncap_first} = new ${entity.className}(); 32 | BeanUtils.copyProperties(form, ${entity.className?uncap_first}); 33 | // TODO 34 | return ${lastRenderResponse.repository.className?uncap_first}.save(${entity.className?uncap_first}); 35 | } 36 | 37 | public void delete(${entity.idClassName} id) { 38 | ${lastRenderResponse.repository.className?uncap_first}.findById(id) 39 | .ifPresent(${lastRenderResponse.repository.className?uncap_first}::delete); 40 | } 41 | 42 | public Optional<${entity.className}> update(${lastRenderResponse.form.className} form, ${entity.idClassName} id) { 43 | return ${lastRenderResponse.repository.className?uncap_first}.findById(id) 44 | .map(${entity.className?uncap_first} -> { 45 | BeanUtils.copyProperties(form, ${entity.className?uncap_first}); 46 | return ${lastRenderResponse.repository.className?uncap_first}.save(${entity.className?uncap_first}); 47 | }); 48 | } 49 | 50 | public Optional<${entity.className}> get(${entity.idClassName} id) { 51 | return ${lastRenderResponse.repository.className?uncap_first}.findById(id); 52 | } 53 | 54 | public List<${entity.className}> list() { 55 | return ${lastRenderResponse.repository.className?uncap_first}.findAll(); 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /src/main/resources/template/spring2/controller.ftl: -------------------------------------------------------------------------------- 1 | package ${packageName}; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.web.bind.annotation.*; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.validation.annotation.Validated; 7 | import com.querydsl.core.BooleanBuilder; 8 | import com.maxtropy.iacp.utils.PageUtils; 9 | import com.maxtropy.iacp.controller.base.BaseController; 10 | import org.springframework.data.domain.Pageable; 11 | import ${entity.packageName}.${entity.className}; 12 | import ${lastRenderResponse.form.packageName}.${lastRenderResponse.form.className}; 13 | import ${lastRenderResponse.service.packageName}.${lastRenderResponse.service.className}; 14 | <#list imports as import> 15 | import ${import}; 16 | 17 | 18 | import java.security.Principal; 19 | 20 | /** 21 | * controller for ${entity.className} generated by jpa-codegen 22 | * ${comments} 23 | * 24 | * @author ${author} 25 | * Created On ${date}. 26 | */ 27 | @RestController 28 | @RequestMapping("/${entity.className?uncap_first}") 29 | public class ${className} extends BaseController { 30 | 31 | @Autowired 32 | private ${lastRenderResponse.service.className} ${lastRenderResponse.service.className?uncap_first}; 33 | 34 | @PostMapping 35 | public ResponseEntity create(@RequestBody @Validated ${lastRenderResponse.form.className} form) { 36 | ${entity.className} ${entity.className?uncap_first} = ${lastRenderResponse.service.className?uncap_first}.create(form); 37 | return created(${entity.className?uncap_first}); 38 | } 39 | 40 | @DeleteMapping("/{id}") 41 | public ResponseEntity delete(${entity.id.className} id) { 42 | ${lastRenderResponse.service.className?uncap_first}.delete(id); 43 | return deleted(); 44 | } 45 | 46 | @PutMapping("/{id}") 47 | public ResponseEntity update(@RequestBody @Validated ${lastRenderResponse.form.className} form, 48 | @PathVariable("id") ${entity.id.className} id) { 49 | ${entity.className} ${entity.className?uncap_first} = ${lastRenderResponse.service.className?uncap_first}.update(form, id); 50 | return created(${entity.className?uncap_first}); 51 | } 52 | 53 | @GetMapping("/{id}") 54 | public ResponseEntity get(@PathVariable("id") ${entity.id.className} id) { 55 | ${entity.className} ${entity.className?uncap_first} = ${lastRenderResponse.service.className?uncap_first}.get(id); 56 | return ok(${entity.className?uncap_first}); 57 | } 58 | 59 | @GetMapping 60 | public ResponseEntity page(@RequestParam(value = "page", required = false) Integer page, 61 | @RequestParam(value = "per_page", required = false) Integer perPage, 62 | @RequestParam(value = "fields", required = false) String fields, 63 | Principal principal) { 64 | BooleanBuilder booleanBuilder = new BooleanBuilder(); 65 | Pageable pageable = PageUtils.page(page, perPage, false, "updateTime"); 66 | // TODO 67 | return ok(${lastRenderResponse.service.className?uncap_first}.page(booleanBuilder, pageable), fields); 68 | } 69 | 70 | } -------------------------------------------------------------------------------- /src/main/resources/template/spring2/form.ftl: -------------------------------------------------------------------------------- 1 | package ${packageName}; 2 | 3 | import lombok.Data; 4 | <#list imports as import> 5 | import ${import}; 6 | 7 | 8 | /** 9 | * form for ${entity.className} generated by jpa-codegen 10 | * ${comments} 11 | * 12 | * @author ${author} 13 | * Created On ${date}. 14 | */ 15 | @Data 16 | public class ${className} { 17 | 18 | <#if entity.fields?? && (entity.fields?size > 0)> 19 | <#list entity.fields as f> 20 | /** 21 | * TODO 字段描述 22 | */ 23 | private ${f.className} ${f.name}; 24 | 25 | 26 | 27 | } -------------------------------------------------------------------------------- /src/main/resources/template/spring2/repository.ftl: -------------------------------------------------------------------------------- 1 | package ${packageName}; 2 | 3 | import ${entity.packageName}.${entity.className}; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.data.querydsl.QuerydslPredicateExecutor; 6 | <#list imports as import> 7 | import ${import}; 8 | 9 | 10 | /** 11 | * repository for ${entity.className} generated by jpa-codegen 12 | * ${comments} 13 | * 14 | * @author ${author} 15 | * Created On ${date}. 16 | */ 17 | public interface ${className} extends JpaRepository<${entity.className}, ${entity.id.className}>, QuerydslPredicateExecutor<${entity.className}> { 18 | } -------------------------------------------------------------------------------- /src/main/resources/template/spring2/service.ftl: -------------------------------------------------------------------------------- 1 | package ${packageName}; 2 | 3 | import org.springframework.beans.BeanUtils; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.stereotype.Service; 6 | import org.springframework.data.domain.Page; 7 | import com.querydsl.core.BooleanBuilder; 8 | import org.springframework.data.domain.Pageable; 9 | import com.maxtropy.iacp.utils.PageUtils; 10 | import ${entity.packageName}.${entity.className}; 11 | import ${lastRenderResponse.repository.packageName}.${lastRenderResponse.repository.className}; 12 | import ${lastRenderResponse.form.packageName}.${lastRenderResponse.form.className}; 13 | <#list imports as import> 14 | import ${import}; 15 | 16 | 17 | import java.util.Map; 18 | import javax.transaction.Transactional; 19 | 20 | /** 21 | * service for ${entity.className} generated by jpa-codegen 22 | * ${comments} 23 | * 24 | * @author ${author} 25 | * Created On ${date}. 26 | */ 27 | @Service 28 | public class ${className} { 29 | 30 | @Autowired 31 | private ${lastRenderResponse.repository.className} ${lastRenderResponse.repository.className?uncap_first}; 32 | 33 | /** 34 | * 创建实体 35 | * 36 | * @param form 表单 37 | * @return 实体对象 38 | */ 39 | @Transactional 40 | public ${entity.className} create(${lastRenderResponse.form.className} form) { 41 | ${entity.className} ${entity.className?uncap_first} = new ${entity.className}(); 42 | BeanUtils.copyProperties(form, ${entity.className?uncap_first}); 43 | // TODO 44 | return ${lastRenderResponse.repository.className?uncap_first}.save(${entity.className?uncap_first}); 45 | } 46 | 47 | /** 48 | * 删除实体 49 | * @param id 实体id 50 | */ 51 | public void delete(${entity.id.className} id) { 52 | ${lastRenderResponse.repository.className?uncap_first}.findById(id) 53 | .ifPresent(${lastRenderResponse.repository.className?uncap_first}::delete); 54 | } 55 | 56 | /** 57 | * 更新实体 58 | * 59 | * @param form 表单 60 | * @param id 实体id 61 | * @return 实体对象 62 | */ 63 | @Transactional 64 | public ${entity.className} update(${lastRenderResponse.form.className} form, ${entity.id.className} id) { 65 | return ${lastRenderResponse.repository.className?uncap_first}.findById(id) 66 | .map(${entity.className?uncap_first} -> { 67 | BeanUtils.copyProperties(form, ${entity.className?uncap_first}); 68 | return ${lastRenderResponse.repository.className?uncap_first}.save(${entity.className?uncap_first}); 69 | }).get(); 70 | } 71 | 72 | /** 73 | * 获取一个实体对象 74 | * 75 | * @param id 实体id 76 | * @return 实体对象 77 | */ 78 | public ${entity.className} get(${entity.id.className} id) { 79 | return ${lastRenderResponse.repository.className?uncap_first}.findById(id).get(); 80 | } 81 | 82 | /** 83 | * 分页接口 84 | * 85 | * @param booleanBuilder 分页查询条件 86 | * @param pageable 分页条件 87 | * @return 分页结果 88 | */ 89 | public Map page(BooleanBuilder booleanBuilder, Pageable pageable) { 90 | Page<${entity.className}> pageResult = ${lastRenderResponse.repository.className?uncap_first}.findAll(booleanBuilder,pageable); 91 | return PageUtils.pageResponse("${entity.className?uncap_first}_list", 92 | pageResult.getContent(), 93 | pageable, 94 | pageResult.getTotalElements()); 95 | } 96 | 97 | } -------------------------------------------------------------------------------- /src/test/java/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | import io.github.gcdd1993.jpa.codegen.CodeGenerator; 2 | import org.junit.Test; 3 | 4 | /** 5 | * TODO 6 | * 7 | * @author gaochen 8 | * Created on 2019/6/19. 9 | */ 10 | public class ApplicationTest { 11 | 12 | @Test 13 | public void generate() { 14 | 15 | new CodeGenerator("/example.properties") 16 | .registerRender("form") 17 | .registerRender("repository") 18 | .registerRender("service") 19 | .registerRender("controller") 20 | .generate(); 21 | 22 | } 23 | } 24 | --------------------------------------------------------------------------------