一、在线地址
Java在线代码生成器:在线访问
二、页面截图
三、核心功能
- 支持Mybatis、MybatisPlus、Jpa代码生成
- 使用 antlr4 解析SQL语句,保证了SQL解析的成功率
- 支持自定义包名、作者名信息
- 支持自定义方法名、接口地址
- 支持自定义选择是否生成某个方法
- 支持选择是否集成Swagger、Lombok、validation等依赖
四、技术介绍
前端: React 16.8.6、Ant-design 4.24.12、Umi 3.5.41
后端: Java 1.8、SpringBoot2.3.4.RELEASE、knife4j(Swagger)2.0.9、Freemarker 2.3.31
五、源码
后台: tool4j-generator
部分代码节选:
/**
* 代码生成入口类
*
* @param params
* @return
*/
public Map<String, Object> generate(JavaCodeConfig params) {
/*
生成代码
*/
List<ParserResult> results = execute(params);
Map<String, Object> map = new HashMap<>();
map.put("results", results);
TreeVO root;
root = new TreeVO().setKey(params.getBasePackage()).setTitle(params.getBasePackage());
String basePath = params.getBasePackage().replace(".", "/") + "/";
for (ParserResult parserResult : results) {
String[] dirArr = parserResult.getFileName().replace(basePath, "").split("/");
buildFileTree(root, dirArr, 0);
}
map.put("fileTree", root);
if (CollUtil.isEmpty(results)) {
log.error("SQL解析失败,建议您切换SQL解析器重试,Input = {}", params.getInput());
throw new IllegalArgumentException("SQL解析失败,建议您切换SQL解析器重试");
}
if (results.stream().anyMatch(ParserResult::isErrored)) {
List<ParserResult> errors = results.stream().filter(ParserResult::isErrored).collect(Collectors.toList());
log.info(">>>>>>>>>>>>>>> generate code exists error: total={}, error={},Input = {}", results.size(), errors.size(), params.getInput());
} else {
log.info(">>>>>>>>>>>>>>> generate code all succeed.");
}
return map;
}
import React, { Component } from 'react';
import { Divider, Input, Modal, Radio, Spin, Switch, Tag } from 'antd';
import styles from './GenerateModal.less';
import MethodSetting from '@/pages/components/MethodSetting';
const ORM_TYPE = {
jpa: {
label: 'JPA',
color: '#138029',
},
mybatis: {
label: 'Mybatis',
color: '#f6b305',
},
mybatisPlus: {
label: 'MybatisPlus',
color: '#008585',
},
};
class GenerateModal extends Component {
constructor (props) {
super(props);
this.state = {
init: false,
parserType: 'common',
basePackage: 'com.tool4j',
author: 'system',
apiPathPrefix: '/api',
tableNamePrefix: '',
restType: 'restful',
ormType: 'mybatisPlus',
methods: {
findPage: {
label: '分页查询',
name: 'findPage',
apiPath: '/findPage',
enabled: true,
},
findList: {
label: '列表查询',
name: 'findList',
apiPath: '/findList',
enabled: true,
},
findById: {
label: '查询详情',
name: 'findById',
apiPath: '/{id}',
enabled: true,
},
save: {
label: '新增',
name: 'insert',
apiPath: '',
enabled: true,
},
update: {
label: '修改',
name: 'update',
apiPath: '',
enabled: true,
},
delete: {
label: '删除',
name: 'delete',
apiPath: '/{id}',
enabled: true,
},
},
dependencies: ['lombok', 'swagger', 'validator'],
};
}
cancel = () => {
this.props.cancel();
};
generate = () => {
const {
parserType,
basePackage,
author,
apiPathPrefix,
tableNamePrefix,
restType,
ormType,
methods,
dependencies,
} = this.state;
const generateConfig = {
parserType,
basePackage,
author,
apiPathPrefix,
tableNamePrefix,
restType,
ormType,
methods,
dependencies,
};
this.props.submit(generateConfig);
};
dependencyChange = (key, checked) => {
let { dependencies = [] } = this.state;
if (checked) {
dependencies.push(key);
} else {
dependencies = dependencies.filter(item => item !== key);
}
this.setState({
dependencies,
});
};
formChange = (key, value) => {
this.setState({
[key]: value,
});
};
init = () => {
const setting = this.props.setting;
if (setting) {
const { methods } = this.state;
if (setting.methods) {
for (let key in methods) {
methods[key] = { ...methods[key], ...setting.methods[key] };
}
}
this.setState({
...setting,
methods,
init: true,
});
}
};
render () {
const { init, dependencies = [], ormType } = this.state;
if (!init) {
this.init();
}
let { templates = [] } = this.props;
if (ormType) {
templates = templates.filter(item => item.ormType === ormType);
}
const templateOptions = [];
templates.forEach(template => templateOptions.push({
value: template.ormType,
label: <div>
<Tag style={{ marginLeft: '5px' }} color={ORM_TYPE[template.ormType].color}>
{ORM_TYPE[template.ormType].label}
</Tag>
</div>,
}));
return <Modal
title={
<div style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
height: '14px',
marginRight: '40px',
}}>
<div>
<span>代码生成配置</span>
</div>
</div>
}
bodyStyle={{ padding: '5px' }}
open={this.props.visible}
maskClosable={false}
onOk={this.generate}
okText={'生成'}
onCancel={this.cancel}
cancelText={'取消'}
width={'800px'}
>
<Spin spinning={this.props.loading} tip={'代码生成中,请稍候...'}>
<div className={styles['module']}>
<div style={{ marginBottom: '10px' }}>
<Divider
type='vertical'
className={styles['divider']}
>
</Divider>
<span style={{ fontSize: '16px', fontWeight: 600 }}>选择解析器</span>
</div>
<div style={{ marginLeft: '20px', display: 'flex' }}>
<Radio.Group value={this.state.parserType} onChange={e => this.formChange('parserType', e.target.value)}>
<Radio.Button value='common'>智能SQL解析器</Radio.Button>
<Radio.Button value='mysql'>MySQL 解析器</Radio.Button>
<Radio.Button value='postgresql'>Postgresql 解析器</Radio.Button>
<Radio.Button value='plsql'>Oracle 解析器</Radio.Button>
</Radio.Group>
</div>
</div>
<div className={styles['module']}>
<div style={{ marginBottom: '10px' }}>
<Divider
type='vertical'
className={styles['divider']}
>
</Divider>
<span style={{ fontSize: '16px', fontWeight: 600 }}>基础配置</span>
</div>
<div style={{ marginLeft: '20px' }}>
<Input
placeholder={'统一包名,如:com.tool4j'}
addonBefore={<div style={{ width: '100px' }}>包名:</div>}
style={{ marginTop: '5px', width: '60%' }}
onChange={e => this.formChange('basePackage', e.target.value)}
value={this.state.basePackage}
/>
<Input
placeholder={'作者姓名,如:system'}
addonBefore={<div style={{ width: '100px' }}>作者:</div>}
style={{ marginTop: '5px', width: '60%' }}
onChange={e => this.formChange('author', e.target.value)}
value={this.state.author}
/>
<Input
placeholder={'统一的接口前缀,如:/api'}
addonBefore={<div style={{ width: '100px' }}>接口前缀:</div>}
style={{ marginTop: '5px', width: '60%' }}
onChange={e => this.formChange('apiPathPrefix', e.target.value)}
value={this.state.apiPathPrefix}
/>
<Input
placeholder={'生成代码时可忽略表的统一前缀,如:t_'}
addonBefore={<div style={{ width: '100px' }}>忽略表前缀:</div>}
style={{ marginTop: '5px', width: '60%' }}
onChange={e => this.formChange('tableNamePrefix', e.target.value)}
value={this.state.tableNamePrefix}
/>
</div>
</div>
<div className={styles['module']}>
<div style={{ marginBottom: '10px' }}>
<Divider
type='vertical'
className={styles['divider']}
>
</Divider>
<span style={{ fontSize: '16px', fontWeight: 600 }}>方法配置</span>
</div>
<div style={{ width: '100%' }}>
<MethodSetting methods={this.state.methods} formChange={this.formChange} />
</div>
</div>
<div className={styles['module']}>
<div style={{ marginBottom: '10px' }}>
<Divider
type='vertical'
className={styles['divider']}
>
</Divider>
<span style={{ fontSize: '16px', fontWeight: 600 }}>更多选项</span>
</div>
<div>
<div style={{ margin: '20px', display: 'flex', textAlign: 'left' }}>
<div style={{ width: '120px', fontSize: '15px', fontWeight: 600 }}>ORM框架:</div>
<Radio.Group onChange={e => this.formChange('ormType', e.target.value)} value={this.state.ormType}>
<Radio value={'mybatisPlus'}>MybatisPlus</Radio>
<Radio value={'mybatis'}>Mybatis</Radio>
<Radio value={'jpa'}>SpringData(JPA)</Radio>
</Radio.Group>
</div>
</div>
</div>
<div className={styles['module']}>
<div>
<div style={{ marginBottom: '10px' }}>
<Divider
type='vertical'
className={styles['divider']}
>
</Divider>
<span style={{ fontSize: '16px', fontWeight: 600 }}>集成依赖</span>
</div>
<div style={{ width: '100%', padding: '20px 40px', display: 'flex', justifyContent: 'space-between' }}>
<div style={{ textAlign: 'center' }}>
<div style={{ height: '60px' }}>
<img src={'/icon/swagger.svg'} width={180} style={{ margin: '0 5px 5px 5px' }} />
</div>
<Switch
style={{ width: '60px', marginTop: '10px' }}
checkedChildren='使用'
unCheckedChildren='移除'
checked={dependencies.filter(item => item === 'swagger').length > 0}
onChange={checked => this.dependencyChange('swagger', checked)}
>
</Switch>
</div>
<div style={{ textAlign: 'center' }}>
<div style={{
height: '60px',
fontSize: '40px',
fontFamily: 'Georgia',
}}>
Lombok
</div>
<Switch
style={{ width: '60px', marginTop: '10px' }}
checkedChildren='使用'
unCheckedChildren='移除'
checked={dependencies.filter(item => item === 'lombok').length > 0}
onChange={checked => this.dependencyChange('lombok', checked)}
>
</Switch>
</div>
<div style={{ textAlign: 'center' }}>
<div style={{
width: '100%',
height: '60px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
}}>
<div style={{
height: '60px',
fontSize: '40px',
fontFamily: 'Georgia',
}}>
Validation 校验
</div>
</div>
<Switch
style={{ width: '60px', marginTop: '10px' }}
checkedChildren='使用'
unCheckedChildren='移除'
checked={dependencies.filter(item => item === 'validator').length > 0}
onChange={checked => this.dependencyChange('validator', checked)}
>
</Switch>
</div>
</div>
</div>
</div>
</Spin>
</Modal>;
}
}
export default GenerateModal;
六、本地运行
可直接 clone 该项目到本地,无需修改任何内容即可运行
后端:
- 使用Maven安装依赖
- 运行 Tool4jGeneratorApplication.java
前端:
3. 切换到前端工程根目录
4. 安装依赖: npm i
5. 运行:umi dev
七、结束语
大家也可以直接使用在线版本,使用过程中有任何问题或建议都可以留言反馈。
Java在线代码生成器:在线访问