荼蘼熊 发表于 2019-3-13 17:43

Spring-boot+Mybatis自动代码生成器-其实是API自动生成,只能放一部分

本帖最后由 荼蘼熊 于 2019-3-22 10:55 编辑

ps:刚来这个网站。总算是注册成功了。放出我的代码,嗯自己写自动代码生成器的。其实是API的阉割版,但是涉及合同。
**回归主题了

# 功能
获取数据库的单表。自动生成 mybatis 需要的映射文件和实体类。此源码不涉及前端、及 service 层和 controller 层

# 思路
按照架构分层,生成顺序为 实体类(model)层 -->> 映射文件(dao)层 -->> 使用映射文件(service)层 -->> 控制器(controller)层
//当前页 mysql 数据库为例
* 获取当前数据库的所有的单表名、字段、字段类型。
* 生成 model 层 .java 文件,将表名字当做类名,字段名当做成员变量,编译 .java 文件 生成 .class 文件。
* 生成 dao 层 .java 文件,将(表名字加 Mapper) 当做类名,生成通用查询方法和语句,编译 .java 文件 生成 .class 文件。
* 通过 spring-boot 的热加载,即可操作生成的 .class

# 实现及源码

### 配置数据库连接、及获取数据库表的映射文件
因为用的是 spring-boot 框架,直接在 .yml 文件配置就可以了。

# Mysql
spring:
datasource:
    name: testDatasource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/XXX?useUnicode=true&characterEncoding=utf8
    username: root
    password: *

mysql 数据库获取当前库所有表和所有表的属性


package top.xiong.snark.dao;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import java.util.List;
import java.util.Map;

/**
* 获取当前数据库的表和字段
*/
@Mapper
public interface TableMapper {
    /**
   * 获取当前数据库的表
   * @Return {"TABLE_NAME":"commodity"}
   */
    @Select("select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA=(select database())")
    List<Map> getAllTable();

    /**
   * 获取当前数据库的表的字段
   * @Param tableName
   * @return {"TABLE_NAME":"commodity","COLUMN_NAME":"id","DATA_TYPE":"bigint"}
   */
    @Select("select TABLE_NAME,COLUMN_NAME,DATA_TYPE from information_schema.columnswhere TABLE_SCHEMA=(select database()) and TABLE_NAME=#{tableName}")
    List<Map<String, String>> getTableColumns(@Param("tableName") String tableName);
}



###service层调用关系
package top.xiong.snark.service;

import org.springframework.stereotype.Service;
import top.xiong.snark.dao.TableMapper;
import top.xiong.snark.helper.FileGenerateHelper;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;


@Service
public class UserService implements IUserService {
   
    @Resource
    private TableMapper tableMapper;

    /**
   * 获取当前数据库的表
   * @return table
   */
    @Override
    public void getAllTable() {
      //获取的所有的表
      List<Map> mapList= tableMapper.getAllTable();
      //文件自动生成帮助类
      FileGenerateHelper helper = new FileGenerateHelper();
      for (int i = 0; i < mapList.size(); i++) {
            Object tableName= mapList.get(i).get("TABLE_NAME");
            //获取当前表的属性
            List<Map<String, String>> mapsColumns=getAllTableColumns(tableName.toString());
            //开始生成文件
            helper.writeToJava(mapsColumns);

      }
    }

    /**
   * 获取当前数据库的表的字段
   * @return tableColumn
   */
    @Override
    public List<Map<String, String>> getAllTableColumns(String tableName) {
      return tableMapper.getTableColumns(tableName);

    }
}


### 构建类和sql语句,很简单,但是你可以自己扩展
package top.xiong.snark.helper;

import com.squareup.javapoet.*;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import top.xiong.snark.annotation.Column;
import top.xiong.snark.annotation.TbName;
import top.xiong.snark.util.StringUtil;
import top.xiong.snark.util.TypeCastUtil;

import javax.lang.model.element.Modifier;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.IOException;
import java.util.*;

/**
* 生成 .java 文件
*
* @AuThor bj
* @version 1.0
*/
public class FileGenerateHelper {
    //生成文件的路径 top.xiong.snark.dao
    private String dao_Path;
    //生成文件的路径 top.xiong.snark.model
    private String model_Path;
    //表名
    private String tableName;
    //表、字段
    private List<Map<String,String>> mapList = new ArrayList<>();
    //项目路径
    private File file;
   
    /**
   * 自动,路径 包含在 启动类 包路径下
   */
    public FileGenerateHelper() {
      dao_Path = "top.xiong.snark.dao";
      model_Path= "top.xiong.snark.model";
      this.file=new File(System.getProperty("user.dir") + "/src/main/java");

    }
    /**
   * 手动,需要设置 model 和 dao 路径
   */
    public FileGenerateHelper(String dao_Path,String model_Path) {
      this.dao_Path=dao_Path;
      this.model_Path=model_Path;
      this.file=new File(System.getProperty("user.dir") + "/src/main/java");
    }


    /**
   * 如若不传 .java 文件的绝对路径
   */
    public void writeToJava(List<Map<String,String>> mapList) {
      this.mapList=mapList;
      //表名称
      this.tableName=StringUtil.toUpperCase(mapList.get(0).get("TABLE_NAME").toString());
      //生成 model
      this.writeToJavaModel();
      //生成 dao
      this.writeToJavaDao();

    }

    /**
   * 生成 .java model 层
   */
    private void writeToJavaModel() {
      List<FieldSpec> fieldSpecIterable=new LinkedList<>();
      List<MethodSpec> methodSpecs = new LinkedList<>();
      int size =mapList.size();
                //初始化 @override toString
      MethodSpec toStringSpec= MethodSpec.methodBuilder("toString")
                .addAnnotation(Override.class)
                .addModifiers(Modifier.PUBLIC)
                .returns(String.class)
                .addCode("return \"$N { \" + \"",tableName)
                .build();
      for (Map map : mapList) {
            //构建 field
            FieldSpec fieldSpec = FieldSpec
                  .builder(TypeCastUtil.getJavaType(map.get("DATA_TYPE").toString()), map.get("COLUMN_NAME").toString(),Modifier.PRIVATE)
                  .addAnnotation(AnnotationSpec.builder(Column.class).addMember("value", "$S",map.get("COLUMN_NAME").toString()).build())
                  .build();
            fieldSpecIterable.add(fieldSpec);
            //构建 get method
            MethodSpec getMethod= MethodSpec.methodBuilder("get"+ StringUtil.toUpperCase(map.get("COLUMN_NAME").toString()))
                  .addModifiers(Modifier.PUBLIC)
                  .returns(TypeCastUtil.getJavaType(map.get("DATA_TYPE").toString()))
                  .addStatement("return this.$N",map.get("COLUMN_NAME").toString())
                  .build();
            methodSpecs.add(getMethod);
            //构建 set method
            MethodSpec setMethod= MethodSpec.methodBuilder("set"+ StringUtil.toUpperCase(map.get("COLUMN_NAME").toString()))
                  .addModifiers(Modifier.PUBLIC)
                  .returns(void.class)
                  .addParameter(TypeCastUtil.getJavaType(map.get("DATA_TYPE").toString()), map.get("COLUMN_NAME").toString())
                  .addStatement("this.$N = $N",map.get("COLUMN_NAME").toString(),map.get("COLUMN_NAME").toString())
                  .build();
            methodSpecs.add(setMethod);
            if (size < mapList.size()) {
                toStringSpec= toStringSpec.toBuilder().addCode("\", ")
                        .build();
            }
            //构建 toString()
            toStringSpec= toStringSpec.toBuilder().addCode("$N =\" + $N +", map.get("COLUMN_NAME").toString(),map.get("COLUMN_NAME").toString())
                  .build();
            size--;
      }
      //构建 toString()
      toStringSpec=toStringSpec.toBuilder().addStatement("'}'")
                .build();
      //构建 构造函数
      MethodSpec methodSpec= MethodSpec.constructorBuilder()
                .addModifiers(Modifier.PUBLIC)
                .build();
      //构建 类
      TypeSpec typeSpec = TypeSpec.classBuilder(tableName)
                .addModifiers(Modifier.PUBLIC)
                .addAnnotation(AnnotationSpec.builder(TbName.class).addMember("name","$S", tableName).build())
                .addMethod(methodSpec)
                .addFields(fieldSpecIterable)
                .addMethods(methodSpecs)
                .addMethod(toStringSpec)
                .build();

      //生成文件
      JavaFile javaFile= JavaFile.builder(model_Path,typeSpec).build();
      try {
            javaFile.writeTo(file);
            JavaCompiler compiler=ToolProvider.getSystemJavaCompiler();
            String matchPath=model_Path;
            compiler.run(null, null, null, "-d", System.getProperty("user.dir")+"/target/classes", System.getProperty("user.dir") + "/src/main/java/"+matchPath.replace(".", "/")+"/"+tableName+".java");

      } catch (IOException e) {
            e.printStackTrace();
      }

    }

    /**
   * 生成 .java dao 层
   */
    public void writeToJavaDao() {
      CoreProviderHelper coreHelper= new CoreProviderHelper();
      try {
            TypeName list = ParameterizedTypeName.get(ClassName.get("java.util", "List"), ClassName.get(model_Path.replace("/", "."),tableName));

            //加载类
            Class cls=Class.forName(model_Path+"."+tableName);

            //构建查全表的方法
            TypeSpec typeSpec= TypeSpec.interfaceBuilder(tableName+"Mapper")
                  .addModifiers(Modifier.PUBLIC)
                  .addMethod(MethodSpec.methodBuilder("select"+tableName+"All")
                            .addModifiers(Modifier.PUBLIC,Modifier.ABSTRACT)
                            .returns(list)
                            .addAnnotation(AnnotationSpec.builder(Select.class)
                                    .addMember("value", "$S",coreHelper.getSelectAllCore(cls.newInstance()))
                                    .build())
                            .build())
                  .addAnnotation(Mapper.class)
                  .build();
            JavaFile javaFile= JavaFile.builder(dao_Path,typeSpec).build();
            javaFile.writeTo(file);
            String matchPath=dao_Path;
            JavaCompiler compiler=ToolProvider.getSystemJavaCompiler();
            compiler.run(null, null, null, "-d", System.getProperty("user.dir")+"/target/classes", System.getProperty("user.dir") + "/src/main/java/"+matchPath.replace(".", "/")+"/"+tableName+"Mapper.java");

      }catch (Exception e){
            e.printStackTrace();
      }

    }





    public void setDao_Path(String dao_Path) {
      this.dao_Path = dao_Path;
    }

    public void setModel_Path(String model_Path) {
      this.model_Path = model_Path;
    }

    public File getFile() {
      return file;
    }

    public void setFile(File file) {
      this.file = file;
    }
}


###生成sql的帮助类
package top.xiong.snark.helper;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.xiong.snark.annotation.Column;
import top.xiong.snark.annotation.TbName;
import top.xiong.snark.util.CollectionUtil;
import top.xiong.snark.util.StringUtil;

import java.lang.reflect.Field;
import java.text.MessageFormat;
import java.util.Map;

/**
* 代码提供
*
* @author bj
* @version 2.0
*/
public class CoreProviderHelper {
    private static final Logger LOG = LoggerFactory.getLogger(CoreProviderHelper.class);

    public static String ClassName = "clz";

    /**
   * 获取表名称
   *
   * @param obj
   * @return tableName
   */
    protected String getTableName(@org.jetbrains.annotations.NotNull Object obj) {
      Class clz = obj.getClass();
      if (clz.isAnnotationPresent(TbName.class)) {
            TbName tableName = obj.getClass().getAnnotation(TbName.class);
            return tableName.name();
      } else {
            return obj.getClass().getName();
      }

    }

    /**
   * sql 条件字符串化
   *
   * @param map
   * @return
   */
    private StringBuilder mapToStringBuilder(Map<String, String> map) {
      int size = map.size();
      if (CollectionUtil.isEmpty(map)) {
            return new StringBuilder(" 1=1 ");
      }
      StringBuilder sb = new StringBuilder();
      for (Map.Entry<String, String> entry : map.entrySet()) {
            // key = #{className.value}
            sb.append(entry.getKey()).append(" = ")
                  .append("#{").append(ClassName).append(".").append(entry.getValue()).append("}");
            if (size > 1) {
                sb.append(" and ");
                size--;
            }
      }
      return sb;
    }

    /**
   * 生成 insert all sql
   *
   * @param obj
   * @param size
   * @param mysql
   * @return
   */
    protected String getInsertAllCore(Object obj, int size, boolean mysql) {
      Class clz = obj.getClass();
      Field[] fields = clz.getDeclaredFields();
      StringBuilder classField = new StringBuilder(" ( ");
      StringBuilder tbColumn = new StringBuilder(" ( ");
      for (Field field : fields) {
            try {
                field.setAccessible(true);
                if (field.isAnnotationPresent(Column.class) && field.get(obj) != null) {
                  Column fieldAnnotation = field.getAnnotation(Column.class);
                  //输出注解属性
                  String columnTemp = StringUtil.isEmpty(fieldAnnotation.value()) ? field.getName() : fieldAnnotation.value();
                  //mysql 格式:`user`, ; 其余 user,
                  tbColumn.append(mysql ? "`" + columnTemp + "`," : columnTemp + ",");
                  classField.append("#'{'").append("list[{0}]").append(".").append(field.getName()).append("'}'")
                            .append(",");
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
      }
      tbColumn.setCharAt(tbColumn.length() - 1, ')');
      StringBuilder sb = new StringBuilder("insert into ");
      sb.append(getTableName(obj));
      sb.append(tbColumn).append(" values ");
      classField.setCharAt(classField.length() - 1, ')');
      MessageFormat messageFormat = new MessageFormat(classField.toString());
      StringBuilder temp = new StringBuilder();
      for (int i = 0; i < size; i++) {
            temp.append(messageFormat.format(new Integer[]{i}));
            temp.append(",");
      }
      temp.deleteCharAt(temp.length() - 1);
      classField = temp;
      sb.append(classField);
      return sb.toString();
    }

    /**
   * 单条 insert sql
   *
   * @param obj
   * @param mysql
   * @return sql
   */
    protected String getInsertCore(Object obj, boolean mysql) {
      Field[] fields = obj.getClass().getDeclaredFields();
      StringBuilder tbColumn = new StringBuilder(" ( ");
      StringBuilder classField = new StringBuilder(" ( ");
      for (Field field : fields) {
            field.setAccessible(true);
            if (field.isAnnotationPresent(Column.class)) {
                Column column = field.getAnnotation(Column.class);
                String columnName = StringUtil.isEmpty(column.value()) ? field.getName() : column.value();
                tbColumn.append(mysql ? "`" + columnName + "`" : columnName + ",");
                classField.append("#{").append(ClassName).append(".").append(columnName).append("}").append(",");
            }

      }
      tbColumn.setCharAt(tbColumn.length() - 1, ')');
      classField.setCharAt(classField.length() - 1, ')');

      StringBuilder sb = new StringBuilder("insert into ");
      sb.append(getTableName(obj)).append(tbColumn).append(" values ").append(classField);

      return sb.toString();
    }

    /**
   * 生成查询全表的 sql
   * @param obj
   * @return select
   */
    public String getSelectAllCore(Object obj) {
      StringBuilder classField = new StringBuilder();
      Field[] fields = obj.getClass().getDeclaredFields();
      for (Field field : fields) {
            field.setAccessible(true);
            if (field.isAnnotationPresent(Column.class)) {
                Column column = field.getAnnotation(Column.class);
                String value = StringUtil.isEmpty(column.value()) ? field.getName() : column.value();
                classField.append(value).append(",");
            }

      }
      classField.deleteCharAt(classField.length()-1);
      StringBuilder sb= new StringBuilder("select ");
      sb.append(classField);
      sb.append(" from " ).append(getTableName(obj));
      return sb.toString();
    }


}

#### pom maven 包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.1.3.RELEASE</version>
      <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>top.xiong</groupId>
    <artifactId>snarkee</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>snark</name>
    <description>Demo project for Spring Boot</description>

    <properties>
      <java.version>1.8</java.version>
      <!-- Mybatis -->
      <snark.mybatis.version>1.3.2</snark.mybatis.version>
      <!-- Mysql -->
      <snark.mysql.version>8.0.13</snark.mysql.version>
      <!-- Generate -->
      <snark.generte.version>1.11.1</snark.generte.version>
      <!--cglib-->
      <snark.cglib.version>3.2.7</snark.cglib.version>

    </properties>

    <dependencies>
      <!-- 可以不用再去关心原生配置方式里的细节,直接使用默认配置就能实现最基本的功能 -->
      <!-- 自动检查Spring Boot的数据源配置并构建DataSource对象
通过SqlSessionFactoryBean使用数据源构建并注册SqlSessionFactory对象
从SqlSessionFactory中创建并注册一个SqlSessionTemplate实例,其实就是构建一个SqlSession对象
自动扫描接口映射器,并将这些映射器与SqlSessionTemplate实例进行关联,同时将它们注册到Spring容器中 -->
      <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>2.1.3.RELEASE</version>
      </dependency>

      <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.1.3.RELEASE</version>
      </dependency>

      <!-- 添加热部署,自动刷新 -->
      <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <version>2.1.3.RELEASE</version>
            <optional>true</optional>
            <scope>true</scope>
      </dependency>

      <!-- 外部 -->
      <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${snark.mybatis.version}</version>
      </dependency>

      <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${snark.mysql.version}</version>
      </dependency>

      <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>${snark.cglib.version}</version>
      </dependency>



      <dependency>
            <groupId>com.squareup</groupId>
            <artifactId>javapoet</artifactId>
            <version>${snark.generte.version}</version>
      </dependency>
      <dependency>
            <groupId>org.jetbrains</groupId>
            <artifactId>annotations</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
      </dependency>

    </dependencies>

    <build>
      <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
                <excludes>
                  <exclude>bootstrap-test.properties</exclude>
                  <exclude>bootstrap-dev.properties</exclude>
                  <exclude>bootstrap-pro.properties</exclude>
                  <exclude>bootstrap.properties</exclude>
                </excludes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
                <includes>
                  <!--<include>bootstrap-${env}.properties</include>-->
                  <include>bootstrap.yml</include>
                </includes>
            </resource>
      </resources>
      <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
      </plugins>
    </build>

</project>

运行就可以的。
如若不懂,我会回复。
我会慢慢分离我自己的代码,早日放源码程序。
第一次发帖好紧张
···今儿才看见有小同志提了问题,关于数据库类型与java类型转换的问题:
*由于我使用的事mybatis自带的type转换注册器,如若您的数据库类型不在org.apache.ibatis.type.JdbcType
此枚举类下,请在我注册转换器的地方top.xiong.snark.util.TypeCastUtil
添加对应转换类型,如:
if ("datetime".equals(typeName)){
    return Date.class;
}
也可以重写我的方法或者重写type注册器,我只是提供一种简单的方式。
链接: https://pan.baidu.com/s/1o9FV9ITOKUEC-Fh-Fcm74Q 提取码: 5gst

荼蘼熊 发表于 2019-3-14 09:08

分离完毕,运行即用
链接: https://pan.baidu.com/s/1o9FV9ITOKUEC-Fh-Fcm74Q 提取码: 5gst

荼蘼熊 发表于 2019-3-22 10:49

WingSky 发表于 2019-3-21 16:46
会报java.lang.IllegalArgumentException: No enum constant org.apache.ibatis.type.JdbcType.INT错误

您的数据库的类型是什么,我用的是mybatis 的枚举转换类,如若没有您的类型,你可以在路径top.xiong.snark.util.TypeCastUtil里面添加此类型的转换;只要在org.apache.ibatis.type.JdbcType枚举类虾没有的数据库参数都需要自己转换,或者自己重写TYPE转换注册类。比如if ("int".equals(typeName)){
                return Integer.class;
            };或者用别的方式

葬天VS晓伟 发表于 2019-3-13 18:09

期待楼主分享最终版

kiritoooo 发表于 2019-3-13 18:29

楼主有springboot的学习视频吗

荼蘼熊 发表于 2019-3-13 18:41

kiritoooo 发表于 2019-3-13 18:29
楼主有springboot的学习视频吗

这个没有,我看的官方文档。可帮你留意一下

荼蘼熊 发表于 2019-3-13 18:41

葬天VS晓伟 发表于 2019-3-13 18:09
期待楼主分享最终版

正在剖离~~

nickle52 发表于 2019-3-13 19:05

期待最终版研究研究

PyScrapy 发表于 2019-3-14 07:42

等待最终版

LLingding 发表于 2019-3-14 09:29

感谢分享,已下载,正在研究

小康康 发表于 2019-3-14 10:25

了解一下{:1_918:}
页: [1] 2 3
查看完整版本: Spring-boot+Mybatis自动代码生成器-其实是API自动生成,只能放一部分