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
分离完毕,运行即用
链接: https://pan.baidu.com/s/1o9FV9ITOKUEC-Fh-Fcm74Q 提取码: 5gst 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;
};或者用别的方式 期待楼主分享最终版 楼主有springboot的学习视频吗 kiritoooo 发表于 2019-3-13 18:29
楼主有springboot的学习视频吗
这个没有,我看的官方文档。可帮你留意一下 葬天VS晓伟 发表于 2019-3-13 18:09
期待楼主分享最终版
正在剖离~~ 期待最终版研究研究 等待最终版 感谢分享,已下载,正在研究 了解一下{:1_918:}