java 小工具 | 封装通用的 Mybatis 生成模板 |1 秒写完增删改查

本贴最后更新于 1807 天前,其中的信息可能已经沧海桑田

开头说两句

Java 基础 Demo 站: https://www.javastudy.cloud
Java 中高级开发博客: https://www.lixiang.red
Java 学习公众号: java 技术大本营
java_subscribe

本文源地址: https://www.lixiang.red/articles/2019/07/31/1564585601970.html

项目背景

前段时间我们介绍了如何使用 thymeleaf 去生成 mybatis 相关的模板:
https://www.lixiang.red/articles/2019/07/23/1563857782748.html
今天小刀和各位小伙伴们一起来深入下这个问题,我们来研究下怎么去封装一个通用的 Mybatis 模板,让简单的增删改查操作,直接通过我们的代码生成工具就能解决,让工具从 demo 级别上升到生产可用的项目

mybatis 简单介绍

相信很多小伙伴都已经用过 Mybatis 了,很简单方便, 在 springboot 的集成环境里面,写个 DAO 接口,加上注解,然后写个 xml 和 DAO 关联起来,然后 xml 里面方法和 DAO 里面的方法关联起来,然后就可以通过调用 DAO 方法的形式来调用这个 sql,并获得相应的返回结果,代码结构如图下所示:
image.png
这是以往的方式,在新版 Mybatis 中,我们有了一个新的选择,用 java 类和注解的方式去完成一个 sql ,官方文档如下:
http://www.mybatis.org/mybatis-3/statement-builders.html
写法如下:
image.png
只建议简单的 sql 用注解的方式去处理,复杂的还是要手写 sql , 可以重新建一个 DAO , 或者在 provider 里面用字符串拼接的方式去完成
具体用法,大家可以参考上面的官方文档中的地址

设计封装的方法

封装规则要求

我们总说,我们一直都在做重复的增删改查工作,现在我们就可以把这些基本的操作都封装起来,把更多的精力放在应用高可用,高响应,业务逻辑的梳理上面
因此 ,我们的封装也是以增删改查四大方向为主.
在封装之前,我们先约定以下规定:
1.数据库对应的实体对象,我们用 DO(domain object)来表示
2.数据库对应的查询对应,我们用 QC(query object)来表示
3.各种操作方法的命名,如,getModel,listModels,saveModels 等等
这样把数据库实体对象和查询对象分开,因为我们的在加查询条件的时候,比如通过 idList 去查询,如果在 DO 里面新加字段就不太好,所以抽象了一个 QC 的概念,专门做数据库查询对象。

从前到后的对应关系

为什么我们项目可以用模板生成工具进行生成呢,大家仔细研究下手中的项目可以发现我们的项目从 controller 开始,到最后的 DAO 其实都是有名字对应起来的,我们以查询为例: listItems,通过这个名字我们可以直观的看到,是获取商品列表的.
Controller 中的代码:

@GetMapping("/item/query")
    @ResponseBody
    public BaseResponse<PageData<ItemDO>> queryItem(ItemDO item, Integer pageIndex , Integer pageSize){

        List<ItemDO> items = itemManager.queryItem(item, pageIndex, pageSize);
        Long totalCount = itemManager.countItem(item);

        pageIndex = pageIndex == null?1:pageIndex;
        pageSize = pageSize == null?20:pageSize;
        return  BaseResponse.assemblePageResponse(items,totalCount,pageIndex,pageSize);
    }

模板:

@GetMapping("/[(${table.javaCamelName})]/query")
    @ResponseBody
    public BaseResponse<PageData<[(${table.javaTableName})]DO>> query[(${table.javaTableName})]([(${table.javaTableName})]DO [(${table.javaCamelName})], Integer pageIndex , Integer pageSize){

        List<[(${table.javaTableName})]DO> [(${table.javaCamelName})]s = [(${table.javaCamelName})]Manager.query[(${table.javaTableName})]([(${table.javaCamelName})], pageIndex, pageSize);
        Long totalCount = [(${table.javaCamelName})]Manager.count[(${table.javaTableName})]([(${table.javaCamelName})]);

        pageIndex = pageIndex == null?1:pageIndex;
        pageSize = pageSize == null?20:pageSize;
        return  BaseResponse.assemblePageResponse([(${table.javaCamelName})]s,totalCount,pageIndex,pageSize);
    }

Manager 中的代码:

public List<ItemDO> queryItem(ItemDO item, int pageIndex , int pageSize);

模板:

    public List<[(${table.javaTableName})]DO> query[(${table.javaTableName})]([(${table.javaTableName})]DO [(${table.javaCamelName})], int pageIndex , int pageSize);

ManagerImpl 中的代码:

@Override
    public List<ItemDO> queryItem(ItemDO item, int pageIndex , int pageSize){
        Page page = new Page(pageIndex,pageSize);
        ItemQC qc  = new ItemQC();
        qc.setPage(page);
        BeanUtils.copyProperties(item,qc);
        List<ItemDO> items = itemMapper.listItems(qc);
        return items;
    }

模板:

@Override
    public List<[(${table.javaTableName})]DO> query[(${table.javaTableName})]([(${table.javaTableName})]DO [(${table.javaCamelName})], int pageIndex , int pageSize){
        Page page = new Page(pageIndex,pageSize);
        [(${table.javaTableName})]QC qc  = new [(${table.javaTableName})]QC();
        qc.setPage(page);
        BeanUtils.copyProperties([(${table.javaCamelName})],qc);
        List<[(${table.javaTableName})]> [(${table.javaCamelName})]s = [(${table.javaCamelName})]Mapper.list[(${table.javaTableName})]s(qc);
        return [(${table.javaCamelName})]s;
    }

Mapper 中的代码:

@SelectProvider(type = ItemProvider.class)
    List<ItemDO> listItems(ItemQC itemQC);

模板:

@SelectProvider(type = [(${table.javaTableName})]Provider.class)
    List<[(${table.javaTableName})]DO> list[(${table.javaTableName})]s([(${table.javaTableName})]QC [(${table.javaCamelName})]QC);

Provider 中的代码:

public String listItems(ItemQC itemQC){
        SQL sql = new SQL() {{
            SELECT(TABLE_FIELDS);
            FROM("item");
        }};
        MapperUtils.richWhereSql(sql, itemQC);

        return sql.toString();
    }

模板:

public String list[(${table.javaTableName})]s([(${table.javaTableName})]QC [(${table.javaCamelName})]QC){
        SQL sql = new SQL() {{
            SELECT(TABLE_FIELDS);
            FROM("[(${table.javaCamelName})]");
        }};
        MapperUtils.richWhereSql(sql, [(${table.javaCamelName})]QC);

        return sql.toString();
    }

这样我们从前到后看过来,发其实都是围绕着一个关键词在走: Item , 有时候把他变成大写的,有时候把他变成驼峰命名,有时候把他加个 DO 后缀,有时候把他加个 Mapper 后缀.

生成文件到对应目录中

在上篇文章中,我们只是生成了对应的字符串,但是在实际开发中,我们是需要生成文件的,最好是能在对应文件夹中.
所以这就有两步,1 是生成文件.2 是找到对应文件夹

生成对应文件

生成文件这个应该是很简单的,直接一句代码搞定:

            Files.write(Paths.get(pathMap.get("model")+tableInfo.getJavaTableName() + "DO.java"),model.getBytes());
            Files.write(Paths.get(pathMap.get("mapper")+tableInfo.getJavaTableName() + "Mapper.java"),dao.getBytes());
            Files.write(Paths.get(pathMap.get("provider")+tableInfo.getJavaTableName() + "Provider.java"),provider.getBytes());
            Files.write(Paths.get(pathMap.get("manager")+tableInfo.getJavaTableName() + "Manager.java"),manager.getBytes());
            Files.write(Paths.get(pathMap.get("managerImpl")+tableInfo.getJavaTableName() + "ManagerImpl.java"),managerImpl.getBytes());
            Files.write(Paths.get(pathMap.get("controller")+tableInfo.getJavaTableName() + "Controller.java"),controller.getBytes());
        

输出到对应文件夹中

这一步,就要我们明确,我们的每个文件是放在什么地方在,如 Mapper/Provider 是查询数据库用的, 我们把他放在 business-impl 模块中的 dao 文件夹中,下面小刀把自己项目中放的位置列出来和大家分享下,大家有好的想法可以一起交流:
XXXDO 放在 business 模块中 models.dos 文件夹中
XXXController 放在 admin 模块中 controller 文件夹中
XXXManager 放在 business 模块中 business 文件夹中
XXXManagerImpl 放在 business-impl 模块中 business.impl 文件夹中
XXXMapper/XXXProvider 放在 business-impl 模块中 dao 文件夹中
这样我们就可以用代码去定位到对应的文件夹中了:

    // 获得当前项目的文件夹
    String currentDir  = System.getProperty("user.dir");
            currentDir=currentDir.substring(0,currentDir.lastIndexOf("/"));
            pathMap.put("model",currentDir);
            pathMap.put("mapper",currentDir);
            pathMap.put("provider",currentDir);
            pathMap.put("manager",currentDir);
            pathMap.put("managerImpl",currentDir);
            pathMap.put("controller",currentDir);
            //如果设置了自动路径,就YY一下路径
            if(autoFilePath){
                Files.list(Paths.get(currentDir)).forEach(file->{
                    if(Files.isDirectory(file)){
                        String fileName = file.toString();

                        if(fileName.endsWith("admin")){
                            pathMap.put("controller",fileName+"/src/main/java/"+packageName.replaceAll("\\.","/")+"/admin/controller/");
                        }
                        if(fileName.endsWith("business")){
                            pathMap.put("model",fileName+"/src/main/java/"+packageName.replaceAll("\\.","/")+"/models/dos/");
                            pathMap.put("manager",fileName+"/src/main/java/"+packageName.replaceAll("\\.","/")+"/business/manager/");
                        }
                        if(fileName.endsWith("business-impl")){
                            pathMap.put("managerImpl",fileName+"/src/main/java/"+packageName.replaceAll("\\.","/")+"/business/manager/impl/"+domainName+"/");
                            pathMap.put("mapper",fileName+"/src/main/java/"+packageName.replaceAll("\\.","/")+"/dao/"+domainName+"/");
                            pathMap.put("provider",fileName+"/src/main/java/"+packageName.replaceAll("\\.","/")+"/dao/"+domainName+"/");
                        }
                    }

                });
            }

这样我们就可以做到,把增删改查从 controller 到 dao 直接一键生成到对应文件夹啦,只要把表建好,剩下的事就一步搞定啦

最后说两句

这个工具一般是做后台管理页面,增删改查的时候用, 业务逻辑不建议用工具类生成,业务逻辑一般是提供 dubbo 接口出来,不要直接把 manager 中的增删改查提供出去,提供出去的一定是要先有业务,再有接口,然后在 serviceImpl 中调用 manager 去完成业务逻辑
大家有什么想法,欢迎留言或者加小刀微信: best396975802

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3190 引用 • 8214 回帖 • 1 关注
  • MyBatis

    MyBatis 本是 Apache 软件基金会 的一个开源项目 iBatis,2010 年这个项目由 Apache 软件基金会迁移到了 google code,并且改名为 MyBatis ,2013 年 11 月再次迁移到了 GitHub。

    170 引用 • 414 回帖 • 387 关注
  • 工具

    子曰:“工欲善其事,必先利其器。”

    288 引用 • 734 回帖 • 2 关注
1 操作
xiaodaojava 在 2019-08-04 21:22:52 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • someone
    作者

    哈哈,实不相瞒,看着确实挺难受的,生产上的代码一直在用阿里巴巴的那个插件在扫, 然后工具类的代码就有点任性了, 时间充足了就改改,时间不充足了就先挖坑了

  • 其他回帖
  • 特别想问有那些警告看着不难受嘛。。。强迫症看着心理真的堵得慌,每次能消就消,不能就强制用注解消。。。写代码一直遵循消除警告原则。