手写Java代码生成工具(三)生成mapper和query文件

230 阅读6分钟

前言

写博客是为了记录和分享自己的学习,所分享内容是网上的资源,由于非原创项目,主要分享学习思路,只展示贴图部分代码(本人不分享本项目源码,支持项目付费


一、创建mapper文件以及生成导入语句

        //创建Mapper包
        File folder = new File(Constants.PATH_MAPPER);
        if(!folder.exists()){
            folder.mkdirs();
        }

        String className= tableInfo.getBeanName()+Constants.SUFFIX_BEAN_MAPPER;
        //创建Mapper文件
        File file = new File(folder,className+".java");
        OutputStream out=null;
        OutputStreamWriter writer=null;
        BufferedWriter bw=null;
        
        out=new FileOutputStream(file);
        writer=new OutputStreamWriter(out,"UTF-8");
        bw=new BufferedWriter(writer);
        //生成包名
        bw.write("package "+Constants.PACKAGE_MAPPER+";");
        bw.newLine();
        bw.newLine();
        bw.write("import org.apache.ibatis.annotations.Param;");
        bw.newLine();
        bw.newLine();
  

这段代码的主要功能是动态创建一个 mapper 接口文件。它首先确定了 mapper 文件的存储路径(通过检查并创建指定的文件夹),然后根据给定的表信息生成接口的类名,并创建文件输出流,最后在文件中写入包名、导入必要的库以及接口的声明和注释。

1.创建文件夹:

  • File folder = new File(Constants.PATH_MAPPER);:通过一个常量指定 Mapper 文件所在的文件夹路径,并创建一个File对象表示该文件夹。
  • if(!folder.exists()){ folder.mkdirs(); }:如果该文件夹不存在,则创建它,确保 Mapper 文件有正确的存储位置。

2.生成类名和创建文件:

  • String className = tableInfo.getBeanName()+Constants.SUFFIX_BEAN_MAPPER;:根据表信息和一个常量后缀生成 Mapper 接口的类名。
  • File file = new File(folder,className+".java");:创建一个表示 Mapper 文件的File对象,确定了文件的存储路径和名称。

3.创建输出流:

  • OutputStream out = null;、OutputStreamWriter writer = null;、BufferedWriter bw = null;:声明输出流相关的变量,用于后续写入文件内容。
  • out = new FileOutputStream(file);、writer = new OutputStreamWriter(out,"UTF-8");、bw = new BufferedWriter(writer);:依次创建文件输出流、将输出流转换为字符输出流并使用指定的字符编码,最后创建缓冲字符输出流,提高写入文件的效率。

4.写入文件内容:

  • bw.write("package "+Constants.PACKAGE_MAPPER+";");:写入包名,通过常量指定包名,方便在不同的项目环境中进行配置。
  • bw.newLine();:写入换行符,使代码格式更加清晰。
  • bw.write("import org.apache.ibatis.annotations.Param;");:导入 MyBatis 中用于参数注解的类,为后续生成的方法做准备。

二、写入mapper需要生成的内容

         //生成包名
         bw.write("package "+Constants.PACKAGE_MAPPER+";");
         bw.newLine();
         bw.newLine();

         bw.write("import org.apache.ibatis.annotations.Param;");
         bw.newLine();
         bw.newLine();

         //生成类名
         bw.newLine();
         BuildComment.createClassComment(bw, tableInfo.getComment()+" Mapper");
         bw.write("public interface "+className+"<T, P> extends BaseMapper {");
         bw.newLine();

         Map<String, List<FieldInfo>> keyIndexMap = tableInfo.getKeyIndexMap();

         for(Map.Entry<String,List<FieldInfo>> entry:keyIndexMap.entrySet()){
             List<FieldInfo> fieldInfoList = entry.getValue();

             Integer index=0;
             StringBuilder methodName = new StringBuilder();
             StringBuilder methodParamName = new StringBuilder();
             for(FieldInfo fieldInfo:fieldInfoList){
                 index++;
                 methodName.append(StringUtils.uperCaseFirstLetter(fieldInfo.getPropertyName()));
                 if(index<fieldInfoList.size()){
                     methodName.append("And");
                 }

                 methodParamName.append("@Param(\""+fieldInfo.getPropertyName() +"\") "+fieldInfo.getJavaType()+" "+fieldInfo.getPropertyName());
                 if(index<fieldInfoList.size()){
                     methodParamName.append(",");
                 }
             }
             BuildComment.createFieldComment(bw,"根据"+methodName+"查询");
             bw.write("\t T selectBy"+ methodName+"("+methodParamName+");");
             bw.newLine();
             bw.newLine();

             BuildComment.createFieldComment(bw,"根据"+methodName+"更新");
             bw.write("\t Integer updateBy"+ methodName+"(@Param(\"bean\")T t,"+methodParamName+");");
             bw.newLine();
             bw.newLine();

             BuildComment.createFieldComment(bw,"根据"+methodName+"删除");
             bw.write("\t Integer deleteBy"+ methodName+"("+methodParamName+");");
             bw.newLine();
             bw.newLine();

在已创建的 Mapper 文件中继续写入内容。首先生成包名和导入语句,然后生成接口的类名和声明。接着,根据表信息中的键索引映射生成一系列的查询、更新和删除方法。

1.生成包名和导入语句:

  • bw.write("package "+Constants.PACKAGE_MAPPER+";");:写入指定的包名,通过常量Constants.PACKAGE_MAPPER获取包名,方便项目中统一管理包结构。
  • bw.write("import org.apache.ibatis.annotations.Param;");:导入 MyBatis 的@Param注解,用于在方法参数中标识参数名称。

2.生成类名和接口声明:

  • BuildComment.createClassComment(bw, tableInfo.getComment()+" Mapper");:调用自定义的方法生成类的注释,根据表的注释信息为接口添加注释。
  • bw.write("public interface "+className+"<T, P> extends BaseMapper {");:写入接口的声明,包括接口名称(根据表信息生成)、泛型参数<T, P>以及继承自BaseMapper接口。

3.生成方法:

  • Map<String, List> keyIndexMap = tableInfo.getKeyIndexMap();:获取表的键索引映射,其中键是索引名称,值是包含字段信息的列表,通过遍历键索引映射,为每个索引生成查询、更新和删除方法:
  • 生成方法注释:BuildComment.createFieldComment(bw,"根据"+methodName+"查询");,根据构建的方法名生成注释,说明方法的功能。
  • 生成查询方法:bw.write("\t T selectBy"+ methodName+"("+methodParamName+");");,写入查询方法,返回类型为泛型T,参数列表为构建的方法参数。
  • 生成更新方法:bw.write("\t Integer updateBy"+ methodName+"(@Param("bean")T t,"+methodParamName+");");,写入更新方法,返回类型为整数,表示受影响的行数,参数包括泛型对象t和方法参数。
  • 生成删除方法:bw.write("\t Integer deleteBy"+ methodName+"("+methodParamName+");");,写入删除方法,返回类型为整数,表示受影响的行数,参数为方法参数。

三、创建query文件以及生成导入语句

        //创建Query包
        File folder = new File(Constants.PATH_VO);
        if(!folder.exists()){
            folder.mkdirs();
        }

        String className= tableInfo.getBeanName()+Constants.SUFFIX_BEAN_Query;
        //创建Query文件
        File file = new File(folder,className+".java");
        OutputStream out=null;
        OutputStreamWriter writer=null;
        BufferedWriter bw=null;

        out=new FileOutputStream(file);
        writer=new OutputStreamWriter(out,"UTF-8");
        bw=new BufferedWriter(writer);


        //生成包名
        bw.write("package "+Constants.PACKAGE_Query+";");
        bw.newLine();
        bw.newLine();
        //生成导入包
        if(tableInfo.getHaveBigDecimal()){
           bw.write("import java.math.BigDecimal;");
           bw.newLine();
         }

         if(tableInfo.getHaveDate()||tableInfo.getHaveDateTime()){
             bw.write("import java.util.Date;");
             bw.newLine();
           }

创建一个Query的Java 类文件所在的包目录,如果目录不存在则创建。然后根据表信息生成类名,并创建文件输出流。接着,在文件中写入包名和可能需要的导入语句,具体导入的包取决于表信息中是否包含特定的数据类型(如BigDecimal、Date等)。

1.创建包目录:

  • File folder = new File(Constants.PATH_Query);:通过常量指定 Query 文件所在的包路径,并创建一个File对象表示该目录。
  • if(!folder.exists()){ folder.mkdirs(); }:如果目录不存在,则创建该目录,确保 Query 文件有正确的存储位置。

2.生成类名和创建文件:

  • String className = tableInfo.getBeanName()+Constants.SUFFIX_BEAN_Query;:根据表信息和一个常量后缀生成 Query 类的类名。
  • File file = new File(folder,className+".java");:创建一个表示 Query 文件的File对象,确定了文件的存储路径和名称。

3.创建输出流:

  • OutputStream out = null;、OutputStreamWriter writer = null;、BufferedWriter bw = null;:声明输出流相关的变量,用于后续写入文件内容。
  • out = new FileOutputStream(file);、writer = new OutputStreamWriter(out,"UTF-8");、bw = new BufferedWriter(writer);:依次创建文件输出流、将输出流转换为字符输出流并使用指定的字符编码,最后创建缓冲字符输出流,提高写入文件的效率。

4.写入文件内容:

  • bw.write("package "+Constants.PACKAGE_Query+";");:写入包名,通过常量指定包名,方便在不同的项目环境中进行配置。
  • 根据表信息判断是否需要导入特定的包:
  • if(tableInfo.getHaveBigDecimal()){ bw.write("import java.math.BigDecimal;"); bw.newLine(); }:如果表中包含BigDecimal类型的数据,则导入java.math.BigDecimal包。
  • if(tableInfo.getHaveDate()||tableInfo.getHaveDateTime()){ bw.write("import java.util.Date;"); bw.newLine(); }:如果表中包含Date或DateTime类型的数据,则导入java.util.Date包。

四、生成的query基本字段、注释以及对应的 getter 和 setter 方法

            //生成基本字段和注解
            for(FieldInfo fieldInfo:tableInfo.getFieldIofoList()){
                BuildComment.createFieldComment(bw, fieldInfo.getComment()+"查询对象");

                bw.write("\tprivate "+fieldInfo.getJavaType()+" "+fieldInfo.getPropertyName()+";");
                bw.newLine();

                if(ArrayUtils.contains(Constants.SQL_STRING_TYPES,fieldInfo.getSqlType())){
                    String propertyName=fieldInfo.getPropertyName()+Constants.SUFFIX_BEAN_VO_LIKE;
                    bw.write("\tprivate "+fieldInfo.getJavaType()+" "+propertyName+";");
                    bw.newLine();
                    bw.newLine();
                }
                if(ArrayUtils.contains(Constants.SQL_DATE_TIME_TYPES,fieldInfo.getSqlType())||ArrayUtils.contains(Constants.SQL_DATE_TYPE,fieldInfo.getSqlType())){
                    bw.write("\tprivate String"+" "+fieldInfo.getPropertyName()+Constants.SUFFIX_BEAN_VO_DATE_START+";");
                    bw.newLine();
                    bw.newLine();

                    bw.write("\tprivate String"+" "+fieldInfo.getPropertyName()+Constants.SUFFIX_BEAN_VO_DATE_END+";");
                    bw.newLine();
                    bw.newLine();
                }
            }



            buildGetSet(tableInfo.getFieldIofoList(),bw);
            buildGetSet(tableInfo.getFieldExtendInfoList(),bw);

            bw.write("}");
            bw.flush();

生成的Query类添加基本字段、注释以及对应的 getter 和 setter 方法。它遍历表的字段信息列表,根据字段的类型生成不同的额外字段,并在最后为所有字段生成 getter 和 setter 方法。

1.遍历字段信息列表:

  • for(FieldInfo fieldInfo:tableInfo.getFieldIofoList()){...}:遍历表信息中的字段信息列表。对于每个字段信息,执行以下操作。
  • BuildComment.createFieldComment(bw, fieldInfo.getComment()+"查询对象");:调用自定义的方法生成字段的注释,描述字段的用途。
  • bw.write("\tprivate "+fieldInfo.getJavaType()+" "+fieldInfo.getPropertyName()+";");:写入字段的声明,包括访问修饰符private、字段类型和字段名称。
  • 根据字段类型生成额外字段:

2.生成 getter 和 setter 方法:

  • buildGetSet(tableInfo.getFieldIofoList(),bw);:调用自定义的方法为字段信息列表中的字段生成 getter 和 setter 方法。
  • buildGetSet(tableInfo.getFieldExtendInfoList(),bw);:如果存在扩展字段信息列表,也为其生成 getter 和 setter 方法。

3.结束类的定义:

  • bw.flush();:刷新缓冲输出流,确保数据被写入文件

如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!

微信图片_20241011223609.jpg