Build a Code Generator from Scratch: Boost Development Efficiency 10×
This article walks through creating a fully automated Spring Boot and MyBatis code generator using Freemarker templates, detailing each step from extracting table metadata to generating DAO, service, controller, and entity classes, and shows how a single-table CRUD can be produced in seconds.
Background
When developing a new feature in a typical MVC project, developers repeatedly write dao, service, controller and the associated dto, entity, vo classes for each table. The core operations are always the same four CRUD actions (Create, Delete, Update, Retrieve), which leads to a lot of boiler‑plate code.
Open‑source code generators exist to reduce this manual effort, allowing developers to focus on business logic.
Implementation Idea
We build a simple generator for a Spring Boot project that uses MyBatis and MySQL. The generator consists of three stages:
Obtain table metadata (column names, types, comments) from information_schema.
Write Freemarker templates for mapper, dao, service, controller, entity, dto and vo.
Render the templates with the metadata to produce Java source files.
1. Get Table Structure
Example SQL to create a demo table:
CREATE TABLE test_db (
id bigint(20) unsigned NOT NULL COMMENT '主键ID',
name varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '名称',
is_delete tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否删除 1:已删除;0:未删除',
create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (id),
KEY idx_create_time (create_time) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='测试表';Metadata is fetched with:
SELECT column_name, data_type, column_comment
FROM information_schema.columns
WHERE table_schema = 'yjgj_base' AND table_name = 'test_db';
SELECT TABLE_COMMENT
FROM INFORMATION_SCHEMA.TABLES
WHERE table_schema = 'yjgj_base' AND table_name = 'test_db';2. Write Templates
Key templates (excerpt):
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="${daoPackageName}.${daoName}">
<!-- BaseResultMap -->
<resultMap id="BaseResultMap" type="${entityPackageName}.${entityName}">
<#list columns as pro>
<#if pro.proName == primaryId>
<id column="${primaryId}" property="${primaryId}" jdbcType="${pro.fieldType}"/>
<#else>
<result column="${pro.fieldName}" property="${pro.proName}" jdbcType="${pro.fieldType}"/>
</#if>
</#list>
</resultMap>
<!-- Insert batch example -->
<insert id="insertList" parameterType="java.util.List">
INSERT INTO ${tableName} (
<#list columns as pro>
<#if pro_index == 0>${pro.fieldName}<#else>, ${pro.fieldName}</#if>
</#list>
) VALUES
<#list columns as pro>
<#if pro_index == 0>(${"#{obj."+pro.proName+"}"})
<#else>, (${"#{obj."+pro.proName+"}"})
</#list>
</insert>
</mapper>Similar Freemarker files are created for dao, service, serviceImpl, controller, entity, dto and vo. The templates use placeholders such as ${entityName}, ${packageNamePre}, ${primaryId}, etc.
3. Core Utility Classes
The generator relies on a few shared abstractions: BaseMapper<T> – defines generic CRUD methods (insert, update, delete, select). BaseService<T> – declares service‑level operations. BaseServiceImpl<M extends BaseMapper<T>, T> – implements BaseService using the mapper. BaseDTO, Pager<T>, IdRequest – common request/response objects.
Example of BaseMapper:
public interface BaseMapper<T> {
int insertList(@Param("list") List<T> list);
int insertPrimaryKeySelective(T entity);
int updatePrimaryKeySelective(T entity);
int updateBatchByIds(@Param("list") List<T> list);
int deleteByPrimaryKey(Serializable id);
T selectByPrimaryKey(Serializable id);
List<T> selectByPrimaryKeySelective(T entity);
List<T> selectByIds(@Param("ids") List<? extends Serializable> ids);
List<T> selectByMap(Map<String, Object> columnMap);
}And BaseServiceImpl implements the methods with transaction handling:
public abstract class BaseServiceImpl<M extends BaseMapper<T>, T> implements BaseService<T> {
@Autowired protected M baseMapper;
@Transactional public boolean insert(T entity) { return returnBool(baseMapper.insertPrimaryKeySelective(entity)); }
@Transactional public boolean insertList(List<T> list) { return returnBool(baseMapper.insertList(list)); }
@Transactional public boolean updateById(T entity) { return returnBool(baseMapper.updatePrimaryKeySelective(entity)); }
@Transactional public boolean deleteById(Serializable id) { return returnBool(baseMapper.deleteByPrimaryKey(id)); }
public T selectById(Serializable id) { return baseMapper.selectByPrimaryKey(id); }
protected boolean returnBool(Integer result) { return result != null && result >= 1; }
}4. Configuration and Main Execution
A simple application.properties defines variables such as package prefix, module name, table name, entity name, database connection, and output directory:
# package prefix
packageNamePre=com.example.generator
# module name
moduleName=test
# table
tableName=test_db
# entity class
entityName=TestEntity
# primary key
primaryId=id
# author
authorName=pzblog
# DB connection
ipName=127.0.0.1
portName=3306
userName=root
passWord=123456
# output path (empty uses src/main/java)
outUrl=The SystemConstant class loads these properties, and GeneratorMain builds a Map<String,Object> with all values, then calls CodeService.generate() to render every file.
public class GeneratorMain {
public static void main(String[] args) {
System.out.println("生成代码 start......");
Map<String, Object> templateData = new HashMap<>();
templateData.put("tableName", SystemConstant.tableName);
templateData.put("entityName", SystemConstant.entityName);
// ... other properties ...
CodeService dataService = new CodeService();
dataService.generate(templateData);
System.out.println("生成代码 end......");
}
}Running the main method produces a full set of Java files (entity, DAO, mapper XML, service, service implementation, controller, DTO, VO) under the configured output directory. The generated controller for the example looks like:
@RestController
@RequestMapping("/testEntity")
public class TestEntityController {
@Autowired private TestEntityService testEntityService;
@PostMapping("/getPage") public Pager<TestEntityVO> getPage(@RequestBody TestEntityDTO request) {
return testEntityService.getPage(request);
}
@PostMapping("/save") public void save(TestEntityDTO request) {
TestEntity entity = new TestEntity();
BeanUtils.copyProperties(request, entity);
testEntityService.insert(entity);
}
// other CRUD endpoints omitted for brevity
}Conclusion
The tutorial demonstrates that, with Freemarker and a few utility classes, a complete CRUD code base for a single table can be generated in seconds, reducing manual effort by an order of magnitude. The approach is easily extensible to multiple tables and more complex business logic.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Pan Zhi's Tech Notes
Sharing frontline internet R&D technology, dedicated to premium original content.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
