Laboo. Top /2018/11/26/…

This article explains how to write a highly customized code generator in Java

MyBatis is an excellent persistence layer framework that supports customized SQL, stored procedures, and advanced mapping. MyBatis avoids almost all of the JDBC code and manual setting of parameters and fetching result sets. MyBatis can configure and map native information using simple XML or annotations.

This paragraph comes from the introduction of Mybatis official website. When I first use Mybatis, I feel that this framework is much more elegant than JDBC, and it is very simple to use as the official website says. But after a period of use, the disadvantages gradually become apparent

Java, mapper. XML (Mapper can be merged into Dao), Dao.java, service. Java hierarchy is very clear, but too much repetitive work, time consuming and error prone

And when the database changes a bit… miserable

Plugins that generate code automatically came along, but they weren’t always easy to control because everyone’s needs were different

This article explains how to write your own code generator easily

Program source code

mybatis-generator

Code implementation

The idea is very simple, first query database table structure, get column name, column type… Information such as

Create a file template, insert this information into the template, and finally package the template into a compressed package and export it

The code implements a total of five Java classes

  • TableDO
  • ColumnDO
  • GeneratorMapper
  • GeneratorUtils
  • GeneratorService

Let’s start with two entity classes

TableDO and ColumnDO

TableDO holds the table name, the class name, and column information

Complete class code tabledo.java

public class TableDO {

    private String tableName;
    private List<ColumnDO> columns;
    private String className;
    private String suffix;

    // get()... set()...
}
Copy the code

ColumnDO holds column names, database field types, and corresponding Java attribute names and types

Columndo.java complete class code

public class ColumnDO {

    private String columnName;
    private String dataType;
    private String attrName;
    private String attrLowerName;
    private String attrType;

    // get()... set()...
}
Copy the code

GeneratorMapper

In GeneratorMapper, we query for automatic information about a table by its name

The complete class code generatorMapper.java

@Mapper
public interface GeneratorMapper {

    @Select("select column_name columnName, data_type dataType from information_schema.columns where table_name = #{tableName} and table_schema = (select database()) order by ordinal_position")
    List<ColumnDO> listColumns(String tableName);
}
Copy the code

GeneratorUtils

Converts class information to and from templates in GeneratorUtils

The complete class code generatorutils.java

Put the table information into the context of the Velocity template

Map<String, Object> map = new HashMap<>();
map.put("tableName", table.getTableName());
map.put("className", table.getClassName());
map.put("pathName", getPackageName().substring(getPackageName().lastIndexOf(".") + 1));
map.put("columns", table.getColumns());
map.put("package", getPackageName());
map.put("suffix", table.getSuffix());

Properties prop = new Properties();
prop.put("file.resource.loader.class"."org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
Velocity.init(prop);
VelocityContext context = new VelocityContext(map);
Copy the code

Add the template

List<String> templates = new ArrayList<>();
templates.add("mybatis/Model.java.vm");
templates.add("mybatis/Query.java.vm");
templates.add("mybatis/Dao.java.vm");
templates.add("mybatis/Mapper.xml.vm");
templates.add("mybatis/Service.java.vm");
Copy the code

Compiling templates

StringWriter sw = new StringWriter();
Template tpl = Velocity.getTemplate(template, "UTF-8");
tpl.merge(context, sw);
Copy the code

The Utils class does most of the work of generating code, but the code is relatively simple

GeneratorService

The Mapper query column information is injected into the Service, the code is generated with Utils, and the compressed package is exported

The complete class code generatorService.java

@Service
public class GeneratorService {

    @Resource
    private GeneratorMapper generatorMapper;

    @Resource
    private Environment environment;

    public void generateZip(String[] tableNames, String zipPath) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);
        for (String tableName : tableNames) {
            TableDO table = new TableDO();
            table.setTableName(tableName);
            table.setColumns(generatorMapper.listColumns(tableName));
            GeneratorUtils.generatorCode(table, zip,getConfig());
        }
        IOUtils.closeQuietly(zip);
        FileOutputStream file = new FileOutputStream(zipPath);
        file.write(outputStream.toByteArray());
        file.close();
    }

    // getConfig ...
}
Copy the code

The VM template

The great thing about writing your own code generator is that you can customize your own templates to your needs. Here are a few of my templates for reference

  • Mapper.xml.vm
  • Dao.java.vm
  • Service.java.vm
  • Model.java.vm
  • Query.java.vm

The generated code is used under the Commons-Mybatis architecture

Dao.java.vm

packageThe ${package}.database.dao;

importThe ${package}.database.model.${className}${suffix};

import org.apache.ibatis.annotations.Mapper;
import org.laziji.commons.mybatis.dao.${suffix}Dao;

@Mapper
public interface ${className}Dao extends ${suffix}Dao<${className}${suffix}> {

}
Copy the code

.

The rest of the template

use

The configuration file

Create application-${name}. Yml file under Resources. ${name} = ${name}

The configuration file contains the following contents, including the database configuration, the package name of the generated code, and the source file path

spring: datasource: url: jdbc:mysql://xxx.xxx.xxx.xxx:3306/xxxx? characterEncoding=utf-8 username: xxxxxx password: xxxxxx generator: package: com.xxx.xxx resources: mapperCopy the code

Test

Create the test class in the test file

  • @ActiveProfiles("example")Fill in the configuration file namename
  • tableNamesYou can create multiple tables
  • zipPathThe code export path simply runs the test method
package pg.laziji.generator;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import pg.laziji.generator.mybatis.GeneratorService;

import javax.annotation.Resource;
import java.io.IOException;

@ActiveProfiles("example")
@RunWith(SpringRunner.class)
@SpringBootTest
public class ExampleTest {

    @Resource
    private GeneratorService generatorService;

    @Test
    public void test(a) throws IOException {
        String[] tableNames = new String[]{"example_table1"."example_table2"};
        String zipPath = "/home/code.zip"; generatorService.generateZip(tableNames,zipPath); }}Copy the code

Welcome to follow my blog public number