Java 从建表语句中提取索引信息, 索引名称, 复合索引包含字段, 索引类别 (普通索引 / 主键索引 / 唯一索引 / 全文索引 / 空间索引) 解决方案
查阅了多方面资料, 对比并尝试了以下几种方案, 我个人在项目中是需要最后一种才能满足需求, 现在列举并说明以下.
一.方案一
DatabaseMetaData , 这种方式代码如下, 这种方式获取的索引类型其实是如下几种
* tableIndexStatistic - 此标识与表的索引描述一起返回的表统计信息
* tableIndexClustered - 此为集群索引
* tableIndexHashed - 此为散列索引
* tableIndexOther - 此为某种其他样式的索引
因为我也没有具体创建过这几种索引,所以也不认识. 但是很明显不符合我们的需求.
代码如下
/**
* 获得一个表的索引信息
* getIndexInfo
* catalog : 类别名称,因为存储在此数据库中,所以它必须匹配类别名称。该参数为 “” 则检索没有类别的描述,为 null 则表示该类别名称不应用于缩小搜索范围
* schema : 模式名称,因为存储在此数据库中,所以它必须匹配模式名称。该参数为 “” 则检索那些没有模式的描述,为 null 则表示该模式名称不应用于缩小搜索范围
* table : 表名称,因为存储在此数据库中,所以它必须匹配表名称
* unique : 该参数为 true 时,仅返回惟一值的索引;该参数为 false 时,返回所有索引,不管它们是否惟一
* approximate : 该参数为 true 时,允许结果是接近的数据值或这些数据值以外的值;该参数为 false 时,要求结果是精确结果
*/
public void queryIndexInfo(String dbName, String tableName, DatabaseMetaData metaData) {
ResultSet rs = null;
try {
rs = metaData.getIndexInfo(null, dbName, tableName, false, true);
while (rs.next()) {
String ascOrDesc = rs.getString("ASC_OR_DESC"); // 列排序顺序: 升序还是降序
int cardinality = rs.getInt("CARDINALITY"); // 基数
short ordinalPosition = rs.getShort("ORDINAL_POSITION");// 在索引列顺序号
boolean nonUnique = rs.getBoolean("NON_UNIQUE"); // 非唯一索引(Can index values be non-unique. false when TYPE is tableIndexStatistic )
String indexQualifier = rs.getString("INDEX_QUALIFIER");// 索引目录(可能为空)
String indexName = rs.getString("INDEX_NAME"); // 索引的名称
short indexType = rs.getShort("TYPE"); // 索引类型
String columnName = rs.getString("COLUMN_NAME");
}
} catch (SQLException e) {
log.error(e.getMessage(), e);
} finally {
//
}
}
二.方案二
自定义查询语句从 information_schema.`COLUMNS` 中获取索引信息, 但是获取的索只有三种类型
PRI: 主键约束;
UNI: 唯一约束;
MUL: 可以重复。
select
table_name, column_name, column_key
from
information_schema.`COLUMNS`
where
table_schema = 'proxy'
and table_name = 'dim_nor'
很明显也是不符合需求的
三.方案三
这种方式也是通过 SQL 来进行查询.
show index from dim_pri
show key from dim_pri
这种方式也没能达到要求.
四.方案四
使用 sql 解析器来解析建表语句
这里同学有空的话可以找找有没有成熟的方案, 例如 DRUID 和 jsqlparser
这里因为我只需要解析建表语句, 所以我还是自己写了一下. 多余的不说了, 上代码
定义两个基本对象
package com.biubiu.dms.core.model;
import lombok.Data;
import java.util.List;
/**
* @author :张音乐
* @date :Created in 2021/8/13 下午12:56
* @description:索引信息
* @email: zhangyule1993@sina.com
* @version:
*/
@Data
public class QueryKey {
private String indexName;
private String indexType;
private List<IndexColumn> indexColumns;
}
package com.biubiu.dms.core.model;
import lombok.Data;
/**
* @author :张音乐
* @date :Created in 2021/8/13 下午12:57
* @description:索引包含列
* @email: zhangyule1993@sina.com
* @version:
*/
@Data
public class IndexColumn {
private String columnName;
private String length;
private String order;
}
具体解析方法, 这里最好从数据库获取建表语句, 因为 mysql 会给我们格式化一下, 这样获取到的是标准的建表语句.
public List<QueryKey> getKeyInfo(String tableName) throws Exception {
List<QueryKey> resList = new ArrayList<>();
String sql = "SHOW CREATE TABLE `" + tableName + "`";
// 执行查询 ..省略, 毕竟每个人不一样
// 假设你是用jdbc来查询, 那么这里ResultSet 中获取 "Create Table" 字段.
String text = rs.getString("Create Table").toString().toLowerCase();
String temp = org.apache.commons.lang3.StringUtils.substringAfter(text, "(");
String temp2 = org.apache.commons.lang3.StringUtils.substringBeforeLast(temp, ")");
String [] arr = org.apache.commons.lang3.StringUtils.split(temp2, "\n");
List<String> keyTextList = Arrays.stream(arr).filter(p -> p.contains("key") || p.contains("index")).collect(Collectors.toList());
for(int i = 0; i < keyTextList.size(); i++) {
QueryKey queryKey = new QueryKey();
String item = keyTextList.get(i);
// 获取 索引类型
String type = getKeyType(item);
queryKey.setIndexType(type);
// 获取索引名称
String indexName = getKeyName(item, type);
queryKey.setIndexName(indexName);
// 获取索引字段
String fieldInfoAfter = org.apache.commons.lang3.StringUtils.substringAfter(item, "(");
String fieldInfo = org.apache.commons.lang3.StringUtils.substringBeforeLast(fieldInfoAfter, ")");
String[] columns = org.apache.commons.lang3.StringUtils.split(fieldInfo, ",");
List<IndexColumn> columnsList = getKeyColumns(columns);
queryKey.setIndexColumns(columnsList);
resList.add(queryKey);
}
return resList;
}
private List<IndexColumn> getKeyColumns(String[] columns) {
List<IndexColumn> columnsList = Arrays.stream(columns).map(p -> {
// `id`
// `name`(2) DESC
IndexColumn indexColumn = new IndexColumn();
String columnName = org.apache.commons.lang3.StringUtils.substringBetween(p, "`", "`");
indexColumn.setColumnName(columnName);
String regEx="[^0-9]";
Pattern pattern = Pattern.compile(regEx);
Matcher m = pattern.matcher(p);
// 前缀限制长度
String length = m.replaceAll("").trim();
indexColumn.setLength(length);
String order = p.contains("desc") ? "DESC" : "ASC";
indexColumn.setOrder(order);
return indexColumn;
}).collect(Collectors.toList());
return columnsList;
}
private String getKeyName(String item, String type) {
String indexName = "";
if(type.equals("Primary")) {
// 主键索引一定是 PRIMARY, 但是在建表语句中是省略的
indexName = "PRIMARY";
} else {
String itemAfter = org.apache.commons.lang3.StringUtils.substringAfter(item, "key");
indexName = org.apache.commons.lang3.StringUtils.substringBefore(itemAfter, "(").replaceAll("`", "").trim();
}
return indexName;
}
private String getKeyType(String item) {
String type = "Normal";
String keyType = org.apache.commons.lang3.StringUtils.substringBefore(item, "key");
if(org.apache.commons.lang3.StringUtils.isNotBlank(keyType)) {
if(keyType.trim().equals("primary") ) {
type = "Primary";
}
if(keyType.trim().equals("unique")) {
type = "Unique";
}
if(keyType.trim().equals("fullText")) {
type = "FullText";
}
if(keyType.trim().equals("spatial")) {
type = "Spatial";
}
}
return type;
}
格式如下:
[
// 例如 Primary Key `id`
{
"indexName":"PRIMARY",
"indexType":"Primary",
"indexColumns":[
{
"columnName":"id",
"length":"",
"order":"ASC"
}
]
},
// 例如 Unique Key (`id`, `name`(2) DESC )
{
"indexName":"idx_ss",
"indexType":"Unique",
"indexColumns":[
{
"columnName":"id",
"length":"",
"order":"ASC"
},
{
"columnName":"name",
"length":"2",
"order":"DESC"
}
]
}
]
用这种方式的化在建表的时候就需要规范以下建表规则了, 但是应该可以覆盖大部分了.
版权声明: 本文为 InfoQ 作者【张音乐】的原创文章。
原文链接:【http://xie.infoq.cn/article/ee36d71c82aeb5e8459780d57】。未经作者许可,禁止转载。
张音乐
求你关注我,别不识抬举.别逼我跪下来求你. 2021.03.28 加入
还未添加个人简介
评论