采用架构,springboot+freemarker
工具:idea最新版,jdk1.8,mysql
创建boot工程,加依赖,jdbc,web,freemarker,lombok
创建生成界面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>代码生成</title>
<!-- 开发环境版本,包含了有帮助的命令行警告 vue-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!--引入样式-->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<!--引入组件库-->
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="app">
<table>
<tr>
<td><el-tag size="mini">数据库用户名:</el-tag></td>
<td><el-input siez="mini" v-model="db.username"></el-input></td>
</tr>
<tr>
<td><el-tag size="mini">数据库密码:</el-tag></td>
<td><el-input siez="mini" v-model="db.password"></el-input></td>
</tr>
<tr>
<td><el-tag size="mini">数据库连接地址:</el-tag></td>
<td><el-input siez="mini" v-model="db.url">
<template slot="prepend">jdbc:mysql://</template>
<template slot="append">
?useUnicode=true&characterEncoding=UTF-8
</template>
</el-input></td>
</tr>
</table>
<div style="display: flex">
<el-button type="primary"size="mini" @click="connect":disabled="!connectBtnEnabled">连接数据库</el-button>
<div style="color: #ff0114;font-weight: bold">*{{msg}}*</div>
<el-input v-model="packageName" size="mini" style="width: 300px"></el-input>
<el-button type="primary" size="mini" @Click="config">配置</el-button>
</div>
<el-table
:data="tableData"
stripe
border
style="width: 100%">
<el-table-column
prop="tableName"
label="表名称"
width="180">
</el-table-column>
<el-table-column
label="实体类名称"
width="180">
<template slot-scope="scope">
<el-input v-model="scope.row.modelName"></el-input>
</template>
</el-table-column>
<el-table-column
label="Mapper名称">
<template slot-scope="scope">
<el-input v-model="scope.row.mapperName"></el-input>
</template>
</el-table-column>
<el-table-column
label="Service名称">
<template slot-scope="scope">
<el-input v-model="scope.row.serviceName"></el-input>
</template>
</el-table-column>
<el-table-column
label="Controller名称">
<template slot-scope="scope">
<el-input v-model="scope.row.controllerName"></el-input>
</template>
</el-table-column>
</el-table>
<div>
<el-button @click="generateCode" type="success">生成代码</el-button>
<div style="color: #ff0114;font-weight: bold">{{result}}</div>
<div>{{codePath}}</div>
</div>
</div>
<script>
new Vue({
el:"#app",
data:function (){
return {
tableData: [],
result:'',
codePath:'',
packageName: 'cn.nixiang',
msg:'数据库未连接',
connectBtnEnabled:true,
db:{
username: "root",
password: "mysql",
url:"localhost:3306/emp"
}
}
},
methods:{
generateCode(){
let _this=this;
axios.post('/generateCode',this.tableData)
.then(function (response) {
_this.result=response.data.msg;
_this.codePath=response.data.obj;
})
.catch(function (error) {
console.log(error);
});
},
config(){
let _this=this;
axios.post('/config', {packageName:this.packageName})
.then(function (response) {
_this.msg=response.data.msg;//把返回数据展示到提示信息
_this.tableData=response.data.obj;//集合
})
.catch(function (error) {
console.log(error);
});
},
connect(){
let _this=this;
this.db.url="jdbc:mysql://"+this.db.url+"?useUnicode=true&characterEncoding=UTF-8";
axios.post('/connect', this.db)
.then(function (response) {
_this.msg=response.data.msg;//把返回数据展示到提示信息
_this.db= {
username: "root",
password: "mysql",
url: "localhost:3306/emp"
}
_this.connectBtnEnabled =false;
})
.catch(function (error) {
console.log(error);
});
}
}
})
</script>
</body>
</html>
运行效果:
、
创建生成的属性类
@Data
public class RespBean {
private Integer status;
private String msg;
private Object obj;
private RespBean(){
}
public static RespBean ok(String msg,Object obj){
return new RespBean(200,msg,obj);
}
//重载,有的时候不需要
public static RespBean ok(String msg) {
return new RespBean(200,msg,null);
}
private RespBean(Integer status, String msg, Object obj) {
this.status = status;
this.msg = msg;
this.obj = obj;
}
public static RespBean error(String msg,Object obj){
return new RespBean(500,msg,obj);
}
public static RespBean error(String msg) {
return new RespBean(500,msg,null);
}
}
创建统一返回类
@Data
public class RespBean {
private Integer status;
private String msg;
private Object obj;
private RespBean(){
}
public static RespBean ok(String msg,Object obj){
return new RespBean(200,msg,obj);
}
//重载,有的时候不需要
public static RespBean ok(String msg) {
return new RespBean(200,msg,null);
}
private RespBean(Integer status, String msg, Object obj) {
this.status = status;
this.msg = msg;
this.obj = obj;
}
public static RespBean error(String msg,Object obj){
return new RespBean(500,msg,obj);
}
public static RespBean error(String msg) {
return new RespBean(500,msg,null);
}
}
创建数据库链接类,规定数据库链接信息
@Data
public class Db {
private String username;
private String password;
private String url;
}
创建数据库读取接口,以及配置接口
@RestController
public class DbController {
@PostMapping("/connect")
public RespBean connection(@RequestBody Db db){
Connection cn= DbUtils.initDb(db);
if(cn!=null){
return RespBean.ok("数据库链接成功");
}
return RespBean.error("数据库链接失败");
}
@PostMapping("/config")
public RespBean config(@RequestBody Map<String,String> map){
String packageName=map.get("packageName");
try {
Connection connection=DbUtils.getConnection();
DatabaseMetaData metaData = connection.getMetaData();
ResultSet tables=metaData.getTables(connection.getCatalog(),null,null,null);
List<TableClass> tableClassList=new ArrayList<>();
while(tables.next()){
TableClass tableClass=new TableClass();
tableClass.setPackageName(packageName);
String table_name=tables.getString("TABLE_NAME");//获取表名
String modelName = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, table_name);//转换成驼峰写法
tableClass.setTableName(table_name);//赋值表名
tableClass.setControllerName(modelName+"Controller");
tableClass.setModelName(modelName);//实体类
tableClass.setMapperName(modelName+"Mapper");//mapper
tableClass.setServiceName(modelName+"Service");
tableClassList.add(tableClass);
}
return RespBean.ok("数据库信息读取成功",tableClassList);
} catch (Exception e) {
e.printStackTrace();
}
return RespBean.error("数据库信息读取失败");
}
}
转换驼峰写法我这里是采用的google工具,
依赖如下:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.2-android</version>
</dependency>
具体实现方法;
@Service
public class GenerateCodeService {
Configuration cfg =null;
{
//freemarker版本
cfg=new Configuration(Configuration.VERSION_2_3_31);
//设置一下模板,第一个当前类 第二个是模板位置
cfg.setTemplateLoader(new ClassTemplateLoader(GenerateCodeService.class,"/templates"));
//设置一下编码
cfg.setDefaultEncoding("UTF-8");
}
public RespBean generateCode(List<TableClass> tableClassList, String realPath) {
try {
//获取模板
Template modelTemplate=cfg.getTemplate("Model.java.ftl");
Template serviceTemplate=cfg.getTemplate("Service.java.ftl");
Template mapperJavaTemplate=cfg.getTemplate("Mapper.java.ftl");
Template mapperXmlTemplate=cfg.getTemplate("Mapper.xml.ftl");
Template controllerTemplate=cfg.getTemplate("Controller.java.ftl");
//获取链接
Connection connection= DbUtils.getConnection();
DatabaseMetaData metaData = connection.getMetaData();//获取列的信息
for (TableClass tableClass: tableClassList) {
//获取列的信息
ResultSet columns = metaData.getColumns(connection.getCatalog(), null, tableClass.getTableName(), null);
//获取表中的主键
ResultSet primaryKeys = metaData.getPrimaryKeys(connection.getCatalog(), null, tableClass.getTableName());
List<ColumnClass>columnClassList=new ArrayList<>();
while (columns.next()){
String column_name = columns.getString("COLUMN_NAME");//列明
String type_name = columns.getString("TYPE_NAME");//类型
String remarks= columns.getString("REMARKS");//备注
ColumnClass columnClass = new ColumnClass();
columnClass.setRemark(remarks);
columnClass.setColumnName(column_name);
columnClass.setType(type_name);
columnClass.setPropertyName(CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL,column_name));//属性的名字。驼峰
primaryKeys.first();//挪到第一位
while (primaryKeys.next()){
String pkName= primaryKeys.getString("COLUMN_NAME");//主键的名字
if(column_name.equals(pkName)){//如果相等说明是主键
columnClass.setIsPrimary(true);
}
}
columnClassList.add(columnClass);
}
tableClass.setColumns(columnClassList);
String path=realPath+"/"+tableClass.getPackageName().replace(".","/");//设置路径
generate(modelTemplate,tableClass,path+"/model/");
generate(mapperJavaTemplate,tableClass,path+"/mapper/");
generate(mapperXmlTemplate,tableClass,path+"/mapper/");
generate(serviceTemplate,tableClass,path+"/service/");
generate(controllerTemplate,tableClass,path+"/controller/");
}
return RespBean.ok("代码已生成",realPath);
}catch (Exception e){
e.printStackTrace();
}
return RespBean.error("代码生成失败");
}
private void generate(Template template, TableClass tableClass, String path) throws IOException, TemplateException {
File floder = new File(path);
if(!floder.exists()){
floder.mkdirs();//不存在创建该路径
}
String fileName=path+"/"+tableClass.getModelName() + template.getName().replace(".ftl","").replace("Model","");
FileOutputStream fos = new FileOutputStream(fileName);
OutputStreamWriter out=new OutputStreamWriter(fos);
template.process(tableClass,out);
fos.close();
}
}
链接数据库工具类
/**连接数据库工具类
* @author
* @date
**/
public class DbUtils {
private static Connection connection;
public static Connection getConnection() {
return connection;
}
public static Connection initDb(Db db){
if(connection ==null){
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection(db.getUrl(),db.getUsername(),db.getPassword());
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
return connection;
}
}
创建生成的ftl模板
Model.java.ftl
package ${packageName}.model;
import java.util.Date;
public class ${modelName}{
<#if columns??>
<#list columns as column>
<#if column.type='VARCHAR'|| column.type='TEXT'||column.type='CHAR'>
/**
*${column.remark}
*/
private String ${column.propertyName?uncap_first};
</#if>
<#if column.type='INT'>
/**
*${column.remark}
*/
private Integer ${column.propertyName?uncap_first};
</#if>
<#if column.type='DATETIME'>
/**
*${column.remark}
*/
private Date ${column.propertyName?uncap_first};
</#if>
<#if column.type='BIGINT'>
/**
*${column.remark}
*/
private Long ${column.propertyName?uncap_first};
</#if>
<#if column.type='DOUBLE'>
/**
*${column.remark}
*/
private Double ${column.propertyName?uncap_first};
</#if>
<#if column.type='BIT'>
/**
*${column.remark}
*/
private Boolean ${column.propertyName?uncap_first};
</#if>
</#list>
</#if>
<#if columns??>
<#list columns as column>
<#if column.type='VARCHAR'|| column.type='TEXT'||column.type='CHAR'>
public String get${column.propertyName}(){
return ${column.propertyName?uncap_first};
}
public void set${column.propertyName}(String ${column.propertyName?uncap_first}){
this.${column.propertyName?uncap_first}=${column.propertyName?uncap_first};
}
</#if>
<#if column.type='INT'>
public Integer get${column.propertyName}(){
return ${column.propertyName?uncap_first};
}
public void set${column.propertyName}(Integer ${column.propertyName?uncap_first}){
this.${column.propertyName?uncap_first}=${column.propertyName?uncap_first};
}
</#if>
<#if column.type='DATETIME'>
public Date get${column.propertyName}(){
return ${column.propertyName?uncap_first};
}
public void set${column.propertyName}(Date ${column.propertyName?uncap_first}){
this.${column.propertyName?uncap_first}=${column.propertyName?uncap_first};
}
</#if>
<#if column.type='BIGINT'>
public Long get${column.propertyName}(){
return ${column.propertyName?uncap_first};
}
public void set${column.propertyName}(Long ${column.propertyName?uncap_first}){
this.${column.propertyName?uncap_first}=${column.propertyName?uncap_first};
}
</#if>
<#if column.type='DOUBLE'>
public Double get${column.propertyName}(){
return ${column.propertyName?uncap_first};
}
public void set${column.propertyName}(Double ${column.propertyName?uncap_first}){
this.${column.propertyName?uncap_first}=${column.propertyName?uncap_first};
}
</#if>
<#if column.type='BIT'>
public Boolean get${column.propertyName}(){
return ${column.propertyName?uncap_first};
}
public void set${column.propertyName}(Boolean ${column.propertyName?uncap_first}){
this.${column.propertyName?uncap_first}=${column.propertyName?uncap_first};
}
</#if>
</#list>
</#if>
}
mapper.xml.ftl
<#if这一长串是为了枚举一些sql类型,比如text转换成varchar类型等等,
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="${packageName}.mapper.${mapperName}">
<resultMap id="BaseResultMap" type="${packageName}.model.${modelName}">
<#list columns as column>
<<#if column.primary??>id<#else>result</#if> column="${column.columnName}" property="${column.propertyName?uncap_first}" jdbcType="<#if column.type='INT'>INTEGER<#elseif column.type='DATETIME'>TIMESTAMP<#elseif column.type='TEXT'>VARCHAR<#else>${column.type}</#if>"/>
</#list>
</resultMap>
<select id="getAll${modelName}s" resultMap="BaseResultMap">
select * from ${tableName};
</select>
</mapper>
Mapper.java.ftl
package ${packageName}.mapper;
import ${packageName}.model.${modelName};
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface ${mapperName}{
List<${modelName}>getAll${modelName}s();
}
Service.java.ftl
package ${packageName}.Service;
import ${packageName}.model.${modelName};
import ${packageName}.mapper.${mapperName};
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
@Service
public class ${serviceName}{
@Autowired
${mapperName} ${mapperName?uncap_first};
public List<${modelName}>getAll${modelName}s(){
return ${mapperName?uncap_first}.getAll${modelName}s();
}
}
Controller.java.ftl
package ${packageName}.controller;
import ${packageName}.model.${modelName};
import ${packageName}.service.${serviceName};
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
@RestController
public class ${controllerName}{
@Autowired
${serviceName} ${serviceName?uncap_first};
@GetMapping("/${modelName?lower_case}s")
public List<${modelName}>getAll${modelName}s(){
return ${serviceName?uncap_first}.getAll${modelName}s();
}
}
生成路径为:服务器上地址,点击对应地址即可查找到相应的生成代码。