提交 2df01cfe 编写于 作者: Tyl666's avatar Tyl666

【Liquibase逆向工程】第一版代码提交

主要功能点:
1、支持自定义数据源来同步实体
2、支持自定义构建liquibase
上级 8938094e
......@@ -25,7 +25,7 @@ import org.springframework.beans.factory.annotation.Autowired;
@SpringBootApplication(exclude = {
com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure.class
})
@ComponentScan(basePackages = {"cn.ibizlab"}
@ComponentScan(basePackages = {"cn.ibizlab","liquibase.integration.spring"}
// ,excludeFilters = {
// @ComponentScan.Filter(type= org.springframework.context.annotation.FilterType.REGEX, pattern="cn.ibizlab.xxx.rest.xxx"),
// }
......
......@@ -2,9 +2,13 @@ package cn.ibizlab.core.lite.extensions.util;
import cn.ibizlab.core.lite.domain.MetaEntity;
import cn.ibizlab.core.lite.extensions.service.LiteModelService;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class LiteStorage {
......@@ -86,5 +90,14 @@ public class LiteStorage {
}
}
}
public static Map parseDatasource (String json) {
JSONArray tableData = JSONArray.parseArray(json);
Map map = new HashMap();
for (int i = 0; i < tableData.size(); i++) {
JSONObject jsonObject = tableData.getJSONObject(i);
map.put(jsonObject.get("param"), jsonObject.get("value"));
}
return map;
}
}
package cn.ibizlab.core.lite.service.impl;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.Map;
import java.util.HashSet;
import java.util.HashMap;
import java.util.Collection;
import java.util.Objects;
import java.util.Optional;
import java.math.BigInteger;
import cn.ibizlab.core.lite.extensions.domain.SysModel;
import cn.ibizlab.core.lite.extensions.service.LiteCoreService;
import liquibase.integration.spring.GenerateLiquibaseChangeLog;
import cn.ibizlab.util.domain.LiquibaseProp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.stereotype.Service;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.util.ObjectUtils;
import org.springframework.beans.factory.annotation.Value;
import cn.ibizlab.util.errors.BadRequestAlertException;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.context.annotation.Lazy;
import cn.ibizlab.core.lite.domain.DstDataSource;
......@@ -30,16 +23,16 @@ import cn.ibizlab.core.lite.filter.DstDataSourceSearchContext;
import cn.ibizlab.core.lite.service.IDstDataSourceService;
import cn.ibizlab.util.helper.CachedBeanCopier;
import cn.ibizlab.util.helper.DEFieldCacheMap;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import cn.ibizlab.core.lite.mapper.DstDataSourceMapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.alibaba.fastjson.JSONObject;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import static cn.ibizlab.core.lite.extensions.util.LiteStorage.parseDatasource;
/**
* 实体[数据源] 服务对象接口实现
......@@ -53,6 +46,11 @@ public class DstDataSourceServiceImpl extends ServiceImpl<DstDataSourceMapper, D
protected cn.ibizlab.core.lite.service.IMetaEntityService metaentityService;
protected int batchSize = 500;
@Resource
public LiteCoreService liteCoreService;
@Resource
public GenerateLiquibaseChangeLog generateLiquibaseChangeLog;
@Override
@Transactional
......@@ -131,7 +129,20 @@ public class DstDataSourceServiceImpl extends ServiceImpl<DstDataSourceMapper, D
@Override
@Transactional
public DstDataSource initDS(DstDataSource et) {
//自定义代码
DstDataSource ds = get(et.getDsId());
String datasourceMessage = ds.getDsCfg();
Map datasourceMap = parseDatasource(datasourceMessage);
LiquibaseProp lp = new LiquibaseProp();
lp.setUsername(datasourceMap.get("username") == null?"":datasourceMap.get("username").toString());
lp.setPassword(datasourceMap.get("password") == null?"":datasourceMap.get("password").toString());
lp.setUrl(datasourceMap.get("url") == null?"":datasourceMap.get("url").toString());
lp.setDefaultSchema(datasourceMap.get("defaultSchema") == null?"":datasourceMap.get("defaultSchema").toString());
// 调用liquibase逆向工程的逻辑,生成一个xml,并返回一个SysModel对象
SysModel sysModel = generateLiquibaseChangeLog.liquibaseGenerateChangeLog(lp,ds.getDsId());
liteCoreService.syncSysModel(sysModel);
return et;
}
......
package cn.ibizlab.core.util.config;
import cn.ibizlab.util.domain.LiquibaseProp;
import com.alibaba.druid.pool.DruidDataSource;
import liquibase.integration.spring.SpringLiquibase;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class LiquibaseGenerateConfiguration {
@Bean
public SpringLiquibase masterliquibase(LiquibaseProp masterProperties) {
return getLiquibase(masterProperties.getUsername(),masterProperties.getPassword(),masterProperties.getUrl(),masterProperties.getDefaultSchema());
}
public SpringLiquibase getLiquibase(String username, String password, String url, String defaultSchema) {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
druidDataSource.setUrl(url);
SpringLiquibase liquibase = new SpringLiquibase();
liquibase.setDataSource(druidDataSource);
liquibase.setChangeLog("classpath:liquibase/empty.xml");
liquibase.setContexts("development,test,production");
liquibase.setShouldRun(true);
liquibase.setDefaultSchema(defaultSchema);
return liquibase;
}
}
package liquibase.integration.spring;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.XML;
import cn.ibizlab.core.lite.domain.MetaField;
import cn.ibizlab.core.lite.extensions.domain.MetaEntityModel;
import cn.ibizlab.core.lite.extensions.domain.SysModel;
import cn.ibizlab.util.domain.LiquibaseProp;
import com.alibaba.fastjson.JSON;
import liquibase.CatalogAndSchema;
import liquibase.Liquibase;
import liquibase.configuration.GlobalConfiguration;
import liquibase.configuration.LiquibaseConfiguration;
import liquibase.database.Database;
import liquibase.database.DatabaseFactory;
import liquibase.database.jvm.JdbcConnection;
import liquibase.diff.output.DiffOutputControl;
import liquibase.diff.output.StandardObjectChangeFilter;
import liquibase.exception.DatabaseException;
import liquibase.exception.UnexpectedLiquibaseException;
import liquibase.executor.ExecutorService;
import liquibase.integration.ant.type.ChangeLogOutputFile;
import liquibase.integration.commandline.CommandLineUtils;
import cn.ibizlab.core.util.config.LiquibaseGenerateConfiguration;
import liquibase.resource.ResourceAccessor;
import liquibase.structure.DatabaseObject;
import liquibase.util.StringUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
@Slf4j
@Component
public class GenerateLiquibaseChangeLog{
private Set<ChangeLogOutputFile> changeLogOutputFiles = new LinkedHashSet<>();
private String includeObjects;
private String excludeObjects;
private DataSource dataSource;
@Value("${ibiz.generateDs.filePath:/app/file/}")
protected String liquibasePath;
@Value("${ibiz.generateDs.catalogName:}")
protected String catalogName;
@Value("${ibiz.generateDs.generateField:tables,views,columns,indexes,foreignkeys,primarykeys,uniqueconstraints}")
protected String generateField;
@Value("${ibiz.generateDs.includeCatalog:false}")
protected Boolean includeCatalog;
@Value("${ibiz.generateDs.includeSchema:false}")
private boolean includeSchema;
@Value("${ibiz.generateDs.includeTablespace:true}")
private boolean includeTablespace;
@Value("${ibiz.generateDs.context:}")
private String context;
@Value("${ibiz.generateDs.dataDir:}")
private String dataDir;
@Resource
public LiquibaseGenerateConfiguration liquibaseGenerateConfiguration;
@SneakyThrows
public SysModel liquibaseGenerateChangeLog(LiquibaseProp liquibaseProp,String name){
// 根据指定数据源生成对象
SpringLiquibase springLiquibase = liquibaseGenerateConfiguration.masterliquibase(liquibaseProp);
Liquibase liquibase = springLiquibase.createLiquibase(springLiquibase.getDataSource().getConnection());
Database database = liquibase.getDatabase();
// 自定义属性参数
byte[] nameBytes = name.getBytes("UTF-8");
String fileid = DigestUtils.md5DigestAsHex(nameBytes);
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String fileName = sdf.format(new Date());
String fileFullPath = this.liquibasePath+"ibizutil"+ File.separator + fileid+File.separator + fileName;
DiffOutputControl diffOutputControl = new DiffOutputControl(includeCatalog, includeSchema, includeTablespace, null);
CommandLineUtils.doGenerateChangeLog(fileFullPath, database, catalogName,liquibaseProp.getDefaultSchema(),StringUtils.trimToNull(generateField),
name, context, dataDir, diffOutputControl);
SysModel sysModel = generateSysModel(liquibasePath);
return sysModel;
}
/**
* 构建一个SysModel对象
* @param path 生成liquibaseChangeLog路径
*/
public SysModel generateSysModel(String path) {
List<MetaEntityModel> metaEntityModels = new CopyOnWriteArrayList<>();
List<Map> primaryKeys = new CopyOnWriteArrayList<>();
SysModel sysModel = new SysModel();
MetaEntityModel metaEntityModel = null;
List<Map> commonMessage = null;
MetaField metaField = null;
List<Map> column = null;
// Map primaryKey = null;
Map head = null;
Map temp = null;
com.alibaba.fastjson.JSONObject jsonObject = JSON.parseObject(ConvertXMLtoJSON(path));
head = (Map) jsonObject.get("databaseChangeLog");
commonMessage = (List<Map>) head.get("changeSet");
// 第一、二层 获取第一个作者以及id
if (!ObjectUtil.isEmpty(commonMessage)){
sysModel.setSystemid((String) commonMessage.get(0).get("author"));
sysModel.setSystemname((String) commonMessage.get(0).get("author"));
}
// for (int k = 0 ; k < commonMessage.size(); k++) {
// // 维护主键组
// primaryKey = (Map) commonMessage.get(k).get("addPrimaryKey");
// if (!ObjectUtil.isEmpty(primaryKey)) {
// primaryKeys.add(primaryKey);
// }
// }
// 第三层 获取实体对象List 在这层还可以放入subEntitys、parentEntitys等关系字段
for (int i = 0;i < commonMessage.size(); i++) {
// createTable主要存储了表明以及字段属性,只要crateTable属性,其余剔除
temp = (Map) commonMessage.get(i).get("createTable");
if(ObjectUtil.isEmpty(temp)){
continue;
}
metaEntityModel = new MetaEntityModel();
// 为了获取MetaEntityModel下那些基本类型字段(此处可补充MetaEntityModel等描述字段)
metaEntityModel.setEntityName(temp.get("tableName") == null ? "" : temp.get("tableName").toString());
metaEntityModel.setCodeName(temp.get("tableName") == null ? "" : temp.get("tableName").toString());
metaEntityModel.setTableName(temp.get("tableName") == null ? "" : temp.get("tableName").toString());
metaEntityModel.setLogicName(temp.get("tableName") == null ? "" : temp.get("tableName").toString());
// 统一关联sysId
metaEntityModel.setSystemId((String) commonMessage.get(0).get("author"));
// 属性List
column = (List<Map>) temp.get("column");
List<MetaField> metaFields = new CopyOnWriteArrayList<>();
for (int j = 0; j < column.size();j++){
// (此处可补充MetaField描述字段,以及父子外键关系等)
metaField = new MetaField();
metaField.setFieldName(column.get(j).get("name") == null? "" : column.get(j).get("name").toString());
metaField.setDataType(column.get(j).get("type") == null? "" : fieldToName(column.get(j).get("type").toString()));
metaField.setDataLength(column.get(j).get("type") == null? 0 : fieldToLenth(column.get(j).get("type").toString()));
metaField.setNullable(column.get(j).get("constraints") != null ? 1 : 0);
// metaField.setKeyField(isPrimaryKey(primaryKeys,column.get(j).get("name") == null? "" : column.get(j).get("name").toString()) ? 1 : 0);
metaField.setKeyField(column.get(j).get("primaryKey") != null ? 1 : 0);
metaField.setPhysicalField(1);
metaFields.add(metaField);
}
metaEntityModel.setFields(metaFields);
metaEntityModels.add(metaEntityModel);
sysModel.setEntities(metaEntityModels);
}
return sysModel;
}
// public static boolean isPrimaryKey(List<Map> primaryKeys,String primaryKey) {
// boolean flag = false;
// for (int i = 0; i < primaryKeys.size(); i++) {
// if (primaryKey.equals(primaryKeys.get(i).get("columnNames"))) {
// flag = true;
// break;
// }
// }
// return flag;
// }
private static Integer fieldToLenth(String s) {
if(s.contains("(")) {
String newStr = s.substring(s.indexOf("(") + 1, s.indexOf(")"));
return Integer.valueOf(newStr);
}
return 0;
}
private static String fieldToName(String s) {
if(s.contains("(")){
String newStr = s.substring(0, s.indexOf("("));
return newStr;
}
return s;
}
Class[] snapshotTypes;
private Class[] getTypes(Set<Class<? extends DatabaseObject>> types){
this.snapshotTypes = new Class[types.size()];
int i = 0;
for (Class<? extends DatabaseObject> type : types) {
this.snapshotTypes[i++] = type;
}
return snapshotTypes;
}
public static String ConvertXMLtoJSON(String path) {
InputStream is = null;
try {
is = new FileInputStream(path);
String xml;
xml = IOUtils.toString(is);
//将xml转为json
JSONObject xmlJSONObj = XML.toJSONObject(xml);
String jsonPrettyPrintString = xmlJSONObj.toString();
return jsonPrettyPrintString;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public SpringLiquibase.SpringResourceOpener createResourceOpener() {
return new SpringLiquibase().new SpringResourceOpener(getLiquibasePath());
}
public String getLiquibasePath() {
return liquibasePath;
}
private DiffOutputControl getDiffOutputControl() {
DiffOutputControl diffOutputControl = new DiffOutputControl(includeCatalog, includeSchema, includeTablespace, null);
if ((excludeObjects != null) && (includeObjects != null)) {
throw new UnexpectedLiquibaseException("Cannot specify both excludeObjects and includeObjects");
}
if (excludeObjects != null) {
diffOutputControl.setObjectChangeFilter(new StandardObjectChangeFilter(StandardObjectChangeFilter.FilterType.EXCLUDE, excludeObjects));
}
if (includeObjects != null) {
diffOutputControl.setObjectChangeFilter(new StandardObjectChangeFilter(StandardObjectChangeFilter.FilterType.INCLUDE, includeObjects));
}
return diffOutputControl;
}
private String getOutputEncoding(ChangeLogOutputFile changeLogOutputFile) {
String encoding = changeLogOutputFile.getEncoding();
return (encoding == null) ? getDefaultOutputEncoding() : encoding;
}
protected String getDefaultOutputEncoding() {
LiquibaseConfiguration liquibaseConfiguration = LiquibaseConfiguration.getInstance();
GlobalConfiguration globalConfiguration = liquibaseConfiguration.getConfiguration(GlobalConfiguration.class);
return globalConfiguration.getOutputEncoding();
}
private Liquibase createLiquibase(String changeLogFile, ResourceAccessor resourceAccessor) {
Database database =getDataBase();
ExecutorService.getInstance().clearExecutor(database);
database.resetInternalState();
return new Liquibase(changeLogFile, resourceAccessor, database);
}
private CatalogAndSchema buildCatalogAndSchema(Database database) {
return new CatalogAndSchema(database.getDefaultCatalogName(), database.getDefaultSchemaName());
}
@SneakyThrows
public Database getDataBase(){
Connection connection = null;
Database database = null;
String name = "unknown";
try {
connection = getDataSource().getConnection();
database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connection));
name = database.getDatabaseProductName();
} catch (SQLException e) {
throw new DatabaseException(e);
} finally {
if (database != null) {
database.close();
} else if (connection != null) {
try {
if (!connection.getAutoCommit()) {
connection.rollback();
}
connection.close();
} catch (SQLException e) {
log.error("problem closing connection", e);
}
}
}
return database;
}
public DataSource getDataSource() {
return dataSource;
}
}
......@@ -88,6 +88,7 @@
<oracle.version>11.2.0.3</oracle.version>
<postgresql.version>42.2.6</postgresql.version>
<hutool.version>5.5.1</hutool.version>
</properties>
......@@ -325,12 +326,18 @@
<version>${eureka-client.version}</version>
</dependency>
<!-- Oracle驱动包 -->
<!-- &lt;!&ndash; Oracle驱动包 &ndash;&gt;
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>${oracle.version}</version>
</dependency>-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<!-- PostgreSQL驱动包 -->
<dependency>
<groupId>org.postgresql</groupId>
......
Markdown 格式
0% or
您添加了 0 到此讨论。请谨慎行事。
先完成此消息的编辑!
想要评论请 注册