https://github.com/wangchao550586585/codegen
设计目的
公司目前从0开发CRM,主要开发文章,视频等内容,发现非常多通用的逻辑可直接代码生成,于是花费3天写了一个复合公司业务场景需要的可视化逆向工程脚手架.
功能:
- 一键生成mapper->service->controller->vue(尚未公开,公司内部使用)增删改查通用模板,即拿即用,无需任何修改,集成swagger接口注释文档
使用方法
- 启动项目
- 配置数据源
- 点击任意一个数据源,即可生成数据源下所有表,以ZIP形式下载
- ZIP自带包名,copy项目即可.无需任何修改.
涉及技术
- spring-boot(动态数据源)
- freemarker
ftl模板技术核心代码
@SneakyThrows
@Override
public byte[] code(GeneratorConf generatorConf) {
//解析生成root
TableEntity root = getRoot(generatorConf);
//获取Configuration
Configuration cfg = getConfiguration();
//生成文件
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ZipOutputStream zip = new ZipOutputStream(outputStream);
genFile(new File(path), root, cfg, zip);
IoUtil.close(zip);
return outputStream.toByteArray();
}
获取ftl配置
/**
* 获取模板配置
* @return
* @throws IOException
*/
private Configuration getConfiguration() throws IOException {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
cfg.setDirectoryForTemplateLoading(new File(path));
cfg.setDefaultEncoding("UTF-8");
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
return cfg;
}
生成文件
private void genFile(File file, TableEntity root, Configuration cfg, ZipOutputStream zip) throws IOException, TemplateException {
if (file.isDirectory()) {
File[] files = file.listFiles();
for (int i = 0; i < files.length; i++) {
genFile(files[i], root, cfg, zip);
}
} else {
String fileName = file.getName();
String s = fileName.split("\\.")[0];
String genFileName = fileName.equals("Entity.java.ftl") ? ".java" : fileName.replaceAll(".ftl", "");
//构造文件路径
String filePath = root.getPackageName().concat("." + s.toLowerCase());
filePath = filePath.replace(".", File.separator).concat(File.separator + root.getClassName() + genFileName);
filePath = filePath.replace("serviceimpl", "service" + File.separator + "impl");
//获取模板
Template temp = cfg.getTemplate(fileName);
//生成zip
genZip(root, zip, filePath, temp);
}
}
生成ZIP
private void genZip(TableEntity root, ZipOutputStream zip, String path, Template temp) throws TemplateException, IOException {
StringWriter sw = new StringWriter();
temp.process(root, sw);
zip.putNextEntry(new ZipEntry(Objects.requireNonNull(path)));
IoUtil.write(zip, StandardCharsets.UTF_8, false, sw.toString());
IoUtil.close(sw);
zip.closeEntry();
}
动态数据源
首先注册动态数据源对象
@Bean
public DataSource dataSource() {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setPassword(globalConfig.getUsername());
dataSource.setUsername(globalConfig.getPassword());
dataSource.setDriverClassName(globalConfig.getDriverClassName());
dataSource.setJdbcUrl(globalConfig.getJdbcUrl());
//自定义动态数据源类
DynamicDataSource dynamicDataSource = new DynamicDataSource();
HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
objectObjectHashMap.put("defaultDataSource", dataSource);
dynamicDataSource.setTargetDataSources(objectObjectHashMap);
dynamicDataSource.setDefaultTargetDataSource(dataSource);
return dynamicDataSource;
}
public static class DataSourceContext {
private static ThreadLocal<DataSourceBean> threadLocal = new InheritableThreadLocal<DataSourceBean>();
/**
* 获取数据源
*/
public static DataSourceBean getDataSource() {
return threadLocal.get();
}
/**
* 设置数据源
*/
public static void setDataSource(DataSourceBean dataSourceBean) {
threadLocal.set(dataSourceBean);
}
/**
* 清除数据源
* 清除后,数据源为默认时间
*/
public static void toDefault() {
threadLocal.remove();
}
}
/**
* 连接数据源前,调用该方法
*/
@Override
protected Object determineCurrentLookupKey() {
DataSourceBean dataSourceBean = DataSourceContext.getDataSource();
if (dataSourceBean == null) {
return null;
}
try {
Map<Object, Object> targetSourceMap = getTargetSource();
synchronized (this) {
if (!targetSourceMap.keySet().contains(dataSourceBean.getBeanName())) {
Object dataSource = createDataSource(dataSourceBean);
targetSourceMap.put(dataSourceBean.getBeanName(), dataSource);
super.afterPropertiesSet();
}
}
return dataSourceBean.getBeanName();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public Object createDataSource(DataSourceBean dataSourceBean) throws IllegalAccessException {
ConfigurableApplicationContext context = (ConfigurableApplicationContext) applicationContext;
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context.getBeanFactory();
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(HikariDataSource.class);
Map<String, Object> propertyKeyValues = getPropertyKeyValues(DataSourceBean.class, dataSourceBean);
for (Map.Entry<String, Object> entry : propertyKeyValues.entrySet()) {
beanDefinitionBuilder.addPropertyValue(entry.getKey(), entry.getValue());
}
beanFactory.registerBeanDefinition(dataSourceBean.getBeanName(), beanDefinitionBuilder.getBeanDefinition());
return context.getBean(dataSourceBean.getBeanName());
}