需求场景:
若依框架的30张数据表 和 业务使用的数据表,同数据源,但分开的两个库,原生若依只支持主库的代码生成,故自己修改添加代码来实现
若依多数据源的使用
修改页面ruoyi-ui\src\views\tool\gen\importTable.vue
el-form 中新增 el-form-item
<el-form-item label="数据源">
<el-select v-model="queryParams.dataSource" placeholder="选择数据源">
<el-option v-for="item in dataSources"
:label="item.desc"
:value="item.dataBaseCode">el-option>
el-select>
el-form-item>
添加 data
// 查询参数
data() {
return {
………………
………………
queryParams: {
pageNum: 1,
pageSize: 10,
dataSource: '',
tableName: undefined,
tableComment: undefined
},
//数据源列表
dataSources:[{dataBaseCode:'',desc:''}],
};
},
添加方法
// 显示弹框
show() {
this.getTableSource();
this.getList();
this.visible = true;
},
//查询数据源
getTableSource(){
tableSourceList().then(res=>{
if(res.code===200){
this.dataSources=res.data;
if(this.queryParams.dataSource===''){
this.queryParams.dataSource=this.dataSources[0].dataBaseCode
}
}
})
},
ruoyi-ui\src\api\tool\gen.js 添加方法
// 查询数据源列表
export function tableSourceList(){
return request({
url: '/tool/gen/tableSourceList',
method: 'get',
})
}
修改 数据源类型 枚举类 ——> com.ruoyi.common.enums.DataSourceType
package com.ruoyi.common.enums;
import com.fasterxml.jackson.annotation.JsonFormat;
/**
* 数据源
*
* @author ruoyi
*/
//使枚举类返回前端是完整的对象,否则是只会有name属性的String数组
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum DataSourceType {
/**
* 主库
*/
MASTER("MASTER","若依框架数据库"),
/**
* 从库
*/
SLAVE("SLAVE","业务数据库");
private String dataBaseCode;
private String desc;
//……省略get set 构造函数……
}
添加数据源列表获取的接口 ——> com.ruoyi.generator.controller.GenController
/**
* 数据源列表
* @return
*/
@PreAuthorize("@ss.hasPermi('tool:gen:list')")
@GetMapping("/tableSourceList")
public AjaxResult tableSourceList(){
return AjaxResult.success(DataSourceType.values());
}
在 genTable 表中添加字段 form_data_source
新增字段后,对应的xml 中 resultMap、各种select以及insert、update方法,记得加上字段
查询条件 添加数据源的属性 ——> com.ruoyi.generator.domain.GenTable
public class GenTable extends BaseEntity
{
private static final long serialVersionUID = 1L;
………………
@NotBlank(message = "请选择数据源")
private String tableSource;
/** 来自的数据源名 */
private String formDataSource;
………………
}
修改查询库表 列表的方法 ——> com.wenhu.generator.service.selectDbTableList()
在genTableMapper 新增 方法
/**
* 查询已经导入了的表名集合
*/
public List<String> selectExcludeGenTable();
对应的 mapper xml中 GenTableMapper.xml
<select id="selectExcludeGenTable" resultType="java.lang.String">
select table_name from wenhu_management.gen_table
select>
找到 GenTableMapper 的 selectDbTableList 方法
新增参数 List excludeTables
/**
* 查询据库列表
*
* @param genTable 业务信息
* @return 数据库表集合
*/
public List<GenTable> selectDbTableList(@Param("genTable") GenTable genTable, @Param("excludeTables") List<String> excludeTables);
修改sql语句 table_name not in 后面的参数使用 以及多参别称的引用
<select id="selectDbTableList" parameterType="com.ruoyi.generator.domain.GenTable" resultMap="GenTableResult">
select table_name, table_comment, create_time, update_time from information_schema.tables
where table_schema = (select database())
AND table_name NOT LIKE 'qrtz_%' AND table_name NOT LIKE 'gen_%'
AND table_name NOT IN
<foreach collection="excludeTables" item="tName" open="(" close=")" separator=",">
#{tName}
foreach>
<if test="genTable.tableName != null and genTable.tableName != ''">
AND lower(table_name) like lower(concat('%', #{genTable.tableName}, '%'))
if>
<if test="genTable.tableComment != null and genTable.tableComment != ''">
AND lower(table_comment) like lower(concat('%', #{genTable.tableComment}, '%'))
if>
<if test="genTable.params.beginTime != null and genTable.params.beginTime != ''">
AND date_format(create_time,'%y%m%d') >= date_format(#{genTable.params.beginTime},'%y%m%d')
if>
<if test="genTable.params.endTime != null and genTable.params.endTime != ''">
AND date_format(create_time,'%y%m%d') <= date_format(#{genTable.params.endTime},'%y%m%d')
if>
order by create_time desc
select>
/**
* 查询据库列表
*
* @param genTable 业务信息
* @return 数据库表集合
*/
@Override
public List<GenTable> selectDbTableList(GenTable genTable) {
List<String> excludeGenTable = genTableMapper.selectExcludeGenTable();
//确保值不为空
if (StringUtils.isNotEmpty(genTable.getDataSource())) {
//找出对应的枚举
DataSourceType sourceType = DataSourceType.valueOf(genTable.getDataSource());
//手动切换数据源
DynamicDataSourceContextHolder.setDataSourceType(sourceType.name());
}
List<GenTable> genTables = genTableMapper.selectDbTableList(genTable,excludeGenTable);
//清空数据源变量
DynamicDataSourceContextHolder.clearDataSourceType();
return genTables;
}
因为没有引入 atomikos 分布式事务插件(若依官网提供的方法仅不分离版),所以在增删改类业务需要开启事务的方法中,Spring开启事务后会维护一个ConnectionHolder,保证在整个事务下,都是用同一个数据库连接
不想引入的简单方式,对方法的逻辑顺序进行改造
/**
* 导入表结构(保存)
*/
@PreAuthorize("@ss.hasPermi('tool:gen:import')")
@Log(title = "代码生成", businessType = BusinessType.IMPORT)
@PostMapping("/importTable")
public AjaxResult importTableSave(String tables,
String dataSource)
{
String[] tableNames = Convert.toStrArray(tables);
// 切换数据源并查询表信息
// 确保值不为空
if(StringUtils.isNotEmpty(dataSource)){
//找出对应的枚举
DataSourceType sourceType = DataSourceType.valueOf(dataSource);
//手动切换数据源
DynamicDataSourceContextHolder.setDataSourceType(sourceType.name());
}else{
throw new ServiceException("指定了无效数据源");
}
//先把需要的表结构数据、字段数据整理好
List<GenTable> tableList = genTableService.selectDbTableListByNames(tableNames);
//导入到若依框架数据库
DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.MASTER.name());
genTableService.importGenTable(tableList,dataSource);
//清空数据源变量
DynamicDataSourceContextHolder.clearDataSourceType();
return success();
}
/**
* 查询据库列表
*
* @param tableNames 表名称组
* @return 数据库表集合
*/
@Override
public List<GenTable> selectDbTableListByNames(String[] tableNames) {
List<GenTable> genTables = genTableMapper.selectDbTableListByNames(tableNames);
String operName = SecurityUtils.getUsername();
for (int i = 0; i < genTables.size(); i++) {
String tableName = genTables.get(i).getTableName();
GenUtils.initTable(genTables.get(i), operName);
List<GenTableColumn> genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName);
genTables.get(i).setColumns(genTableColumns);
}
return genTables;
}
/**
* 导入表结构
*
* @param tableList 导入表列表
*/
@Override
@Transactional
public void importGenTable(List<GenTable> tableList,
String dataSource) {
try {
for (GenTable table : tableList) {
table.setFormDataSource(dataSource);
int row = genTableMapper.insertGenTable(table);
if (row > 0) {
for (GenTableColumn column : table.getColumns()) {
GenUtils.initColumnField(column, table);
genTableColumnMapper.insertGenTableColumn(column);
}
}
}
} catch (Exception e) {
throw new ServiceException("导入失败:" + e.getMessage());
}
}
/**
* 同步数据库
*/
@PreAuthorize("@ss.hasPermi('tool:gen:edit')")
@Log(title = "代码生成", businessType = BusinessType.UPDATE)
@GetMapping("/synchDb/{tableName}")
public AjaxResult synchDb(@PathVariable("tableName") String tableName) {
//目标表的最新数据
List<GenTableColumn> dbTableColumns = genTableService.getDbTableColumns(tableName);
genTableService.synchDb(tableName,dbTableColumns);
return success();
}
新增方法
/**
* 获取数据库中指定表的当前字段数据
* @param tableName
* @return
*/
@Override
public List<GenTableColumn> getDbTableColumns(String tableName){
GenTable genTable = genTableMapper.selectGenTableByName(tableName);
if(genTable==null){
throw new ServiceException("表结构已被删除,请重新导入");
}
if(StringUtils.isNotEmpty(genTable.getFormDataSource())) {
DynamicDataSourceContextHolder.setDataSourceType(genTable.getFormDataSource());
}
//最新的目标表的字段
List<GenTableColumn> genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName);
DynamicDataSourceContextHolder.clearDataSourceType();
return genTableColumns;
}
修改原方法
synchDb方法中,只是将原来的临时变量 dbTableColumns 在方法内获取的逻辑改为了用上面的方法单独获取
由controller调用传参进来
避免了事务中无法切换数据源的问题
/**
* 同步数据库
*
* @param tableName 表名称
*/
@Override
@Transactional
public void synchDb(String tableName,List<GenTableColumn> dbTableColumns) {
//找出已导入的当前表
GenTable table = genTableMapper.selectGenTableByName(tableName);
//表的字段
List<GenTableColumn> tableColumns = table.getColumns();
Map<String, GenTableColumn> tableColumnMap = tableColumns.stream().collect(Collectors.toMap(GenTableColumn::getColumnName, Function.identity()));
if (StringUtils.isEmpty(dbTableColumns)) {
throw new ServiceException("同步数据失败,原表结构不存在");
}
List<String> dbTableColumnNames = dbTableColumns.stream().map(GenTableColumn::getColumnName).collect(Collectors.toList());
dbTableColumns.forEach(column -> {
GenUtils.initColumnField(column, table);
if (tableColumnMap.containsKey(column.getColumnName())) {
GenTableColumn prevColumn = tableColumnMap.get(column.getColumnName());
column.setColumnId(prevColumn.getColumnId());
if (column.isList()) {
// 如果是列表,继续保留查询方式/字典类型选项
column.setDictType(prevColumn.getDictType());
column.setQueryType(prevColumn.getQueryType());
}
if (StringUtils.isNotEmpty(prevColumn.getIsRequired()) && !column.isPk()
&& (column.isInsert() || column.isEdit())
&& ((column.isUsableColumn()) || (!column.isSuperColumn()))) {
// 如果是(新增/修改&非主键/非忽略及父属性),继续保留必填/显示类型选项
column.setIsRequired(prevColumn.getIsRequired());
column.setHtmlType(prevColumn.getHtmlType());
}
genTableColumnMapper.updateGenTableColumn(column);
} else {
genTableColumnMapper.insertGenTableColumn(column);
}
});
List<GenTableColumn> delColumns = tableColumns.stream().filter(column -> !dbTableColumnNames.contains(column.getColumnName())).collect(Collectors.toList());
if (StringUtils.isNotEmpty(delColumns)) {
genTableColumnMapper.deleteGenTableColumns(delColumns);
}
}
至此满足了动态生成不同数据源的代码的需求
出现了什么问题,欢迎查缺补漏,提出bug