java實現代碼自動生成工具,基於spring boot

前言

此項目主要解決在項目搭建初期,創建項目時很多代碼手動創建太過繁瑣,耗費不必要的開發時間。通過此代碼生成工具可以自動生成相關代碼,當然不侷限於controller層、service層、entity層、mapper層的代碼生成!因爲本項目中自定義代碼生成規則的配置比較簡單,只需自定義模板並創建工廠實例即可。同時可自定義代碼的生成路徑,未設置則默認生成在本項目下(可更直觀查看生成的結構效果,文末有效果圖)。

本項目基於SpringBoot,通過beetl模板引擎自動渲染生成代碼。

項目源碼地址:bhy702-generator


項目結構

在這裏插入圖片描述


一: 導入pom文件相關依賴
<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配置類
/**
 * @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文件:

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代碼模板文件爲例:
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參數類:
/**
 * @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;
    }
}

工廠實例:
/**
 * @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");
    }
}

六: 識別數據庫類型,讀取數據庫表結構
/**
 * @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;
}

七: 數據清洗,設置生成代碼的包名,獲取工廠實例生成代碼文件
/**
 * @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


歡迎訪問本文的個人博客鏈接: https://br-bai.github.io/2019/12/26/Java實現代碼自動生成工具,基於Spring Boot/

發佈了30 篇原創文章 · 獲贊 18 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章