From 191918dc79ce691f93bae19f613a4cb6713f88c5 Mon Sep 17 00:00:00 2001 From: mengweijin <1002284406@qq.com> Date: Sun, 2 Jun 2024 20:10:09 +0800 Subject: [PATCH 1/3] refactor code --- pom.xml | 80 +++++--- .../code/generator/driver/DriverShim.java | 64 ++++++ .../driver/DynamicDriverDataSource.java} | 117 ++++++++--- .../mengweijin/code/generator/dto/Config.java | 39 ++++ .../entity => code/generator/dto}/DbInfo.java | 4 +- .../generator/engine/ITemplateEngine.java | 63 ++++++ .../VelocityClasspathTemplateEngine.java | 59 ++++++ .../VelocityFileSystemTemplateEngine.java | 40 ++++ .../engine/VelocityTemplateEngine.java | 102 ++++++++++ .../generator/enums/TemplateType.java | 4 +- .../mojo/AbstractClasspathGeneratorMojo.java | 32 +++ .../mojo/AbstractFileSystemGeneratorMojo.java | 48 +++++ .../generator/mojo/AbstractGeneratorMojo.java | 182 +++++++++++++++++ .../code/generator/mojo/CodeMojo.java | 24 +++ .../generator/mojo/MybatisMojo.java} | 23 ++- .../code/generator/mojo/MybatisPlusMojo.java | 28 +++ .../generator/reader/BootFileReader.java | 6 +- .../reader/BootFileReaderFactory.java | 17 +- .../generator/reader/BootFileType.java | 2 +- .../reader/PropertiesBootFileReader.java | 37 ++++ .../generator/reader/YamlBootFileReader.java | 32 +-- .../generator/util/DbInfoUtils.java | 74 +++---- .../code/generator/util/GeneratorUtils.java | 94 +++++++++ .../{ => code}/generator/util/YamlUtils.java | 15 +- .../generator/DefaultAutoGenerator.java | 188 ------------------ .../generator/config/FileOutput.java | 87 -------- .../engine/BeetlStringTemplateEngine.java | 56 ------ .../mengweijin/generator/entity/Docker.java | 13 -- .../mengweijin/generator/entity/IdField.java | 16 -- .../generator/entity/Parameters.java | 51 ----- .../generator/entity/ProjectInfo.java | 27 --- .../mengweijin/generator/enums/Template.java | 22 -- .../factory/TemplateEngineFactory.java | 35 ---- .../generator/mojo/AbstractDockerMojo.java | 110 ---------- .../generator/mojo/AbstractGeneratorMojo.java | 122 ------------ .../generator/mojo/CustomerGeneratorMojo.java | 41 ---- .../generator/mojo/DockerBuildMojo.java | 31 --- .../generator/mojo/DockerDeleteMojo.java | 28 --- .../generator/mojo/DockerDeployMojo.java | 31 --- .../mojo/DockerfileGeneratorMojo.java | 29 --- .../generator/mojo/JpaGeneratorMojo.java | 24 --- .../mojo/MybatisPlusGeneratorMojo.java | 28 --- .../reader/PropertiesBootFileReader.java | 44 ---- .../generator/util/TemplateUtils.java | 55 ----- .../mybatis-plus/${entityName}.java.vm | 60 ++++++ ...va.btl => ${entityName}Controller.java.vm} | 90 ++++----- .../mybatis-plus/${entityName}DTO.java.vm | 25 +++ .../mybatis-plus/${entityName}Mapper.java.vm | 28 +++ .../mybatis-plus/${entityName}Mapper.xml.vm | 46 +++++ .../mybatis-plus/${entityName}Service.java.vm | 32 +++ .../templates/mybatis-plus/entity.java.btl | 151 -------------- .../templates/mybatis-plus/mapper.java.btl | 21 -- .../templates/mybatis-plus/mapper.xml.btl | 44 ---- .../templates/mybatis-plus/service.java.btl | 36 ---- .../generator/mojo/DockerDeployMojoTest.java | 6 +- 55 files changed, 1268 insertions(+), 1495 deletions(-) create mode 100644 src/main/java/com/github/mengweijin/code/generator/driver/DriverShim.java rename src/main/java/com/github/mengweijin/{generator/config/CustomerDataSource.java => code/generator/driver/DynamicDriverDataSource.java} (37%) create mode 100644 src/main/java/com/github/mengweijin/code/generator/dto/Config.java rename src/main/java/com/github/mengweijin/{generator/entity => code/generator/dto}/DbInfo.java (76%) create mode 100644 src/main/java/com/github/mengweijin/code/generator/engine/ITemplateEngine.java create mode 100644 src/main/java/com/github/mengweijin/code/generator/engine/VelocityClasspathTemplateEngine.java create mode 100644 src/main/java/com/github/mengweijin/code/generator/engine/VelocityFileSystemTemplateEngine.java create mode 100644 src/main/java/com/github/mengweijin/code/generator/engine/VelocityTemplateEngine.java rename src/main/java/com/github/mengweijin/{ => code}/generator/enums/TemplateType.java (66%) create mode 100644 src/main/java/com/github/mengweijin/code/generator/mojo/AbstractClasspathGeneratorMojo.java create mode 100644 src/main/java/com/github/mengweijin/code/generator/mojo/AbstractFileSystemGeneratorMojo.java create mode 100644 src/main/java/com/github/mengweijin/code/generator/mojo/AbstractGeneratorMojo.java create mode 100644 src/main/java/com/github/mengweijin/code/generator/mojo/CodeMojo.java rename src/main/java/com/github/mengweijin/{generator/mojo/MybatisGeneratorMojo.java => code/generator/mojo/MybatisMojo.java} (35%) create mode 100644 src/main/java/com/github/mengweijin/code/generator/mojo/MybatisPlusMojo.java rename src/main/java/com/github/mengweijin/{ => code}/generator/reader/BootFileReader.java (83%) rename src/main/java/com/github/mengweijin/{ => code}/generator/reader/BootFileReaderFactory.java (70%) rename src/main/java/com/github/mengweijin/{ => code}/generator/reader/BootFileType.java (64%) create mode 100644 src/main/java/com/github/mengweijin/code/generator/reader/PropertiesBootFileReader.java rename src/main/java/com/github/mengweijin/{ => code}/generator/reader/YamlBootFileReader.java (47%) rename src/main/java/com/github/mengweijin/{ => code}/generator/util/DbInfoUtils.java (34%) create mode 100644 src/main/java/com/github/mengweijin/code/generator/util/GeneratorUtils.java rename src/main/java/com/github/mengweijin/{ => code}/generator/util/YamlUtils.java (67%) delete mode 100644 src/main/java/com/github/mengweijin/generator/DefaultAutoGenerator.java delete mode 100644 src/main/java/com/github/mengweijin/generator/config/FileOutput.java delete mode 100644 src/main/java/com/github/mengweijin/generator/engine/BeetlStringTemplateEngine.java delete mode 100644 src/main/java/com/github/mengweijin/generator/entity/Docker.java delete mode 100644 src/main/java/com/github/mengweijin/generator/entity/IdField.java delete mode 100644 src/main/java/com/github/mengweijin/generator/entity/Parameters.java delete mode 100644 src/main/java/com/github/mengweijin/generator/entity/ProjectInfo.java delete mode 100644 src/main/java/com/github/mengweijin/generator/enums/Template.java delete mode 100644 src/main/java/com/github/mengweijin/generator/factory/TemplateEngineFactory.java delete mode 100644 src/main/java/com/github/mengweijin/generator/mojo/AbstractDockerMojo.java delete mode 100644 src/main/java/com/github/mengweijin/generator/mojo/AbstractGeneratorMojo.java delete mode 100644 src/main/java/com/github/mengweijin/generator/mojo/CustomerGeneratorMojo.java delete mode 100644 src/main/java/com/github/mengweijin/generator/mojo/DockerBuildMojo.java delete mode 100644 src/main/java/com/github/mengweijin/generator/mojo/DockerDeleteMojo.java delete mode 100644 src/main/java/com/github/mengweijin/generator/mojo/DockerDeployMojo.java delete mode 100644 src/main/java/com/github/mengweijin/generator/mojo/DockerfileGeneratorMojo.java delete mode 100644 src/main/java/com/github/mengweijin/generator/mojo/JpaGeneratorMojo.java delete mode 100644 src/main/java/com/github/mengweijin/generator/mojo/MybatisPlusGeneratorMojo.java delete mode 100644 src/main/java/com/github/mengweijin/generator/reader/PropertiesBootFileReader.java delete mode 100644 src/main/java/com/github/mengweijin/generator/util/TemplateUtils.java create mode 100644 src/main/resources/templates/mybatis-plus/${entityName}.java.vm rename src/main/resources/templates/mybatis-plus/{controller.java.btl => ${entityName}Controller.java.vm} (32%) create mode 100644 src/main/resources/templates/mybatis-plus/${entityName}DTO.java.vm create mode 100644 src/main/resources/templates/mybatis-plus/${entityName}Mapper.java.vm create mode 100644 src/main/resources/templates/mybatis-plus/${entityName}Mapper.xml.vm create mode 100644 src/main/resources/templates/mybatis-plus/${entityName}Service.java.vm delete mode 100644 src/main/resources/templates/mybatis-plus/entity.java.btl delete mode 100644 src/main/resources/templates/mybatis-plus/mapper.java.btl delete mode 100644 src/main/resources/templates/mybatis-plus/mapper.xml.btl delete mode 100644 src/main/resources/templates/mybatis-plus/service.java.btl diff --git a/pom.xml b/pom.xml index 0ffb858..4b3cb37 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.github.mengweijin code-generator-maven-plugin maven-plugin - 1.0.16 + 1.1.0-SNAPSHOT code-generator-maven-plugin code-generator-maven-plugin @@ -16,20 +16,24 @@ UTF-8 1.8 - 3.5.1 - 3.5.2 - 3.10.0.RELEASE + 3.5.6 + 3.5.6 + + 6.0.0-M12 + 5.7.4 + 2.2 + 2.0.49 + 1.5.6 + 2.3 2.3.31 - 5.8.2 - 5.7.4 - 1.30 - 1.2.79 + 3.16.1.RELEASE + 5.1.3 - 3.8.4 + 3.9.6 3.8.4 3.6.4 - 1.18.22 + 1.18.32 @@ -44,15 +48,15 @@ - cn.hutool + org.dromara.hutool hutool-all ${hutool.version} - com.alibaba - fastjson - ${fastjson.vsersion} + com.alibaba.fastjson2 + fastjson2 + ${fastjson2.vsersion} @@ -62,10 +66,11 @@ - com.ibeetl - beetl - ${beetl.version} + ch.qos.logback + logback-classic + ${logback.vsersion} + org.apache.velocity velocity-engine-core @@ -75,6 +80,19 @@ org.freemarker freemarker ${freemarker.version} + provided + + + com.ibeetl + beetl + ${beetl.version} + provided + + + com.jfinal + enjoy + ${enjoy.version} + provided @@ -111,25 +129,12 @@ - - - oss-snapshots - https://oss.sonatype.org/content/repositories/snapshots - - false - - - true - - - - org.apache.maven.plugins maven-plugin-plugin - 3.6.0 + 3.13.0 @@ -219,6 +224,19 @@ + + + huawei + https://mirrors.huaweicloud.com/repository/maven/ + + + + + huawei + https://mirrors.huaweicloud.com/repository/maven/ + + + diff --git a/src/main/java/com/github/mengweijin/code/generator/driver/DriverShim.java b/src/main/java/com/github/mengweijin/code/generator/driver/DriverShim.java new file mode 100644 index 0000000..ff9d2d1 --- /dev/null +++ b/src/main/java/com/github/mengweijin/code/generator/driver/DriverShim.java @@ -0,0 +1,64 @@ +package com.github.mengweijin.code.generator.driver; + +import lombok.Getter; + +import java.sql.Connection; +import java.sql.Driver; +import java.sql.DriverPropertyInfo; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.util.Properties; +import java.util.logging.Logger; + +/** + * 使用了 DriverShim 包装类后,在 getConnection() 时 DriverManager 同样要验证驱动是否对应用程序类加载器可见, + * 只是这时候要验证的是这个 DriverShim 而非通过自定义类加载器弄进来的 “com.mysql.jdbc.Driver” 了。 + * 而后在使用 DriverShim 桥接到实际的 com.mysql.jdbc.Driver 时 DriverManager 就管不着了。 + * 大致意思就是:你 DriverManager 不是想验证数据库驱动是否是应用程序类加载器加载的吗?给个壳逗你玩一下, + * 我在壳里面呢,你管我是由哪个类加载器加载的。 + * @author mengweijin + * @date 2022/10/30 + */ +@Getter +public class DriverShim implements Driver { + private Driver driver; + + DriverShim(Driver driver) { + this.driver = driver; + } + + @Override + public Connection connect(String url, Properties info) throws SQLException { + return this.driver.connect(url, info); + } + + @Override + public boolean acceptsURL(String url) throws SQLException { + return this.driver.acceptsURL(url); + } + + @Override + public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException { + return this.driver.getPropertyInfo(url, info); + } + + @Override + public int getMajorVersion() { + return this.driver.getMajorVersion(); + } + + @Override + public int getMinorVersion() { + return this.driver.getMinorVersion(); + } + + @Override + public boolean jdbcCompliant() { + return this.driver.jdbcCompliant(); + } + + @Override + public Logger getParentLogger() throws SQLFeatureNotSupportedException { + return null; + } +} diff --git a/src/main/java/com/github/mengweijin/generator/config/CustomerDataSource.java b/src/main/java/com/github/mengweijin/code/generator/driver/DynamicDriverDataSource.java similarity index 37% rename from src/main/java/com/github/mengweijin/generator/config/CustomerDataSource.java rename to src/main/java/com/github/mengweijin/code/generator/driver/DynamicDriverDataSource.java index ef60476..a0d2baf 100644 --- a/src/main/java/com/github/mengweijin/generator/config/CustomerDataSource.java +++ b/src/main/java/com/github/mengweijin/code/generator/driver/DynamicDriverDataSource.java @@ -1,29 +1,34 @@ -package com.github.mengweijin.generator.config; +package com.github.mengweijin.code.generator.driver; + +import lombok.Getter; +import org.dromara.hutool.db.driver.DriverIdentifier; -import cn.hutool.core.util.ReflectUtil; -import cn.hutool.db.dialect.DriverUtil; import javax.sql.DataSource; import java.io.PrintWriter; -import java.lang.reflect.Method; import java.sql.Connection; +import java.sql.Driver; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; -import java.util.Properties; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; import java.util.logging.Logger; /** + * 动态加载数据库驱动包的 DataSource * @author mengweijin */ -public class CustomerDataSource implements DataSource { +@Getter +public class DynamicDriverDataSource implements DataSource { - private String url; + private final String url; - private String username; + private final String username; - private String password; + private final String password; - public CustomerDataSource(String url, String username, String password) { + public DynamicDriverDataSource(String url, String username, String password) { this.url = url; this.username = username; this.password = password; @@ -36,31 +41,15 @@ public class CustomerDataSource implements DataSource { @Override public Connection getConnection(String username, String password) throws SQLException { - Connection conn; + DriverShim driverShim = null; + Connection connection; try { - // hutool 5.6.3 已修复:https://gitee.com/dromara/hutool/issues/I3EWBI - // 关于DriverUtil.identifyDriver(url); 的 Classloader 的问题,hutool 内部已经默认使用了线程上下文类加载器,因此这里无需处理 - Class.forName(DriverUtil.identifyDriver(url), true, Thread.currentThread().getContextClassLoader()); - - Properties info = new Properties(); - if (username != null) { - info.put("user", username); - } - if (password != null) { - info.put("password", password); - } - - Method method = ReflectUtil.getMethod( - DriverManager.class, - "getConnection", - String.class, Properties.class, Class.class); - method.setAccessible(true); - conn = ReflectUtil.invokeStatic(method, this.url, info, null); - } catch (Exception e) { - e.printStackTrace(); - throw new RuntimeException(e); + driverShim = this.registerDriverByUrl(this.url); + connection = DriverManager.getConnection(this.url, username, password); + } finally { + this.deregisterDriver(driverShim); } - return conn; + return connection; } @Override @@ -97,4 +86,66 @@ public class CustomerDataSource implements DataSource { public Logger getParentLogger() throws SQLFeatureNotSupportedException { throw new SQLFeatureNotSupportedException("DataSource can't support getParentLogger method!"); } + + public DriverShim registerDriverByUrl(String jdbcUrl) { + return this.registerDriver(new DriverIdentifier(Thread.currentThread().getContextClassLoader()).identifyDriver(jdbcUrl)); + } + + /** + * + * @param driverClass For Example: com.mysql.cj.jdbc.Driver + * @return DriverShim + */ + public DriverShim registerDriver(String driverClass) { + try { + Driver driver = (Driver) Class.forName(driverClass, true, Thread.currentThread().getContextClassLoader()).getDeclaredConstructor().newInstance(); + DriverShim driverShim = new DriverShim(driver); + DriverManager.registerDriver(driverShim); + return driverShim; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public void deregisterDriver(DriverShim driverShim) { + try { + if (driverShim != null) { + DriverManager.deregisterDriver(driverShim); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static void deregisterAllDriverShim() { + try { + Enumeration enumeration = DriverManager.getDrivers(); + while (enumeration.hasMoreElements()) { + Object element = enumeration.nextElement(); + if (element.getClass() == DriverShim.class) { + DriverManager.deregisterDriver((DriverShim) element); + } + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static List listRegisteredDrivers() { + List list = new ArrayList<>(); + + Enumeration enumeration = DriverManager.getDrivers(); + String driverName; + while (enumeration.hasMoreElements()) { + Driver element = enumeration.nextElement(); + if (element.getClass() == DriverShim.class) { + driverName = ((DriverShim) element).getDriver().getClass().getName(); + } else { + driverName = element.getClass().getName(); + } + list.add(driverName); + } + + return list; + } } diff --git a/src/main/java/com/github/mengweijin/code/generator/dto/Config.java b/src/main/java/com/github/mengweijin/code/generator/dto/Config.java new file mode 100644 index 0000000..68ce430 --- /dev/null +++ b/src/main/java/com/github/mengweijin/code/generator/dto/Config.java @@ -0,0 +1,39 @@ +package com.github.mengweijin.code.generator.dto; + +import lombok.Data; +import org.dromara.hutool.core.util.SystemUtil; + +/** + * @author mengweijin + */ +@Data +public class Config { + + private String packages; + + private String moduleName; + + private String author = SystemUtil.get("user.name", false); + + private String outputDir = "target/code-generator"; + + private String templateDir; + + /** + * Optional for Spring Boot if exists application.yml. + */ + private DbInfo dbInfo; + + /** + * Optional. + * For example: SYS_, VTL_ + * Note: Separated by commas. + */ + private String[] tablePrefix; + + /** + * Optional. + */ + private String baseEntity; + +} diff --git a/src/main/java/com/github/mengweijin/generator/entity/DbInfo.java b/src/main/java/com/github/mengweijin/code/generator/dto/DbInfo.java similarity index 76% rename from src/main/java/com/github/mengweijin/generator/entity/DbInfo.java rename to src/main/java/com/github/mengweijin/code/generator/dto/DbInfo.java index d0f2699..31c4969 100644 --- a/src/main/java/com/github/mengweijin/generator/entity/DbInfo.java +++ b/src/main/java/com/github/mengweijin/code/generator/dto/DbInfo.java @@ -1,4 +1,4 @@ -package com.github.mengweijin.generator.entity; +package com.github.mengweijin.code.generator.dto; import lombok.Data; @@ -13,4 +13,6 @@ public class DbInfo { private String username; private String password; + + } diff --git a/src/main/java/com/github/mengweijin/code/generator/engine/ITemplateEngine.java b/src/main/java/com/github/mengweijin/code/generator/engine/ITemplateEngine.java new file mode 100644 index 0000000..fd824cc --- /dev/null +++ b/src/main/java/com/github/mengweijin/code/generator/engine/ITemplateEngine.java @@ -0,0 +1,63 @@ +package com.github.mengweijin.code.generator.engine; + +import com.baomidou.mybatisplus.generator.config.po.TableField; +import com.baomidou.mybatisplus.generator.config.po.TableInfo; +import com.github.mengweijin.code.generator.dto.Config; +import com.github.mengweijin.code.generator.util.GeneratorUtils; +import org.dromara.hutool.core.collection.CollUtil; +import org.dromara.hutool.core.date.DatePattern; +import org.dromara.hutool.core.date.DateUtil; +import org.dromara.hutool.core.text.StrUtil; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author mengweijin + */ +public interface ITemplateEngine { + + ITemplateEngine init(Config config, List tableInfoList); + + void write(); + + default Map getObjectMap(Config config, TableInfo tableInfo) { + List baseEntityColumns = GeneratorUtils.resolveBaseEntityColumns(config); + String entityName = GeneratorUtils.resolveEntityName(tableInfo.getName(), config); + List entityFields = GeneratorUtils.resolveEntityFields(tableInfo, baseEntityColumns); + List commonFields = GeneratorUtils.resolveCommonFields(tableInfo, baseEntityColumns); + + List entityColumns = GeneratorUtils.resolveEntityColumns(entityFields); + List commonColumns = GeneratorUtils.resolveCommonColumns(commonFields); + + String requestMapping = "/" + StrUtil.toSymbolCase(entityName, '-'); + if(StrUtil.isNotBlank(config.getModuleName())) { + requestMapping = StrUtil.addPrefixIfNot(config.getModuleName(), "/") + requestMapping; + } + + Map objectMap = new HashMap<>(); + objectMap.put("package", config.getPackages()); + objectMap.put("author", config.getAuthor()); + objectMap.put("date", DateUtil.format(LocalDateTime.now(), DatePattern.NORM_DATE_PATTERN)); + objectMap.put("baseEntity", config.getBaseEntity()); + objectMap.put("baseEntityPackage", StrUtil.subBefore(config.getBaseEntity(), ".", true)); + objectMap.put("baseEntityName", StrUtil.subAfter(config.getBaseEntity(), ".", true)); + objectMap.put("baseEntityColumns", baseEntityColumns); + objectMap.put("table", tableInfo); + objectMap.put("entityName", entityName); + objectMap.put("entityPropertyName", StrUtil.lowerFirst(entityName)); + objectMap.put("entityFields", entityFields); + objectMap.put("commonFields", commonFields); + objectMap.put("allFields", CollUtil.addAll(new ArrayList<>(entityFields), new ArrayList<>(commonFields))); + objectMap.put("entityColumns", entityColumns); + objectMap.put("commonColumns", commonColumns); + objectMap.put("allColumns", CollUtil.addAll(new ArrayList<>(entityColumns), new ArrayList<>(commonColumns))); + objectMap.put("requestMapping", requestMapping); + return objectMap; + } + + +} diff --git a/src/main/java/com/github/mengweijin/code/generator/engine/VelocityClasspathTemplateEngine.java b/src/main/java/com/github/mengweijin/code/generator/engine/VelocityClasspathTemplateEngine.java new file mode 100644 index 0000000..5219b95 --- /dev/null +++ b/src/main/java/com/github/mengweijin/code/generator/engine/VelocityClasspathTemplateEngine.java @@ -0,0 +1,59 @@ +package com.github.mengweijin.code.generator.engine; + +import com.baomidou.mybatisplus.core.toolkit.StringPool; +import com.baomidou.mybatisplus.generator.config.ConstVal; +import com.github.mengweijin.code.generator.dto.Config; +import com.github.mengweijin.code.generator.enums.TemplateType; +import org.apache.velocity.app.Velocity; +import org.apache.velocity.app.VelocityEngine; +import org.dromara.hutool.core.io.resource.ClassPathResource; +import org.dromara.hutool.core.io.resource.JarResource; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.Properties; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + * @author mengweijin + */ +public class VelocityClasspathTemplateEngine extends VelocityTemplateEngine { + + /** + * classpath + * @param templateDir keep null value + * @return VelocityEngine + */ + @Override + protected VelocityEngine getVelocityEngine(String templateDir) { + Properties p = new Properties(); + p.setProperty(ConstVal.VM_LOAD_PATH_KEY, ConstVal.VM_LOAD_PATH_VALUE); + p.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, StringPool.EMPTY); + p.setProperty(Velocity.ENCODING_DEFAULT, ConstVal.UTF8); + p.setProperty(Velocity.INPUT_ENCODING, ConstVal.UTF8); + p.setProperty("file.resource.loader.unicode", StringPool.TRUE); + return new VelocityEngine(p); + } + + @Override + protected List getTemplates() { + Config config = this.getConfig(); + List templateFilePathList = new ArrayList<>(); + + ClassPathResource classPathResource = new ClassPathResource(config.getTemplateDir()); + JarResource jarResource = new JarResource(classPathResource.getUrl()); + JarFile jarFile = jarResource.getJarFile(); + Enumeration enumeration = jarFile.entries(); + + String templateLocationPrefixPath = config.getTemplateDir().endsWith("/") ? config.getTemplateDir() : config.getTemplateDir() + "/"; + while (enumeration.hasMoreElements()) { + String jarEntryName = enumeration.nextElement().getName(); + if(jarEntryName.startsWith(templateLocationPrefixPath) && jarEntryName.endsWith(TemplateType.velocity.getSuffix())) { + templateFilePathList.add(jarEntryName); + } + } + return templateFilePathList; + } +} diff --git a/src/main/java/com/github/mengweijin/code/generator/engine/VelocityFileSystemTemplateEngine.java b/src/main/java/com/github/mengweijin/code/generator/engine/VelocityFileSystemTemplateEngine.java new file mode 100644 index 0000000..2a42e56 --- /dev/null +++ b/src/main/java/com/github/mengweijin/code/generator/engine/VelocityFileSystemTemplateEngine.java @@ -0,0 +1,40 @@ +package com.github.mengweijin.code.generator.engine; + +import com.baomidou.mybatisplus.generator.config.ConstVal; +import com.github.mengweijin.code.generator.dto.Config; +import com.github.mengweijin.code.generator.enums.TemplateType; +import org.apache.velocity.app.Velocity; +import org.apache.velocity.app.VelocityEngine; +import org.dromara.hutool.core.io.file.FileUtil; + +import java.io.File; +import java.util.List; +import java.util.Properties; +import java.util.stream.Collectors; + +/** + * @author mengweijin + */ +public class VelocityFileSystemTemplateEngine extends VelocityTemplateEngine { + + /** + * file system + * @param templateDir absolute path. E.g.: D:/code/template + * @return VelocityEngine + */ + @Override + protected VelocityEngine getVelocityEngine(String templateDir) { + Properties p = new Properties(); + p.setProperty(VelocityEngine.FILE_RESOURCE_LOADER_PATH, templateDir); + p.setProperty(Velocity.ENCODING_DEFAULT, ConstVal.UTF8); + p.setProperty(Velocity.INPUT_ENCODING, ConstVal.UTF8); + return new VelocityEngine(p); + } + + @Override + protected List getTemplates() { + Config config = this.getConfig(); + List loopFiles = FileUtil.loopFiles(config.getTemplateDir(), file -> file.isFile() && file.getName().toLowerCase().endsWith(TemplateType.velocity.getSuffix())); + return loopFiles.stream().map(File::getName).collect(Collectors.toList()); + } +} diff --git a/src/main/java/com/github/mengweijin/code/generator/engine/VelocityTemplateEngine.java b/src/main/java/com/github/mengweijin/code/generator/engine/VelocityTemplateEngine.java new file mode 100644 index 0000000..2b631c3 --- /dev/null +++ b/src/main/java/com/github/mengweijin/code/generator/engine/VelocityTemplateEngine.java @@ -0,0 +1,102 @@ +package com.github.mengweijin.code.generator.engine; + +import com.baomidou.mybatisplus.generator.config.ConstVal; +import com.baomidou.mybatisplus.generator.config.po.TableInfo; +import com.github.mengweijin.code.generator.dto.Config; +import com.github.mengweijin.code.generator.util.GeneratorUtils; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.VelocityEngine; +import org.dromara.hutool.core.collection.CollUtil; +import org.dromara.hutool.core.io.file.FileUtil; +import org.dromara.hutool.core.text.StrUtil; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.util.List; +import java.util.Map; + +/** + * @author mengweijin + */ +@Slf4j +@Getter +public abstract class VelocityTemplateEngine implements ITemplateEngine { + + private VelocityEngine velocityEngine; + + private Config config; + + private List tableInfoList; + + static { + try { + Class.forName("org.apache.velocity.util.DuckType"); + } catch (ClassNotFoundException e) { + // velocity1.x的生成格式错乱 https://github.com/baomidou/generator/issues/5 + log.warn("Velocity 1.x is outdated, please upgrade to 2.x or later."); + } + } + + protected abstract VelocityEngine getVelocityEngine(String templateDir); + + protected abstract List getTemplates(); + + @Override + public ITemplateEngine init(Config config, List tableInfoList) { + this.config = config; + this.tableInfoList = tableInfoList; + this.velocityEngine = this.getVelocityEngine(config.getTemplateDir()); + return this; + } + + @Override + public void write(){ + this.tableInfoList.forEach(tableInfo -> { + Map objectMap = this.getObjectMap(this.config, tableInfo); + + List templateFilePathList = this.getTemplates(); + if (CollUtil.isEmpty(templateFilePathList)) { + throw new RuntimeException("No template files found in location " + this.config.getTemplateDir()); + } else { + String message = "Found " + templateFilePathList.size() + " template files in location " + this.config.getTemplateDir(); + log.info(message); + } + + templateFilePathList.forEach(tpl -> { + String tplName = tpl.contains("/") ? StrUtil.subAfter(tpl, "/", true) : tpl; + String outputFileName = StrUtil.subBefore(GeneratorUtils.renderString(tplName, objectMap), ".", true); + String outputFilePath = config.getOutputDir() + "/" + outputFileName; + FileUtil.mkParentDirs(outputFilePath); + this.write(objectMap, tpl, FileUtil.file(outputFilePath)); + }); + }); + } + + /** + * + * @param objectMap template engine args + * @param templatePath E.g. 1 file system: demo.vm + * E.g. 2 classpath: generator/mybatis/demo.vm + * @param outputFile E.g.: D:/code/vitality/Demo.java + */ + private void write(Map objectMap, String templatePath, File outputFile) { + Template template = velocityEngine.getTemplate(templatePath, ConstVal.UTF8); + try (FileOutputStream fos = new FileOutputStream(outputFile); + OutputStreamWriter ow = new OutputStreamWriter(fos, ConstVal.UTF8); + BufferedWriter writer = new BufferedWriter(ow)) { + template.merge(new VelocityContext(objectMap), writer); + log.info("生成成功!模板:{};文件:{}", templatePath, outputFile); + } catch (IOException e) { + log.error("生成失败!模板:{}; 文件:{}", templatePath, outputFile); + log.error(e.getMessage(), e); + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/com/github/mengweijin/generator/enums/TemplateType.java b/src/main/java/com/github/mengweijin/code/generator/enums/TemplateType.java similarity index 66% rename from src/main/java/com/github/mengweijin/generator/enums/TemplateType.java rename to src/main/java/com/github/mengweijin/code/generator/enums/TemplateType.java index 3fed25a..65b25ae 100644 --- a/src/main/java/com/github/mengweijin/generator/enums/TemplateType.java +++ b/src/main/java/com/github/mengweijin/code/generator/enums/TemplateType.java @@ -1,4 +1,4 @@ -package com.github.mengweijin.generator.enums; +package com.github.mengweijin.code.generator.enums; /** * @author mengweijin @@ -6,7 +6,7 @@ package com.github.mengweijin.generator.enums; public enum TemplateType { - beetl("btl"), velocity("vm"), freemarker("ftl"); + velocity("vm"), freemarker("ftl"), beetl("btl"), enjoy("ej"); private final String suffix; diff --git a/src/main/java/com/github/mengweijin/code/generator/mojo/AbstractClasspathGeneratorMojo.java b/src/main/java/com/github/mengweijin/code/generator/mojo/AbstractClasspathGeneratorMojo.java new file mode 100644 index 0000000..103e391 --- /dev/null +++ b/src/main/java/com/github/mengweijin/code/generator/mojo/AbstractClasspathGeneratorMojo.java @@ -0,0 +1,32 @@ +package com.github.mengweijin.code.generator.mojo; + +import com.github.mengweijin.code.generator.dto.Config; +import com.github.mengweijin.code.generator.engine.ITemplateEngine; +import com.github.mengweijin.code.generator.engine.VelocityClasspathTemplateEngine; +import lombok.extern.slf4j.Slf4j; + +/** + * @author mengweijin + */ +@Slf4j +public abstract class AbstractClasspathGeneratorMojo extends AbstractGeneratorMojo { + + @Override + protected void checkTemplateExists() throws RuntimeException { + // ignore for classpath + } + + @Override + protected void initConfigTemplateDir() { + Config config = this.getConfig(); + config.setTemplateDir(this.getTemplateDir()); + } + + @Override + protected ITemplateEngine getTemplateEngine() { + return new VelocityClasspathTemplateEngine(); + } + + protected abstract String getTemplateDir(); + +} diff --git a/src/main/java/com/github/mengweijin/code/generator/mojo/AbstractFileSystemGeneratorMojo.java b/src/main/java/com/github/mengweijin/code/generator/mojo/AbstractFileSystemGeneratorMojo.java new file mode 100644 index 0000000..a3cd580 --- /dev/null +++ b/src/main/java/com/github/mengweijin/code/generator/mojo/AbstractFileSystemGeneratorMojo.java @@ -0,0 +1,48 @@ +package com.github.mengweijin.code.generator.mojo; + +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.github.mengweijin.code.generator.dto.Config; +import com.github.mengweijin.code.generator.engine.ITemplateEngine; +import com.github.mengweijin.code.generator.engine.VelocityFileSystemTemplateEngine; +import com.github.mengweijin.code.generator.enums.TemplateType; +import lombok.extern.slf4j.Slf4j; +import org.dromara.hutool.core.io.file.FileUtil; +import org.dromara.hutool.core.text.StrUtil; + +import java.io.File; +import java.util.List; + +/** + * @author mengweijin + */ +@Slf4j +public abstract class AbstractFileSystemGeneratorMojo extends AbstractGeneratorMojo { + + @Override + protected void checkTemplateExists() throws RuntimeException { + Config config = this.getConfig(); + File baseDir = this.getBaseDir(); + if(StrUtil.isBlank(config.getTemplateDir())) { + throw new RuntimeException("No template location is configured!"); + } + String templateLocation = baseDir.getAbsolutePath() + StrUtil.addPrefixIfNot(config.getTemplateDir(), "/"); + List templateFileList = FileUtil.loopFiles(templateLocation, file -> file.isFile() && file.getName().toLowerCase().endsWith(TemplateType.velocity.getSuffix())); + if(CollectionUtils.isEmpty(templateFileList)) { + throw new RuntimeException("No template file exists in path " + templateLocation); + } + } + + @Override + protected void initConfigTemplateDir() { + Config config = this.getConfig(); + File baseDir = this.getBaseDir(); + String templateDir = baseDir.getAbsolutePath() + StrUtil.addPrefixIfNot(config.getTemplateDir(), "/"); + config.setTemplateDir(templateDir); + } + + @Override + protected ITemplateEngine getTemplateEngine() { + return new VelocityFileSystemTemplateEngine(); + } + +} diff --git a/src/main/java/com/github/mengweijin/code/generator/mojo/AbstractGeneratorMojo.java b/src/main/java/com/github/mengweijin/code/generator/mojo/AbstractGeneratorMojo.java new file mode 100644 index 0000000..6031260 --- /dev/null +++ b/src/main/java/com/github/mengweijin/code/generator/mojo/AbstractGeneratorMojo.java @@ -0,0 +1,182 @@ +package com.github.mengweijin.code.generator.mojo; + +import com.baomidou.mybatisplus.generator.config.DataSourceConfig; +import com.baomidou.mybatisplus.generator.config.StrategyConfig; +import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder; +import com.baomidou.mybatisplus.generator.config.builder.GeneratorBuilder; +import com.baomidou.mybatisplus.generator.config.po.TableInfo; +import com.github.mengweijin.code.generator.driver.DynamicDriverDataSource; +import com.github.mengweijin.code.generator.dto.Config; +import com.github.mengweijin.code.generator.dto.DbInfo; +import com.github.mengweijin.code.generator.engine.ITemplateEngine; +import com.github.mengweijin.code.generator.util.DbInfoUtils; +import com.github.mengweijin.code.generator.util.GeneratorUtils; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.model.Resource; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.project.MavenProject; +import org.dromara.hutool.core.array.ArrayUtil; +import org.dromara.hutool.core.classloader.JarClassLoader; +import org.dromara.hutool.core.io.file.FileUtil; +import org.dromara.hutool.core.text.StrUtil; + +import java.io.File; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Scanner; +import java.util.Set; + +/** + * @author mengweijin + */ +@Slf4j +@Getter +public abstract class AbstractGeneratorMojo extends AbstractMojo { + + @Parameter + private Config config; + + @Parameter(defaultValue = "${project}") + private MavenProject project; + + @Parameter(defaultValue = "${basedir}") + private File baseDir; + + @Parameter(defaultValue = "${project.build.resources}", readonly = true, required = true) + private List resources; + + @Parameter(defaultValue = "${project.build.sourceDirectory}", required = true, readonly = true) + private File sourceDir; + + @Parameter(defaultValue = "${project.build.testResources}", readonly = true, required = true) + private List testResources; + + @Parameter(defaultValue = "${project.build.testSourceDirectory}", readonly = true, required = true) + private File testSourceDir; + + @Parameter(defaultValue = "${project.compileClasspathElements}", readonly = true, required = true) + private List compilePath; + + @Parameter(defaultValue = "${session}", readonly = true) + private MavenSession session; + + protected abstract void checkTemplateExists() throws RuntimeException; + + protected abstract void initConfigTemplateDir(); + + protected abstract ITemplateEngine getTemplateEngine(); + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + try { + this.checkTemplateExists(); + + this.initConfigTemplateDir(); + + config.setPackages(project.getGroupId()); + + String baseOutputDirPath = baseDir.getAbsolutePath() + "/target/code-generator/"; + String packagePath = StrUtil.replace(config.getPackages(), ".", "/"); + config.setOutputDir(baseOutputDirPath + packagePath); + + Set allProjectRuntimeClasspathElements = this.getAllProjectRuntimeClasspathElements(); + this.loadProjectClassToContextClassLoader(allProjectRuntimeClasspathElements); + + if(config.getDbInfo() == null) { + DbInfo dbInfo = DbInfoUtils.getDbInfo(config, allProjectRuntimeClasspathElements); + config.setDbInfo(dbInfo); + } + if(config.getDbInfo() == null) { + throw new RuntimeException("No datasource info was found! Please add database url, username, password configurations."); + } + + Scanner sc = new Scanner(System.in); + System.out.println("请输入数据库表名称(多个表名用逗号隔开),按 Enter 键继续:"); + String tableNames = sc.nextLine(); + while (StrUtil.isBlank(tableNames)) { + System.out.println("请输入数据库表名称(多个表名用逗号隔开),按 Enter 键继续:"); + tableNames = sc.nextLine(); + } + String[] tables = GeneratorUtils.trimItems(tableNames.split("[,,]")); + + System.out.println("请输入包模块名称(可以为空,直接按 Enter 键继续),然后按 Enter 键继续:"); + String moduleName = sc.nextLine(); + if(StrUtil.isNotBlank(moduleName)) { + config.setModuleName(StrUtil.trim(moduleName.toLowerCase())); + config.setPackages(config.getPackages() + "." + config.getModuleName()); + config.setOutputDir(config.getOutputDir() + "/" + config.getModuleName()); + } + + DynamicDriverDataSource dataSource = new DynamicDriverDataSource(config.getDbInfo().getUrl(), config.getDbInfo().getUsername(), config.getDbInfo().getPassword()); + DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder(dataSource).build(); + + StrategyConfig.Builder strategyConfigBuilder = GeneratorBuilder.strategyConfigBuilder(); + if(ArrayUtil.isNotEmpty(config.getTablePrefix())) { + strategyConfigBuilder.addTablePrefix(config.getTablePrefix()); + } + StrategyConfig strategyConfig = strategyConfigBuilder.addInclude(tables).build(); + + // ConfigBuilder 类不能扩展,会报错。 + ConfigBuilder configBuilder = new ConfigBuilder(null, dataSourceConfig, strategyConfig, null, null, null); + List tableInfoList = configBuilder.getTableInfoList(); + + System.out.println("----------------------------------------------------------------------------------------"); + System.out.println("主要输出配置信息:"); + System.out.println("数据库表:" + String.join(",", tables)); + System.out.println("数据库表前缀配置:" + (config.getTablePrefix() == null ? "" : String.join(",", config.getTablePrefix()))); + System.out.println("模板文件夹:" + config.getTemplateDir()); + System.out.println("包路径:" + config.getPackages()); + System.out.println("作者:" + config.getAuthor()); + System.out.println("实体父类:" + config.getBaseEntity()); + System.out.println("输出位置:" + config.getOutputDir()); + System.out.println("----------------------------------------------------------------------------------------"); + + log.info("---------------------------------- 清理输出目录!----------------------------------"); + FileUtil.del(baseOutputDirPath); + log.info("---------------------------------- 开始生成代码!----------------------------------"); + this.getTemplateEngine().init(config, tableInfoList).write(); + log.info("---------------------------------- 代码生成成功!----------------------------------"); + log.info("代码输出位置:{}", config.getOutputDir()); + log.info("---------------------------------- 代码生成完成!----------------------------------"); + } catch (Exception e) { + getLog().error(e); + throw new RuntimeException(e); + } + } + + private Set getAllProjectRuntimeClasspathElements() { + try { + List allProjects = session.getAllProjects(); + Set runtimeElements = new HashSet<>(); + for (MavenProject project : allProjects) { + runtimeElements.addAll(project.getRuntimeClasspathElements()); + } + return runtimeElements; + } catch (Exception e) { + getLog().error(e.getMessage()); + throw new RuntimeException(e); + } + } + + private void loadProjectClassToContextClassLoader(Set allProjectRuntimeClasspathElements) { + try { + List urlList = new ArrayList<>(); + for (String element : allProjectRuntimeClasspathElements) { + urlList.add(new File(element).toURI().toURL()); + } + JarClassLoader jarClassLoader = new JarClassLoader(urlList.toArray(new URL[0])); + Thread.currentThread().setContextClassLoader(jarClassLoader); + } catch (Exception e) { + getLog().error(e.getMessage()); + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/com/github/mengweijin/code/generator/mojo/CodeMojo.java b/src/main/java/com/github/mengweijin/code/generator/mojo/CodeMojo.java new file mode 100644 index 0000000..1e1ba89 --- /dev/null +++ b/src/main/java/com/github/mengweijin/code/generator/mojo/CodeMojo.java @@ -0,0 +1,24 @@ +package com.github.mengweijin.code.generator.mojo; + +import org.apache.maven.plugins.annotations.Execute; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.ResolutionScope; + +/** + * 说明:@Execute(phase = LifecyclePhase.COMPILE) 在运行该目标前,让maven先运行一个并行的生命周期,到指定的阶段为止。到phase执行完,才执行插件目标 + * ResolutionScope.COMPILE_PLUS_RUNTIME: + * Will add the classpath jars into list when call method project.getRuntimeClasspathElements(); + * maven scope=compile + system + provided + runtime dependencies + * + * @author mengweijin + */ +@Execute(phase = LifecyclePhase.COMPILE) +@Mojo( + name = "code", + aggregator = true, + requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME +) +public class CodeMojo extends AbstractFileSystemGeneratorMojo { + +} diff --git a/src/main/java/com/github/mengweijin/generator/mojo/MybatisGeneratorMojo.java b/src/main/java/com/github/mengweijin/code/generator/mojo/MybatisMojo.java similarity index 35% rename from src/main/java/com/github/mengweijin/generator/mojo/MybatisGeneratorMojo.java rename to src/main/java/com/github/mengweijin/code/generator/mojo/MybatisMojo.java index 1749597..64f74dc 100644 --- a/src/main/java/com/github/mengweijin/generator/mojo/MybatisGeneratorMojo.java +++ b/src/main/java/com/github/mengweijin/code/generator/mojo/MybatisMojo.java @@ -1,25 +1,28 @@ -package com.github.mengweijin.generator.mojo; +package com.github.mengweijin.code.generator.mojo; -import com.github.mengweijin.generator.entity.Parameters; -import com.github.mengweijin.generator.entity.ProjectInfo; -import com.github.mengweijin.generator.enums.Template; -import com.github.mengweijin.generator.enums.TemplateType; +import org.apache.maven.plugins.annotations.Execute; +import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.ResolutionScope; /** + * 说明:@Execute(phase = LifecyclePhase.COMPILE) 在运行该目标前,让maven先运行一个并行的生命周期,到指定的阶段为止。到phase执行完,才执行插件目标 * ResolutionScope.COMPILE_PLUS_RUNTIME: * Will add the classpath jars into list when call method project.getRuntimeClasspathElements(); * maven scope=compile + system + provided + runtime dependencies * * @author mengweijin */ -@Mojo(name = "MyBatis", requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME) -public class MybatisGeneratorMojo extends AbstractGeneratorMojo { +@Execute(phase = LifecyclePhase.COMPILE) +@Mojo( + name = "mybatis", + aggregator = true, + requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME +) +public class MybatisMojo extends AbstractClasspathGeneratorMojo { @Override - protected void setDefaultFixedParameters(Parameters parameters) { - parameters.setTemplateLocation(ProjectInfo.TMP_DIR + Template.MYBATIS.getPath()); - parameters.setTemplateType(TemplateType.beetl); + protected String getTemplateDir() { + return "templates/mybatis"; } } diff --git a/src/main/java/com/github/mengweijin/code/generator/mojo/MybatisPlusMojo.java b/src/main/java/com/github/mengweijin/code/generator/mojo/MybatisPlusMojo.java new file mode 100644 index 0000000..c734eec --- /dev/null +++ b/src/main/java/com/github/mengweijin/code/generator/mojo/MybatisPlusMojo.java @@ -0,0 +1,28 @@ +package com.github.mengweijin.code.generator.mojo; + +import org.apache.maven.plugins.annotations.Execute; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.ResolutionScope; + +/** + * 说明:@Execute(phase = LifecyclePhase.COMPILE) 在运行该目标前,让maven先运行一个并行的生命周期,到指定的阶段为止。到phase执行完,才执行插件目标 + * ResolutionScope.COMPILE_PLUS_RUNTIME: + * Will add the classpath jars into list when call method project.getRuntimeClasspathElements(); + * maven scope=compile + system + provided + runtime dependencies + * + * @author mengweijin + */ +@Execute(phase = LifecyclePhase.COMPILE) +@Mojo( + name = "mybatis-plus", + aggregator = true, + requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME +) +public class MybatisPlusMojo extends AbstractClasspathGeneratorMojo { + + @Override + protected String getTemplateDir() { + return "templates/mybatis-plus"; + } +} diff --git a/src/main/java/com/github/mengweijin/generator/reader/BootFileReader.java b/src/main/java/com/github/mengweijin/code/generator/reader/BootFileReader.java similarity index 83% rename from src/main/java/com/github/mengweijin/generator/reader/BootFileReader.java rename to src/main/java/com/github/mengweijin/code/generator/reader/BootFileReader.java index 66b057c..adaddea 100644 --- a/src/main/java/com/github/mengweijin/generator/reader/BootFileReader.java +++ b/src/main/java/com/github/mengweijin/code/generator/reader/BootFileReader.java @@ -1,6 +1,6 @@ -package com.github.mengweijin.generator.reader; +package com.github.mengweijin.code.generator.reader; -import com.github.mengweijin.generator.entity.DbInfo; +import com.github.mengweijin.code.generator.dto.DbInfo; import java.io.File; @@ -22,7 +22,7 @@ public interface BootFileReader { * @param file file * @return active profiles env */ - String getActiveProfilesEnv(File file); + String[] getActiveProfiles(File file); /** * get db info diff --git a/src/main/java/com/github/mengweijin/generator/reader/BootFileReaderFactory.java b/src/main/java/com/github/mengweijin/code/generator/reader/BootFileReaderFactory.java similarity index 70% rename from src/main/java/com/github/mengweijin/generator/reader/BootFileReaderFactory.java rename to src/main/java/com/github/mengweijin/code/generator/reader/BootFileReaderFactory.java index 63b20f3..53350c1 100644 --- a/src/main/java/com/github/mengweijin/generator/reader/BootFileReaderFactory.java +++ b/src/main/java/com/github/mengweijin/code/generator/reader/BootFileReaderFactory.java @@ -1,9 +1,9 @@ -package com.github.mengweijin.generator.reader; - -import cn.hutool.core.io.file.FileNameUtil; -import cn.hutool.core.util.EnumUtil; -import com.github.mengweijin.generator.entity.DbInfo; +package com.github.mengweijin.code.generator.reader; +import com.github.mengweijin.code.generator.dto.DbInfo; +import lombok.extern.slf4j.Slf4j; +import org.dromara.hutool.core.io.file.FileNameUtil; +import org.dromara.hutool.core.util.EnumUtil; import java.io.File; import java.util.HashMap; import java.util.Map; @@ -11,6 +11,7 @@ import java.util.Map; /** * @author mengweijin */ +@Slf4j public class BootFileReaderFactory { private static final Map map = new HashMap<>(); @@ -21,8 +22,8 @@ public class BootFileReaderFactory { map.put(BootFileType.properties, PropertiesBootFileReader.class); } - public static String getActiveProfilesEnv(File file) { - return getBootFileReader(file).getActiveProfilesEnv(file); + public static String[] getActiveProfiles(File file) { + return getBootFileReader(file).getActiveProfiles(file); } public static DbInfo getDbInfo(File file) { @@ -36,7 +37,7 @@ public class BootFileReaderFactory { Class cls = map.get(bootFileType); return (BootFileReader) cls.newInstance(); } catch (IllegalAccessException | InstantiationException e) { - e.printStackTrace(); + log.error(e.getMessage(), e); throw new RuntimeException(e); } } diff --git a/src/main/java/com/github/mengweijin/generator/reader/BootFileType.java b/src/main/java/com/github/mengweijin/code/generator/reader/BootFileType.java similarity index 64% rename from src/main/java/com/github/mengweijin/generator/reader/BootFileType.java rename to src/main/java/com/github/mengweijin/code/generator/reader/BootFileType.java index 0e33af5..1bcbc13 100644 --- a/src/main/java/com/github/mengweijin/generator/reader/BootFileType.java +++ b/src/main/java/com/github/mengweijin/code/generator/reader/BootFileType.java @@ -1,4 +1,4 @@ -package com.github.mengweijin.generator.reader; +package com.github.mengweijin.code.generator.reader; /** * @author mengweijin diff --git a/src/main/java/com/github/mengweijin/code/generator/reader/PropertiesBootFileReader.java b/src/main/java/com/github/mengweijin/code/generator/reader/PropertiesBootFileReader.java new file mode 100644 index 0000000..7063f1c --- /dev/null +++ b/src/main/java/com/github/mengweijin/code/generator/reader/PropertiesBootFileReader.java @@ -0,0 +1,37 @@ +package com.github.mengweijin.code.generator.reader; + +import com.github.mengweijin.code.generator.dto.DbInfo; +import org.dromara.hutool.core.text.StrUtil; +import org.dromara.hutool.setting.props.Props; + +import java.io.File; +import java.nio.charset.StandardCharsets; + +public class PropertiesBootFileReader implements BootFileReader { + + @Override + public String[] getActiveProfiles(File file) { + Props props = new Props(file, StandardCharsets.UTF_8); + String activeProfiles = props.getStr(SPRING_PROFILES_ACTIVE); + activeProfiles = StrUtil.cleanBlank(activeProfiles); + if(StrUtil.isBlank(activeProfiles)) { + return null; + } + return activeProfiles.split(","); + } + + @Override + public DbInfo getDbInfo(File file) { + Props props = new Props(file, StandardCharsets.UTF_8); + String url = props.getStr(SPRING_DATASOURCE_URL); + if(StrUtil.isBlank(url)) { + return null; + } + + DbInfo dbInfo = new DbInfo(); + dbInfo.setUrl(url); + dbInfo.setUsername(props.getStr(SPRING_DATASOURCE_USERNAME)); + dbInfo.setPassword(props.getStr(SPRING_DATASOURCE_PASSWORD)); + return dbInfo; + } +} diff --git a/src/main/java/com/github/mengweijin/generator/reader/YamlBootFileReader.java b/src/main/java/com/github/mengweijin/code/generator/reader/YamlBootFileReader.java similarity index 47% rename from src/main/java/com/github/mengweijin/generator/reader/YamlBootFileReader.java rename to src/main/java/com/github/mengweijin/code/generator/reader/YamlBootFileReader.java index 99e3cf2..58a4f52 100644 --- a/src/main/java/com/github/mengweijin/generator/reader/YamlBootFileReader.java +++ b/src/main/java/com/github/mengweijin/code/generator/reader/YamlBootFileReader.java @@ -1,8 +1,9 @@ -package com.github.mengweijin.generator.reader; +package com.github.mengweijin.code.generator.reader; -import com.alibaba.fastjson.JSONPath; -import com.github.mengweijin.generator.entity.DbInfo; -import com.github.mengweijin.generator.util.YamlUtils; +import com.alibaba.fastjson2.JSONPath; +import com.github.mengweijin.code.generator.dto.DbInfo; +import com.github.mengweijin.code.generator.util.YamlUtils; +import org.dromara.hutool.core.text.StrUtil; import java.io.File; import java.util.LinkedHashMap; @@ -12,10 +13,14 @@ import java.util.LinkedHashMap; */ public class YamlBootFileReader implements BootFileReader { @Override - public String getActiveProfilesEnv(File file) { + public String[] getActiveProfiles(File file) { LinkedHashMap map = YamlUtils.loadAs(file, LinkedHashMap.class); - Object object = JSONPath.eval(map, "$." + SPRING_PROFILES_ACTIVE); - return object == null ? null : String.valueOf(object); + Object object = JSONPath.eval(map, "$.spring.profiles.active"); + String activeProfiles = StrUtil.cleanBlank(StrUtil.toStringOrNull(object)); + if(StrUtil.isBlank(activeProfiles)) { + return null; + } + return activeProfiles.split(","); } @Override @@ -26,16 +31,11 @@ public class YamlBootFileReader implements BootFileReader { return null; } - DbInfo dbInfo = new DbInfo(); - Object url = JSONPath.eval(map, "$." + SPRING_DATASOURCE_URL); - Object driverName = JSONPath.eval(map, "$." + SPRING_DATASOURCE_DRIVERCLASSNAME); - if (driverName == null) { - // JSONPath中,中划线是特殊字符 - driverName = JSONPath.eval(map, "$.spring.datasource['driver-class-name']"); - } - Object username = JSONPath.eval(map, "$." + SPRING_DATASOURCE_USERNAME); - Object password = JSONPath.eval(map, "$." + SPRING_DATASOURCE_PASSWORD); + Object url = JSONPath.eval(map, "$.spring.datasource.url"); + Object username = JSONPath.eval(map, "$.spring.datasource.username"); + Object password = JSONPath.eval(map, "$.spring.datasource.password"); + DbInfo dbInfo = new DbInfo(); dbInfo.setUrl(url == null ? null : String.valueOf(url)); dbInfo.setUsername(username == null ? null : String.valueOf(username)); dbInfo.setPassword(password == null ? null : String.valueOf(password)); diff --git a/src/main/java/com/github/mengweijin/generator/util/DbInfoUtils.java b/src/main/java/com/github/mengweijin/code/generator/util/DbInfoUtils.java similarity index 34% rename from src/main/java/com/github/mengweijin/generator/util/DbInfoUtils.java rename to src/main/java/com/github/mengweijin/code/generator/util/DbInfoUtils.java index 90e515a..06f099b 100644 --- a/src/main/java/com/github/mengweijin/generator/util/DbInfoUtils.java +++ b/src/main/java/com/github/mengweijin/code/generator/util/DbInfoUtils.java @@ -1,16 +1,18 @@ -package com.github.mengweijin.generator.util; +package com.github.mengweijin.code.generator.util; + +import com.alibaba.fastjson2.JSON; +import com.github.mengweijin.code.generator.dto.Config; +import com.github.mengweijin.code.generator.dto.DbInfo; +import com.github.mengweijin.code.generator.reader.BootFileReaderFactory; +import org.dromara.hutool.core.collection.CollUtil; +import org.dromara.hutool.core.io.file.FileNameUtil; +import org.dromara.hutool.core.io.file.FileUtil; +import org.dromara.hutool.core.text.StrUtil; -import cn.hutool.core.collection.CollectionUtil; -import cn.hutool.core.io.FileUtil; -import cn.hutool.core.io.file.FileNameUtil; -import cn.hutool.core.util.StrUtil; -import com.alibaba.fastjson.JSON; -import com.github.mengweijin.generator.entity.DbInfo; -import com.github.mengweijin.generator.entity.ProjectInfo; -import com.github.mengweijin.generator.reader.BootFileReaderFactory; -import org.apache.maven.model.Resource; import java.io.File; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; /** * @author mengweijin @@ -37,50 +39,54 @@ public class DbInfoUtils { /** * Initialize the default parameter. */ - public static DbInfo getDbInfo(ProjectInfo projectInfo) { - DbInfo dbInfo = projectInfo.getParameters().getDbInfo(); + public static DbInfo getDbInfo(Config config, Set allProjectRuntimeClasspathElements) { + DbInfo dbInfo = config.getDbInfo(); if (dbInfo == null || StrUtil.isBlank(dbInfo.getUrl())) { - dbInfo = generateDefaultDbInfo(projectInfo); + dbInfo = generateDefaultDbInfo(allProjectRuntimeClasspathElements); } return dbInfo; } - private static DbInfo generateDefaultDbInfo(ProjectInfo projectInfo) { - List resourceList = projectInfo.getResourceList(); - Resource resource = resourceList.stream().filter(res -> res.getDirectory().endsWith("\\resources")).findFirst().get(); - - File applicationFile = getBootFile(resource, APPLICATION_FILE); + private static DbInfo generateDefaultDbInfo(Set allProjectRuntimeClasspathElements) { + List classesList = allProjectRuntimeClasspathElements.stream().filter(el -> el.endsWith("\\classes")).collect(Collectors.toList()); + // classes: D:\code\vitality\vitality-admin\target\classes + File applicationFile = null; + for (String classes : classesList) { + applicationFile = getBootFile(classes); + if(applicationFile != null) { + break; + } + } if (applicationFile == null) { - throw new RuntimeException("Can't find any file " + JSON.toJSONString(APPLICATION_FILE)); + throw new RuntimeException("Can't find any file " + JSON.toJSONString(APPLICATION_FILE)); } - String activeProfilesEnv = BootFileReaderFactory.getActiveProfilesEnv(applicationFile); + String[] activeProfilesEnv = BootFileReaderFactory.getActiveProfiles(applicationFile); DbInfo dbInfo = null; - if(StrUtil.isNotBlank(activeProfilesEnv)) { - String activeBootFilePath = resource.getDirectory() + File.separator + - "application-" + activeProfilesEnv + StrUtil.DOT + FileNameUtil.getSuffix(applicationFile); - File activeBootFile = FileUtil.file(activeBootFilePath); - dbInfo = BootFileReaderFactory.getDbInfo(activeBootFile); + if(activeProfilesEnv != null) { + for (String env : activeProfilesEnv) { + String activeBootFilePath = applicationFile.getParent() + File.separator + "application-" + env + StrUtil.DOT + FileNameUtil.getSuffix(applicationFile); + dbInfo = BootFileReaderFactory.getDbInfo(FileUtil.file(activeBootFilePath)); + if(dbInfo != null) { + break; + } + } } - if(dbInfo == null) { - dbInfo = BootFileReaderFactory.getDbInfo(applicationFile); + dbInfo = BootFileReaderFactory.getDbInfo(applicationFile); } - return dbInfo; } - private static File getBootFile(Resource resource, String[] filterNames) { - File resourcesDir = FileUtil.file(resource.getDirectory()); - List fileList = FileUtil.loopFiles(resourcesDir, 1, file -> { - for (String fileName : filterNames) { + private static File getBootFile(String resources) { + List fileList = FileUtil.loopFiles(FileUtil.file(resources), 1, file -> { + for (String fileName : DbInfoUtils.APPLICATION_FILE) { if (fileName.equals(file.getName())) { return true; } } return false; }); - - return CollectionUtil.isEmpty(fileList) ? null : fileList.get(0); + return CollUtil.isEmpty(fileList) ? null : fileList.get(0); } } diff --git a/src/main/java/com/github/mengweijin/code/generator/util/GeneratorUtils.java b/src/main/java/com/github/mengweijin/code/generator/util/GeneratorUtils.java new file mode 100644 index 0000000..06ec37d --- /dev/null +++ b/src/main/java/com/github/mengweijin/code/generator/util/GeneratorUtils.java @@ -0,0 +1,94 @@ +package com.github.mengweijin.code.generator.util; + +import com.baomidou.mybatisplus.generator.config.po.TableField; +import com.baomidou.mybatisplus.generator.config.po.TableInfo; +import com.github.mengweijin.code.generator.dto.Config; +import lombok.extern.slf4j.Slf4j; +import org.dromara.hutool.core.reflect.FieldUtil; +import org.dromara.hutool.core.text.StrUtil; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * @author mengweijin + */ +@Slf4j +public class GeneratorUtils { + + /** + * If the user configured superEntityColumns, the configuration will prevail; + * if not, the default configuration of superEntityColumns will be generated according to the superEntityClass. + * + * @return String + */ + public static List resolveBaseEntityColumns(Config config) { + String baseEntity = config.getBaseEntity(); + if(StrUtil.isBlank(baseEntity)) { + return new ArrayList<>(); + } + try { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + Class cls = Class.forName(baseEntity, true, classLoader); + Field[] declaredFields = FieldUtil.getFieldsDirectly(cls, true); + return Arrays.stream(declaredFields).map(field -> StrUtil.toUnderlineCase(field.getName()).toUpperCase()).collect(Collectors.toList()); + } catch (ClassNotFoundException e) { + log.error(e.getMessage(), e); + throw new RuntimeException(e); + } + } + + public static String[] trimItems(String[] items) { + if (items == null) { + return new String[]{}; + } + return Arrays.stream(items).map(String::trim).toArray(String[]::new); + } + + public static String resolveEntityName(String tableName, Config config) { + String val = tableName; + if (config.getTablePrefix() != null) { + for (String tablePrefix : config.getTablePrefix()) { + if(tableName.toLowerCase().startsWith(tablePrefix.toLowerCase())) { + val = tableName.substring(tablePrefix.length()); + break; + } + } + } + return StrUtil.upperFirst(StrUtil.toCamelCase(val.toLowerCase())); + } + + public static List resolveCommonColumns(List commonFields) { + return commonFields.stream().map(tableField -> tableField.getColumnName().toUpperCase()).collect(Collectors.toList()); + } + + public static List resolveEntityColumns(List entityFields) { + return entityFields.stream().map(tableField -> tableField.getColumnName().toUpperCase()).collect(Collectors.toList()); + } + + public static List resolveCommonFields(TableInfo tableInfo, List baseEntityColumns) { + return tableInfo.getFields().stream().filter(tableField -> baseEntityColumns.contains(tableField.getColumnName().toUpperCase())).collect(Collectors.toList()); + } + + public static List resolveEntityFields(TableInfo tableInfo, List baseEntityColumns) { + return tableInfo.getFields().stream().filter(tableField -> !baseEntityColumns.contains(tableField.getColumnName().toUpperCase())).collect(Collectors.toList()); + } + + public static String renderString(String content, Map map){ + Set> sets = map.entrySet(); + for(Map.Entry entry : sets) { + String regex = "\\$\\{" + entry.getKey() + "}"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(content); + content = matcher.replaceAll(StrUtil.toString(entry.getValue())); + } + return content; + } +} diff --git a/src/main/java/com/github/mengweijin/generator/util/YamlUtils.java b/src/main/java/com/github/mengweijin/code/generator/util/YamlUtils.java similarity index 67% rename from src/main/java/com/github/mengweijin/generator/util/YamlUtils.java rename to src/main/java/com/github/mengweijin/code/generator/util/YamlUtils.java index d53827a..6792c5b 100644 --- a/src/main/java/com/github/mengweijin/generator/util/YamlUtils.java +++ b/src/main/java/com/github/mengweijin/code/generator/util/YamlUtils.java @@ -1,9 +1,8 @@ -package com.github.mengweijin.generator.util; +package com.github.mengweijin.code.generator.util; -import cn.hutool.core.io.FileUtil; -import cn.hutool.core.util.ReUtil; -import cn.hutool.core.util.StrUtil; -import com.github.mengweijin.generator.entity.ProjectInfo; +import org.dromara.hutool.core.io.file.FileUtil; +import org.dromara.hutool.core.regex.ReUtil; +import org.dromara.hutool.core.text.StrUtil; import org.yaml.snakeyaml.Yaml; import java.io.File; @@ -30,11 +29,9 @@ public class YamlUtils { } return line; }).collect(Collectors.toList()); - File tempFile = FileUtil.file(ProjectInfo.TMP_DIR + file.getName()); - FileUtil.writeLines(lineCollect, tempFile, StandardCharsets.UTF_8); - + String joined = String.join("\r\n", lineCollect); Yaml yaml = new Yaml(); - return yaml.loadAs(FileUtil.getInputStream(tempFile), type); + return yaml.loadAs(joined, type); } } diff --git a/src/main/java/com/github/mengweijin/generator/DefaultAutoGenerator.java b/src/main/java/com/github/mengweijin/generator/DefaultAutoGenerator.java deleted file mode 100644 index fbad12e..0000000 --- a/src/main/java/com/github/mengweijin/generator/DefaultAutoGenerator.java +++ /dev/null @@ -1,188 +0,0 @@ -package com.github.mengweijin.generator; - -import cn.hutool.core.io.FileUtil; -import cn.hutool.core.util.ClassUtil; -import cn.hutool.core.util.StrUtil; -import com.baomidou.mybatisplus.generator.FastAutoGenerator; -import com.baomidou.mybatisplus.generator.config.DataSourceConfig; -import com.baomidou.mybatisplus.generator.config.TemplateConfig; -import com.baomidou.mybatisplus.generator.config.po.TableField; -import com.baomidou.mybatisplus.generator.config.po.TableInfo; -import com.baomidou.mybatisplus.generator.config.rules.DateType; -import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; -import com.github.mengweijin.generator.config.CustomerDataSource; -import com.github.mengweijin.generator.config.FileOutput; -import com.github.mengweijin.generator.entity.DbInfo; -import com.github.mengweijin.generator.entity.IdField; -import com.github.mengweijin.generator.entity.Parameters; -import com.github.mengweijin.generator.entity.ProjectInfo; -import com.github.mengweijin.generator.factory.TemplateEngineFactory; -import com.github.mengweijin.generator.util.DbInfoUtils; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; - -/** - * @author mengweijin - */ -@Slf4j -public class DefaultAutoGenerator { - - @Getter - private ProjectInfo projectInfo; - - public DefaultAutoGenerator(ProjectInfo projectInfo) { - this.projectInfo = projectInfo; - } - - public void execute() { - Parameters parameters = projectInfo.getParameters(); - - String outputDir = FileUtil.file(projectInfo.getBaseDir(), "target/code-generator/").getAbsolutePath(); - // clean directory target/code-generator - FileUtil.del(outputDir); - - FastAutoGenerator fastAutoGenerator = FastAutoGenerator.create(dataSourceConfigBuilder()) - .globalConfig(builder -> builder - .fileOverride() - .author(parameters.getAuthor()) - .enableSwagger() - .disableOpenDir() - .outputDir(outputDir) - .dateType(DateType.TIME_PACK).commentDate("yyyy-MM-dd")) - .packageConfig(builder -> builder.parent(parameters.getOutputPackage())) - // 禁用所有默认模板,我们需要使用自定义的模板 - .templateConfig((Consumer) TemplateConfig.Builder::disable) - .strategyConfig(builder -> builder - .addInclude(this.trimItemName(parameters.getTables())) - .addTablePrefix(this.trimItemName(parameters.getTablePrefix())) - .entityBuilder() - .superClass(parameters.getSuperEntityClass()) - //.disableSerialVersionUID() - .enableChainModel() - .enableLombok() - .enableTableFieldAnnotation() - // 乐观锁数据库列名 - .versionColumnName("version") - // 乐观锁 Entity 属性名 - .versionPropertyName("version") - // 逻辑删除数据库列名 - .logicDeleteColumnName("deleted") - // 逻辑删除 Entity 属性名 - .logicDeletePropertyName("deleted") - // 数据库表映射到实体的命名策略 - .naming(NamingStrategy.underline_to_camel) - .addSuperEntityColumns(this.generateDefaultSuperEntityColumns()) - - .controllerBuilder() - .superClass(parameters.getSuperControllerClass()) - // 开启驼峰转连字符 - .enableHyphenStyle() - // 开启生成@RestController控制器 - .enableRestStyle() - - .serviceBuilder() - .superServiceClass(parameters.getSuperServiceClass()) - .superServiceImplClass(parameters.getSuperServiceImplClass()) - - .mapperBuilder() - .superClass(parameters.getSuperDaoClass()) - .enableBaseColumnList() - .enableBaseResultMap()) - .injectionConfig(builder -> { - builder - .beforeOutputFile(((tableInfo, objectMap) -> { - enhanceObjectMap(objectMap, parameters); - FileOutput.outputFile(tableInfo, objectMap, projectInfo, outputDir); - })) - //.customMap(Collections.singletonMap("test", "baomidou")) - //.customFile(Collections.singletonMap("test.txt", "/templates/test.vm")) - ; - }) - .templateEngine(TemplateEngineFactory.getTemplateEngine(this.projectInfo.getParameters().getTemplateType())); - - - fastAutoGenerator.execute(); - } - - private DataSourceConfig.Builder dataSourceConfigBuilder() { - DbInfo dbInfo = DbInfoUtils.getDbInfo(projectInfo); - // 自定义 CustomerDataSource, 使用自定义的 ClassLoader 加载类获取连接。 - CustomerDataSource dataSource = new CustomerDataSource(dbInfo.getUrl(), dbInfo.getUsername(), dbInfo.getPassword()); - return new DataSourceConfig.Builder(dataSource); - } - - /** - * If the user configured superEntityColumns, the configuration will prevail; - * if not, the default configuration of superEntityColumns will be generated according to the superEntityClass. - * - * @return String[] - */ - private String[] generateDefaultSuperEntityColumns() { - String superEntityClass = projectInfo.getParameters().getSuperEntityClass(); - if(StrUtil.isBlank(superEntityClass)) { - return new String[]{}; - } - try { - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); - Class cls = Class.forName(superEntityClass, true, classLoader); - Field[] declaredFields = ClassUtil.getDeclaredFields(cls); - return Arrays.stream(declaredFields) - .map(field -> StrUtil.toUnderlineCase(field.getName())).toArray(String[]::new); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - } - - private String[] trimItemName(String[] items) { - if (items == null) { - return new String[]{}; - } - return Arrays.stream(items).map(String::trim).toArray(String[]::new); - } - - private static void enhanceObjectMap(Map objectMap, Parameters parameters) { - objectMap.remove("package"); - objectMap.put("parameters", parameters); - objectMap.put("idField", getIdField((TableInfo) objectMap.get("table"))); - objectMap.put("allFieldList", handleAllFieldList((TableInfo) objectMap.get("table"))); - objectMap.put("superEntityClassPackage", parameters.getSuperEntityClass()); - objectMap.put("hasLongField", hasLongField((TableInfo) objectMap.get("table"))); - - log.info("Beetl parameter map: {}", objectMap); - } - - private static boolean hasLongField(TableInfo tableInfo) { - return tableInfo.getFields().stream().anyMatch(item -> "Long".equalsIgnoreCase(item.getPropertyType())); - } - - private static IdField getIdField(TableInfo tableInfo) { - TableField tableField = tableInfo.getFields().stream().filter(TableField::isKeyFlag).findFirst().orElse(null); - - IdField idField = new IdField(); - if(tableField != null) { - idField.setColumnName(tableField.getName()); - idField.setPropertyName(tableField.getPropertyName()); - idField.setPropertyType(tableField.getColumnType().getType()); - } - return idField; - } - - private static List handleAllFieldList(TableInfo tableInfo) { - List fieldList = tableInfo.getFields(); - List commonFields = tableInfo.getCommonFields(); - - // 为了不影响其他地方的引用,这里必须创建一个新的集合来存放所有字段 - List allList = new ArrayList<>(); - allList.addAll(fieldList); - allList.addAll(commonFields); - return allList; - } -} diff --git a/src/main/java/com/github/mengweijin/generator/config/FileOutput.java b/src/main/java/com/github/mengweijin/generator/config/FileOutput.java deleted file mode 100644 index 3318892..0000000 --- a/src/main/java/com/github/mengweijin/generator/config/FileOutput.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.github.mengweijin.generator.config; - -import cn.hutool.core.collection.CollectionUtil; -import cn.hutool.core.io.FileUtil; -import cn.hutool.core.util.StrUtil; -import com.baomidou.mybatisplus.generator.config.po.TableInfo; -import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; -import com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine; -import com.github.mengweijin.generator.entity.Parameters; -import com.github.mengweijin.generator.entity.ProjectInfo; -import com.github.mengweijin.generator.factory.TemplateEngineFactory; -import lombok.extern.slf4j.Slf4j; -import java.io.File; -import java.util.List; -import java.util.Map; - -/** - * @author mengweijin - */ -@Slf4j -public class FileOutput { - - public static void outputFile(TableInfo tableInfo, Map objectMap, ProjectInfo projectInfo, String outputDir) { - Parameters parameters = projectInfo.getParameters(); - AbstractTemplateEngine templateEngine = TemplateEngineFactory.getTemplateEngine(parameters.getTemplateType()); - String outputPackage = parameters.getOutputPackage(); - - List templateFileList = FileUtil.loopFiles(parameters.getTemplateLocation(), - file -> file.isFile() && file.getName().toLowerCase().endsWith(parameters.getTemplateType().getSuffix())); - - if (CollectionUtil.isEmpty(templateFileList)) { - throw new RuntimeException("No template files found in location " + parameters.getTemplateLocation()); - } else { - String message = "Found " + templateFileList.size() + " template files in location " + parameters.getTemplateLocation(); - log.info(message); - } - - try { - for (File templateFile: templateFileList) { - // 初始化输出文件的名称和路径 - File outputFile = buildOutputFile(tableInfo, templateFile.getAbsolutePath(), outputDir, outputPackage); - FileUtil.mkParentDirs(outputFile); - // 使用模板引擎,渲染并输出文件 - templateEngine.writer(objectMap, templateFile.getAbsolutePath(), outputFile); - } - } catch (Exception e) { - log.error("Template engine writer error!", e); - throw new RuntimeException(e); - } - } - - /** - * - * @param tableInfo - * @param templatePath 绝对路径 - * @param outputDir - * @param outputPackage - * @return 全路径名文件 - */ - private static File buildOutputFile(TableInfo tableInfo, String templatePath, String outputDir, String outputPackage) { - StringBuilder outputPath = new StringBuilder(outputDir); - - if(!outputDir.endsWith(StrUtil.SLASH) && !outputDir.endsWith(StrUtil.BACKSLASH)) { - outputPath.append(File.separator); - } - if(!StrUtil.isBlank(outputPackage)) { - outputPath.append(outputPackage).append(File.separator); - } - - StringBuilder componentName = new StringBuilder(); - File templateFile = FileUtil.file(templatePath); - String[] packageHierarchy = templateFile.getName().split("\\."); - if ("entity".equalsIgnoreCase(packageHierarchy[0])) { - outputPath.append(packageHierarchy[0]).append(File.separator); - } else { - for (int i = 0; i < packageHierarchy.length - 2; i++) { - componentName.append(NamingStrategy.capitalFirst(packageHierarchy[i])); - outputPath.append(packageHierarchy[i].toLowerCase()).append(File.separator); - } - } - - outputPath.append(tableInfo.getEntityName()).append(componentName); - String path = StrUtil.replace(outputPath.toString(), StrUtil.DOT, StrUtil.SLASH); - return new File(path + StrUtil.DOT + packageHierarchy[packageHierarchy.length - 2]); - } - -} diff --git a/src/main/java/com/github/mengweijin/generator/engine/BeetlStringTemplateEngine.java b/src/main/java/com/github/mengweijin/generator/engine/BeetlStringTemplateEngine.java deleted file mode 100644 index 5b2575f..0000000 --- a/src/main/java/com/github/mengweijin/generator/engine/BeetlStringTemplateEngine.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.github.mengweijin.generator.engine; - -import cn.hutool.core.io.FileUtil; -import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder; -import com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine; -import lombok.extern.slf4j.Slf4j; -import org.beetl.core.Configuration; -import org.beetl.core.GroupTemplate; -import org.beetl.core.Template; -import org.beetl.core.resource.StringTemplateResourceLoader; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.Map; - -/** - * @author mengweijin - */ -@Slf4j -public class BeetlStringTemplateEngine extends AbstractTemplateEngine { - - private GroupTemplate groupTemplate; - - @Override - public AbstractTemplateEngine init(ConfigBuilder configBuilder) { - try { - Configuration cfg = new Configuration(ClassLoader.getSystemClassLoader()); - groupTemplate = new GroupTemplate(new StringTemplateResourceLoader(), cfg); - return this; - } catch (IOException e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - } - - @Override - public void writer(Map objectMap, String templatePath, File outputFile) throws Exception { - // read template content from template path - String templateContent = FileUtil.readUtf8String(templatePath); - Template template = groupTemplate.getTemplate(templateContent); - try (FileOutputStream fileOutputStream = new FileOutputStream(outputFile)) { - template.binding(objectMap); - template.renderTo(fileOutputStream); - } catch (Exception e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - - log.info("模板:" + templatePath + "; 文件:" + outputFile); - } - - @Override - public String templateFilePath(String filePath) { - return filePath + ".btl"; - } -} diff --git a/src/main/java/com/github/mengweijin/generator/entity/Docker.java b/src/main/java/com/github/mengweijin/generator/entity/Docker.java deleted file mode 100644 index 6e8bb6a..0000000 --- a/src/main/java/com/github/mengweijin/generator/entity/Docker.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.github.mengweijin.generator.entity; - -import lombok.Data; - -/** - * @author mengweijin - */ -@Data -public class Docker { - - private String name; - -} diff --git a/src/main/java/com/github/mengweijin/generator/entity/IdField.java b/src/main/java/com/github/mengweijin/generator/entity/IdField.java deleted file mode 100644 index 4135a80..0000000 --- a/src/main/java/com/github/mengweijin/generator/entity/IdField.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.github.mengweijin.generator.entity; - -import lombok.Data; - -/** - * @author mengweijin - */ -@Data -public class IdField { - - private String columnName = "id"; - - private String propertyName = "id"; - - private String propertyType = "Long"; -} diff --git a/src/main/java/com/github/mengweijin/generator/entity/Parameters.java b/src/main/java/com/github/mengweijin/generator/entity/Parameters.java deleted file mode 100644 index b3c35ce..0000000 --- a/src/main/java/com/github/mengweijin/generator/entity/Parameters.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.github.mengweijin.generator.entity; - -import cn.hutool.system.SystemUtil; -import com.github.mengweijin.generator.enums.TemplateType; -import lombok.Data; - -/** - * @author mengweijin - */ -@Data -public class Parameters { - - /** - * default: com.github.mengweijin - */ - private String outputPackage = "com.github.mengweijin"; - - private String author = SystemUtil.get("user.name", false); - - /** - * The absolute path to the folder. - */ - private String templateLocation; - - private TemplateType templateType = TemplateType.beetl; - - private DbInfo dbInfo; - - /** - * For example: SYS_USER, SYS_ROLE - * Note: Separated by commas. - */ - private String[] tables; - - /** - * For example: SYS_, FTL_ - * Note: Separated by commas. - */ - private String[] tablePrefix; - - private String superEntityClass; - private String superDaoClass; - private String superServiceClass; - private String superServiceImplClass; - private String superControllerClass; - - /** - * 【实体】是否为lombok模型(默认 true) - */ - private boolean lombokModel = true; -} diff --git a/src/main/java/com/github/mengweijin/generator/entity/ProjectInfo.java b/src/main/java/com/github/mengweijin/generator/entity/ProjectInfo.java deleted file mode 100644 index 1dee686..0000000 --- a/src/main/java/com/github/mengweijin/generator/entity/ProjectInfo.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.github.mengweijin.generator.entity; - -import cn.hutool.system.SystemUtil; -import lombok.Data; -import org.apache.maven.model.Resource; - -import java.io.File; -import java.util.List; - -/** - * @author mengweijin - */ -@Data -public class ProjectInfo { - - public static final String TMP_DIR = SystemUtil.get(SystemUtil.TMPDIR) + "code-generator/"; - - private Parameters parameters; - - private List resourceList; - - private File baseDir; - - private File sourceDir; - - -} diff --git a/src/main/java/com/github/mengweijin/generator/enums/Template.java b/src/main/java/com/github/mengweijin/generator/enums/Template.java deleted file mode 100644 index b5f5798..0000000 --- a/src/main/java/com/github/mengweijin/generator/enums/Template.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.github.mengweijin.generator.enums; - -/** - * @author mengweijin - */ - -public enum Template { - - JPA("templates/jpa/"), - MYBATIS("templates/mybatis/"), - MYBATIS_PLUS("templates/mybatis-plus/"); - - private final String path; - - Template(String path) { - this.path = path; - } - - public String getPath() { - return this.path; - } -} diff --git a/src/main/java/com/github/mengweijin/generator/factory/TemplateEngineFactory.java b/src/main/java/com/github/mengweijin/generator/factory/TemplateEngineFactory.java deleted file mode 100644 index 3a97824..0000000 --- a/src/main/java/com/github/mengweijin/generator/factory/TemplateEngineFactory.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.github.mengweijin.generator.factory; - -import com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine; -import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine; -import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine; -import com.github.mengweijin.generator.engine.BeetlStringTemplateEngine; -import com.github.mengweijin.generator.enums.TemplateType; - -import java.util.HashMap; - -/** - * @author mengweijin - */ -public class TemplateEngineFactory { - - public static final HashMap map = new HashMap<>(3); - - static { - map.put(TemplateType.beetl, new BeetlStringTemplateEngine()); - map.put(TemplateType.freemarker, new FreemarkerTemplateEngine()); - map.put(TemplateType.velocity, new VelocityTemplateEngine()); - } - - private TemplateEngineFactory() { - } - - public static AbstractTemplateEngine getTemplateEngine(TemplateType templateType) { - AbstractTemplateEngine engine = map.get(templateType); - if(engine == null) { - throw new RuntimeException("TemplateType can't be null!"); - } - - return engine; - } -} diff --git a/src/main/java/com/github/mengweijin/generator/mojo/AbstractDockerMojo.java b/src/main/java/com/github/mengweijin/generator/mojo/AbstractDockerMojo.java deleted file mode 100644 index 1c611d8..0000000 --- a/src/main/java/com/github/mengweijin/generator/mojo/AbstractDockerMojo.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.github.mengweijin.generator.mojo; - -import cn.hutool.core.io.FileUtil; -import cn.hutool.core.util.RuntimeUtil; -import cn.hutool.core.util.StrUtil; -import com.github.mengweijin.generator.engine.BeetlStringTemplateEngine; -import com.github.mengweijin.generator.entity.Docker; -import com.github.mengweijin.generator.entity.ProjectInfo; -import com.github.mengweijin.generator.util.TemplateUtils; -import lombok.Getter; -import org.apache.maven.execution.MavenSession; -import org.apache.maven.model.Resource; -import org.apache.maven.plugin.AbstractMojo; -import org.apache.maven.plugins.annotations.Parameter; -import org.apache.maven.project.MavenProject; -import java.io.File; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -/** - * 注释说明:@Execute(phase = LifecyclePhase.PACKAGE) 在执行当前 Mojo 前先打包 - * @author mengweijin - */ -@Getter -public abstract class AbstractDockerMojo extends AbstractMojo { - - @Parameter - private Docker docker; - - @Parameter(defaultValue = "${project}") - private MavenProject project; - - @Parameter(defaultValue = "${basedir}") - private File baseDir; - - @Parameter(defaultValue = "${project.build.resources}", readonly = true, required = true) - private List resources; - - @Parameter(defaultValue = "${project.build.sourceDirectory}", required = true, readonly = true) - private File sourceDir; - - @Parameter(defaultValue = "${project.build.testResources}", readonly = true, required = true) - private List testResources; - - @Parameter(defaultValue = "${project.build.testSourceDirectory}", readonly = true, required = true) - private File testSourceDir; - - @Parameter(defaultValue = "${project.compileClasspathElements}", readonly = true, required = true) - private List compilePath; - - @Parameter(defaultValue = "${session}", readonly = true) - private MavenSession session; - - protected void generate() throws Exception { - // clean TMP folder - FileUtil.del(FileUtil.file(ProjectInfo.TMP_DIR)); - TemplateUtils.copyTemplateFolderToJavaTmp("templates/"); - - Map objectMap = new HashMap<>(2); - objectMap.put("ARTIFACT_ID", project.getArtifactId()); - objectMap.put("VERSION", project.getVersion()); - - BeetlStringTemplateEngine templateEngine = new BeetlStringTemplateEngine(); - templateEngine.init(null); - - this.generateDockerScript(templateEngine, objectMap); - } - - protected void execBash(String bashName) throws Exception { - this.generate(); - - // return C: - String currentFileSystemMount = StrUtil.subPre(this.dockerfileDirector(), 2).toUpperCase(Locale.ROOT); - - StringBuilder builder = - new StringBuilder("cmd /c ").append(currentFileSystemMount).append(" && ") - .append("cd ").append(this.dockerfileDirector()).append(" && ") - .append(bashName); - - String cmdOutput = RuntimeUtil.execForStr(builder.toString()); - getLog().info(cmdOutput); - } - - private void generateDockerScript(BeetlStringTemplateEngine templateEngine, Map objectMap) throws Exception { - String templatePath = ProjectInfo.TMP_DIR + "templates/docker"; - List templateFileList = FileUtil.loopFiles(templatePath, - file -> file.isFile() && file.getName().toLowerCase().endsWith(".btl")); - - String outputFilePath; - for (File templateFile: templateFileList) { - outputFilePath = dockerfileDirector() + StrUtil.subBefore(templateFile.getName(), StrUtil.DOT, true); - getLog().info("Template " + templateFile.getAbsolutePath() + "--->" + outputFilePath); - render(templateFile.getAbsolutePath(), outputFilePath, templateEngine, objectMap); - } - } - - protected String dockerfileDirector() { - return baseDir.getAbsolutePath() + File.separator + "target" + File.separator; - } - - private File render(String templatePath, String outputFilePath, BeetlStringTemplateEngine templateEngine, Map objectMap) throws Exception { - File outputFile = FileUtil.file(outputFilePath); - FileUtil.mkParentDirs(outputFile); - templateEngine.writer(objectMap, templatePath, outputFile); - return outputFile; - } - -} diff --git a/src/main/java/com/github/mengweijin/generator/mojo/AbstractGeneratorMojo.java b/src/main/java/com/github/mengweijin/generator/mojo/AbstractGeneratorMojo.java deleted file mode 100644 index be74b9c..0000000 --- a/src/main/java/com/github/mengweijin/generator/mojo/AbstractGeneratorMojo.java +++ /dev/null @@ -1,122 +0,0 @@ -package com.github.mengweijin.generator.mojo; - -import cn.hutool.core.io.FileUtil; -import cn.hutool.core.lang.JarClassLoader; -import com.github.mengweijin.generator.entity.Parameters; -import com.github.mengweijin.generator.entity.ProjectInfo; -import com.github.mengweijin.generator.DefaultAutoGenerator; -import com.github.mengweijin.generator.util.TemplateUtils; -import lombok.Getter; -import org.apache.maven.execution.MavenSession; -import org.apache.maven.model.Resource; -import org.apache.maven.plugin.AbstractMojo; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.plugins.annotations.Parameter; -import org.apache.maven.project.MavenProject; - -import java.io.File; -import java.lang.reflect.Method; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.List; -import java.util.Optional; - -/** - * @author mengweijin - */ -@Getter -public abstract class AbstractGeneratorMojo extends AbstractMojo { - - @Parameter - private Parameters parameters; - - @Parameter(defaultValue = "${project}") - private MavenProject project; - - @Parameter(defaultValue = "${basedir}") - private File baseDir; - - @Parameter(defaultValue = "${project.build.resources}", readonly = true, required = true) - private List resources; - - @Parameter(defaultValue = "${project.build.sourceDirectory}", required = true, readonly = true) - private File sourceDir; - - @Parameter(defaultValue = "${project.build.testResources}", readonly = true, required = true) - private List testResources; - - @Parameter(defaultValue = "${project.build.testSourceDirectory}", readonly = true, required = true) - private File testSourceDir; - - @Parameter(defaultValue = "${project.compileClasspathElements}", readonly = true, required = true) - private List compilePath; - - @Parameter(defaultValue = "${session}", readonly = true) - private MavenSession session; - - @Override - public void execute() throws MojoExecutionException, MojoFailureException { - try { - // clean TMP folder - FileUtil.del(FileUtil.file(ProjectInfo.TMP_DIR)); - - this.loadParentProjectClassToJarClassLoader(); - TemplateUtils.copyTemplateFolderToJavaTmp("templates/"); - this.parameters = Optional.ofNullable(this.parameters).orElse(new Parameters()); - this.setDefaultFixedParameters(this.parameters); - - ProjectInfo projectInfo = new ProjectInfo(); - projectInfo.setParameters(this.parameters); - projectInfo.setResourceList(this.getResources()); - projectInfo.setBaseDir(this.baseDir); - projectInfo.setSourceDir(this.sourceDir); - - new DefaultAutoGenerator(projectInfo).execute(); - } catch (Exception e) { - getLog().error(e); - throw new RuntimeException(e); - } - } - - /** - * set default fixed parameters - * @param parameters parameters - */ - protected abstract void setDefaultFixedParameters(Parameters parameters); - - private void loadParentProjectClassToJarClassLoader() { - try { - List runtimeElements = project.getRuntimeClasspathElements(); - - URL[] urls = new URL[runtimeElements.size()]; - for (int i = 0; i < runtimeElements.size(); ++i) { - urls[i] = new File(runtimeElements.get(i)).toURI().toURL(); - } - JarClassLoader jarClassLoader = new JarClassLoader(urls); - Thread.currentThread().setContextClassLoader(jarClassLoader); - } catch (Exception e) { - getLog().error(e.getMessage()); - throw new RuntimeException(e); - } - } - - private void loadParentProjectClassToApplicationClassLoader() { - URLClassLoader urlLoader = (URLClassLoader) ClassLoader.getSystemClassLoader(); - - try { - Method method = URLClassLoader.class.getDeclaredMethod("addURL", new Class[] { URL.class }); - method.setAccessible(true); - - List classpathElements = project.getCompileClasspathElements(); - URL[] urls = new URL[classpathElements.size()]; - for (int i = 0; i < classpathElements.size(); ++i) { - urls[i] = new File(classpathElements.get(i)).toURI().toURL(); - method.invoke(urlLoader, urls[i]); - } - } catch (Exception e) { - getLog().error(e.getMessage()); - throw new RuntimeException(e); - } - } -} diff --git a/src/main/java/com/github/mengweijin/generator/mojo/CustomerGeneratorMojo.java b/src/main/java/com/github/mengweijin/generator/mojo/CustomerGeneratorMojo.java deleted file mode 100644 index 812675d..0000000 --- a/src/main/java/com/github/mengweijin/generator/mojo/CustomerGeneratorMojo.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.github.mengweijin.generator.mojo; - -import cn.hutool.core.io.FileUtil; -import cn.hutool.core.util.ArrayUtil; -import com.github.mengweijin.generator.entity.Parameters; -import com.github.mengweijin.generator.enums.TemplateType; -import org.apache.maven.plugins.annotations.Mojo; -import org.apache.maven.plugins.annotations.ResolutionScope; -import java.io.File; - -/** - * ResolutionScope.COMPILE_PLUS_RUNTIME: - * Will add the classpath jars into list when call method project.getRuntimeClasspathElements(); - * maven scope=compile + system + provided + runtime dependencies - * - * @author mengweijin - */ -@Mojo(name = "Customer", requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME) -public class CustomerGeneratorMojo extends AbstractGeneratorMojo { - - @Override - protected void setDefaultFixedParameters(Parameters parameters) { - getLog().info("Note: The [templateLocation] and correct [templateType] must be configured! The default [templateType] value is btl."); - String templateLocation = parameters.getTemplateLocation(); - if(!FileUtil.isDirectory(templateLocation)) { - String msg = "Can't found valid template location, please check your configuration for args templateLocation. ===>"; - getLog().error(msg + templateLocation); - throw new RuntimeException(msg + templateLocation); - } - - TemplateType templateType = parameters.getTemplateType(); - File[] tplFiles = FileUtil.file(templateLocation).listFiles((dir, name) -> name.toLowerCase().endsWith(templateType.getSuffix())); - if(ArrayUtil.isEmpty(tplFiles)) { - String msg = "Can't found valid template files. Please check your configuration for args [templateLocation] and [templateType]!"; - getLog().error(msg); - getLog().error("Current args [templateLocation] is: " + templateLocation); - getLog().error("Current args [templateType] is: " + templateType.name()); - throw new RuntimeException(msg); - } - } -} diff --git a/src/main/java/com/github/mengweijin/generator/mojo/DockerBuildMojo.java b/src/main/java/com/github/mengweijin/generator/mojo/DockerBuildMojo.java deleted file mode 100644 index a6b52b7..0000000 --- a/src/main/java/com/github/mengweijin/generator/mojo/DockerBuildMojo.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.github.mengweijin.generator.mojo; - -import lombok.Getter; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.plugins.annotations.Execute; -import org.apache.maven.plugins.annotations.LifecyclePhase; -import org.apache.maven.plugins.annotations.Mojo; - -/** - * 注释说明:@Execute(phase = LifecyclePhase.PACKAGE) 在执行当前 Mojo 前先打包 - * @author mengweijin - */ -@Getter -@Mojo(name = "Docker-Build") -@Execute(phase = LifecyclePhase.PACKAGE) -public class DockerBuildMojo extends DockerfileGeneratorMojo { - - public static final String DOCKER_IMAGE_BUILD = "DockerImageBuild.bat"; - - @Override - public void execute() throws MojoExecutionException, MojoFailureException { - try { - this.execBash(DOCKER_IMAGE_BUILD); - } catch (Exception e) { - getLog().error(e); - throw new RuntimeException(e); - } - } - -} diff --git a/src/main/java/com/github/mengweijin/generator/mojo/DockerDeleteMojo.java b/src/main/java/com/github/mengweijin/generator/mojo/DockerDeleteMojo.java deleted file mode 100644 index d3304a6..0000000 --- a/src/main/java/com/github/mengweijin/generator/mojo/DockerDeleteMojo.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.github.mengweijin.generator.mojo; - -import lombok.Getter; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.plugins.annotations.Mojo; - -/** - * 注释说明:@Execute(phase = LifecyclePhase.PACKAGE) 在执行当前 Mojo 前先打包 - * @author mengweijin - */ -@Getter -@Mojo(name = "Docker-Delete") -public class DockerDeleteMojo extends DockerfileGeneratorMojo { - - public static final String DOCKER_IMAGE_DELETE = "DockerImageDelete.bat"; - - @Override - public void execute() throws MojoExecutionException, MojoFailureException { - try { - this.execBash(DOCKER_IMAGE_DELETE); - } catch (Exception e) { - getLog().error(e); - throw new RuntimeException(e); - } - } - -} diff --git a/src/main/java/com/github/mengweijin/generator/mojo/DockerDeployMojo.java b/src/main/java/com/github/mengweijin/generator/mojo/DockerDeployMojo.java deleted file mode 100644 index 6482488..0000000 --- a/src/main/java/com/github/mengweijin/generator/mojo/DockerDeployMojo.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.github.mengweijin.generator.mojo; - -import lombok.Getter; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.plugins.annotations.Execute; -import org.apache.maven.plugins.annotations.LifecyclePhase; -import org.apache.maven.plugins.annotations.Mojo; - -/** - * 注释说明:@Execute(phase = LifecyclePhase.PACKAGE) 在执行当前 Mojo 前先打包 - * @author mengweijin - */ -@Getter -@Mojo(name = "Docker-Deploy") -@Execute(phase = LifecyclePhase.PACKAGE) -public class DockerDeployMojo extends DockerfileGeneratorMojo { - - public static final String DOCKER_IMAGE_BUILD_RUN = "DockerImageBuildRun.bat"; - - @Override - public void execute() throws MojoExecutionException, MojoFailureException { - try { - this.execBash(DOCKER_IMAGE_BUILD_RUN); - } catch (Exception e) { - getLog().error(e); - throw new RuntimeException(e); - } - } - -} diff --git a/src/main/java/com/github/mengweijin/generator/mojo/DockerfileGeneratorMojo.java b/src/main/java/com/github/mengweijin/generator/mojo/DockerfileGeneratorMojo.java deleted file mode 100644 index 7ee9f28..0000000 --- a/src/main/java/com/github/mengweijin/generator/mojo/DockerfileGeneratorMojo.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.github.mengweijin.generator.mojo; - -import lombok.Getter; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.plugins.annotations.Execute; -import org.apache.maven.plugins.annotations.LifecyclePhase; -import org.apache.maven.plugins.annotations.Mojo; - -/** - * 注释说明:@Execute(phase = LifecyclePhase.PACKAGE) 在执行当前 Mojo 前先打包 - * @author mengweijin - */ -@Getter -@Mojo(name = "Dockerfile") -@Execute(phase = LifecyclePhase.PACKAGE) -public class DockerfileGeneratorMojo extends AbstractDockerMojo { - - @Override - public void execute() throws MojoExecutionException, MojoFailureException { - try { - this.generate(); - } catch (Exception e) { - getLog().error(e); - throw new RuntimeException(e); - } - } - -} diff --git a/src/main/java/com/github/mengweijin/generator/mojo/JpaGeneratorMojo.java b/src/main/java/com/github/mengweijin/generator/mojo/JpaGeneratorMojo.java deleted file mode 100644 index 1f1af44..0000000 --- a/src/main/java/com/github/mengweijin/generator/mojo/JpaGeneratorMojo.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.github.mengweijin.generator.mojo; - -import com.github.mengweijin.generator.entity.Parameters; -import com.github.mengweijin.generator.entity.ProjectInfo; -import com.github.mengweijin.generator.enums.Template; -import com.github.mengweijin.generator.enums.TemplateType; -import org.apache.maven.plugins.annotations.Mojo; -import org.apache.maven.plugins.annotations.ResolutionScope; - -/** - * @author mengweijin - */ -@Mojo(name = "JPA", requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME) -public class JpaGeneratorMojo extends AbstractGeneratorMojo { - - private static final String JpaRepository = "org.springframework.data.jpa.repository.JpaRepository"; - - @Override - protected void setDefaultFixedParameters(Parameters parameters) { - parameters.setTemplateLocation(ProjectInfo.TMP_DIR + Template.JPA.getPath()); - parameters.setSuperDaoClass(JpaRepository); - parameters.setTemplateType(TemplateType.beetl); - } -} diff --git a/src/main/java/com/github/mengweijin/generator/mojo/MybatisPlusGeneratorMojo.java b/src/main/java/com/github/mengweijin/generator/mojo/MybatisPlusGeneratorMojo.java deleted file mode 100644 index 258518a..0000000 --- a/src/main/java/com/github/mengweijin/generator/mojo/MybatisPlusGeneratorMojo.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.github.mengweijin.generator.mojo; - -import com.github.mengweijin.generator.entity.Parameters; -import com.github.mengweijin.generator.entity.ProjectInfo; -import com.github.mengweijin.generator.enums.Template; -import com.github.mengweijin.generator.enums.TemplateType; -import org.apache.maven.plugins.annotations.Mojo; -import org.apache.maven.plugins.annotations.ResolutionScope; - -/** - * @author mengweijin - */ -@Mojo(name = "MyBatis-Plus", requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME) -public class MybatisPlusGeneratorMojo extends AbstractGeneratorMojo { - - private static final String BaseMapper = "com.baomidou.mybatisplus.core.mapper.BaseMapper"; - private static final String IService = "com.baomidou.mybatisplus.extension.service.IService"; - private static final String ServiceImpl = "com.baomidou.mybatisplus.extension.service.impl.ServiceImpl"; - - @Override - protected void setDefaultFixedParameters(Parameters parameters) { - parameters.setTemplateLocation(ProjectInfo.TMP_DIR + Template.MYBATIS_PLUS.getPath()); - parameters.setSuperDaoClass(BaseMapper); - parameters.setSuperServiceClass(IService); - parameters.setSuperServiceImplClass(ServiceImpl); - parameters.setTemplateType(TemplateType.beetl); - } -} diff --git a/src/main/java/com/github/mengweijin/generator/reader/PropertiesBootFileReader.java b/src/main/java/com/github/mengweijin/generator/reader/PropertiesBootFileReader.java deleted file mode 100644 index c73201d..0000000 --- a/src/main/java/com/github/mengweijin/generator/reader/PropertiesBootFileReader.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.github.mengweijin.generator.reader; - -import cn.hutool.core.util.StrUtil; -import cn.hutool.setting.dialect.Props; -import com.github.mengweijin.generator.entity.DbInfo; - -import java.io.File; -import java.net.MalformedURLException; -import java.nio.charset.StandardCharsets; - -public class PropertiesBootFileReader implements BootFileReader { - - @Override - public String getActiveProfilesEnv(File file) { - try { - Props props = new Props(file.toURI().toURL(), StandardCharsets.UTF_8); - return props.getStr(SPRING_PROFILES_ACTIVE); - } catch (MalformedURLException e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - } - - @Override - public DbInfo getDbInfo(File file) { - try { - Props props = new Props(file.toURI().toURL(), StandardCharsets.UTF_8); - String url = props.getStr(SPRING_DATASOURCE_URL); - if(StrUtil.isBlank(url)) { - return null; - } - - DbInfo dbInfo = new DbInfo(); - dbInfo.setUrl(url); - dbInfo.setUsername(props.getStr(SPRING_DATASOURCE_USERNAME)); - dbInfo.setPassword(props.getStr(SPRING_DATASOURCE_PASSWORD)); - - return dbInfo; - } catch (MalformedURLException e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - } -} diff --git a/src/main/java/com/github/mengweijin/generator/util/TemplateUtils.java b/src/main/java/com/github/mengweijin/generator/util/TemplateUtils.java deleted file mode 100644 index 3e7246c..0000000 --- a/src/main/java/com/github/mengweijin/generator/util/TemplateUtils.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.github.mengweijin.generator.util; - -import cn.hutool.core.io.FileUtil; -import cn.hutool.core.io.IoUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.core.util.URLUtil; -import com.github.mengweijin.generator.entity.ProjectInfo; -import lombok.extern.slf4j.Slf4j; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.Enumeration; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; - -/** - * @author mengweijin - */ -@Slf4j -public final class TemplateUtils { - /** - * - * @param classPathResource "templates/" - */ - public static void copyTemplateFolderToJavaTmp(String classPathResource) { - File tmpFile; - JarFile jarFile = null; - InputStream inputStream = null; - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); - URL url = classLoader.getResource(classPathResource); - try { - if (URLUtil.isJarURL(url)) { - jarFile = URLUtil.getJarFile(url); - Enumeration enumeration = jarFile.entries(); - String jarEntryName; - while (enumeration.hasMoreElements()) { - jarEntryName = enumeration.nextElement().getName(); - if (jarEntryName.startsWith(classPathResource) && !jarEntryName.endsWith(StrUtil.SLASH)) { - inputStream = classLoader.getResource(jarEntryName).openConnection().getInputStream(); - tmpFile = FileUtil.file(ProjectInfo.TMP_DIR + jarEntryName); - FileUtil.writeFromStream(inputStream, tmpFile); - } - } - } - } catch (IOException e) { - log.error(e.getMessage()); - e.printStackTrace(); - throw new RuntimeException(e); - } finally { - IoUtil.close(inputStream); - IoUtil.close(jarFile); - } - } -} diff --git a/src/main/resources/templates/mybatis-plus/${entityName}.java.vm b/src/main/resources/templates/mybatis-plus/${entityName}.java.vm new file mode 100644 index 0000000..fe70675 --- /dev/null +++ b/src/main/resources/templates/mybatis-plus/${entityName}.java.vm @@ -0,0 +1,60 @@ +package ${package}; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.annotation.TableField; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +#if("$!{baseEntity}" != "") +import ${baseEntity}; +#else +import java.io.Serializable; +#end + +/** + *

+ * $!{table.comment} + *

+ * + * @author ${author} + * @since ${date} + */ +@Data +#if("$!{baseEntity}" != "") +@EqualsAndHashCode(callSuper = true) +#else +@EqualsAndHashCode(callSuper = false) +#end +@Accessors(chain = true) +@TableName("${table.name}") +#if("$!{baseEntityName}" != "") +public class ${entityName} extends ${baseEntityName} { +#else +public class ${entityName} implements Serializable { + + private static final long serialVersionUID = 1L; +#end +#foreach($field in ${entityFields}) + +#if(${field.keyFlag}) +#set($keyPropertyName=${field.propertyName}) +#end +#if("$!field.comment" != "") + /** + * ${field.comment} + */ +#end +#if(${field.keyFlag}) + @TableId("${field.annotationColumnName}") +#end +## 乐观锁注解 +#if(${field.versionField}) + @Version +#end +## 逻辑删除注解 +#if(${field.logicDeleteField}) + @TableLogic +#end + private ${field.propertyType} ${field.propertyName}; +#end +} diff --git a/src/main/resources/templates/mybatis-plus/controller.java.btl b/src/main/resources/templates/mybatis-plus/${entityName}Controller.java.vm similarity index 32% rename from src/main/resources/templates/mybatis-plus/controller.java.btl rename to src/main/resources/templates/mybatis-plus/${entityName}Controller.java.vm index 5db3f90..94637c2 100644 --- a/src/main/resources/templates/mybatis-plus/controller.java.btl +++ b/src/main/resources/templates/mybatis-plus/${entityName}Controller.java.vm @@ -1,116 +1,98 @@ -package ${parameters.outputPackage}.controller; +package ${package}; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO; -<% if(entityLombokModel){ %> +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.github.mengweijin.vitality.framework.domain.R; +import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; -<% } %> -import javax.validation.Valid; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestParam; -<% if(restControllerStyle){ %> +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -<% }else{ %> -import org.springframework.stereotype.Controller; -<% } %> -<% if(isNotEmpty(superControllerClassPackage)){ %> -import ${superControllerClassPackage}; -<% } %> -import ${parameters.outputPackage}.entity.${entity}; -import ${parameters.outputPackage}.service.${entity}Service; +import org.springframework.web.bind.annotation.RequestParam; /** *

- * ${table.comment!} Controller + * $!{table.comment} ${entityName} Controller *

* * @author ${author} * @since ${date} */ -<% if(entityLombokModel){ %> @Slf4j -<% } %> @Validated -<% if(restControllerStyle){ %> @RestController -<% }else{ %> -@Controller -<% } %> -@RequestMapping("<% if(isNotEmpty(package.ModuleName)){ %>/${package.ModuleName}<% } %>/<% if(isNotEmpty(controllerMappingHyphenStyle)){ %>${controllerMappingHyphen}<% }else{ %>${table.entityPath}<% } %>") -public class ${entity}Controller <% if(isNotEmpty(superControllerClass)){ %>extends ${superControllerClass}<% } %> { +@RequestMapping("${requestMapping}") +public class ${entityName}Controller { - /** - *

- * ${entity}Service - *

- */ @Autowired - private ${entity}Service ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}Service; + private ${entityName}Service ${entityPropertyName}Service; /** *

- * Get ${entity} page list by ${entity} + * Get ${entityName} page by ${entityName} *

* @param page page - * @param ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)} ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)} - * @return PageDTO<${entity}> + * @param entity ${entityPropertyName} + * @return Page<${entityName}> */ @GetMapping("/page") - public PageDTO<${entity}> page(PageDTO<${entity}> page, ${entity} ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}) { - return ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}Service.page(page, new QueryWrapper<>(${@cn.hutool.core.util.StrUtil.lowerFirst(entity)})); + public Page<${entityName}> page(Page<${entityName}> page, ${entityName} entity) { + return ${entityPropertyName}Service.page(page, new QueryWrapper<>(entity)); } /** *

- * Get ${entity} by id + * Get ${entityName} by id *

* @param id id - * @return ${entity} + * @return ${entityName} */ @GetMapping("/{id}") - public ${entity} getById(@PathVariable("id") ${idField.propertyType} id) { - return ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}Service.getById(id); + public ${entityName} getById(@PathVariable("id") Long id) { + return ${entityPropertyName}Service.getById(id); } /** *

- * Add ${entity} + * Add ${entityName} *

- * @param ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)} ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)} + * @param entity ${entityName} */ @PostMapping - public void add(@Valid @RequestBody ${entity} ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}) { - ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}Service.save(${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}); + public R add(@Valid @RequestBody ${entityName} entity) { + boolean bool = ${entityPropertyName}Service.save(entity); + return R.bool(bool); } /** *

- * Update ${entity} + * Update ${entityName} *

- * @param ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)} ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)} + * @param entity ${entityName} */ @PutMapping - public void update(@Valid @RequestBody ${entity} ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}) { - ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}Service.updateById(${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}); + public R update(@Valid @RequestBody ${entityName} entity) { + boolean bool = ${entityPropertyName}Service.updateById(entity); + return R.bool(bool); } /** *

- * Delete ${entity} by id + * Delete ${entityName} by id(s), Multiple ids can be separated by commas ",". *

- * @param id id + * @param ids id */ - @DeleteMapping("/{id}") - public void delete(@PathVariable("id") ${idField.propertyType} id) { - ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}Service.removeById(id); + @DeleteMapping("/{ids}") + public R delete(@PathVariable("ids") Long[] ids) { + int i = vtlUserService.getBaseMapper().deleteBatchIds(Arrays.asList(ids)); + return R.ajax(i); } } diff --git a/src/main/resources/templates/mybatis-plus/${entityName}DTO.java.vm b/src/main/resources/templates/mybatis-plus/${entityName}DTO.java.vm new file mode 100644 index 0000000..145c510 --- /dev/null +++ b/src/main/resources/templates/mybatis-plus/${entityName}DTO.java.vm @@ -0,0 +1,25 @@ +package ${package}; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import java.io.Serial; +import java.io.Serializable; + +/** + *

+ * ${entityName} DTO + *

+ * + * @author ${author} + * @since ${date} + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Accessors(chain = true) +public class ${entityName}DTO extends ${entityName} implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + +} diff --git a/src/main/resources/templates/mybatis-plus/${entityName}Mapper.java.vm b/src/main/resources/templates/mybatis-plus/${entityName}Mapper.java.vm new file mode 100644 index 0000000..32fe5be --- /dev/null +++ b/src/main/resources/templates/mybatis-plus/${entityName}Mapper.java.vm @@ -0,0 +1,28 @@ +package ${package}; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + *

+ * $!{table.comment} ${entityName} Mapper + *

+ * + * @author ${author} + * @since ${date} + */ +@Mapper +public interface ${entityName}Mapper extends BaseMapper<${entityName}> { + + /** + * 自定义分页查询 + * @param page page + * @param entity {@link ${entityName}} + * @return IPage + */ + IPage<${entityName}> page(IPage<${entityName}> page, @Param("p") ${entityName} entity); + +} + diff --git a/src/main/resources/templates/mybatis-plus/${entityName}Mapper.xml.vm b/src/main/resources/templates/mybatis-plus/${entityName}Mapper.xml.vm new file mode 100644 index 0000000..99cf9c3 --- /dev/null +++ b/src/main/resources/templates/mybatis-plus/${entityName}Mapper.xml.vm @@ -0,0 +1,46 @@ + + + + + + +## id +#foreach($field in ${allFields}) + #if(${field.keyFlag}) + + #end +#end +##生成其他字段 +#foreach($field in ${allFields}) + #if(!${field.keyFlag}) + + #end +#end + + + + +#foreach($field in ${allFields}) + ${field.columnName}#if($foreach.hasNext),#end +#end + + + + + +#foreach($field in ${allFields}) + + and ${field.name} = #{${field.propertyName}} + +#end + + + + + + diff --git a/src/main/resources/templates/mybatis-plus/${entityName}Service.java.vm b/src/main/resources/templates/mybatis-plus/${entityName}Service.java.vm new file mode 100644 index 0000000..37f91d9 --- /dev/null +++ b/src/main/resources/templates/mybatis-plus/${entityName}Service.java.vm @@ -0,0 +1,32 @@ +package ${package}; + +import lombok.extern.slf4j.Slf4j; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + *

+ * $!{table.comment} ${entityName} Service + * Add @Transactional(rollbackFor = Exception.class) if you need. + *

+ * + * @author ${author} + * @since ${date} + */ +@Slf4j +@Service +public class ${entityName}Service extends ServiceImpl<${entityName}Mapper, ${entityName}> { + + /** + * Custom paging queries + * @param page page + * @param entity {@link ${entityName}} + * @return IPage + */ + public IPage<${entityName}> page(IPage<${entityName}> page, ${entityName} entity){ + return this.getBaseMapper().page(page, entity); + } +} + diff --git a/src/main/resources/templates/mybatis-plus/entity.java.btl b/src/main/resources/templates/mybatis-plus/entity.java.btl deleted file mode 100644 index 407a12f..0000000 --- a/src/main/resources/templates/mybatis-plus/entity.java.btl +++ /dev/null @@ -1,151 +0,0 @@ -package ${parameters.outputPackage}.entity; - -import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.annotation.TableField; -<% if(hasLongField){ %> -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; -<% } %> -<% if(isNotEmpty(superEntityClassPackage)){ %> -import ${superEntityClassPackage}; -<% }else{ %> -import java.io.Serializable; -<% } %> -<% if(entityLombokModel){ %> -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.experimental.Accessors; -<% } %> - -/** - *

- * ${table.comment!} - *

- * - * @author ${author} - * @since ${date} - */ -<% if(entityLombokModel){ %> -@Data - <% if(isNotEmpty(superEntityClass)){ %> -@EqualsAndHashCode(callSuper = true) - <% }else{ %> -@EqualsAndHashCode(callSuper = false) - <% } %> -@Accessors(chain = true) -<% } %> -<% if(table.convert){ %> -@TableName("${table.name}") -<% } %> -<% if(isNotEmpty(superEntityClass)){ %> -public class ${entity} extends ${superEntityClass} { -<% }else{ %> -public class ${entity} implements Serializable { -<% } %> - -<% if(entitySerialVersionUID){ %> - private static final long serialVersionUID = 1L; -<% } %> -<% /** -----------BEGIN 字段循环遍历----------- **/ %> -<% for(field in table.fields){ %> - <% - if(field.keyFlag){ - var keyPropertyName = field.propertyName; - } - %> - - <% if(isNotEmpty(field.comment)){ %> - /** - * ${field.comment} - */ - <% } %> - <% if(field.keyFlag){ %> - <% - /*主键*/ - %> - <% if(field.keyIdentityFlag){ %> - @TableId(value = "${field.name}", type = IdType.AUTO) - <% }else if(isNotEmpty(idType)){ %> - @TableId(value = "${field.name}", type = IdType.${idType}) - <% }else if(field.convert){ %> - @TableId("${field.name}") - <% } %> - <% - /*普通字段*/ - %> - <% }else if(isNotEmpty(field.fill)){ %> - <% if(field.convert){ %> - @TableField(value = "${field.name}", fill = FieldFill.${field.fill}) - <% }else{ %> - @TableField(fill = FieldFill.${field.fill}) - <% } %> - <% }else if(field.convert){ %> - @TableField("${field.name}") - <% } %> - <% - /*乐观锁注解*/ - %> - <% if(versionFieldName!'' == field.name){ %> - @Version - <% } %> - <% - /*逻辑删除注解*/ - %> - <% if(logicDeleteFieldName!'' == field.name){ %> - @TableLogic - <% } %> - <% if('Long' == field.propertyType){ %> - @JsonSerialize(using = ToStringSerializer.class) - <% } %> - private ${field.propertyType} ${field.propertyName}; -<% } %> -<% /** -----------END 字段循环遍历----------- **/ %> - -<% if(!entityLombokModel){ %> - <% for(field in table.fields){ %> - <% - var getprefix =''; - if(field.propertyType=='boolean'){ - getprefix='is'; - }else{ - getprefix='get'; - } - %> - public ${field.propertyType} ${getprefix}${field.capitalName}() { - return ${field.propertyName}; - } - - <% if(entityBuilderModel){ %> - public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) { - <% }else{ %> - public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) { - <% } %> - this.${field.propertyName} = ${field.propertyName}; - <% if(entityBuilderModel){ %> - return this; - <% } %> - } - - <% } %> -<% } %> -<% if(entityColumnConstant){ %> - <% for(field in table.fields){ %> - public static final String ${strutil.toUpperCase(field.name)} = "${field.name}"; - - <% } %> -<% } %> -<% if(!entityLombokModel){ %> - @Override - public String toString() { - return "${entity}{" + - <% for(field in table.fields){ %> - <% if(fieldLP.index==0){ %> - "${field.propertyName}=" + ${field.propertyName} + - <% }else{ %> - ", ${field.propertyName}=" + ${field.propertyName} + - <% } %> - <% } %> - "}"; - } -<% } %> -} diff --git a/src/main/resources/templates/mybatis-plus/mapper.java.btl b/src/main/resources/templates/mybatis-plus/mapper.java.btl deleted file mode 100644 index 903fd2f..0000000 --- a/src/main/resources/templates/mybatis-plus/mapper.java.btl +++ /dev/null @@ -1,21 +0,0 @@ -package ${parameters.outputPackage}.mapper; - -import ${parameters.outputPackage}.entity.${entity}; -<% if(isNotEmpty(superMapperClassPackage)){ %> -import ${superMapperClassPackage}; -<% } %> -import org.apache.ibatis.annotations.Mapper; - -/** - *

- * ${table.comment!} Mapper Interface - *

- * - * @author ${author} - * @since ${date} - */ -@Mapper -public interface ${entity}Mapper extends ${superMapperClass}<${entity}> { - -} - diff --git a/src/main/resources/templates/mybatis-plus/mapper.xml.btl b/src/main/resources/templates/mybatis-plus/mapper.xml.btl deleted file mode 100644 index da9a6a5..0000000 --- a/src/main/resources/templates/mybatis-plus/mapper.xml.btl +++ /dev/null @@ -1,44 +0,0 @@ - - - -<% if(enableCache){ %> - - -<% } %> - -<% if(baseResultMap){ %> - - -<% for(field in table.fields){ %> - <% /** 生成主键排在第一位 **/ %> - <% if(field.keyFlag){ %> - - <% } %> -<% } %> -<% for(field in table.commonFields){ %> - <% /** 生成公共字段 **/ %> - -<% } %> -<% for(field in table.fields){ %> - <% /** 生成普通字段 **/ %> - <% if(!field.keyFlag){ %> - - <% } %> -<% } %> - -<% } %> -<% if(baseColumnList){ %> - - -<% for(field in allFieldList){ %> - <% if(!fieldLP.last){ %> - ${field.name}, - <% } %> - <% if(fieldLP.last){ %> - ${field.name} - <% } %> -<% } %> - - -<% } %> - diff --git a/src/main/resources/templates/mybatis-plus/service.java.btl b/src/main/resources/templates/mybatis-plus/service.java.btl deleted file mode 100644 index bdddfe0..0000000 --- a/src/main/resources/templates/mybatis-plus/service.java.btl +++ /dev/null @@ -1,36 +0,0 @@ -package ${parameters.outputPackage}.service; - -<% if(entityLombokModel){ %> -import lombok.extern.slf4j.Slf4j; -<% } %> -import ${parameters.outputPackage}.entity.${entity}; -import ${parameters.outputPackage}.mapper.${entity}Mapper; -import ${superServiceClassPackage}; -import ${superServiceImplClassPackage}; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -/** - *

- * ${table.comment!} implement - * Add @Transactional(rollbackFor = Exception.class) if you need. - *

- * - * @author ${author} - * @since ${date} - */ -<% if(entityLombokModel){ %> -@Slf4j -<% } %> -@Service -public class ${entity}Service extends ${superServiceImplClass}<${entity}Mapper, ${entity}> implements ${superServiceClass}<${entity}> { - - /** - *

- * ${entity}Mapper - *

- */ - @Autowired - private ${entity}Mapper ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}Mapper; -} - diff --git a/src/test/java/com/github/mengweijin/generator/mojo/DockerDeployMojoTest.java b/src/test/java/com/github/mengweijin/generator/mojo/DockerDeployMojoTest.java index ba284bc..262ce56 100644 --- a/src/test/java/com/github/mengweijin/generator/mojo/DockerDeployMojoTest.java +++ b/src/test/java/com/github/mengweijin/generator/mojo/DockerDeployMojoTest.java @@ -1,8 +1,8 @@ package com.github.mengweijin.generator.mojo; -import cn.hutool.core.io.FileUtil; -import cn.hutool.core.util.RuntimeUtil; -import cn.hutool.core.util.StrUtil; +import org.dromara.hutool.core.io.file.FileUtil; +import org.dromara.hutool.core.text.StrUtil; +import org.dromara.hutool.core.util.RuntimeUtil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -- Gitee From 3d4d7bbb4a7424e3f9b08b22d8704847137be28c Mon Sep 17 00:00:00 2001 From: mengweijin <1002284406@qq.com> Date: Sun, 9 Jun 2024 09:50:50 +0800 Subject: [PATCH 2/3] templates --- .../code/generator/driver/DriverShim.java | 1 + .../mengweijin/code/generator/dto/Config.java | 2 +- .../generator/engine/ITemplateEngine.java | 1 + .../generator/mojo/AbstractGeneratorMojo.java | 2 +- .../code/generator/util/GeneratorUtils.java | 5 + .../templates/docker/DockerImageBuild.bat.btl | 13 -- .../docker/DockerImageBuildRun.bat.btl | 7 -- .../docker/DockerImageDelete.bat.btl | 27 ---- .../resources/templates/docker/Dockerfile.btl | 11 -- .../templates/jpa/${entityName}.java.vm | 54 ++++++++ .../${entityName}Controller.java.vm} | 73 ++++------- .../${entityName}DTO.java.vm | 0 .../jpa/${entityName}Repository.java.vm | 17 +++ .../jpa/${entityName}Service.java.vm | 25 ++++ .../templates/jpa/controller.java.btl | 114 ----------------- .../resources/templates/jpa/entity.java.btl | 117 ------------------ .../templates/jpa/repository.java.btl | 21 ---- .../resources/templates/jpa/service.java.btl | 33 ----- .../templates/mybatis/${entityName}.java.vm | 43 +++++++ .../mybatis/${entityName}Controller.java.vm | 98 +++++++++++++++ .../mybatis/${entityName}DTO.java.vm | 25 ++++ .../mybatis/${entityName}Mapper.java.vm | 67 ++++++++++ .../mybatis/${entityName}Mapper.xml.vm | 88 +++++++++++++ .../mybatis/${entityName}Service.java.vm | 85 +++++++++++++ .../templates/mybatis/entity.java.btl | 106 ---------------- .../templates/mybatis/mapper.java.btl | 67 ---------- .../templates/mybatis/mapper.xml.btl | 116 ----------------- .../templates/mybatis/service.java.btl | 102 --------------- .../${entityName}.java.vm | 3 - .../${entityName}Controller.java.vm | 27 ++-- .../mybatisplus/${entityName}DTO.java.vm | 25 ++++ .../${entityName}Mapper.java.vm | 6 +- .../${entityName}Mapper.xml.vm | 6 +- .../${entityName}Service.java.vm | 8 +- 34 files changed, 587 insertions(+), 808 deletions(-) delete mode 100644 src/main/resources/templates/docker/DockerImageBuild.bat.btl delete mode 100644 src/main/resources/templates/docker/DockerImageBuildRun.bat.btl delete mode 100644 src/main/resources/templates/docker/DockerImageDelete.bat.btl delete mode 100644 src/main/resources/templates/docker/Dockerfile.btl create mode 100644 src/main/resources/templates/jpa/${entityName}.java.vm rename src/main/resources/templates/{mybatis/controller.java.btl => jpa/${entityName}Controller.java.vm} (31%) rename src/main/resources/templates/{mybatis-plus => jpa}/${entityName}DTO.java.vm (100%) create mode 100644 src/main/resources/templates/jpa/${entityName}Repository.java.vm create mode 100644 src/main/resources/templates/jpa/${entityName}Service.java.vm delete mode 100644 src/main/resources/templates/jpa/controller.java.btl delete mode 100644 src/main/resources/templates/jpa/entity.java.btl delete mode 100644 src/main/resources/templates/jpa/repository.java.btl delete mode 100644 src/main/resources/templates/jpa/service.java.btl create mode 100644 src/main/resources/templates/mybatis/${entityName}.java.vm create mode 100644 src/main/resources/templates/mybatis/${entityName}Controller.java.vm create mode 100644 src/main/resources/templates/mybatis/${entityName}DTO.java.vm create mode 100644 src/main/resources/templates/mybatis/${entityName}Mapper.java.vm create mode 100644 src/main/resources/templates/mybatis/${entityName}Mapper.xml.vm create mode 100644 src/main/resources/templates/mybatis/${entityName}Service.java.vm delete mode 100644 src/main/resources/templates/mybatis/entity.java.btl delete mode 100644 src/main/resources/templates/mybatis/mapper.java.btl delete mode 100644 src/main/resources/templates/mybatis/mapper.xml.btl delete mode 100644 src/main/resources/templates/mybatis/service.java.btl rename src/main/resources/templates/{mybatis-plus => mybatisplus}/${entityName}.java.vm (94%) rename src/main/resources/templates/{mybatis-plus => mybatisplus}/${entityName}Controller.java.vm (70%) create mode 100644 src/main/resources/templates/mybatisplus/${entityName}DTO.java.vm rename src/main/resources/templates/{mybatis-plus => mybatisplus}/${entityName}Mapper.java.vm (80%) rename src/main/resources/templates/{mybatis-plus => mybatisplus}/${entityName}Mapper.xml.vm (82%) rename src/main/resources/templates/{mybatis-plus => mybatisplus}/${entityName}Service.java.vm (78%) diff --git a/src/main/java/com/github/mengweijin/code/generator/driver/DriverShim.java b/src/main/java/com/github/mengweijin/code/generator/driver/DriverShim.java index ff9d2d1..6d10666 100644 --- a/src/main/java/com/github/mengweijin/code/generator/driver/DriverShim.java +++ b/src/main/java/com/github/mengweijin/code/generator/driver/DriverShim.java @@ -21,6 +21,7 @@ import java.util.logging.Logger; */ @Getter public class DriverShim implements Driver { + private Driver driver; DriverShim(Driver driver) { diff --git a/src/main/java/com/github/mengweijin/code/generator/dto/Config.java b/src/main/java/com/github/mengweijin/code/generator/dto/Config.java index 68ce430..fa94b07 100644 --- a/src/main/java/com/github/mengweijin/code/generator/dto/Config.java +++ b/src/main/java/com/github/mengweijin/code/generator/dto/Config.java @@ -15,7 +15,7 @@ public class Config { private String author = SystemUtil.get("user.name", false); - private String outputDir = "target/code-generator"; + private String outputDir = "/target/code-generator"; private String templateDir; diff --git a/src/main/java/com/github/mengweijin/code/generator/engine/ITemplateEngine.java b/src/main/java/com/github/mengweijin/code/generator/engine/ITemplateEngine.java index fd824cc..d9903c4 100644 --- a/src/main/java/com/github/mengweijin/code/generator/engine/ITemplateEngine.java +++ b/src/main/java/com/github/mengweijin/code/generator/engine/ITemplateEngine.java @@ -47,6 +47,7 @@ public interface ITemplateEngine { objectMap.put("baseEntityName", StrUtil.subAfter(config.getBaseEntity(), ".", true)); objectMap.put("baseEntityColumns", baseEntityColumns); objectMap.put("table", tableInfo); + objectMap.put("idField", GeneratorUtils.getIdField(tableInfo)); objectMap.put("entityName", entityName); objectMap.put("entityPropertyName", StrUtil.lowerFirst(entityName)); objectMap.put("entityFields", entityFields); diff --git a/src/main/java/com/github/mengweijin/code/generator/mojo/AbstractGeneratorMojo.java b/src/main/java/com/github/mengweijin/code/generator/mojo/AbstractGeneratorMojo.java index 6031260..0505374 100644 --- a/src/main/java/com/github/mengweijin/code/generator/mojo/AbstractGeneratorMojo.java +++ b/src/main/java/com/github/mengweijin/code/generator/mojo/AbstractGeneratorMojo.java @@ -106,7 +106,7 @@ public abstract class AbstractGeneratorMojo extends AbstractMojo { } String[] tables = GeneratorUtils.trimItems(tableNames.split("[,,]")); - System.out.println("请输入包模块名称(可以为空,直接按 Enter 键继续),然后按 Enter 键继续:"); + System.out.println("请输入包模块名称(可以为空),按 Enter 键继续:"); String moduleName = sc.nextLine(); if(StrUtil.isNotBlank(moduleName)) { config.setModuleName(StrUtil.trim(moduleName.toLowerCase())); diff --git a/src/main/java/com/github/mengweijin/code/generator/util/GeneratorUtils.java b/src/main/java/com/github/mengweijin/code/generator/util/GeneratorUtils.java index 06ec37d..7094f3e 100644 --- a/src/main/java/com/github/mengweijin/code/generator/util/GeneratorUtils.java +++ b/src/main/java/com/github/mengweijin/code/generator/util/GeneratorUtils.java @@ -81,6 +81,10 @@ public class GeneratorUtils { return tableInfo.getFields().stream().filter(tableField -> !baseEntityColumns.contains(tableField.getColumnName().toUpperCase())).collect(Collectors.toList()); } + public static TableField getIdField(TableInfo tableInfo) { + return tableInfo.getFields().stream().filter(TableField::isKeyFlag).findFirst().orElse(null); + } + public static String renderString(String content, Map map){ Set> sets = map.entrySet(); for(Map.Entry entry : sets) { @@ -91,4 +95,5 @@ public class GeneratorUtils { } return content; } + } diff --git a/src/main/resources/templates/docker/DockerImageBuild.bat.btl b/src/main/resources/templates/docker/DockerImageBuild.bat.btl deleted file mode 100644 index ad1b142..0000000 --- a/src/main/resources/templates/docker/DockerImageBuild.bat.btl +++ /dev/null @@ -1,13 +0,0 @@ -@echo off - -call DockerImageDelete.bat - -echo Building docker image - -docker build -t ${ARTIFACT_ID}:${VERSION} . - -rem docker build -t registry.cn-hangzhou.aliyuncs.com/mengweijin/${ARTIFACT_ID}:${VERSION} . -rem docker login --username=***@**.com registry.cn-hangzhou.aliyuncs.com -rem docker tag ${ARTIFACT_ID}:${VERSION} registry.cn-hangzhou.aliyuncs.com/mengweijin/${ARTIFACT_ID}:${VERSION} -rem docker push registry.cn-hangzhou.aliyuncs.com/mengweijin/${ARTIFACT_ID}:${VERSION} - diff --git a/src/main/resources/templates/docker/DockerImageBuildRun.bat.btl b/src/main/resources/templates/docker/DockerImageBuildRun.bat.btl deleted file mode 100644 index 1d49898..0000000 --- a/src/main/resources/templates/docker/DockerImageBuildRun.bat.btl +++ /dev/null @@ -1,7 +0,0 @@ -@echo off - -call DockerImageBuild.bat - -echo Starting docker container - -docker run --name ${ARTIFACT_ID} -d -p 8080:8080 ${ARTIFACT_ID}:${VERSION} diff --git a/src/main/resources/templates/docker/DockerImageDelete.bat.btl b/src/main/resources/templates/docker/DockerImageDelete.bat.btl deleted file mode 100644 index ddcd986..0000000 --- a/src/main/resources/templates/docker/DockerImageDelete.bat.btl +++ /dev/null @@ -1,27 +0,0 @@ -@echo off - -echo Deleting docker container and image - -set REPOSITORY=${ARTIFACT_ID} -set TAG=${VERSION} - -for /f "skip=1 tokens=1,2 delims= " %%i in ('docker ps -a') do ( - if "%REPOSITORY%:%TAG%"=="%%j" ( - echo delete docker container %%i %%j - docker rm -f %%i - ) -) - -rem skip=1 skip the first row -rem tokens=1,2,3 Get only the 1,2,3 entries of the delims delimiter, and the three corresponding variables are also required in do() to receive, for example: echo %%i %%j %%k -rem delims= A string separated by a space on each line in the list -rem %%i in ('docker images') %%i is used to receive the values returned by the 'docker images' command and delimit them according to delims -rem do() The code in do() is executed once for each line in the list -for /f "skip=1 tokens=1,2,3 delims= " %%i in ('docker images') do ( - if "%REPOSITORY%"=="%%i" ( - if "%TAG%"=="%%j" ( - echo delete docker image %%i %%j %%k - docker rmi -f %%k - ) - ) -) diff --git a/src/main/resources/templates/docker/Dockerfile.btl b/src/main/resources/templates/docker/Dockerfile.btl deleted file mode 100644 index 7419e92..0000000 --- a/src/main/resources/templates/docker/Dockerfile.btl +++ /dev/null @@ -1,11 +0,0 @@ -FROM java:8 -MAINTAINER Meng Wei Jin mengweijin.work@foxmail.com - -ARG JAR_FILE=${ARTIFACT_ID}-${VERSION}.jar - -RUN mkdir /app -COPY \${JAR_FILE} /app/application.jar -# RUN bash -c 'touch /app/application.jar' - -ENTRYPOINT ["java", "-jar", "/app/application.jar"] -EXPOSE 8080 diff --git a/src/main/resources/templates/jpa/${entityName}.java.vm b/src/main/resources/templates/jpa/${entityName}.java.vm new file mode 100644 index 0000000..c5345ed --- /dev/null +++ b/src/main/resources/templates/jpa/${entityName}.java.vm @@ -0,0 +1,54 @@ +package ${package}; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +#if("$!{baseEntity}" != "") +import ${baseEntity}; +#else +import java.io.Serializable; +#end +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +/** + *

+ * $!{table.comment} + *

+ * + * @author ${author} + * @since ${date} + */ +@Data +#if("$!{baseEntity}" != "") +@EqualsAndHashCode(callSuper = true) +#else +@EqualsAndHashCode(callSuper = false) +#end +@Accessors(chain = true) +@Entity +@Table(name = "${table.name}") +#if("$!{baseEntityName}" != "") +public class ${entityName} extends ${baseEntityName} { +#else +public class ${entityName} implements Serializable { + + private static final long serialVersionUID = 1L; +#end +#foreach($field in ${entityFields}) + +#if("$!field.comment" != "") + /** + * ${field.comment} + */ +#end +#if(${field.keyFlag}) + @Id +#else + @Column(name= "${field.name}") +#end + private ${field.propertyType} ${field.propertyName}; +#end +} diff --git a/src/main/resources/templates/mybatis/controller.java.btl b/src/main/resources/templates/jpa/${entityName}Controller.java.vm similarity index 31% rename from src/main/resources/templates/mybatis/controller.java.btl rename to src/main/resources/templates/jpa/${entityName}Controller.java.vm index 72d0eb6..098d993 100644 --- a/src/main/resources/templates/mybatis/controller.java.btl +++ b/src/main/resources/templates/jpa/${entityName}Controller.java.vm @@ -1,100 +1,81 @@ -package ${parameters.outputPackage}.controller; +package ${package}; -<% if(entityLombokModel){ %> +import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; -<% } %> -import javax.validation.Valid; +import jakarta.validation.Valid; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; -<% if(restControllerStyle){ %> +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -<% }else{ %> -import org.springframework.stereotype.Controller; -<% } %> -<% if(isNotEmpty(superControllerClassPackage)){ %> -import ${superControllerClassPackage}; -<% } %> -import ${parameters.outputPackage}.entity.${entity}; -import ${parameters.outputPackage}.service.${entity}Service; +import org.springframework.web.bind.annotation.RequestParam; +import java.util.Optional; /** *

- * ${table.comment!} Controller + * $!{table.comment} Controller *

* * @author ${author} * @since ${date} */ -<% if(entityLombokModel){ %> @Slf4j -<% } %> @Validated -<% if(restControllerStyle){ %> @RestController -<% }else{ %> -@Controller -<% } %> -@RequestMapping("<% if(isNotEmpty(moduleName)){ %>/${moduleName}<% } %>/${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}") -public class ${entity}Controller <% if(isNotEmpty(superControllerName)){ %>extends ${superControllerName}<% } %> { +@RequestMapping("${requestMapping}") +public class ${entityName}Controller { - /** - *

- * ${entity}Service - *

- */ - @Autowired - private ${entity}Service ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}Service; + private ${entityName}Service ${entityPropertyName}Service; + + private ${entityName}Repository ${entityPropertyName}Repository; /** *

- * Get ${entity} by id + * Get ${entityName} by id *

* @param id id - * @return ${entity} + * @return ${entityName} */ @GetMapping("/{id}") - public ${entity} getById(@PathVariable("id") ${idField.propertyType} id) { - return ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}Service.getById(id); + public Optional<${entityName}> getById(@PathVariable("id") ${idField.columnType.type} id) { + return ${entityPropertyName}Repository.findById(id); } /** *

- * Add ${entity} + * Add ${entityName} *

- * @param ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)} ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)} + * @param ${entityPropertyName} {@link ${entityName}} */ @PostMapping - public void add(@Valid @RequestBody ${entity} ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}) { - ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}Service.save(${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}); + public void add(@Valid @RequestBody ${entityName} ${entityPropertyName}) { + ${entityPropertyName}Repository.save(${entityPropertyName}); } /** *

- * Update ${entity} + * Update ${entityName} *

- * @param ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)} ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)} + * @param ${entityPropertyName} {@link ${entityName}} */ @PutMapping - public void update(@Valid @RequestBody ${entity} ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}) { - ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}Service.updateById(${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}); + public void update(@Valid @RequestBody ${entityName} ${entityPropertyName}) { + ${entityPropertyName}Repository.save(${entityPropertyName}); } /** *

- * Delete ${entity} by id + * Delete ${entityName} by id *

* @param id id */ @DeleteMapping("/{id}") - public void delete(@PathVariable("id") ${idField.propertyType} id) { - ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}Service.removeById(id); + public void delete(@Valid @PathVariable("id") ${idField.columnType.type} id) { + ${entityPropertyName}Repository.deleteById(id); } } diff --git a/src/main/resources/templates/mybatis-plus/${entityName}DTO.java.vm b/src/main/resources/templates/jpa/${entityName}DTO.java.vm similarity index 100% rename from src/main/resources/templates/mybatis-plus/${entityName}DTO.java.vm rename to src/main/resources/templates/jpa/${entityName}DTO.java.vm diff --git a/src/main/resources/templates/jpa/${entityName}Repository.java.vm b/src/main/resources/templates/jpa/${entityName}Repository.java.vm new file mode 100644 index 0000000..ce0bf42 --- /dev/null +++ b/src/main/resources/templates/jpa/${entityName}Repository.java.vm @@ -0,0 +1,17 @@ +package ${package}; + +import org.springframework.stereotype.Repository; + +/** + *

+ * $!{table.comment} ${entityName} Repository + *

+ * + * @author ${author} + * @since ${date} + */ +@Repository +public interface ${entityName}Repository { + +} + diff --git a/src/main/resources/templates/jpa/${entityName}Service.java.vm b/src/main/resources/templates/jpa/${entityName}Service.java.vm new file mode 100644 index 0000000..b51f9f1 --- /dev/null +++ b/src/main/resources/templates/jpa/${entityName}Service.java.vm @@ -0,0 +1,25 @@ +package ${package}; + +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +/** + *

+ * $!{table.comment} ${entityName} Service + * Add @Transactional(rollbackFor = Exception.class) if you need. + *

+ * + * @author ${author} + * @since ${date} + */ +@Slf4j +@AllArgsConstructor +@Service +public class ${entityName}Service { + + private ${entityName}Repository ${entityPropertyName}Repository; + +} + diff --git a/src/main/resources/templates/jpa/controller.java.btl b/src/main/resources/templates/jpa/controller.java.btl deleted file mode 100644 index 03b3ff8..0000000 --- a/src/main/resources/templates/jpa/controller.java.btl +++ /dev/null @@ -1,114 +0,0 @@ -package ${parameters.outputPackage}.controller; - -<% if(entityLombokModel){ %> -import lombok.extern.slf4j.Slf4j; -<% } %> -import javax.validation.Valid; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -<% if(restControllerStyle){ %> -import org.springframework.web.bind.annotation.RestController; -<% }else{ %> -import org.springframework.stereotype.Controller; -<% } %> -<% if(isNotEmpty(superControllerClassPackage)){ %> -import ${superControllerClassPackage}; -<% } %> -import ${parameters.outputPackage}.entity.${entity}; -import ${parameters.outputPackage}.repository.${entity}Repository; -import ${parameters.outputPackage}.service.${entity}Service; -import java.util.Optional; - -/** - *

- * ${table.comment!} Controller - *

- * - * @author ${author} - * @since ${date} - */ -<% if(entityLombokModel){ %> -@Slf4j -<% } %> -@Validated -<% if(restControllerStyle){ %> -@RestController -<% }else{ %> -@Controller -<% } %> -@RequestMapping("<% if(isNotEmpty(package.ModuleName)){ %>/${package.ModuleName}<% } %>/<% if(isNotEmpty(controllerMappingHyphenStyle)){ %>${controllerMappingHyphen}<% }else{ %>${table.entityPath}<% } %>") -<% if(isNotEmpty(superControllerClass)){ %> -public class ${entity}Controller extends ${superControllerClass} { -<% }else{ %> -public class ${entity}Controller { -<% } %> - - /** - *

- * ${entity}Service - *

- */ - @Autowired - private ${entity}Service ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}Service; - - /** - *

- * ${entity}Repository - *

- */ - @Autowired - private ${entity}Repository ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}Repository; - - /** - *

- * Get ${entity} by id - *

- * @param id id - * @return ${entity} - */ - @GetMapping("/{id}") - public Optional<${entity}> getById(@PathVariable("id") ${idField.propertyType} id) { - return ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}Repository.findById(id); - } - - /** - *

- * Add ${entity} - *

- * @param ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)} ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)} - */ - @PostMapping - public void add(@Valid @RequestBody ${entity} ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}) { - ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}Repository.save(${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}); - } - - /** - *

- * Update ${entity} - *

- * @param ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)} ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)} - */ - @PutMapping - public void update(@Valid @RequestBody ${entity} ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}) { - ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}Repository.save(${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}); - } - - /** - *

- * Delete ${entity} by id - *

- * @param id id - */ - @DeleteMapping("/{id}") - public void delete(@Valid @PathVariable("id") ${idField.propertyType} id) { - ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}Repository.deleteById(id); - } - -} diff --git a/src/main/resources/templates/jpa/entity.java.btl b/src/main/resources/templates/jpa/entity.java.btl deleted file mode 100644 index 5748441..0000000 --- a/src/main/resources/templates/jpa/entity.java.btl +++ /dev/null @@ -1,117 +0,0 @@ -package ${parameters.outputPackage}.entity; - -<% if(hasLongField){ %> -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; -<% } %> -<% if(isNotEmpty(superEntityClassPackage)){ %> -import ${superEntityClassPackage}; -<% }else{ %> -import java.io.Serializable; -<% } %> -<% if(entityLombokModel){ %> -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.experimental.Accessors; -<% } %> -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Table; - -/** - *

- * ${table.comment!} - *

- * - * @author ${author} - * @since ${date} - */ -<% if(entityLombokModel){ %> -@Data - <% if(isNotEmpty(superEntityClass)){ %> -@EqualsAndHashCode(callSuper = true) - <% }else{ %> -@EqualsAndHashCode(callSuper = false) - <% } %> -@Accessors(chain = true) -<% } %> -@Entity -@Table(name = "${table.name}") -<% if(isNotEmpty(superEntityClass)){ %> -public class ${entity} extends ${superEntityClass} { -<% }else{ %> -public class ${entity} implements Serializable { -<% } %> - -<% if(entitySerialVersionUID){ %> - private static final long serialVersionUID = 1L; -<% } %> -<% /** -----------BEGIN 字段循环遍历----------- **/ %> -<% for(field in table.fields){ %> - - <% if(isNotEmpty(field.comment)){ %> - /** - * ${field.comment!} - */ - <% } %> - <% if(field.keyFlag){ %> - @Id - <% } else { %> - @Column(name= "${field.name}") - <% } %> - <% if('Long' == field.propertyType){ %> - @JsonSerialize(using = ToStringSerializer.class) - <% } %> - private ${field.propertyType} ${field.propertyName}; -<% } %> -<% /** -----------END 字段循环遍历----------- **/ %> - -<% if(!entityLombokModel){ %> - <% for(field in table.fields){ %> - <% - var getprefix =''; - if(field.propertyType=='boolean'){ - getprefix='is'; - }else{ - getprefix='get'; - } - %> - public ${field.propertyType} ${getprefix}${field.capitalName}() { - return ${field.propertyName}; - } - - <% if(entityBuilderModel){ %> - public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) { - <% }else{ %> - public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) { - <% } %> - this.${field.propertyName} = ${field.propertyName}; - <% if(entityBuilderModel){ %> - return this; - <% } %> - } - - <% } %> -<% } %> -<% if(entityColumnConstant){ %> - <% for(field in table.fields){ %> - public static final String ${strutil.toUpperCase(field.name)} = "${field.name}"; - - <% } %> -<% } %> -<% if(!entityLombokModel){ %> - @Override - public String toString() { - return "${entity}{" + - <% for(field in table.fields){ %> - <% if(fieldLP.index==0){ %> - "${field.propertyName}=" + ${field.propertyName} + - <% }else{ %> - ", ${field.propertyName}=" + ${field.propertyName} + - <% } %> - <% } %> - "}"; - } -<% } %> -} diff --git a/src/main/resources/templates/jpa/repository.java.btl b/src/main/resources/templates/jpa/repository.java.btl deleted file mode 100644 index 2ab5316..0000000 --- a/src/main/resources/templates/jpa/repository.java.btl +++ /dev/null @@ -1,21 +0,0 @@ -package ${parameters.outputPackage}.repository; - -import ${parameters.outputPackage}.entity.${entity}; -<% if(isNotEmpty(superMapperClassPackage)){ %> -import ${superMapperClassPackage}; -<% } %> -import org.springframework.stereotype.Repository; -import java.io.Serializable; - -/** - *

- * ${entity} Repository Interface - *

- * - * @author ${author} - * @since ${date} - */ -@Repository -public interface ${entity}Repository extends ${superMapperClass}<${entity}, Serializable> { - -} diff --git a/src/main/resources/templates/jpa/service.java.btl b/src/main/resources/templates/jpa/service.java.btl deleted file mode 100644 index 9047d83..0000000 --- a/src/main/resources/templates/jpa/service.java.btl +++ /dev/null @@ -1,33 +0,0 @@ -package ${parameters.outputPackage}.service; - -<% if(entityLombokModel){ %> -import lombok.extern.slf4j.Slf4j; -<% } %> -import ${parameters.outputPackage}.entity.${entity}; -import ${parameters.outputPackage}.repository.${entity}Repository; -import ${parameters.outputPackage}.service.${entity}Service; -<% if(isNotEmpty(superServiceImplClassPackage)){ %> -import ${superServiceImplClassPackage}; -<% } %> -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -/** - *

- * ${table.comment!} implement - * Add @Transactional(rollbackFor = Exception.class) if you need. - *

- * - * @author ${author} - * @since ${date} - */ -<% if(entityLombokModel){ %> -@Slf4j -<% } %> -@Service -public class ${entity}Service { - - @Autowired - private ${entity}Repository ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}Repository; - -} diff --git a/src/main/resources/templates/mybatis/${entityName}.java.vm b/src/main/resources/templates/mybatis/${entityName}.java.vm new file mode 100644 index 0000000..db2a01f --- /dev/null +++ b/src/main/resources/templates/mybatis/${entityName}.java.vm @@ -0,0 +1,43 @@ +package ${package}; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +#if("$!{baseEntity}" != "") +import ${baseEntity}; +#else +import java.io.Serializable; +#end + +/** + *

+ * $!{table.comment} + *

+ * + * @author ${author} + * @since ${date} + */ +@Data +#if("$!{baseEntity}" != "") +@EqualsAndHashCode(callSuper = true) +#else +@EqualsAndHashCode(callSuper = false) +#end +@Accessors(chain = true) +#if("$!{baseEntityName}" != "") +public class ${entityName} extends ${baseEntityName} { +#else +public class ${entityName} implements Serializable { + + private static final long serialVersionUID = 1L; +#end +#foreach($field in ${entityFields}) + +#if("$!field.comment" != "") + /** + * ${field.comment} + */ +#end + private ${field.propertyType} ${field.propertyName}; +#end +} diff --git a/src/main/resources/templates/mybatis/${entityName}Controller.java.vm b/src/main/resources/templates/mybatis/${entityName}Controller.java.vm new file mode 100644 index 0000000..0f15a2d --- /dev/null +++ b/src/main/resources/templates/mybatis/${entityName}Controller.java.vm @@ -0,0 +1,98 @@ +package ${package}; + +import com.github.mengweijin.vitality.framework.domain.R; +import jakarta.validation.Valid; +import java.util.Arrays; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.RequestParam; + +/** + *

+ * $!{table.comment} ${entityName} Controller + *

+ * + * @author ${author} + * @since ${date} + */ +@Slf4j +@AllArgsConstructor +@Validated +@RestController +@RequestMapping("${requestMapping}") +public class ${entityName}Controller { + + private ${entityName}Service ${entityPropertyName}Service; + + /** + *

+ * Get ${entityName} list by ${entityName} + *

+ * @param ${entityPropertyName} {@link ${entityName}} + * @return List<${entityName}> + */ + @GetMapping("/list") + public List<${entityName}> list(${entityName} ${entityPropertyName}) { + return ${entityPropertyName}Service.list(${entityPropertyName}); + } + + /** + *

+ * Get ${entityName} by id + *

+ * @param id id + * @return ${entityName} + */ + @GetMapping("/{id}") + public ${entityName} getById(@PathVariable("id") ${idField.columnType.type} id) { + return ${entityPropertyName}Service.getById(id); + } + + /** + *

+ * Add ${entityName} + *

+ * @param ${entityPropertyName} {@link ${entityName}} + */ + @PostMapping + public R add(@Valid @RequestBody ${entityName} ${entityPropertyName}) { + int i = ${entityPropertyName}Service.save(${entityPropertyName}); + return R.ajax(i); + } + + /** + *

+ * Update ${entityName} + *

+ * @param ${entityPropertyName} {@link ${entityName}} + */ + @PutMapping + public R update(@Valid @RequestBody ${entityName} ${entityPropertyName}) { + int i = ${entityPropertyName}Service.updateById(${entityPropertyName}); + return R.ajax(i); + } + + /** + *

+ * Delete ${entityName} by id(s), Multiple ids can be separated by commas ",". + *

+ * @param ids id + */ + @DeleteMapping("/{ids}") + public R delete(@PathVariable("ids") ${idField.columnType.type}[] ids) { + int i = ${entityPropertyName}Service.deleteBatchIds(Arrays.asList(ids)); + return R.ajax(i); + } + +} + diff --git a/src/main/resources/templates/mybatis/${entityName}DTO.java.vm b/src/main/resources/templates/mybatis/${entityName}DTO.java.vm new file mode 100644 index 0000000..145c510 --- /dev/null +++ b/src/main/resources/templates/mybatis/${entityName}DTO.java.vm @@ -0,0 +1,25 @@ +package ${package}; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import java.io.Serial; +import java.io.Serializable; + +/** + *

+ * ${entityName} DTO + *

+ * + * @author ${author} + * @since ${date} + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Accessors(chain = true) +public class ${entityName}DTO extends ${entityName} implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + +} diff --git a/src/main/resources/templates/mybatis/${entityName}Mapper.java.vm b/src/main/resources/templates/mybatis/${entityName}Mapper.java.vm new file mode 100644 index 0000000..44e626b --- /dev/null +++ b/src/main/resources/templates/mybatis/${entityName}Mapper.java.vm @@ -0,0 +1,67 @@ +package ${package}; + +import java.util.List; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + *

+ * $!{table.comment} ${entityName} Mapper + *

+ * + * @author ${author} + * @since ${date} + */ +@Mapper +public interface ${entityName}Mapper { + + /** + * select ${entityName} by id + * + * @param id id + * @return ${entityName} + */ + ${entityName} getById(${idField.columnType.type} id); + + /** + * select ${entityName} List + * + * @param ${entityPropertyName} {@link ${entityName}} + * @return ${entityName} + */ + List<${entityName}> selectList(${entityName} ${entityPropertyName}); + + /** + * insert ${entityName} + * + * @param ${entityPropertyName} {@link ${entityName}} + * @return int + */ + int save(${entityName} ${entityPropertyName}); + + /** + * update ${entityName} + * + * @param ${entityPropertyName} {@link ${entityName}} + * @return int + */ + int updateById(${entityName} ${entityPropertyName}); + + /** + * delete ${entityName} by id + * + * @param id id + * @return int + */ + int deleteById(${idField.columnType.type} id); + + /** + * delete batch by ids + * + * @param ids ids + * @return int + */ + int deleteBatchIds(List<${idField.columnType.type}> ids); + +} + diff --git a/src/main/resources/templates/mybatis/${entityName}Mapper.xml.vm b/src/main/resources/templates/mybatis/${entityName}Mapper.xml.vm new file mode 100644 index 0000000..f24abe6 --- /dev/null +++ b/src/main/resources/templates/mybatis/${entityName}Mapper.xml.vm @@ -0,0 +1,88 @@ + + + + + + +## id +#foreach($field in ${allFields}) + #if(${field.keyFlag}) + + #end +#end +##生成其他字段 +#foreach($field in ${allFields}) + #if(!${field.keyFlag}) + + #end +#end + + + + +#foreach($field in ${allFields}) + ${field.columnName}#if($foreach.hasNext),#end +#end + + + + + +#foreach($field in ${allFields}) + + AND ${field.name} = #{${field.propertyName}} + +#end + + + + + + + + + insert into ${table.name} ( + #foreach($field in ${allFields}) + + ${field.name}#if($foreach.hasNext),#end + + #end + ) VALUES ( + #foreach($field in ${allFields}) + + ${field.propertyName}#if($foreach.hasNext),#end + + #end + ) + + + + UPDATE ${table.name} + + #foreach($field in ${allFields}) + + ${field.name} = #{${field.propertyName}}#if($foreach.hasNext),#end + + #end + + + ${idField.name} = #{${idField.propertyName}} + + + + + DELETE FROM ${table.name} WHERE ${idField.name} = #{${idField.propertyName}} + + + + DELETE FROM ${table.name} WHERE ${idField.columnName} IN + + #{id} + + + + diff --git a/src/main/resources/templates/mybatis/${entityName}Service.java.vm b/src/main/resources/templates/mybatis/${entityName}Service.java.vm new file mode 100644 index 0000000..7d74d68 --- /dev/null +++ b/src/main/resources/templates/mybatis/${entityName}Service.java.vm @@ -0,0 +1,85 @@ +package ${package}; + +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + *

+ * $!{table.comment} ${entityName} Service + * Add @Transactional(rollbackFor = Exception.class) if you need. + *

+ * + * @author ${author} + * @since ${date} + */ +@Slf4j +@AllArgsConstructor +@Service +public class ${entityName}Service { + + private ${entityName}Mapper ${entityPropertyName}Mapper; + + /** + *

+ * Select ${entityName} by id + *

+ * @param id id + */ + public ${entityName} getById(${idField.columnType.type} id) { + return ${entityPropertyName}Mapper.getById(id); + } + + /** + *

+ * Select List<${entityName}> + *

+ * @param ${entityPropertyName} {@link ${entityName}} + */ + public List<${entityName}> list(${entityName} ${entityPropertyName}) { + return ${entityPropertyName}Mapper.selectList(${entityPropertyName}); + } + + /** + *

+ * Add ${entityName} + *

+ * @param ${entityPropertyName} {@link ${entityName}} + */ + public int save(${entityName} ${entityPropertyName}) { + return ${entityPropertyName}Mapper.save(${entityPropertyName}); + } + + /** + *

+ * Update ${entityName} + *

+ * @param ${entityPropertyName} {@link ${entityName}} + */ + public int updateById(${entityName} ${entityPropertyName}) { + return ${entityPropertyName}Mapper.updateById(${entityPropertyName}); + } + + /** + *

+ * Delete ${entityName} by id + *

+ * @param id id + */ + public int deleteById(${idField.columnType.type} id) { + return ${entityPropertyName}Mapper.deleteById(id); + } + + /** + *

+ * Delete ${entityName} batch by ids + *

+ * @param ids id + */ + public int deleteBatchIds(List<${idField.columnType.type}> ids) { + return ${entityPropertyName}Mapper.deleteBatchIds(ids); + } +} + diff --git a/src/main/resources/templates/mybatis/entity.java.btl b/src/main/resources/templates/mybatis/entity.java.btl deleted file mode 100644 index a91253d..0000000 --- a/src/main/resources/templates/mybatis/entity.java.btl +++ /dev/null @@ -1,106 +0,0 @@ -package ${parameters.outputPackage}.entity; - -<% if(hasLongField){ %> -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; -<% } %> -<% if(isNotEmpty(superEntityClassPackage)){ %> -import ${superEntityClassPackage}; -<% }else{ %> -import java.io.Serializable; -<% } %> -<% if(entityLombokModel){ %> -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.experimental.Accessors; -<% } %> - -/** - *

- * ${table.comment!} - *

- * - * @author ${author} - * @since ${date} - */ -<% if(entityLombokModel){ %> -@Data - <% if(isNotEmpty(superEntityClass)){ %> -@EqualsAndHashCode(callSuper = true) - <% }else{ %> -@EqualsAndHashCode(callSuper = false) - <% } %> -@Accessors(chain = true) -<% } %> -<% if(isNotEmpty(superEntityClass)){ %> -public class ${entity} extends ${superEntityClass} { -<% }else{ %> -public class ${entity} implements Serializable { -<% } %> - -<% if(entitySerialVersionUID){ %> - private static final long serialVersionUID = 1L; -<% } %> -<% /** -----------BEGIN 字段循环遍历----------- **/ %> -<% for(field in table.fields){ %> - - <% if(isNotEmpty(field.comment)){ %> - /** - * ${field.comment!} - */ - <% } %> - <% if('Long' == field.propertyType){ %> - @JsonSerialize(using = ToStringSerializer.class) - <% } %> - private ${field.propertyType} ${field.propertyName}; -<% } %> -<% /** -----------END 字段循环遍历----------- **/ %> - -<% if(!entityLombokModel){ %> - <% for(field in table.fields){ %> - <% - var getprefix =''; - if(field.propertyType=='boolean'){ - getprefix='is'; - }else{ - getprefix='get'; - } - %> - public ${field.propertyType} ${getprefix}${field.capitalName}() { - return ${field.propertyName}; - } - - <% if(entityBuilderModel){ %> - public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) { - <% }else{ %> - public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) { - <% } %> - this.${field.propertyName} = ${field.propertyName}; - <% if(entityBuilderModel){ %> - return this; - <% } %> - } - - <% } %> -<% } %> -<% if(entityColumnConstant){ %> - <% for(field in table.fields){ %> - public static final String ${strutil.toUpperCase(field.name)} = "${field.name}"; - - <% } %> -<% } %> -<% if(!entityLombokModel){ %> - @Override - public String toString() { - return "${entity}{" + - <% for(field in table.fields){ %> - <% if(fieldLP.index==0){ %> - "${field.propertyName}=" + ${field.propertyName} + - <% }else{ %> - ", ${field.propertyName}=" + ${field.propertyName} + - <% } %> - <% } %> - "}"; - } -<% } %> -} diff --git a/src/main/resources/templates/mybatis/mapper.java.btl b/src/main/resources/templates/mybatis/mapper.java.btl deleted file mode 100644 index dff2ac4..0000000 --- a/src/main/resources/templates/mybatis/mapper.java.btl +++ /dev/null @@ -1,67 +0,0 @@ -package ${parameters.outputPackage}.mapper; - -import ${parameters.outputPackage}.entity.${entity}; -import org.apache.ibatis.annotations.Mapper; -import java.util.List; - -/** - *

- * ${table.comment!} Mapper Interface - *

- * - * @author ${author} - * @since ${date} - */ -@Mapper -public interface ${entity}Mapper { - - /** - * select ${entity} by id - * - * @param id id - * @return ${entity} - */ - ${entity} getById(${idField.propertyType} id); - - /** - * select ${entity} List - * - * @param ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)} ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)} - * @return ${entity} - */ - List<${entity}> selectList(${entity} ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}); - - /** - * insert ${entity} - * - * @param ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)} ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)} - * @return int - */ - int save(${entity} ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}); - - /** - * update ${entity} - * - * @param ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)} ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)} - * @return int - */ - int updateById(${entity} ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}); - - /** - * delete ${entity} by id - * - * @param id id - * @return int - */ - int removeById(${idField.propertyType} id); - - /** - * delete batch by ids - * - * @param ids ids - * @return int - */ - int deleteBatchByIds(${idField.propertyType}[] ids); - -} - diff --git a/src/main/resources/templates/mybatis/mapper.xml.btl b/src/main/resources/templates/mybatis/mapper.xml.btl deleted file mode 100644 index 908bcb6..0000000 --- a/src/main/resources/templates/mybatis/mapper.xml.btl +++ /dev/null @@ -1,116 +0,0 @@ - - - - -<% if(enableCache){ %> - - - -<% } %> -<% if(baseResultMap){ %> - - -<% for(field in table.fields){ %> - <% /** 生成主键排在第一位 **/ %> - <% if(field.keyFlag){ %> - - <% } %> -<% } %> -<% for(field in table.commonFields){ %> - <% /** 生成公共字段 **/ %> - -<% } %> -<% for(field in table.fields){ %> - <% /** 生成普通字段 **/ %> - <% if(!field.keyFlag){ %> - - <% } %> -<% } %> - -<% } %> -<% if(baseColumnList){ %> - - -<% for(field in allFieldList){ %> - <% if(!fieldLP.last){ %> - ${field.name}, - <% } %> - <% if(fieldLP.last){ %> - ${field.name} - <% } %> -<% } %> - - - - - - <% for(field in allFieldList){ %> - and ${field.propertyName} != ''<%}%>"> - and ${field.name} = #{${field.propertyName}} - - <% } %> - - - -<% } %> - - - - - - insert into ${table.name} ( - <% for(field in allFieldList){ %> - and ${field.propertyName} != ''<%}%>"> - ${field.name}<% if(!fieldLP.last){ %>,<%}%> - - <% } %> - )values( - <% for(field in allFieldList){ %> - and ${field.propertyName} != ''<%}%>"> - #{${field.propertyName}}<% if(!fieldLP.last){ %>,<%}%> - - <% } %> - ) - - - - update ${table.name} - - <% for(field in allFieldList){ %> - and ${field.propertyName} != ''<%}%>"> - ${field.name} = #{${field.propertyName}}<% if(!fieldLP.last){ %>,<%}%> - - <% } %> - - - ${idField.columnName} = #{${idField.propertyName}} - - - - - delete from ${table.name} - - ${idField.columnName} = #{${idField.propertyName}} - - - - - delete from ${table.name} where ${idField.columnName} in - - #{itemId} - - - - diff --git a/src/main/resources/templates/mybatis/service.java.btl b/src/main/resources/templates/mybatis/service.java.btl deleted file mode 100644 index d74b1f7..0000000 --- a/src/main/resources/templates/mybatis/service.java.btl +++ /dev/null @@ -1,102 +0,0 @@ -package ${parameters.outputPackage}.service; - -<% if(entityLombokModel){ %> -import lombok.extern.slf4j.Slf4j; -<% } %> -import ${parameters.outputPackage}.entity.${entity}; -import ${parameters.outputPackage}.mapper.${entity}Mapper; -<% if(isNotEmpty(superServiceClassPackage)){ %> -import ${superServiceClassPackage}; -<% } %> -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import java.util.List; - -/** - *

- * ${entity} Service implement - * Add @Transactional(rollbackFor = Exception.class) if you need. - *

- * - * @author ${author} - * @since ${date} - */ -<% if(entityLombokModel){ %> -@Slf4j -<% } %> -@Service -public class ${entity}Service <% if(isNotEmpty(superServiceClass)){ %>implements ${superServiceClass}<% } %> { - - /** - *

- * ${entity}Mapper - *

- */ - @Autowired - private ${entity}Mapper ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}Mapper; - - /** - *

- * Select ${entity} by id - *

- * @param id id - */ - <% if(isNotEmpty(superServiceClass)){ %> - @Override - <% } %> - public ${entity} getById(${idField.propertyType} id) { - return ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}Mapper.getById(id); - } - - /** - *

- * Select List<${entity}> - *

- * @param ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)} ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)} - */ - <% if(isNotEmpty(superServiceClass)){ %> - @Override - <% } %> - public List<${entity}> list(${entity} ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}) { - return ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}Mapper.selectList(${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}); - } - - /** - *

- * Add ${entity} - *

- * @param ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)} ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)} - */ - <% if(isNotEmpty(superServiceClass)){ %> - @Override - <% } %> - public void save(${entity} ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}) { - ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}Mapper.save(${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}); - } - - /** - *

- * Update ${entity} - *

- * @param ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)} ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)} - */ - <% if(isNotEmpty(superServiceClass)){ %> - @Override - <% } %> - public void updateById(${entity} ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}) { - ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}Mapper.updateById(${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}); - } - - /** - *

- * Delete ${entity} by id - *

- * @param id id - */ - <% if(isNotEmpty(superServiceClass)){ %> - @Override - <% } %> - public void removeById(${idField.propertyType} id) { - ${@cn.hutool.core.util.StrUtil.lowerFirst(entity)}Mapper.removeById(id); - } -} diff --git a/src/main/resources/templates/mybatis-plus/${entityName}.java.vm b/src/main/resources/templates/mybatisplus/${entityName}.java.vm similarity index 94% rename from src/main/resources/templates/mybatis-plus/${entityName}.java.vm rename to src/main/resources/templates/mybatisplus/${entityName}.java.vm index fe70675..f6f2a99 100644 --- a/src/main/resources/templates/mybatis-plus/${entityName}.java.vm +++ b/src/main/resources/templates/mybatisplus/${entityName}.java.vm @@ -36,9 +36,6 @@ public class ${entityName} implements Serializable { #end #foreach($field in ${entityFields}) -#if(${field.keyFlag}) -#set($keyPropertyName=${field.propertyName}) -#end #if("$!field.comment" != "") /** * ${field.comment} diff --git a/src/main/resources/templates/mybatis-plus/${entityName}Controller.java.vm b/src/main/resources/templates/mybatisplus/${entityName}Controller.java.vm similarity index 70% rename from src/main/resources/templates/mybatis-plus/${entityName}Controller.java.vm rename to src/main/resources/templates/mybatisplus/${entityName}Controller.java.vm index 94637c2..dc6a1ae 100644 --- a/src/main/resources/templates/mybatis-plus/${entityName}Controller.java.vm +++ b/src/main/resources/templates/mybatisplus/${entityName}Controller.java.vm @@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.github.mengweijin.vitality.framework.domain.R; import jakarta.validation.Valid; +import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; @@ -26,12 +27,12 @@ import org.springframework.web.bind.annotation.RequestParam; * @since ${date} */ @Slf4j +@AllArgsConstructor @Validated @RestController @RequestMapping("${requestMapping}") public class ${entityName}Controller { - @Autowired private ${entityName}Service ${entityPropertyName}Service; /** @@ -39,12 +40,12 @@ public class ${entityName}Controller { * Get ${entityName} page by ${entityName} *

* @param page page - * @param entity ${entityPropertyName} + * @param ${entityPropertyName} {@link ${entityName}} * @return Page<${entityName}> */ @GetMapping("/page") - public Page<${entityName}> page(Page<${entityName}> page, ${entityName} entity) { - return ${entityPropertyName}Service.page(page, new QueryWrapper<>(entity)); + public Page<${entityName}> page(Page<${entityName}> page, ${entityName} ${entityPropertyName}) { + return ${entityPropertyName}Service.page(page, new QueryWrapper<>(${entityPropertyName})); } /** @@ -55,7 +56,7 @@ public class ${entityName}Controller { * @return ${entityName} */ @GetMapping("/{id}") - public ${entityName} getById(@PathVariable("id") Long id) { + public ${entityName} getById(@PathVariable("id") ${idField.columnType.type} id) { return ${entityPropertyName}Service.getById(id); } @@ -63,11 +64,11 @@ public class ${entityName}Controller { *

* Add ${entityName} *

- * @param entity ${entityName} + * @param ${entityPropertyName} {@link ${entityName}} */ @PostMapping - public R add(@Valid @RequestBody ${entityName} entity) { - boolean bool = ${entityPropertyName}Service.save(entity); + public R add(@Valid @RequestBody ${entityName} ${entityPropertyName}) { + boolean bool = ${entityPropertyName}Service.save(${entityPropertyName}); return R.bool(bool); } @@ -75,11 +76,11 @@ public class ${entityName}Controller { *

* Update ${entityName} *

- * @param entity ${entityName} + * @param ${entityPropertyName} {@link ${entityName}} */ @PutMapping - public R update(@Valid @RequestBody ${entityName} entity) { - boolean bool = ${entityPropertyName}Service.updateById(entity); + public R update(@Valid @RequestBody ${entityName} ${entityPropertyName}) { + boolean bool = ${entityPropertyName}Service.updateById(${entityPropertyName}); return R.bool(bool); } @@ -90,8 +91,8 @@ public class ${entityName}Controller { * @param ids id */ @DeleteMapping("/{ids}") - public R delete(@PathVariable("ids") Long[] ids) { - int i = vtlUserService.getBaseMapper().deleteBatchIds(Arrays.asList(ids)); + public R delete(@PathVariable("ids") ${idField.columnType.type}[] ids) { + int i = ${entityPropertyName}Service.getBaseMapper().deleteBatchIds(Arrays.asList(ids)); return R.ajax(i); } diff --git a/src/main/resources/templates/mybatisplus/${entityName}DTO.java.vm b/src/main/resources/templates/mybatisplus/${entityName}DTO.java.vm new file mode 100644 index 0000000..145c510 --- /dev/null +++ b/src/main/resources/templates/mybatisplus/${entityName}DTO.java.vm @@ -0,0 +1,25 @@ +package ${package}; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import java.io.Serial; +import java.io.Serializable; + +/** + *

+ * ${entityName} DTO + *

+ * + * @author ${author} + * @since ${date} + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Accessors(chain = true) +public class ${entityName}DTO extends ${entityName} implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + +} diff --git a/src/main/resources/templates/mybatis-plus/${entityName}Mapper.java.vm b/src/main/resources/templates/mybatisplus/${entityName}Mapper.java.vm similarity index 80% rename from src/main/resources/templates/mybatis-plus/${entityName}Mapper.java.vm rename to src/main/resources/templates/mybatisplus/${entityName}Mapper.java.vm index 32fe5be..47131e0 100644 --- a/src/main/resources/templates/mybatis-plus/${entityName}Mapper.java.vm +++ b/src/main/resources/templates/mybatisplus/${entityName}Mapper.java.vm @@ -17,12 +17,12 @@ import org.apache.ibatis.annotations.Param; public interface ${entityName}Mapper extends BaseMapper<${entityName}> { /** - * 自定义分页查询 + * Custom paging query * @param page page - * @param entity {@link ${entityName}} + * @param ${entityPropertyName} {@link ${entityName}} * @return IPage */ - IPage<${entityName}> page(IPage<${entityName}> page, @Param("p") ${entityName} entity); + IPage<${entityName}> page(IPage<${entityName}> page, @Param("p") ${entityName} ${entityPropertyName}); } diff --git a/src/main/resources/templates/mybatis-plus/${entityName}Mapper.xml.vm b/src/main/resources/templates/mybatisplus/${entityName}Mapper.xml.vm similarity index 82% rename from src/main/resources/templates/mybatis-plus/${entityName}Mapper.xml.vm rename to src/main/resources/templates/mybatisplus/${entityName}Mapper.xml.vm index 99cf9c3..645cfe9 100644 --- a/src/main/resources/templates/mybatis-plus/${entityName}Mapper.xml.vm +++ b/src/main/resources/templates/mybatisplus/${entityName}Mapper.xml.vm @@ -21,7 +21,7 @@ #foreach($field in ${allFields}) - ${field.columnName}#if($foreach.hasNext),#end + ${field.name}#if($foreach.hasNext),#end #end @@ -29,8 +29,8 @@ #foreach($field in ${allFields}) - - and ${field.name} = #{${field.propertyName}} + + AND ${field.name} = #{${field.propertyName}} #end diff --git a/src/main/resources/templates/mybatis-plus/${entityName}Service.java.vm b/src/main/resources/templates/mybatisplus/${entityName}Service.java.vm similarity index 78% rename from src/main/resources/templates/mybatis-plus/${entityName}Service.java.vm rename to src/main/resources/templates/mybatisplus/${entityName}Service.java.vm index 37f91d9..28fffb0 100644 --- a/src/main/resources/templates/mybatis-plus/${entityName}Service.java.vm +++ b/src/main/resources/templates/mybatisplus/${entityName}Service.java.vm @@ -20,13 +20,13 @@ import org.springframework.stereotype.Service; public class ${entityName}Service extends ServiceImpl<${entityName}Mapper, ${entityName}> { /** - * Custom paging queries + * Custom paging query * @param page page - * @param entity {@link ${entityName}} + * @param ${entityPropertyName} {@link ${entityName}} * @return IPage */ - public IPage<${entityName}> page(IPage<${entityName}> page, ${entityName} entity){ - return this.getBaseMapper().page(page, entity); + public IPage<${entityName}> page(IPage<${entityName}> page, ${entityName} ${entityPropertyName}){ + return this.getBaseMapper().page(page, ${entityPropertyName}); } } -- Gitee From a6c7d1ec8116b63b17aca73344c2fe0440856e84 Mon Sep 17 00:00:00 2001 From: mengweijin <1002284406@qq.com> Date: Sat, 15 Jun 2024 16:15:58 +0800 Subject: [PATCH 3/3] template --- README.md | 173 +++++++----------- README.zh.md | 138 -------------- docs/image/code-generator-maven-plugin.png | Bin 38243 -> 52459 bytes pom.xml | 6 +- ...va => AbstractVelocityTemplateEngine.java} | 2 +- .../VelocityClasspathTemplateEngine.java | 2 +- .../VelocityFileSystemTemplateEngine.java | 2 +- .../generator/mojo/AbstractGeneratorMojo.java | 4 +- .../code/generator/mojo/ScriptMojo.java | 70 +++++++ src/main/resources/script/docker/Dockerfile | 20 ++ src/main/resources/script/linux/app.service | 16 ++ src/main/resources/script/linux/app.sh | 86 +++++++++ src/main/resources/script/windows/app.bat | 68 +++++++ src/main/resources/script/windows/start.bat | 26 +++ .../${entityName}.java.vm | 0 .../${entityName}Controller.java.vm | 0 .../${entityName}DTO.java.vm | 0 .../${entityName}Mapper.java.vm | 0 .../${entityName}Mapper.xml.vm | 0 .../${entityName}Service.java.vm | 0 20 files changed, 360 insertions(+), 253 deletions(-) delete mode 100644 README.zh.md rename src/main/java/com/github/mengweijin/code/generator/engine/{VelocityTemplateEngine.java => AbstractVelocityTemplateEngine.java} (97%) create mode 100644 src/main/java/com/github/mengweijin/code/generator/mojo/ScriptMojo.java create mode 100644 src/main/resources/script/docker/Dockerfile create mode 100644 src/main/resources/script/linux/app.service create mode 100644 src/main/resources/script/linux/app.sh create mode 100644 src/main/resources/script/windows/app.bat create mode 100644 src/main/resources/script/windows/start.bat rename src/main/resources/templates/{mybatisplus => mybatis-plus}/${entityName}.java.vm (100%) rename src/main/resources/templates/{mybatisplus => mybatis-plus}/${entityName}Controller.java.vm (100%) rename src/main/resources/templates/{mybatisplus => mybatis-plus}/${entityName}DTO.java.vm (100%) rename src/main/resources/templates/{mybatisplus => mybatis-plus}/${entityName}Mapper.java.vm (100%) rename src/main/resources/templates/{mybatisplus => mybatis-plus}/${entityName}Mapper.xml.vm (100%) rename src/main/resources/templates/{mybatisplus => mybatis-plus}/${entityName}Service.java.vm (100%) diff --git a/README.md b/README.md index 2147be2..7ad0166 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # code-generator-maven-plugin -Language: [中文](README.zh.md) +Language: [English](README.md)

@@ -20,124 +20,81 @@ Language: [中文](README.zh.md)

-## Description -code-generator-maven-plugin is based on baomidou's mybatis-plus-generator,a Maven plugin that generates code in a Maven project。Key features: -- code-generator:**MyBatis**:Generate Controller.java, Service.java, Mapper.java, mapper.xml, Entity.java under MyBatis based on database tables; -- code-generator:**MyBatis-Plus**:Generate Controller.java, Service.java, Mapper.java, mapper.xml, Entity.java under MyBatis-Plus based on database tables; -- code-generator:**JPA**:Generate Controller.java, Service.java, Repository.java, Entity.java under JPA based on database tables; -- code-generator:**Customer**:Generate CRUD code based on database tables and specify custom template locations; -- code-generator:**Dockerfile**:Generate the Dockerfile file for the current project, along with the associated scripts: DockerImageBuild.bat, DockerImageBuildRun.bat, DockerImageDelete.bat -- code-generator:**Docker-Build**:Build Docker Image based on the locally installed Docker -- code-generator:**Docker-Deploy**:Build Docker image and deploy Docker container based on locally installed Docker -- code-generator:**Docker-Delete**:Delete deployed Docker containers and Docker images based on the locally installed Docker -- Theory can be extended to any background and front database table related technology: such as: vue.js. -- The theory supports all databases that support JDBC connection: for example: DB2, DM, H2, Mariadb, MySQL, Oracle, Postgre, Sqlite, SQLServer, etc. +## 简介 +code-generator-maven-plugin 在 Maven 项目中生成代码的 Maven 插件。主要包括: +- code-generator:**code**:基于数据库表,自定义 velocity 代码模板,生成 CRUD 或前端代码; +- code-generator:**mybatis**:基于数据库表,使用插件默认 MyBatis 模板生成 CRUD 代码; +- code-generator:**mybatis-plus**:基于数据库表,使用插件默认 MyBatis-Plus 模板生成 CRUD 代码; +- code-generator:**jpa**:基于数据库表,使用插件默认 JPA 模板生成 CRUD 代码; +- code-generator:**script**:生成脚本。包括:Dockerfile, app.sh, app.bat 等脚本。 -### SpringBoot Code Generator -[generator-spring-boot-starter](https://gitee.com/mengweijin/vitality) +备注: +- code-generator:**code** 插件理论可以生成任意前后台跟数据库表有关系的代码:如:Vue, Element-UI 代码等。 +- 理论支持所有支持JDBC连接的数据库:例如:DB2, DM, H2, Mariadb, MySQL, Oracle, Postgre, Sqlite, SQLServer -## how to use? -Locate the code-generator-maven-plugin in the Intellij IDEA Maven module shown below and double-click the corresponding plug-in command. +## 如何使用? -![image](docs/image/code-generator-maven-plugin.png) - -**Notes** -* The default Java code generation is under the target/code-generator/ directory of the current project. -* The default package path is com.github.mengweijin. -* The default Dockerfile and other files are generated in the target directory of the current project. -* The Customer plug-in must be configured with templateLocation parameters. By default, the templateType parameter is beetl. - -## Generating Java Code -### 1. General Use -In the standard SpringBoot project, take Intellij IDEA, a development tool, as an example: the code-generator-maven-plugin was introduced into Maven +在标准 SpringBoot 项目,以开发工具 Intellij IDEA 为例:在 Maven 中引入 code-generator-maven-plugin 插件 ~~~~xml - com.github.mengweijin - code-generator-maven-plugin - Latest Version - - - sys_user, sys_role - com.github.mengweijin.quickboot.mybatis.BaseEntity - - + com.github.mengweijin + code-generator-maven-plugin + Latest Version -~~~~ +~~~~ + +在Intellij IDEA 的 Maven 模块中找到下面图中的 code-generator 插件,然后双击对应的插件命令即可。 + +code-generator:**code** 插件需要在 maven 运行窗口根据提示输入数据库表名称,模块名称。并且执行前需要指定用户自定义模板的位置,参数参考文章下面的表格。 + +![image](docs/image/code-generator-maven-plugin.png) + +代码生成位置:在当前工程的 target/code-generator/ 目录下。 + +至此,初步使用完成。 -### 2. Full Configuration to Use +## 全部配置使用(以 code-generator:code 插件为例) ~~~~xml - com.github.mengweijin - code-generator-maven-plugin - Latest Version - - - com.github.mengweijin - mengweijin - - root - root - jdbc:mysql://192.168.83.128:3306/mwj_cms - - sys_user, rlt_user_role - sys_, rlt_ - com.github.mengweijin.BaseEntity - true - - D:\code-generator-maven-plugin\src\main\resources\templates - beetl - - + com.github.mengweijin + code-generator-maven-plugin + Latest Version + + + com.github.mengweijin.vitality.framework.mybatis.entity.BaseEntity + generator/vue + vtl_ + mengweijin + + root + root + jdbc:mysql://localhost:3306/vitality + + + ~~~~ -## Parameter configuration instructions -| Parameter Name | Mandatory | Sample | Description | -|-----------------:|:----------|:------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| outputPackage | No | com.github.mengweijin | The package path to code generation. The default: com.github.mengweijin | -| author | No | mengweijin | Class annotation with the @Author value above. Default: take the user name of the current computer | -| dbInfo.username | No | root | The database connection information. If it is a standard SpringBoot project, can be omitted, automatically read application.yml/yaml/properties file. | -| dbInfo.password | No | root | Same as above | -| dbInfo.url | No | jdbc:mysql://192.168.83.128:3306/test | Same as above | -| tables | No | sys_user, rlt_user_role | Same as above | -| tablePrefix | No | sys_, rlt_ | The prefix of the database table name corresponding to the code to be generated. Once configured, the generated Entity class will no longer have a table prefix. For example, User, UserRole. If not configured, the generated Entity class is prefixed with a table. For example, SysUser, RltUserRole. Multiple table name prefixes are separated by commas. | -| superEntityClass | No | com.github.mengweijin.quickboot.mybatis.BaseEntity | The generated Entity class inherits the parent class. | -| lombokModel | No | true | Whether the generated Entity is Lombok enabled. Unconfigured or true: Enable Lombok mode; Set to false: If Lombok is not enabled, the generated entity contains getter/setter/toString methods. | -| templateLocation | Yes | D:\code-generator-maven-plugin\src\main\resources\templates | Customer plug-in parameter only, user-defined template location. If the user wants to write the template file that generates the code himself, he can use the Customer plug-in and configure the absolute path to the folder where the template file resides. For example, it can be placed in the resources/generator/ directory of your project code. | -| templateType | No | beetl | Customer plug-in parameter only, user-defined template type. Optional values are "beetl, velocity, freemarker" and the corresponding template suffix is "btl, vm, ftl". This parameter is optional. If this parameter is not configured, the default value is beetl, which means that the template suffix should end with .btl. | +## 参数配置说明 +| 参数名称 | 是否必填 | 配置示例 | 说明 | +|----------------:|:-----|:-------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------| +| author | 否 | mengweijin | 类注释上面@author的值。 默认:取当前电脑的用户名 | +| dbInfo.username | 否 | root | 数据库连接信息。如果是标准的SpringBoot工程,可以省略,会自动读取application.yml/yaml/properties文件。 | +| dbInfo.password | 否 | root | 同上 | +| dbInfo.url | 否 | jdbc:mysql://192.168.83.128:3306/test | 同上。 | +| tablePrefix | 否 | sys_, rlt_ | 要生成代码对应的数据库表名称的前缀。配置后,生成的entity类就不会带有表前缀了。如:User, UserRole。如果不配置,生成的entity类就会带有表前缀。如:SysUser, RltUserRole。多个表名称前缀使用逗号分隔 | +| baseEntity | 否 | com.github.mengweijin.vitality.framework.mybatis.entity.BaseEntity | 生成的entity类继承的父类 | +| templateDir | 是 | generator/vue | 仅 code-generator:code 插件参数,用户自定义模板相对于项目根目录的位置。 | -## FAQs -1. The database table exists, but no code file is generated, and the program does not report an error. - * Configure database table names to be exactly the same as table names in the database. - For example, when an H2 database creates a table with a script, the script name is written in lowercase, - but the generated table name may be in upper case, so you need to configure the upper case table name here. -2. The problem "Table \[table_name] does not exist in database!!" when generating code for a multi-module project using the H2 database. - * When the project structure is multi-module, the project structure is as follows: - ````txt - - project-parent - - h2 - - test.mv.db - - project-child - - src - - main - - java - - resources - - pom.xml(The code-generator-maven-plugin is configured here) - - pom.xml(The project-parent's pom.xml) - ```` - * As you can see, the /h2/test.mv.db file is in the root path of the entire project - * The URL configured in our program is jdbc:h2:file:./h2/test;AUTO_SERVER=TRUE;DB_CLOSE_ON_EXIT=FALSE;MODE=MYSQL - * When using the code-generator-maven-plugin, the above URL cannot be configured because the root path of the plug-in in a multi-module project is project-child and is not where the /h2/test.mv.db file is located - * At this point we can use the following two ways to manually specify: - * Use absolute path: jdbc:h2:file:C:/Source/code/gitee/quickboot/h2/test;AUTO_SERVER=TRUE;DB_CLOSE_ON_EXIT=FALSE;MODE=MYSQL - * Use relative paths (one more layer ../ symbol): jdbc:h2:file:./../h2/test;AUTO_SERVER=TRUE;DB_CLOSE_ON_EXIT=FALSE;MODE=MYSQL - * Note: Only multi-module projects need to be specified this way; individual projects are not affected. -3. How do I customize templates? - * See the template file in the src\main\resources\templates directory in the code-generator-maven-plugin project. You can also change the suffix to anything else. See the templateType parameter description. - * Note the naming of the template file. The first paragraph is part of the name of the generated file. The second paragraph is the suffix of the generated file; The third part is the suffix of template type; - * Template content parameter. Refer to the existing template file content, you can use in the end there are basic inside, can be used directly. +## 常见问题 +1. H2 数据库中表存在,但没有生成代码。 + * H2数据库默认区分表名称大小写,要么保证输入的表名称大小写完全一致,要么第一次创建 H2 数据库表时,jdbc url 参数增加 IGNORECASE=TRUE 参数。 +2. 如何自定义生成代码的模板? + * 使用 code-generator:code 插件,并在 pom.xml 中配置 templateDir 参数。 + * 模板文件参考 code-generator-maven-plugin 工程下,src\main\resources\templates 目录下的 velocity 模板 *.vm 文件。 + * 模板参数参考 ITemplateEngine.java 类中的 getObjectMap 方法或其他 .vm 中用到的参数。 -## Futures -You are welcome to suggest better ways to improve this widget. -## Contributions -You are welcome to contribute code, let more people more time to accompany the people you care about. +## 期望 +欢迎您提出更好的意见,帮助完善这个小插件. +## 贡献 +欢迎您贡献代码,让更多的人多点时间陪陪关心的人。 \ No newline at end of file diff --git a/README.zh.md b/README.zh.md deleted file mode 100644 index 50605f3..0000000 --- a/README.zh.md +++ /dev/null @@ -1,138 +0,0 @@ -# code-generator-maven-plugin - -Language: [English](README.md) - -

- - - - - - - - - - - gitee star - - - github star - -

- -## 简介 -code-generator-maven-plugin 是一个基于baomidou mybatis-plus-generator实现的,在 Maven 项目中生成代码的 Maven 插件。主要包括: -- code-generator:**MyBatis**:基于数据库表生成 Mybatis 下的 Controller.java, Service.java, Mapper.java, mapper.xml, Entity.java 层CRUD代码; -- code-generator:**MyBatis-Plus**:基于数据库表生成 Mybatis-plus 下的 Controller.java, Service.java, Mapper.java, mapper.xml, Entity.java 层CRUD代码; -- code-generator:**JPA**:基于数据库表生成 Jpa 下的 Controller.java, Service.java, Repository.java, Entity.java 层CRUD代码; -- code-generator:**Customer**:基于数据库表生成CRUD代码,可以指定自定义模板位置; -- code-generator:**Dockerfile**:生成当前项目的 Dockerfile 文件,以及相关脚本:DockerImageBuild.bat, DockerImageBuildRun.bat, DockerImageDelete.bat -- code-generator:**Docker-Build**:基于本地安装的 Docker 来构建 docker image -- code-generator:**Docker-Deploy**:基于本地安装的 Docker 来构建 docker image 并部署 docker 容器 -- code-generator:**Docker-Delete**:基于本地安装的 Docker 来删除已经部署的 docker 容器和 docker image -- 理论可以扩展任意前后台跟数据库表有关系的技术:如:Vue, Element-UI 代码等。 -- 理论支持所有支持JDBC连接的数据库:例如:DB2, DM, H2, Mariadb, MySQL, Oracle, Postgre, Sqlite, SQLServer - -## 如何使用? -在Intellij IDEA 的 Maven 模块中找到下面图中的 code-generator 插件,然后双击对应的插件命令即可。 - -![image](docs/image/code-generator-maven-plugin.png) - -**注意** -* 默认 Java 代码生成在当前工程的 target/code-generator/ 目录下。 -* 默认包路径为:com.github.mengweijin -* 默认 Dockerfile 等文件生成在当前工程的 target 目录下。 -* Customer 插件必须配置 templateLocation 参数,默认 templateType 参数值为 beetl。 - -## 生成 java 代码 -### 1. 一般使用 -在标准 SpringBoot 项目,以开发工具 Intellij IDEA 为例:在 Maven 中引入 code-generator-maven-plugin 插件 -~~~~xml - - com.github.mengweijin - code-generator-maven-plugin - Latest Version - - - sys_user, sys_role - com.github.mengweijin.quickboot.mybatis.BaseEntity - - - -~~~~ - -### 2. 全部配置使用 -~~~~xml - - com.github.mengweijin - code-generator-maven-plugin - Latest Version - - - com.github.mengweijin - mengweijin - - root - root - jdbc:mysql://192.168.83.128:3306/mwj_cms - - sys_user, rlt_user_role - sys_, rlt_ - com.github.mengweijin.BaseEntity - true - - D:\code-generator-maven-plugin\src\main\resources\templates - beetl - - - -~~~~ - -## 参数配置说明 -| 参数名称 | 是否必填 | 配置示例 | 说明 | -|-----------------:|:-----|:------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------| -| outputPackage | 否 | com.github.mengweijin | 代码生成的包路径。 默认:com.github.mengweijin | -| author | 否 | mengweijin | 类注释上面@author的值。 默认:取当前电脑的用户名 | -| dbInfo.username | 否 | root | 数据库连接信息。如果是标准的SpringBoot工程,可以省略,会自动读取application.yml/yaml/properties文件。 | -| dbInfo.password | 否 | root | 同上 | -| dbInfo.url | 否 | jdbc:mysql://192.168.83.128:3306/test | 同上。注意:如果是多模块项目使用 H2 数据库生成代码时,要注意 URL 的书写方式,详情参考**常见问题**章节 | -| tables | 否 | sys_user, rlt_user_role | 要生成代码对应的数据库表名称。如果不配置,会生成数据库中所有的表。部分数据库对表名称大小写敏感,此时需要配置的表名称跟数据库中的完全一致。多个表名称使用英文逗号分隔 | -| tablePrefix | 否 | sys_, rlt_ | 要生成代码对应的数据库表名称的前缀。配置后,生成的entity类就不会带有表前缀了。如:User, UserRole。如果不配置,生成的entity类就会带有表前缀。如:SysUser, RltUserRole。多个表名称前缀使用英文逗号分隔 | -| superEntityClass | 否 | com.github.mengweijin.quickboot.mybatis.BaseEntity | 生成的entity类继承的父类 | -| lombokModel | 否 | true | 生成的entity是否启用lombok方式。不配置或者配置为true: 启用lombok方式;配置为false: 不启用lombok方式,则生成的entity中包含getter/setter/toString方法。 | -| templateLocation | 是 | D:\code-generator-maven-plugin\src\main\resources\templates | 仅Customer插件参数,用户自定义模板位置。如果用户想自己编写生成代码的模板文件,可以用Customer插件,并配置模板文件所在的文件夹的绝对路径。比如:可以放到你工程代码的 resources/generator/ 目录下。 | -| templateType | 否 | beetl | 仅Customer插件参数,用户自定义模板类型。非必填,可选值为:"beetl, velocity, freemarker",对应的模板后缀为:"btl, vm, ftl"。不配置的话默认值为:beetl,即模板后缀应该为 .btl 结尾。 | - -## 常见问题 -1. 数据库表存在,但没有生成代码文件,程序也没有报错。 - * 配置数据库表名称(tables)一定要跟数据库中的表名称大小写完全一致。例如H2数据库用脚本创建表时的脚本中写的名称是小写,但真实生成的表名称可能是大写的,因此这里需要配置为大写的表名称。 -2. 多模块项目使用 H2 数据库生成代码时,提示“表\[table_name]在数据库中不存在!!!” 的问题。 - * 项目结构为多模块时,项目结构如下: - ````txt - - project-parent - - h2 - - test.mv.db - - project-child - - src - - main - - java - - resources - - pom.xml(code-generator-maven-plugin 在这里配置的) - - pom.xml(project-parent 的) - ```` - * 可以发现,/h2/test.mv.db 文件在整个项目的根路径下 - * 我们程序中配置的 url 为 jdbc:h2:file:./h2/test;AUTO_SERVER=TRUE;DB_CLOSE_ON_EXIT=FALSE;MODE=MYSQL - * 当使用 code-generator-maven-plugin 时,就不能配置上面的 url 了, 因为多模块项目中的插件的根路径为 project-child 的,并不是 /h2/test.mv.db 文件所在的位置 - * 此时我们可以使用以下两种方式来手动指定: - * 使用绝对路径:jdbc:h2:file:C:/Source/code/gitee/quickboot/h2/test;AUTO_SERVER=TRUE;DB_CLOSE_ON_EXIT=FALSE;MODE=MYSQL - * 使用相对路径(多加了一层 ../ 符号):jdbc:h2:file:./../h2/test;AUTO_SERVER=TRUE;DB_CLOSE_ON_EXIT=FALSE;MODE=MYSQL - * 注意:只用多模块项目才需要这样指定,单个项目不受影响。 -3. 如何自定义模板? - * 参考 code-generator-maven-plugin工程下,src\main\resources\templates 目录下的模板文件。用的 btl 后缀,当然你也可以改为其他的,参考 templateType 参数说明。 - * 注意文件命名,分为三段,第一段为生成的文件的名称的一部分;第二段为生成的文件的后缀;第三部分为模板类型后缀; - * 模板内容参数:参考已有的各个模板文件内容,你能用到底里面基本都有,直接使用即可。 - -## 期望 -欢迎您提出更好的意见,帮助完善这个小插件. -## 贡献 -欢迎您贡献代码,让更多的人多点时间陪陪关心的人。 \ No newline at end of file diff --git a/docs/image/code-generator-maven-plugin.png b/docs/image/code-generator-maven-plugin.png index 7e46b7ef6b6509f0190a6e42ff9ec6b08a838aa6..8ae7aa3572ea4758ae6f4d404ae35c4630856f42 100644 GIT binary patch literal 52459 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%0DdHmy0?64xo|r>ER)<^c(y6?B?ycp5BpqKgSwP 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

%uA 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 L22>=h?fZWKjTtz6 diff --git a/pom.xml b/pom.xml index 4b3cb37..dc08b23 100644 --- a/pom.xml +++ b/pom.xml @@ -16,10 +16,10 @@ UTF-8 1.8 - 3.5.6 - 3.5.6 + 3.5.7 + 3.5.7 - 6.0.0-M12 + 6.0.0-M13 5.7.4 2.2 2.0.49 diff --git a/src/main/java/com/github/mengweijin/code/generator/engine/VelocityTemplateEngine.java b/src/main/java/com/github/mengweijin/code/generator/engine/AbstractVelocityTemplateEngine.java similarity index 97% rename from src/main/java/com/github/mengweijin/code/generator/engine/VelocityTemplateEngine.java rename to src/main/java/com/github/mengweijin/code/generator/engine/AbstractVelocityTemplateEngine.java index 2b631c3..ea054e8 100644 --- a/src/main/java/com/github/mengweijin/code/generator/engine/VelocityTemplateEngine.java +++ b/src/main/java/com/github/mengweijin/code/generator/engine/AbstractVelocityTemplateEngine.java @@ -26,7 +26,7 @@ import java.util.Map; */ @Slf4j @Getter -public abstract class VelocityTemplateEngine implements ITemplateEngine { +public abstract class AbstractVelocityTemplateEngine implements ITemplateEngine { private VelocityEngine velocityEngine; diff --git a/src/main/java/com/github/mengweijin/code/generator/engine/VelocityClasspathTemplateEngine.java b/src/main/java/com/github/mengweijin/code/generator/engine/VelocityClasspathTemplateEngine.java index 5219b95..97d2adb 100644 --- a/src/main/java/com/github/mengweijin/code/generator/engine/VelocityClasspathTemplateEngine.java +++ b/src/main/java/com/github/mengweijin/code/generator/engine/VelocityClasspathTemplateEngine.java @@ -19,7 +19,7 @@ import java.util.jar.JarFile; /** * @author mengweijin */ -public class VelocityClasspathTemplateEngine extends VelocityTemplateEngine { +public class VelocityClasspathTemplateEngine extends AbstractVelocityTemplateEngine { /** * classpath diff --git a/src/main/java/com/github/mengweijin/code/generator/engine/VelocityFileSystemTemplateEngine.java b/src/main/java/com/github/mengweijin/code/generator/engine/VelocityFileSystemTemplateEngine.java index 2a42e56..87fae78 100644 --- a/src/main/java/com/github/mengweijin/code/generator/engine/VelocityFileSystemTemplateEngine.java +++ b/src/main/java/com/github/mengweijin/code/generator/engine/VelocityFileSystemTemplateEngine.java @@ -15,7 +15,7 @@ import java.util.stream.Collectors; /** * @author mengweijin */ -public class VelocityFileSystemTemplateEngine extends VelocityTemplateEngine { +public class VelocityFileSystemTemplateEngine extends AbstractVelocityTemplateEngine { /** * file system diff --git a/src/main/java/com/github/mengweijin/code/generator/mojo/AbstractGeneratorMojo.java b/src/main/java/com/github/mengweijin/code/generator/mojo/AbstractGeneratorMojo.java index 0505374..96d8e82 100644 --- a/src/main/java/com/github/mengweijin/code/generator/mojo/AbstractGeneratorMojo.java +++ b/src/main/java/com/github/mengweijin/code/generator/mojo/AbstractGeneratorMojo.java @@ -80,7 +80,9 @@ public abstract class AbstractGeneratorMojo extends AbstractMojo { this.initConfigTemplateDir(); - config.setPackages(project.getGroupId()); + if(StrUtil.isBlank(config.getPackages())) { + config.setPackages(project.getGroupId()); + } String baseOutputDirPath = baseDir.getAbsolutePath() + "/target/code-generator/"; String packagePath = StrUtil.replace(config.getPackages(), ".", "/"); diff --git a/src/main/java/com/github/mengweijin/code/generator/mojo/ScriptMojo.java b/src/main/java/com/github/mengweijin/code/generator/mojo/ScriptMojo.java new file mode 100644 index 0000000..890057f --- /dev/null +++ b/src/main/java/com/github/mengweijin/code/generator/mojo/ScriptMojo.java @@ -0,0 +1,70 @@ +package com.github.mengweijin.code.generator.mojo; + +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.dromara.hutool.core.io.file.FileUtil; +import org.dromara.hutool.core.io.resource.ClassPathResource; +import org.dromara.hutool.core.io.resource.JarResource; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Enumeration; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + * 说明:@Execute(phase = LifecyclePhase.COMPILE) 在运行该目标前,让maven先运行一个并行的生命周期,到指定的阶段为止。到phase执行完,才执行插件目标 + * ResolutionScope.COMPILE_PLUS_RUNTIME: + * Will add the classpath jars into list when call method project.getRuntimeClasspathElements(); + * maven scope=compile + system + provided + runtime dependencies + * + * @author mengweijin + */ +@Mojo( + name = "script", + aggregator = true, + requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME +) +public class ScriptMojo extends AbstractMojo { + + @Parameter(defaultValue = "${basedir}") + private File baseDir; + + private static final String SCRIPT_DIR = "script/"; + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + String baseOutputDirPath = baseDir.getAbsolutePath() + "/target/code-generator/"; + FileUtil.del(baseOutputDirPath); + + try { + this.outputScripts(baseOutputDirPath); + } catch (IOException e) { + getLog().error(e); + throw new RuntimeException(e); + } + } + + private void outputScripts(String baseOutputDirPath) throws IOException { + ClassPathResource classPathResource = new ClassPathResource(SCRIPT_DIR); + JarResource jarResource = new JarResource(classPathResource.getUrl()); + JarFile jarFile = jarResource.getJarFile(); + Enumeration enumeration = jarFile.entries(); + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + while (enumeration.hasMoreElements()) { + JarEntry jarEntry = enumeration.nextElement(); + String jarEntryName = jarEntry.getName(); + if(!jarEntry.isDirectory() && jarEntryName.startsWith(SCRIPT_DIR)) { + InputStream inputStream = classLoader.getResource(jarEntryName).openConnection().getInputStream(); + File fileTargetDir = FileUtil.file(baseOutputDirPath + jarEntryName); + FileUtil.mkParentDirs(fileTargetDir); + FileUtil.copy(inputStream, fileTargetDir); + } + } + } +} diff --git a/src/main/resources/script/docker/Dockerfile b/src/main/resources/script/docker/Dockerfile new file mode 100644 index 0000000..e458018 --- /dev/null +++ b/src/main/resources/script/docker/Dockerfile @@ -0,0 +1,20 @@ +#FROM findepi/graalvm:java17-native +FROM openjdk:17.0.2-oraclelinux8 + +LABEL maintainer="Meng Wei Jin" + +ARG DIR=/opt/app + +RUN mkdir -p ${DIR} + +WORKDIR ${DIR} + +ENV SERVER_PORT=8080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS="" + +EXPOSE ${SERVER_PORT} + +#ADD ./target/app-admin.jar ./app.jar + +ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \ + -XX:+HeapDumpOnOutOfMemoryError -XX:+UseZGC ${JAVA_OPTS} \ + -jar app.jar \ No newline at end of file diff --git a/src/main/resources/script/linux/app.service b/src/main/resources/script/linux/app.service new file mode 100644 index 0000000..526af39 --- /dev/null +++ b/src/main/resources/script/linux/app.service @@ -0,0 +1,16 @@ +[Unit] +Description=app-server +After=syslog.target network.target + +[Service] +Type=forking +WorkingDirectory=/opt/app/ +ExecStart=/bin/bash ./app.sh start +ExecStop=/bin/bash ./app.sh stop +ExecReload=/bin/bash ./app.sh restart +PrivateTmp=true +TimeoutStartSec=0 +KillMode=none + +[Install] +WantedBy=multi-user.target diff --git a/src/main/resources/script/linux/app.sh b/src/main/resources/script/linux/app.sh new file mode 100644 index 0000000..876d5ba --- /dev/null +++ b/src/main/resources/script/linux/app.sh @@ -0,0 +1,86 @@ +#!/bin/sh +# ./app.sh start 启动 stop 停止 restart 重启 status 状态 +AppName=app-admin.jar + +# JVM参数 +JVM_OPTS="-Dname=$AppName -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+UseZGC" +APP_HOME=`pwd` +LOG_PATH=$APP_HOME/logs/$AppName.log + +if [ "$1" = "" ]; +then + echo -e "\033[0;31m 未输入操作名 \033[0m \033[0;34m {start|stop|restart|status} \033[0m" + exit 1 +fi + +if [ "$AppName" = "" ]; +then + echo -e "\033[0;31m 未输入应用名 \033[0m" + exit 1 +fi + +function start() +{ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'` + + if [ x"$PID" != x"" ]; then + echo "$AppName is running..." + else + nohup java $JVM_OPTS -jar $AppName > /dev/null 2>&1 & + echo "Start $AppName success..." + fi +} + +function stop() +{ + echo "Stop $AppName" + + PID="" + query(){ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'` + } + + query + if [ x"$PID" != x"" ]; then + kill -TERM $PID + echo "$AppName (pid:$PID) exiting..." + while [ x"$PID" != x"" ] + do + sleep 1 + query + done + echo "$AppName exited." + else + echo "$AppName already stopped." + fi +} + +function restart() +{ + stop + sleep 2 + start +} + +function status() +{ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|wc -l` + if [ $PID != 0 ];then + echo "$AppName is running..." + else + echo "$AppName is not running..." + fi +} + +case $1 in + start) + start;; + stop) + stop;; + restart) + restart;; + status) + status;; + *) + +esac diff --git a/src/main/resources/script/windows/app.bat b/src/main/resources/script/windows/app.bat new file mode 100644 index 0000000..b4a132b --- /dev/null +++ b/src/main/resources/script/windows/app.bat @@ -0,0 +1,68 @@ +rem 使用者应根据自身平台编码自行转换 防止乱码 例如 win使用gbk编码 +@echo off + +rem jar平级目录 +set AppName=app-admin.jar + +rem JVM参数 +set JVM_OPTS="-Dname=%AppName% -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+UseZGC" + + +ECHO. + ECHO. [1] 启动%AppName% + ECHO. [2] 关闭%AppName% + ECHO. [3] 重启%AppName% + ECHO. [4] 启动状态 %AppName% + ECHO. [5] 退 出 +ECHO. + +ECHO.请输入选择项目的序号: +set /p ID= + IF "%id%"=="1" GOTO start + IF "%id%"=="2" GOTO stop + IF "%id%"=="3" GOTO restart + IF "%id%"=="4" GOTO status + IF "%id%"=="5" EXIT +PAUSE +:start + for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do ( + set pid=%%a + set image_name=%%b + ) + if defined pid ( + echo %%is running + PAUSE + ) + +start javaw %JVM_OPTS% -jar %AppName% + +echo starting…… +echo Start %AppName% success... +goto:eof + +rem 函数stop通过jps命令查找pid并结束进程 +:stop + for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do ( + set pid=%%a + set image_name=%%b + ) + if not defined pid (echo process %AppName% does not exists) else ( + echo prepare to kill %image_name% + echo start kill %pid% ... + rem 根据进程ID,kill进程 + taskkill /f /pid %pid% + ) +goto:eof +:restart + call :stop + call :start +goto:eof +:status + for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do ( + set pid=%%a + set image_name=%%b + ) + if not defined pid (echo process %AppName% is dead ) else ( + echo %image_name% is running + ) +goto:eof diff --git a/src/main/resources/script/windows/start.bat b/src/main/resources/script/windows/start.bat new file mode 100644 index 0000000..4d6d9ed --- /dev/null +++ b/src/main/resources/script/windows/start.bat @@ -0,0 +1,26 @@ +rem 使用者应根据自身平台编码自行转换 防止乱码 例如 win使用gbk编码 +@echo off +title=App-Service + +echo 启动倒计时: +timeout /T 10 /NOBREAK + +rem jar平级目录 +set AppName=app-admin.jar + +rem JVM参数 +set JVM_OPTS="-Dname=%AppName% -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+UseZGC" + +for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do ( + set pid=%%a + set image_name=%%b +) +if defined pid ( + echo %%is running + PAUSE +) + +echo starting…… +java %JVM_OPTS% -jar %AppName% +PAUSE + diff --git a/src/main/resources/templates/mybatisplus/${entityName}.java.vm b/src/main/resources/templates/mybatis-plus/${entityName}.java.vm similarity index 100% rename from src/main/resources/templates/mybatisplus/${entityName}.java.vm rename to src/main/resources/templates/mybatis-plus/${entityName}.java.vm diff --git a/src/main/resources/templates/mybatisplus/${entityName}Controller.java.vm b/src/main/resources/templates/mybatis-plus/${entityName}Controller.java.vm similarity index 100% rename from src/main/resources/templates/mybatisplus/${entityName}Controller.java.vm rename to src/main/resources/templates/mybatis-plus/${entityName}Controller.java.vm diff --git a/src/main/resources/templates/mybatisplus/${entityName}DTO.java.vm b/src/main/resources/templates/mybatis-plus/${entityName}DTO.java.vm similarity index 100% rename from src/main/resources/templates/mybatisplus/${entityName}DTO.java.vm rename to src/main/resources/templates/mybatis-plus/${entityName}DTO.java.vm diff --git a/src/main/resources/templates/mybatisplus/${entityName}Mapper.java.vm b/src/main/resources/templates/mybatis-plus/${entityName}Mapper.java.vm similarity index 100% rename from src/main/resources/templates/mybatisplus/${entityName}Mapper.java.vm rename to src/main/resources/templates/mybatis-plus/${entityName}Mapper.java.vm diff --git a/src/main/resources/templates/mybatisplus/${entityName}Mapper.xml.vm b/src/main/resources/templates/mybatis-plus/${entityName}Mapper.xml.vm similarity index 100% rename from src/main/resources/templates/mybatisplus/${entityName}Mapper.xml.vm rename to src/main/resources/templates/mybatis-plus/${entityName}Mapper.xml.vm diff --git a/src/main/resources/templates/mybatisplus/${entityName}Service.java.vm b/src/main/resources/templates/mybatis-plus/${entityName}Service.java.vm similarity index 100% rename from src/main/resources/templates/mybatisplus/${entityName}Service.java.vm rename to src/main/resources/templates/mybatis-plus/${entityName}Service.java.vm -- Gitee