前言

此项目主要解决在项目搭建初期,创建项目时很多代码手动创建太过繁琐,耗费不必要的开发时间。通过此代码生成工具可以自动生成相关代码,当然不局限于controller层、service层、entity层、mapper层的代码生成!因为本项目中自定义代码生成规则的配置比较简单,只需自定义模板并创建工厂实例即可。同时可自定义代码的生成路径,未设置则默认生成在本项目下(可更直观查看生成的结构效果,文末有效果图)。

本项目基于SpringBoot,通过beetl模板引擎自动渲染生成代码。

项目源码地址:bhy702-generator


项目结构

在这里插入图片描述


一: 导入pom文件相关依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>

<dependency>
<groupId>com.ibeetl</groupId>
<artifactId>beetl-framework-starter</artifactId>
<version>1.1.68.RELEASE</version>
</dependency>
<!-- 升级beetl的依赖,保证在1.8上不出现警告 -->
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
<version>4.7.1</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

二: 编写beetl配置类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
* @author: brbai
* @create: 2019-10-16 17:12:45
* @description:
*/
@Configuration
@Slf4j
@Data
public class BeetlConfiguration {

@Value("${beetl.template-path}")
private String templatePath;

@Value("${beetl.delimiter-statement-start}")
private String delimiterStatementStart;

@Value("${beetl.delimiter-statement-end}")
private String delimiterStatementEnd;

@Bean(name = "beetlConfig")
public BeetlGroupUtilConfiguration beetlGroupUtilConfiguration() {
BeetlGroupUtilConfiguration beetlGroupUtilConfiguration = new BeetlGroupUtilConfiguration();

// Beetl的配置
Properties properties = new Properties();
properties.setProperty("statementStart",delimiterStatementStart);
properties.setProperty("statementEnd",delimiterStatementEnd);
properties.setProperty("DELIMITER_STATEMENT_START", delimiterStatementStart);
properties.setProperty("DELIMITER_STATEMENT_END", delimiterStatementEnd);
beetlGroupUtilConfiguration.setConfigProperties(properties);

ClasspathResourceLoader resourceLoader = new ClasspathResourceLoader(templatePath);

beetlGroupUtilConfiguration.setResourceLoader(resourceLoader);
// 调用Beetl的初始化方法
beetlGroupUtilConfiguration.init();

return beetlGroupUtilConfiguration;
}

}

三: 配置properties文件

application.properties文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server.port=8090
server.servlet.context-path=/

spring.datasource.url=jdbc:mysql://localhost:3306/building_materials?useUnicode=true&characterEncoding=UTF-8&nullCatalogMeansCurrent=true&tinyInt1isBit=false
spring.datasource.username=root
spring.datasource.password=bhy702

# beetl模板引擎的加载路径。以resources为根路径,此时表示加载resources/template文件夹中的模板文件
beetl.template-path = template
# beetl模板引擎语法开始标记
beetl.delimiter-statement-start = @
# beetl模板引擎语法结束标记。为空时在有开始标记@的行末结束
beetl.delimiter-statement-end =

# 代码文件的保存路径,注释后默认保存在本项目中
#project.base-path = F:\\360MoveData\\Users\\n551\\Desktop

为了便于查看代码生成效果,默认代码生成在此项目内,同时提供自定义代码生成位置。自定义代码生成位置只需配置application.properties中的project.base-path


四: 设计代码模板(代码模板没限制,想怎么设计都行)
下面以Entity.tpl代码模板文件为例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package ${props.params.packageName};

import lombok.Data;
@for(column in props.columnList){
@if(column.type=='Date'){
import java.util.Date;
@break;
@}}

\@Data
public class ${props.className}{

@for(column in props.columnList){
private ${column.type} ${column.lowerPropertyName};

@}
}

五: 根据代码模板确定所需参数数据,添加代码模板的工厂实例。

为了便于开发,我封装了一个模板所需的参数类TplProperties

TplProperties参数类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/**
* @author: brbai
* @create: 2019-12-26 11:13:42
* @description: 模板参数
*/
@Data
public class TplProperties {

private String basePath;
private String rootPackage;
private String tableName;
private List<Column> columnList;

private Map<String,Object> params = new HashMap();;
/**
* 表名格式转换
* xxx_yyy->XxxYyy
*/
private String className;

/**
* 表名格式转换
* xxx_yyy->xxxYyy
*/
private String propertyName;

public TplProperties(String basePath, String rootPackage, String tableName, List<Column> columnList) {
this.basePath = basePath;
this.rootPackage = rootPackage;
this.tableName = tableName;
this.columnList = columnList;
}

public TplProperties(String rootPackage, String tableName, List<Column> columnList) {
this.rootPackage = rootPackage;
this.tableName = tableName;
this.columnList = columnList;
}

public String getClassName() {
return StringUtil.mapTableNameToClassName(tableName);
}
public String getPropertyName() {
return StringUtil.mapTableNameToPropertyName(tableName);
}

public void setParam(String key,Object value) {
this.params.put(key,value);
}

public String getBasePath() {
if(basePath == null){
//未在properties文件中设置代码生成路径时,代码默认生成在此项目下
return System.getProperty("user.dir") + "/src";
}
return basePath;
}
}
工厂实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* @author: brbai
* @create: 2019-12-14 19:12:59
* @description:
*/
public class Entity implements Code {

@Override
public void create(GroupTemplate gt, TplProperties properties, String tplPath) throws IOException {

String packageName = properties.getRootPackage()+ ".entity";
properties.setParam("packageName",packageName);

Template t = gt.getTemplate(tplPath);
t.binding("props", properties);

GenerateUtil.createFile(t,properties.getBasePath()+"/main/java/"
+ packageName.replace(".", "/") + "/" + properties.getClassName() + ".java");
}
}

六: 识别数据库类型,读取数据库表结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/**
* @Description: 获取数据库表结构
* @Param: [sqlSessionFactory]
* @return: java.util.Map<java.lang.String,java.util.Map<java.lang.String,java.lang.String>>
*/
public static Map<String, Map<String, String>> getDatabaseAttribute(SqlSession sqlSession) throws SQLException {

Map<String, Map<String, String>> databaseAttribute = new HashMap<>();

Connection connection = sqlSession.getConnection();

//获得数据库信息
DatabaseMetaData metaData = connection.getMetaData();

String databaseName = metaData.getDatabaseProductName();

if ("MySQL".equals(databaseName)) {
//获得数据库表
ResultSet resultSet = metaData.getTables(null, null, null, new String[]{"TABLE"});
while (resultSet.next()) {
String tableName = resultSet.getString(3);
String sql = "SELECT * FROM " + tableName;
PreparedStatement preparedStatement = connection.prepareStatement(sql);
ResultSet set = preparedStatement.executeQuery();
//获得表信息
ResultSetMetaData md = set.getMetaData();
//获得表字段数
int columnCount = md.getColumnCount();

Map<String, String> attributes = new LinkedHashMap<>();

for (int i = 1; i <= columnCount; i++) {
//字段名
String columnName = md.getColumnName(i);
//字段属性
String columnType = md.getColumnTypeName(i);

attributes.put(columnName, columnType);
}
databaseAttribute.put(tableName,attributes);
}
}else if("Oracle".equals(databaseName)){
//DEVTEST是oracle的用户名。此Oracle代码还没怎么测试。
ResultSet rs = metaData.getTables("null", "DEVTEST", "%", new String[]{"TABLE"});

while (rs.next()) {
String tableName = rs.getString("TABLE_NAME");
ResultSet columns = metaData
.getColumns(null, "DEVTEST", tableName, "%");

Map<String, String> attributes = new LinkedHashMap<>();
while (columns.next()) {
attributes.put(columns.getString("COLUMN_NAME"), columns.getString("TYPE_NAME"));
}
databaseAttribute.put(tableName, attributes);
}
}


return databaseAttribute;
}

七: 数据清洗,设置生成代码的包名,获取工厂实例生成代码文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/**
* @author: brbai
* @create: 2019-11-20 14:12:12
* @description: test
*/

@RunWith(SpringRunner.class)
@SpringBootTest
public class CodeTest {

@Autowired
private SqlSessionFactory sqlSessionFactory;

@Autowired
private BeetlGroupUtilConfiguration beetlConfig;

@Autowired
private CodeFactory codeFactory;

@Value("${project.base-path:#{null}}")
private String basePath;

@Test
public void codeGeneratorTest() throws SQLException{

//生成代码的项目包名
String rootPackage = "aaa.bbb.ccc";

GroupTemplate gt = beetlConfig.getGroupTemplate();
//获取数据库表结构
SqlSession sqlSession = sqlSessionFactory.openSession();
Map<String, Map<String, String>> databaseAttribute = DBUtil.getDatabaseAttribute(sqlSession);

String databaseName = sqlSession.getConnection().getMetaData().getDatabaseProductName();

databaseAttribute.forEach((tableName,columnMap)->{
List<Column> columnList = new ArrayList<>();
columnMap.forEach((name,type)->{
String javaType = null;
//数据库行字段类型以及字段名格式转换,并存入模板参数集合
if("MySQL".equals(databaseName)){
javaType = DBUtil.sqlTypeToJavaType(type);
}else if("Oracle".equals(databaseName)){
javaType = DBUtil.oracleSqlTypeToJavaType(type);
}
columnList.add(new Column(name, javaType, StringUtil.mapUnderscoreToCamelCase(name), StringUtil.mapTableNameToClassName(name)));
});

//构建参数
TplProperties properties = null;
if(basePath != null){
properties = new TplProperties(basePath,rootPackage,tableName,columnList);
}else{
properties = new TplProperties(rootPackage,tableName,columnList);
}

try {
//代码生成
codeFactory.getCodeInstance("CONTROLLER").create(gt,properties,"/Controller.tpl");
codeFactory.getCodeInstance("SERVICE").create(gt,properties,"/Service.tpl");
codeFactory.getCodeInstance("ENTITY").create(gt,properties,"/Entity.tpl");
codeFactory.getCodeInstance("MAPPER").create(gt,properties,"/Mapper.tpl");
codeFactory.getCodeInstance("MAPPER_XML").create(gt,properties,"/MapperXml.tpl");
} catch (IOException e) {
e.printStackTrace();
}
});

}
}

运行codeGeneratorTest()方法

效果图如下:

在这里插入图片描述
未配置代码生成路径时:
在这里插入图片描述
配置了代码生成路径时:
在这里插入图片描述


由于项目代码比较多,没能全部贴出来,源码我已经上传码云了,想看源码的小伙伴可以点这里。bhy702-generator