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: 可以重复。
selecttable_name, column_name, column_keyfrominformation_schema.`COLUMNS`where table_schema = 'proxy' and table_name = 'dim_nor'
很明显也是不符合需求的
三.方案三
这种方式也是通过 SQL 来进行查询.
show index from dim_prishow 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: */@Datapublic 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: */@Datapublic 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 加入
还未添加个人简介











评论