This article has participated in the good article call order activity, click to see: back end, big front end double track submission, 20,000 yuan prize pool for you to challenge!

Code generators are often used in projects to generate code. Here’s how Velocity code generation works and how to write a code generator.

Velocity is introduced

Velocity is a Java-based template engine implemented based on the MVC Model. It provides a Context container (equivalent to Spring’s Model) into which we can store values in Java code. It is then fetched in the VM file using a specific syntax (equivalent to Spring page values such as freemarker, thymeleaf).

Liverpoolfc.tv: velocity.apache.org/

Maven is introduced into

<! -- https://mvnrepository.com/artifact/org.apache.velocity/velocity -->
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity</artifactId>
    <version>1.7</version>
</dependency>

Copy the code

Velocity Basic Syntax

variable

Set variable #set($foo = “hello”) value $foo

${user.name} ${user.name}

When you use VARi to retrieve a variable, the Velocity engine will print it as it is if it does not exist. When you use VARi to retrieve a variable, the Velocity engine will print it as it is if it does not exist. By use! The {} form turns a nonexistent variable into blank output. See the example ${notExist} $! {notExistEmpty}

Velocity is case sensitive.

cycle

#foreach($i in $list)
    $i
#end
Copy the code

Velocity only replaces variables, so velocity statements are written at the top to preserve the file format

Spaces before $I as above will be printed as is

conditions

#if(condition) ... dosonmething... #elseif(condition) ... dosomething... #else ... dosomething... #endCopy the code

hello world generator

  1. Initialize VelocityEngine, the template engine, and specify that the ClasspathResourceLoader is used to load VM files.

  2. Objects are stored in the Velocity container VelocityContext.

  3. So in the.vm file we can pull out these variables,

  4. Template. merge(CTX,sw)

public class HelloWorldVelocity {

  public static void main(String[] args) {
    // Initialize the template engine
    VelocityEngine velocityEngine = new VelocityEngine();
    velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
    velocityEngine.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
    velocityEngine.init();
    // Obtain the template file
    Template template = velocityEngine.getTemplate("helloVelocity.vm");
    // Set variables
    VelocityContext ctx = new VelocityContext();
    ctx.put("name"."Velocity");
    User user = new User();
    user.setName("zhang san");
    user.setPhone("18612345678");
    ctx.put("user", user);
    List list = new ArrayList();
    list.add("1");
    list.add("2");
    ctx.put("list", list);
    / / output
    StringWriter sw = new StringWriter();
    template.merge(ctx,sw);
    System.out.println(sw.toString());
  }
Copy the code

The template file helloVelocity. Vm is in the resouces directory

#set($foo = 'hello') $foo $name ${notExist} $! {notExistEmpty} $user.name ${user.name} #foreach($i in $list) $i #endCopy the code

Gitee: gitee.com/tg_seahorse…

mybatis-plus-generator

Mybatsi-plus official website documentation

Git

The generator using

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.4.1 track</version>
</dependency>
Copy the code

MyBatis-Plus supports Velocity (default), Freemarker, and Beetl template engines

<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-engine-core</artifactId>
    <version>2.3</version>
</dependency>
Copy the code

Refer to the official website to modify the generated code class

  1. The configuration information is written at the beginning of the main method, the project path, the package name, the table to be generated, the table prefix

  2. Configure the data source mysql introduction to rely on mysql-connector-java

  3. CFG. SetFileCreate File generation policy. Return true will generate files and overwrite the original files.

  4. Open swaggergc. SetSwagger2 (true); Swagger dependencies are introduced when code is used

    <dependency>
      <groupId>com.github.xiaoymin</groupId>
      <artifactId>knife4j-spring-boot-starter</artifactId>
    </dependency>
    Copy the code

Code generation class

public class CodeGenerator {

  public static void main(String[] args) {

    String projectPath = "/Users/rubble/workSpace/paw/paw-demos/paw-generator";
    String author = "Rubble";
    String packageParent = "com.paw.generator";
    String module = "system";
    // Separate multiple objects by (,)
    String tables = "sys_user";
    String tablePrefix = "sys_";

    // Code generator
    AutoGenerator mpg = new AutoGenerator();

    // Global configuration
    GlobalConfig gc = new GlobalConfig();
    gc.setOutputDir(projectPath + "/src/main/java");
    gc.setAuthor(author);
    gc.setOpen(false);
    // gc.setSwagger2(true); Entity attribute Swagger2 annotated
    mpg.setGlobalConfig(gc);

    // Data source configuration
    DataSourceConfig dsc = new DataSourceConfig();
    dsc.setUrl("jdbc:mysql://localhost:3306/ry? useUnicode=true&useSSL=false&characterEncoding=utf8");
    // dsc.setSchemaName("public");
    dsc.setDriverName("com.mysql.cj.jdbc.Driver");
    dsc.setUsername("root");
    dsc.setPassword("123456");
    mpg.setDataSource(dsc);

    / / package configuration
    PackageConfig pc = new PackageConfig();
    pc.setModuleName(module);
    pc.setParent(packageParent);
    mpg.setPackageInfo(pc);

    // Customize the configuration
    InjectionConfig cfg = new InjectionConfig() {
      @Override
      public void initMap(a) {
        // to do nothing}};// If the template engine is freemarker
// String templatePath = "/templates/mapper.xml.ftl";
    // If the templating engine is Velocity
     String templatePath = "/templates/mapper.xml.vm";

    // Customize the output configuration
    List<FileOutConfig> focList = new ArrayList<>();
    // The custom configuration will be printed first
    focList.add(new FileOutConfig(templatePath) {
      @Override
      public String outputFile(TableInfo tableInfo) {
        // Customize the output file name. If you set the prefix and suffix of the Entity, note that the XML name will change accordingly!
        return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
            + "/" + tableInfo.getEntityName() + "Mapper"+ StringPool.DOT_XML; }});/* Allow generation of template files */
        cfg.setFileCreate(new IFileCreate() {
            @Override
            public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
                // Determine whether a custom folder needs to be created
                checkDir(projectPath);
                if (fileType == FileType.MAPPER) {
                    // The mapper file has already been generated
                    return !new File(filePath).exists();
                }
                // Allow template file generation
                return true; }}); cfg.setFileOutConfigList(focList); mpg.setCfg(cfg);// Configure the template
    TemplateConfig templateConfig = new TemplateConfig();

    // Configure a custom output template
    // Specify a custom template path. Be careful not to include.ftl/.vm, which will be automatically identified based on the template engine used
    // templateConfig.setEntity("templates/entity2.java");
    // templateConfig.setService();
    // templateConfig.setController();

    templateConfig.setXml(null);
    mpg.setTemplate(templateConfig);

    // Configure the policy
    StrategyConfig strategy = new StrategyConfig();
    strategy.setNaming(NamingStrategy.underline_to_camel);
    strategy.setColumnNaming(NamingStrategy.underline_to_camel);
/ / strategy. SetSuperEntityClass (" your parent entity, there is no need not set!" );
    strategy.setEntityLombokModel(true);
    strategy.setRestControllerStyle(true);
    // Public parent
/ / strategy. SetSuperControllerClass (" your parent controller, no need not set up!" );
    // Public fields written in the parent class
    strategy.setSuperEntityColumns("id");
    strategy.setInclude(tables.split(","));
    strategy.setControllerMappingHyphenStyle(true);
    strategy.setTablePrefix(tablePrefix);
    mpg.setStrategy(strategy);
    mpg.setTemplateEngine(newVelocityTemplateEngine()); mpg.execute(); }}Copy the code

CodeGenerator can be used in everyday development based on MyBatis plus projects.

Custom Templates

  1. Extending the Control template

    You can modify the templatePath in your configuration under resources/templates, the default location for Mybatis-plus templates

    Copy controller.java.vm from git project or JAR package to add CRUD method, just write simple add, list method, can be extended, such as add paging search.

    package ${package.Controller}; import java.util.List; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.paw.generator.system.entity.User; import com.paw.generator.system.service.IUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; #if(${restControllerStyle}) import org.springframework.web.bind.annotation.RestController; #else import org.springframework.stereotype.Controller; #end #if(${superControllerClassPackage}) import ${superControllerClassPackage}; #end /** * <p> * $! </p> * * @author ${author} * @since ${date} */ #if(${restControllerStyle}) @RestController #else  @Controller #end @RequestMapping("#if(${package.ModuleName})/${package.ModuleName}#end/#if(${controllerMappingHyphenStyle})${controllerMa ppingHyphen}#else${table.entityPath}#end") #if(${kotlin}) class ${table.controllerName}#if(${superControllerClass}) : ${superControllerClass}()#end #else #if(${superControllerClass}) public class ${table.controllerName} extends ${superControllerClass} { #else public class ${table.controllerName} { #end @Autowired private ${table.serviceName} service; @GetMapping("add") public Object add(${entity} entity){ boolean saved = service.save(entity); return entity; } @GetMapping("list") public List<${entity}> list(${entity} entity){ return service.list(new QueryWrapper<>(entity)); } } #endCopy the code

    The generated code

    /** * <p> * User information table front-end controller * </p> **@author Rubble
     * @sinceThe 2021-07-07 * /
    @RestController
    @RequestMapping("/system/user")
    public class UserController {
    
        @Autowired
        private IUserService service;
    
    
        @GetMapping("add")
        public Object add(User entity){
            boolean saved = service.save(entity);
            return entity;
        }
    
        @GetMapping("list")
        public List<User> list(User entity){
            return service.list(newQueryWrapper<>(entity)); }}Copy the code
  2. Custom template Hello

    Added the template hello.java.vm to the configuration to define the output location of the file

    String helloTemplatePath = "/templates/hello.java.vm";
    focList.add(new FileOutConfig(helloTemplatePath) {
      @Override
      public String outputFile (TableInfo tableInfo) {
        return projectPath + "/src/main/java/" + packageParent.replace(".", File.separator) + File.separator + pc.getModuleName() + File.separator + "entity"
            + File.separator + "Hello"+ tableInfo.getEntityName() + StringPool.DOT_JAVA; }});Copy the code

    The simplest template

    package ${package.Entity};
    
    public class Hello${entity}{
    
    }
    Copy the code

    Perform output

    package com.paw.generator.system.entity;
    
    public class HelloUser{}Copy the code

Mybatis – plus – generator parsing

Git download the project and compile it in jdk8 and Gradle 6.3.

AutoGenerator. Default Settings for all but datasource.

/** * protected ConfigBuilder config; /** * InjectionConfig injection; /** * private DataSourceConfig dataSource; Private StrategyConfig; /** * private StrategyConfig; /** * Private PackageConfig packageInfo; /** * Private TemplateConfig template; */ private GlobalConfig GlobalConfig;Copy the code

A template engine

AbstractTemplateEngine implements file output controller, Service, Entity, and mapper.

VelocityTemplateEngine Template engine

Init () initializes VelocityEngine to specify file location, encoding, etc.

Template-merge (new VelocityContext(objectMap), writer);

Map<String, Object> objectMap = this.getObjectMap(config, tableInfo);

@Override
public @NotNull VelocityTemplateEngine init(@NotNull ConfigBuilder configBuilder) {
    if (null == velocityEngine) {
        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);
        velocityEngine = new VelocityEngine(p);
    }
    return this;
}


@Override
public void writer(@NotNull Map<String, Object> objectMap, @NotNull String templatePath, @NotNull File outputFile) throws Exception {
    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(newVelocityContext(objectMap), writer); }}Copy the code

ObjectMap generates context variables from the configuration file in the AbstractTemplateEngine class and adds them to the context

Objectmap.put is the property available in the template.

The main properties are PackageInfo, TableInfo

@NotNull
public Map<String, Object> getObjectMap(@NotNull ConfigBuilder config, @NotNull TableInfo tableInfo) {
    GlobalConfig globalConfig = config.getGlobalConfig();
    Map<String, Object> controllerData = config.getStrategyConfig().controller().renderData(tableInfo);
    Map<String, Object> objectMap = new HashMap<>(controllerData);
    Map<String, Object> mapperData = config.getStrategyConfig().mapper().renderData(tableInfo);
    objectMap.putAll(mapperData);
    Map<String, Object> serviceData = config.getStrategyConfig().service().renderData(tableInfo);
    objectMap.putAll(serviceData);
    Map<String, Object> entityData = config.getStrategyConfig().entity().renderData(tableInfo);
    objectMap.putAll(entityData);
    objectMap.put("config", config);
    objectMap.put("package", config.getPackageConfig().getPackageInfo());
    objectMap.put("author", globalConfig.getAuthor());
    objectMap.put("kotlin", globalConfig.isKotlin());
    objectMap.put("swagger", globalConfig.isSwagger());
    objectMap.put("date", globalConfig.getCommentDate());
    // schemaName exists to set concatenation. Combination table name
    String schemaName = config.getDataSourceConfig().getSchemaName();
    if (StringUtils.isNotBlank(schemaName)) {
        schemaName += ".";
        tableInfo.setConvert(true);
    } else {
        schemaName = "";
    }
    objectMap.put("schemaName", schemaName);
    objectMap.put("table", tableInfo);
    objectMap.put("entity", tableInfo.getEntityName());
    return objectMap;
}
Copy the code

The download source extension allows you to put any properties you want.

Use ${cfg.abc} in the template to add global custom configuration.

// Customize the configuration
InjectionConfig cfg = new InjectionConfig() {
  @Override
  public void initMap (a) {
    // to do nothing
    Map<String,Object> map = new HashMap<>();
    map.put("abc"."123"); setMap(map); }};Copy the code

Mybatis- Plus-Generator does a lot of work with generality, extensibility, superclasses, humps, Swagger, various database adaptors, etc. It’s a great tool, kudos to the author.

A generator in a Ruoyi framework

This article is based on a single project version of Thymeleaf. module: ruoyi-generator.

Generation tool through the background management interface for users to set, configuration information saved in the database, according to the configuration of a SET of CRUD code, is very convenient.

Generate code general configuration class GenConfig, set the package name rules, author and other global information.

GenController Preview preview code, download zip package, genCode generate code.

  1. GenTable adds the database query configuration information to VelocityContext, and the value can be set in the VM template
  2. Render the defined templatetpl.merge(context, sw)
public Map<String, String> previewCode(Long tableId)
{
    Map<String, String> dataMap = new LinkedHashMap<>();
    // Query table information
    GenTable table = genTableMapper.selectGenTableById(tableId);
    // Set master and child table information
    setSubTable(table);
    // Set primary key column information
    setPkColumn(table);
    VelocityInitializer.initVelocity();

    VelocityContext context = VelocityUtils.prepareContext(table);

    // Get the list of templates
    List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());
    for (String template : templates)
    {
        // Render the template
        StringWriter sw = new StringWriter();
        Template tpl = Velocity.getTemplate(template, Constants.UTF8);
        tpl.merge(context, sw);
        dataMap.put(template, sw.toString());
    }
    return dataMap;
}
Copy the code

Variables Put into VelocityContext

public static VelocityContext prepareContext(GenTable genTable)
{
    String moduleName = genTable.getModuleName();
    String businessName = genTable.getBusinessName();
    String packageName = genTable.getPackageName();
    String tplCategory = genTable.getTplCategory();
    String functionName = genTable.getFunctionName();

    VelocityContext velocityContext = new VelocityContext();
    velocityContext.put("tplCategory", genTable.getTplCategory());
    velocityContext.put("tableName", genTable.getTableName());
    velocityContext.put("functionName", StringUtils.isNotEmpty(functionName) ? functionName : "[Please fill in the function name]");
    velocityContext.put("ClassName", genTable.getClassName());
    velocityContext.put("className", StringUtils.uncapitalize(genTable.getClassName()));
    velocityContext.put("moduleName", genTable.getModuleName());
    velocityContext.put("businessName", genTable.getBusinessName());
    velocityContext.put("basePackage", getPackagePrefix(packageName));
    velocityContext.put("packageName", packageName);
    velocityContext.put("author", genTable.getFunctionAuthor());
    velocityContext.put("datetime", DateUtils.getDate());
    velocityContext.put("pkColumn", genTable.getPkColumn());
    velocityContext.put("importList", getImportList(genTable));
    velocityContext.put("permissionPrefix", getPermissionPrefix(moduleName, businessName));
    velocityContext.put("columns", genTable.getColumns());
    velocityContext.put("table", genTable);
    setMenuVelocityContext(velocityContext, genTable);
    if (GenConstants.TPL_TREE.equals(tplCategory))
    {
        setTreeVelocityContext(velocityContext, genTable);
    }
    if (GenConstants.TPL_SUB.equals(tplCategory))
    {
        setSubVelocityContext(velocityContext, genTable);
    }
    return velocityContext;
}
Copy the code

The template that generates the code,

HTML: crud methods add HTML, edit. HTML, the list. The HTML, the list – tree. HTML

Java: controller,service,domain,mapper,serviceImpl

Sql: used to generate menus

Custom mapper.xml

Ruoyy-admin, the framework Generator for the front-end page framework, generates a perfect fit of rapid development CRUD code, and supports the user’s choice configuration, page query function has been encapsulated, here to learn, to the great god.

Conclusion:

The Velocity template engine consists of three steps: 1. Initialize the loading address of the template. 2. Place the context variable velocityContext; 3. Render template.

Projects with frameworks typically have a custom code generator for the framework to normalize the code and improve development efficiency.

Personal projects or small and medium-sized projects can be developed using ready-made framework, if not meet the needs, to modify, understand the template principle, can quickly expand.

Thank you for your encouragement.