美文网首页java面试总结
人生苦短, 编写Mybatis自动代码生成器, 解放自己

人生苦短, 编写Mybatis自动代码生成器, 解放自己

作者: asfwaefdwqd | 来源:发表于2018-11-29 17:14 被阅读36425次

    原博地址https://laboo.top/2018/11/26/a-db/#more

    本文介绍如何用Java编写高度自定义的代码生成器

    MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息。

    上面这一段话来自Mybatis官网的介绍, 初用Mybatis时感觉这个框架相比于JDBC优雅多了, 用起来也如官网说的非常简单。但是用了一段时间之后, 弊端就慢慢凸显出来了

    使用Mybatis时不得不为每个表创建一个Entity.javaMapper.xml(Mapper可以融合入Dao中)Dao.java,Service.java 层次很清晰, 但是太多重复性的工作了, 费时间且易于出错

    并且当数据库发生一点改动的时候... 苦不堪言

    后来出现了自动生成代码的插件, 但是总是不尽人意, 不能随心所欲地控制, 毕竟每个人的需求都不一样

    本文就来介绍如何简单的编写一个自己的代码生成器

    项目源码

    mybatis-generator

    代码实现

    实现的思路很简单, 首先查询数据库的表结构, 得到列名, 列类型...等信息

    创建文件模版, 将这些信息插入模版中, 最后打包模版进压缩包导出

    代码实现 一共五个Java类

    • TableDO
    • ColumnDO
    • GeneratorMapper
    • GeneratorUtils
    • GeneratorService

    首先来看两个实体类

    TableDO 和 ColumnDO

    TableDO 存放表名, 对于的类名, 以及列信息

    完整类代码 TableDO.java

    public class TableDO {
    
        private String tableName;
        private List<ColumnDO> columns;
        private String className;
        private String suffix;
    
        // get()... set()...
    }
    

    ColumnDO 存放列名, 数据库字段类型, 以及对应Java中的属性名和类型

    完整类代码 ColumnDO.java

    public class ColumnDO {
    
        private String columnName;
        private String dataType;
        private String attrName;
        private String attrLowerName;
        private String attrType;
    
        // get()... set()...
    }
    

    GeneratorMapper

    在GeneratorMapper 中, 我们通过表名查询表自动的信息

    完整类代码 GeneratorMapper.java

    @Mapper
    public interface GeneratorMapper {
    
        @Select("select column_name columnName, data_type dataType from information_schema.columns where table_name = #{tableName} and table_schema = (select database()) order by ordinal_position")
        List<ColumnDO> listColumns(String tableName);
    }
    

    GeneratorUtils

    在GeneratorUtils 中进行类信息与模版之间的转换

    完整类代码 GeneratorUtils.java

    将表信息放入Velocity模版的上下文中

    Map<String, Object> map = new HashMap<>();
    map.put("tableName", table.getTableName());
    map.put("className", table.getClassName());
    map.put("pathName", getPackageName().substring(getPackageName().lastIndexOf(".") + 1));
    map.put("columns", table.getColumns());
    map.put("package", getPackageName());
    map.put("suffix", table.getSuffix());
    
    Properties prop = new Properties();
    prop.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
    Velocity.init(prop);
    VelocityContext context = new VelocityContext(map);
    

    添加模版

    List<String> templates = new ArrayList<>();
    templates.add("mybatis/Model.java.vm");
    templates.add("mybatis/Query.java.vm");
    templates.add("mybatis/Dao.java.vm");
    templates.add("mybatis/Mapper.xml.vm");
    templates.add("mybatis/Service.java.vm");
    

    编译模版

    StringWriter sw = new StringWriter();
    Template tpl = Velocity.getTemplate(template, "UTF-8");
    tpl.merge(context, sw);
    

    Utils类完成了生成代码的主要工作, 但是代码也是比较简单的

    GeneratorService

    在Service 中注入Mapper 查询列信息, 并用Utils生成代码, 然后导出压缩包

    完整类代码 GeneratorService.java

    @Service
    public class GeneratorService {
    
        @Resource
        private GeneratorMapper generatorMapper;
    
        @Resource
        private Environment environment;
    
        public void generateZip(String[] tableNames, String zipPath) throws IOException {
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ZipOutputStream zip = new ZipOutputStream(outputStream);
            for (String tableName : tableNames) {
                TableDO table = new TableDO();
                table.setTableName(tableName);
                table.setColumns(generatorMapper.listColumns(tableName));
                GeneratorUtils.generatorCode(table, zip,getConfig());
            }
            IOUtils.closeQuietly(zip);
            FileOutputStream file = new FileOutputStream(zipPath);
            file.write(outputStream.toByteArray());
            file.close();
        }
    
        // getConfig ...
    }
    

    VM模版

    自己写代码生成器的好处就是, 可以根据需求定制自己的模版, 下面是我的几个模版可以供参考

    • Mapper.xml.vm
    • Dao.java.vm
    • Service.java.vm
    • Model.java.vm
    • Query.java.vm

    生成的代码是在commons-mybatis架构下使用的

    Dao.java.vm

    package ${package}.database.dao;
    
    import ${package}.database.model.${className}${suffix};
    
    import org.apache.ibatis.annotations.Mapper;
    import org.laziji.commons.mybatis.dao.${suffix}Dao;
    
    @Mapper
    public interface ${className}Dao extends ${suffix}Dao<${className}${suffix}> {
    
    }
    

    ...

    其余模版

    使用

    配置文件

    resources下创建application-${name}.yml文件, ${name}随意, 例如: application-example.yml, 可创建多个

    配置文件内容如下, 填入数据库配置, 以及生成代码的包名, 源文件路径

    spring:
      datasource:
        url: jdbc:mysql://xxx.xxx.xxx.xxx:3306/xxxx?characterEncoding=utf-8
        username: xxxxxx
        password: xxxxxx
    
    generator:
      package: com.xxx.xxx
      resources: mapper
    

    Test

    在test文件下创建测试类

    • @ActiveProfiles("example")中填入刚才配置文件名的name
    • tableNames需要生成的表, 可以多个
    • zipPath 代码导出路径
      运行测试方法即可
    package pg.laziji.generator;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.ActiveProfiles;
    import org.springframework.test.context.junit4.SpringRunner;
    import pg.laziji.generator.mybatis.GeneratorService;
    
    import javax.annotation.Resource;
    import java.io.IOException;
    
    @ActiveProfiles("example")
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class ExampleTest {
    
        @Resource
        private GeneratorService generatorService;
    
        @Test
        public void test() throws IOException {
            String[] tableNames = new String[]{"example_table1", "example_table2"};
            String zipPath = "/home/code.zip";
            generatorService.generateZip(tableNames,zipPath);
        }
    }
    
    

    欢迎关注我的博客公众号


    2018_11_16_0048241709.png

    相关文章

      网友评论

        本文标题:人生苦短, 编写Mybatis自动代码生成器, 解放自己

        本文链接:https://www.haomeiwen.com/subject/ftifcqtx.html