java自动生成代码

java自动生成代码

​ 为了简化日常编写代码,根据公司的自身情况,编写了一个自动生成代码的简易工具。

主要需要熟悉freemarker模板引擎,JDBC等相关知识;

<dependency>
    <groupId>freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.8</version>
</dependency>
<!-- 这个是生成驼峰命名的工具 -->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>21.0</version>
</dependency>
  1. 创建实体类ColumnClass

ColumnClass 用来封装 数据库表元数据的信息,如字段名称,字段类型,字段注释等等。

public class ColumnClass {

    /** 数据库字段名称 **/
    private String columnName;
    /** 数据库字段类型 **/
    private String columnType;
    /** 数据库字段首字母小写且去掉下划线字符串 **/
    private String changeColumnName;
    /** 数据库字段注释 **/
    private String columnComment;

    /**
     * 列的长度
     */
    private int columnSize;

    /**
     * 是否允许为空
     */
    private String nullable;

    /**
     * 主键名
     */
    private String pkName;
    
    ...get/set方法
}
  1. 主要代码
package cn.sh.cjvision.CodeGenerator.jdbc;

import cn.sh.cjvision.CodeGenerator.ColumnClass;
import cn.sh.cjvision.CodeGenerator.FreeMarkerTemplateUtils;
import com.google.common.base.CaseFormat;
import org.apache.commons.jexl2.UnifiedJEXL;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
//import com.evada.inno.pm.code.generate.model.ColumnClass;

import freemarker.template.Template;
import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.sql.*;
import java.time.LocalDate;
import java.util.*;

import static cn.sh.cjvision.CodeGenerator.FreeMarkerTemplateUtils.createDir;

public class CodeGenerateUtils
{
    /**
     * 链接地址
     */
    @Value("${spring.datasource.url}")
    private String URL;

    /**
     * 用户名
     */
    @Value("@{spring.datasource.username}")
    private String USER;

    /**
     * 密码
     */
    @Value("@{spring.datasource.password}")
    private String PASSWORD;

    /**
     * 连接驱动
     */
    @Value("@{spring.datasource.driver-class-name}")
    private String DRIVER;

    /**
     * 作者
     */
    private final String AUTHOR = "wowoToffon";

    /**
     * 时间
     */
    private final String CURRENT_DATE = LocalDate.now().toString();

    /**
     * 表名
     */
    private final String tableName = "BOOK";

    /**
     * 包名
     */
    private final String packageName = "cn.sh.cjvision";

    /**
     * 表中文注释
     */
    private final String tableAnnotation = "书";

    /**
     * 生成位置
     */
    private final String diskPath = "E:\\MyProject\\Quartz\\Quartz\\app-quartz\\src\\main\\java\\cn\\sh\\cjvision";

    /**
     * 将类似为 A_TABLE  转换为驼峰命名 aTable
     */
    private final String changeTableName = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, tableName);

    /**
     * 获取jdbc链接
     * @return
     * @throws Exception
     */
    public  Connection getConnection() throws Exception
    {
        Class.forName(DRIVER);

        Properties props =new Properties();
        props.put("user", USER);
        props.put("password", PASSWORD);
        // 允许获取注解(这个是重点,在oracle中不加这个,获得注释就是 null)
        props.put("remarksReporting","true");
        Connection connection = DriverManager.getConnection(URL, props);
        return connection;
    }

    public static void main(String[] args) throws Exception
    {
        CodeGenerateUtils codeGenerateUtils = new CodeGenerateUtils();
        codeGenerateUtils.generate();
    }

    public void generate() throws Exception
    {
        Connection connection = getConnection();

        //生成Model文件
        generateModelFile(connection);

        // 生成Controller文件
        generateControllerFile();

        // 生成service文件
        generateServiceFile();
    }

    public List<ColumnClass> setColumnClass(Connection connection) throws SQLException
    {
        DatabaseMetaData databaseMetaData = connection.getMetaData();
        // 获取表中列值信息
        ResultSet resultSet = databaseMetaData.getColumns(null, "%", tableName, "%");
        // 获取表主键信息	
        ResultSet primaryKeys = databaseMetaData.getPrimaryKeys(null, null, "BOOK");
        List<ColumnClass> columnClassList = new LinkedList<>();

        ColumnClass columnClass = null;
        while (resultSet.next())
        {
            columnClass = new ColumnClass();
            //获取字段名称
            columnClass.setColumnName(resultSet.getString("COLUMN_NAME"));
            //获取字段类型
            columnClass.setColumnType(resultSet.getString("TYPE_NAME").split("\\(")[0]);
            //转换字段名称,如 sys_name 变成 SysName
            columnClass.setChangeColumnName(CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL,(resultSet.getString("COLUMN_NAME"))));
            //字段在数据库的注释
            columnClass.setColumnComment(resultSet.getString("REMARKS"));
            // 字段长度
            columnClass.setColumnSize(resultSet.getInt("COLUMN_SIZE"));
            // 是否为必填
            columnClass.setNullable(resultSet.getString("NULLABLE"));

            columnClassList.add(columnClass);
        }

        while (primaryKeys.next())
        {
            int i = 0;
            columnClass = columnClassList.get(i);
            columnClass.setPkName(primaryKeys.getString("PK_NAME"));
            i++;
        }
        return columnClassList;
    }

    private void generateModelFile(Connection connection) throws Exception
    {
        DatabaseMetaData databaseMetaData = connection.getMetaData();
        ResultSet resultSet = databaseMetaData.getColumns(null, "%", tableName, "%");

        String newPath = createDir(diskPath, "bean");
        final String suffix = ".java";
        final String path = newPath + changeTableName + suffix;
        final String templateName = "Model.ftl";
        File mapperFile = new File(path);

        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("model_column", setColumnClass(connection));
        generateFileByTemplate(templateName, mapperFile, dataMap);

    }

    private void generateServiceFile() throws Exception
    {
        String newPath = createDir(diskPath, "service");
        final String suffix = "Service.java";
        final String path = newPath + changeTableName + suffix;
        final String templateName = "Service.ftl";
        File mapperFile = new File(path);
        Map<String, Object> dataMap = new HashMap<>();
        generateFileByTemplate(templateName, mapperFile, dataMap);
    }

    private void generateControllerFile() throws Exception
    {
        String newPath = createDir(diskPath, "controller");
        final String suffix = "Controller.java";
        final String path = newPath + changeTableName + suffix;
        final String templateName = "Controller.ftl";
        File mapperFile = new File(path);
        Map<String, Object> dataMap = new HashMap<>();
        generateFileByTemplate(templateName, mapperFile, dataMap);
    }

    private void generateFileByTemplate(final String templateName, File file, Map<String, Object> dataMap) throws Exception
    {
        Template template = FreeMarkerTemplateUtils.getTemplate(templateName);
        FileOutputStream fos = new FileOutputStream(file);
        dataMap.put("table_name_small", tableName);
        dataMap.put("table_name", changeTableName);
        dataMap.put("author", AUTHOR);
        dataMap.put("date", CURRENT_DATE);
        dataMap.put("package_name", packageName);
        dataMap.put("table_annotation", tableAnnotation);
        Writer out = new BufferedWriter(new OutputStreamWriter(fos, "utf-8"), 10240);
        template.process(dataMap, out);
    }
}

  1. FreeMarkerTemplateUtils工具类

    FreeMarkerTemplateUtils工具类用来配置模板所在的路径

    package cn.sh.cjvision.CodeGenerator;
    
    // import com.evada.inno.core.exception.BusinessException;
    import freemarker.cache.ClassTemplateLoader;
    import freemarker.cache.NullCacheStorage;
    import freemarker.template.Configuration;
    import freemarker.template.Template;
    import freemarker.template.TemplateExceptionHandler;
    
    import java.io.File;
    import java.io.IOException;
    
    public class FreeMarkerTemplateUtils {
    
        private FreeMarkerTemplateUtils(){}
        private static final Configuration CONFIGURATION = new Configuration();
    
        static{
            //这里比较重要,用来指定加载模板所在的路径
            CONFIGURATION.setTemplateLoader(new ClassTemplateLoader(FreeMarkerTemplateUtils.class, "/templates"));
            CONFIGURATION.setDefaultEncoding("UTF-8");
            CONFIGURATION.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
            CONFIGURATION.setCacheStorage(NullCacheStorage.INSTANCE);
        }
    
        public static Template getTemplate(String templateName) throws IOException {
            try {
                return CONFIGURATION.getTemplate(templateName);
            } catch (IOException e) {
                throw e;
            }
        }
    
        public static void clearCache() {
            CONFIGURATION.clearTemplateCache();
        }
    
        /**
         * 创建 文件夹
         * @param FileAddress
         * @param fileName
         * @return
         */
        public static String createDir (String FileAddress, String fileName){
            FileAddress = FileAddress + "/" +fileName + "/";
    
            File file  = new File(FileAddress);
            if(file.exists()){
    
                System.out.println("存在该目录");
                return FileAddress;
            }
    
            /*if (!FileAddress.endsWith(File.separator)) {
                FileAddress = FileAddress + File.separator;
            }*/
    
            if(file.mkdir()){
                System.out.println("创建目录" + FileAddress + "创建成功");
            }else {
                System.out.println("创建失败");
            }
            return FileAddress;
        }
    }
    
    
  2. freemarker 模板文件

    Freemarker的模板文件,后缀都是以ftl结尾的。

    创建Model.ftl

    package ${package_name}.bean;
    
    import cn.sh.cjvision.framework.bean.bean.BaseBean;
    
    import io.swagger.annotations.ApiModel;
    import io.swagger.annotations.ApiModelProperty;
    import org.hibernate.annotations.GenericGenerator;
    import org.hibernate.validator.constraints.Length;
    import org.hibernate.validator.constraints.NotBlank;
    import cn.sh.cjvision.framework.utils.constant.AppConstants;
    
    import javax.persistence.*;
    import java.util.Date;
    
    /**
    * 描述:${table_annotation}模型
    * @author ${author}
    * @date ${date}
    */
    
    @Entity
    @Table(name = "${table_name_small}")
    @ApiModel
    public class ${table_name} extends BaseBean {
    
    <#if model_column?exists>
    <#list model_column as model>
        /**
        *${model.columnComment!}
        */
        <#if (model.columnType = 'VARCHAR2' || model.columnType = 'VARCHAR' || model.columnType = 'NUMBER' || model.columnType = 'TEXT')>
    
        <#if model.pkName?exists>
        @Id
        @GeneratedValue(generator = "${model.columnName}")
        @GenericGenerator(name = "${model.columnName}", strategy = AppConstants.SEQ_GENERATOR_STR)
        <#else >
        @Column(name = "${model.columnName}")
        <#if (model.nullable = '0')>
        @NotBlank(message = "${model.columnComment!}不能为空")
        @ApiModelProperty(value = "${model.columnComment!}不能为空", required = true)
        </#if>
        </#if>
        @Length(max = ${model.columnSize},message = "最长为${model.columnSize}")
        private String ${model.changeColumnName?uncap_first};
    
        </#if>
        <#if model.columnType = 'TIMESTAMP' >
        <#if (model.nullable = '0')>
        @NotNull(message = "${model.columnComment!}不能为空")
        @ApiModelProperty(value = "${model.columnComment!}不能为空", required = true)
        </#if>
        @Column(name = "${model.columnName}",columnDefinition = "TIMESTAMP")
        private Date ${model.changeColumnName?uncap_first};
    
        </#if>
    </#list>
    </#if>
    
    <#if model_column?exists>
    <#list model_column as model>
        <#if (model.columnType = 'VARCHAR2' || model.columnType = 'TEXT')>
        public String get${model.changeColumnName}() {
            return this.${model.changeColumnName?uncap_first};
        }
    
        public void set${model.changeColumnName}(String ${model.changeColumnName?uncap_first}) {
            this.${model.changeColumnName?uncap_first} = ${model.changeColumnName?uncap_first};
        }
    
        </#if>
        <#if model.columnType = 'TIMESTAMP' >
        public Date get${model.changeColumnName}() {
            return this.${model.changeColumnName?uncap_first};
        }
    
        public void set${model.changeColumnName}(Date ${model.changeColumnName?uncap_first}) {
            this.${model.changeColumnName?uncap_first} = ${model.changeColumnName?uncap_first};
        }
    
        </#if>
    </#list>
    </#if>
    
    }
    

    Service.flt文件

    package ${package_name}.service;
    
    import cn.sh.cjvision.framework.repository.CustomRepository;
    import ${package_name}.bean.${table_name}Bean;
    
    public interface ${table_name}Service extends CustomRepository<${table_name}Bean, String>
    {
    
    }
    

    Controller.flt文件

    package ${package_name}.controller;
    
    import ${package_name}.bean.${table_name}Bean;
    import cn.sh.cjvision.framework.annotation.log.LogWrite;
    import cn.sh.cjvision.framework.controller.BaseController;
    import cn.sh.cjvision.framework.utils.out.OutPutJson;
    import ${package_name}.service.${table_name}Service;
    import com.alibaba.fastjson.JSONObject;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import io.swagger.annotations.Api;
    import org.hibernate.validator.constraints.NotBlank;
    import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
    import org.springframework.validation.annotation.Validated;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.beans.factory.annotation.Autowired;
    
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    
    @RestController
    @RequestMapping("/app/${table_name?uncap_first}")
    @Validated
    @Api(tags = "${table_annotation}")
    public class ${table_name}Controller
    {
        private final static Logger logger = LoggerFactory.getLogger(${table_name}Controller.class);
    
        @Autowired
        private ${table_name}Service ${table_name?uncap_first}Service;
    
        @Autowired
        private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
    
        /**
        * 描述:根据Id 查询
        * @param id  ${table_annotation}id
        */
        @PostMapping(value = "get${table_name}ById")
        @LogWrite(moduleName = "获取${table_annotation}", op = LogWrite.OP.QUERY)
        public JSONObject findById(@NotBlank(message = "ID不能为空") String id)throws Exception {
            JSONObject obj = new JSONObject();
    
            ${table_name}Bean ${table_name?uncap_first}Bean = ${table_name?uncap_first}Service.findOne(id);
    
            obj.put("${table_name?uncap_first}Bean", ${table_name?uncap_first}Bean);
            return OutPutJson.success("获取成功", obj);
        }
    
        /**
        * 描述:创建${table_annotation}
        * @param
        */
        @PostMapping(value = "add${table_name}")
        @LogWrite(moduleName = "新增${table_annotation}", op = LogWrite.OP.ADD)
        public JSONObject add${table_name}(@Validated ${table_name}Bean ${table_name?uncap_first}Bean) throws Exception {
    
            JSONObject obj = new JSONObject();
            try{
                ${table_name}Bean ${table_name?uncap_first}Save = ${table_name?uncap_first}Service.save(${table_name?uncap_first}Bean);
    
                obj.put("${table_name?uncap_first}Bean", ${table_name?uncap_first}Save);
                return OutPutJson.success("新增成功", obj);
            } catch (Exception e){
                logger.error("URL:/app/${table_name?uncap_first}/add${table_name} 异常 " + e.getMessage(), e);
                return OutPutJson.failure("新增失败:" + e.getMessage());
            }
        }
    
        /**
        * 描述:删除${table_annotation}
        * @param id ${table_annotation}id
        */
        @PostMapping(value = "del${table_name}ByIds")
        @LogWrite(moduleName = "删除${table_annotation}", op = LogWrite.OP.DELETE)
        public JSONObject del${table_name}ByIds(@RequestBody List<String> ids) throws Exception {
            Map<String, Object> map = new HashMap<>(1);
    
            if(ids.size() > 0){
                map.put("ids", ids);
                namedParameterJdbcTemplate.update("DELETE FROM ${date} WHERE RWRY_ID IN (:ids)", map);
            }
    
            return OutPutJson.success("移除成功");
        }
    
        /**
        * 描述:编辑${table_annotation}
        * @param
        */
        @PostMapping(value = "update${table_name}")
        @LogWrite(moduleName = "编辑${table_annotation}", op = LogWrite.OP.UPDATE)
        public JSONObject update${table_name}(@Validated ${table_name}Bean ${table_name?uncap_first}Bean) throws Exception {
    
            JSONObject obj = new JSONObject();
            try{
                ${table_name}Bean ${table_name?uncap_first}Update = ${table_name?uncap_first}Service.saveAndFlush(${table_name?uncap_first}Bean);
    
                obj.put("${table_name?uncap_first}Bean", ${table_name?uncap_first}Update);
                return OutPutJson.success("编辑成功", obj);
            } catch (Exception e){
                logger.error("URL:/app/${table_name?uncap_first}/update${table_name} 异常 " + e.getMessage(), e);
                return OutPutJson.failure("编辑失败:" + e.getMessage());
            }
        }
    
    }
    

注意:

  1. 在连接数据库的时候一定要 remarksReporting 设置为true,不然获取不到注解
  2. 在JDBC元数据的操作是很消耗性能的,有点慢
  3. JDBC获取信息有7中方法,每个方法获取的信息不一样,就是这个东西,databaseMetaData.getColumns是获取不到主键信息的,获主键的时候一直说的是“列名不存在” 有点蛋疼,需要单独的获取主键信息。详情
  4. freemarker 开上面写的应该能看懂。

参考:

JDBC元数据操作(一)-- DatabaseMetaData接口详解

Java之利用Freemarker模板引擎实现代码生成器,提高效率(我主要看的这篇,在他的基础上加了生成文件夹, 没有用他的驼峰名)

java 驼峰字符和下划线字符相互转换工具类

Freemarker操作字符串

Freemarker官网

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值