/*
* JEF - Copyright 2009-2010 Jiyi (mr.jiyi@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jef.database.meta;
import java.sql.DatabaseMetaData;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import jef.database.dialect.DatabaseDialect;
import jef.tools.StringUtils;
/**
* 数据库中查到的索引信息
*
* @author Administrator
*
*/
public class Index {
/**
* 表的schema
*/
private String tableSchema;
/**
* 表的indexQualifier
*/
private String indexQualifier;
/**
* 表名
*/
private String tableName;
/**
* 索引名
*/
private String indexName;
/**
* 各个列
* 其中KEY是列名,value=true表示正序ASC,false表示倒序DESC
*/
private final List<IndexItem> columns = new ArrayList<IndexItem>(4);
/**
* 是否唯一
*/
private boolean unique;
/**
* 过滤条件
*/
private String filterCondition;
/**
* 用户定义的索引关键字,如
* hash partitioned
* CLUSTERED COLUMNSTORE 这些关键字
*/
private String userDefinition;
/**
* <UL>
* <LI>tableIndexStatistic - this identifies table statistics that are
* returned in conjuction with a table's index descriptions</LI>
* <LI>tableIndexClustered - this is a clustered index</LI>
* <LI>tableIndexHashed - this is a hashed index</LI>
* <LI>tableIndexOther - this is some other style of index</LI>
* </UL>
*/
private int type;
/**
* 描述索引的用途
*/
public enum Usage{
/**
* 主键产生的索引
*/
PK,
/**
* 外键产生的索引
*/
FK,
/**
* Unique约束产生的索引
*/
UNIQUE,
/**
* 独立索引
*/
INDEX
}
/*
* 索引的用途
* 一般来说有大部分数据库有四种约束 PK FK UNIQUE CHECK (NOT NULL & DEFAULT不算),前三种都会产生Index。
* 一个索引能否直接删除,取决于其是属于约束。 问题是现在无法判断一个Index究竟属于谁。
* 由于JDBC不支持直接获得Index的类型,也不支持获得数据库CONSTRAINT。因此我们只能用获得Index的方法
* 来间接的获得UNIQUE。此时如何判定INDEX是一个独立的INDEX还是一个隶属于表的CONSTRAINT。是个麻烦的问题。
*
* 在MySQL上,Unique index等同于UNIQUE CONSTRAINT。MySQL上的Constraint增加的索引,除了PK外都可以drop index删除。
* 在ORALCE/Derby/Postgres上,UNIQIE index是UNIQUE CONSTRAINT的一个组成部分,不能直接删除
*
* 因此,究竟是一个独立索引,还是一个附属于约束 (PK.FK.UNIQUE)的索引,这是个问题。
* 怎么才能判断出来呢?
* 相对来说,PK FK 比较好判断。
*
* 复杂的方法不是没有,对于MySQL可以用
* show create table得到建表语句,然后解析可以得到所有约束——反正除了主键受保护,别的随便删
* ORACLE麻烦一点,查系统表,或者用select table_name,dbms_metadata.get_ddl('TABLE','TABLEMASTER')from user_tables where table_name='TABLEMASTER';
* 得到建表语句。PG需要查系统表pg_constraint。
* Derby?
* MSSQL ?
*
* 要么先不支持。反正索引如果删不掉那就是约束。
*/
//private Usage usage;
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("index ");
sb.append(indexName).append(" on ").append(tableName);
sb.append("(");
Iterator<IndexItem> iter=columns.iterator();
sb.append(iter.next());
for(;iter.hasNext();){
sb.append(',').append(iter.next());
}
sb.append(")");
if (unique)
sb.append("\t[UNIQUE]");
return sb.toString();
}
public Index(String indexName){
this.indexName=indexName;
}
public Index(){
}
/**
* 索引类型,有以下几类
* <UL>
* <LI>tableIndexStatistic(0) - this identifies table statistics that are
* returned in conjuction with a table's index descriptions
* <LI>tableIndexClustered(1) - this is a clustered index
* <LI>tableIndexHashed (2) - this is a hashed index
* <LI>tableIndexOther (3) - this is some other style of index
* </UL>
*
* @return 索引类型
*/
public int getType() {
return type;
}
/**
* 设置索引类型
*
* @param type
* 索引类型
*/
public void setType(int type) {
this.type = type;
}
/**
* 得到索引中的所有列
*
* @return 所有列名
*/
public String[] getColumnNames() {
String[] result=new String[columns.size()];
for(int i=0;i<columns.size();i++){
result[i]=columns.get(i).column;
}
return result;
}
/**
* 添加一个列,在最后的位置
*
* @param column
* 列名称
*/
public void addColumn(String column,Boolean isAsc) {
columns.add(new IndexItem(column,isAsc,columns.size()+1));
}
/**
* 添加一个列
*/
public void addColumn(String column,Boolean isAsc,int seq) {
columns.add(seq-1, new IndexItem(column,isAsc,seq));
}
/**
* 删除一个列
* @param column 列名
* @return
*/
public boolean removeColumn(String column){
return columns.remove(column);
}
/**
* 获得索引名称
*
* @return 索引名称
*/
public String getIndexName() {
return indexName;
}
/**
* 设置索引名称
*
* @param indexName
* 索引名称
*/
public void setIndexName(String indexName) {
this.indexName = indexName;
}
/**
* 该索引是否唯一约束
*
* @return 如有唯一约束返回true
*/
public boolean isUnique() {
return unique;
}
/**
* 设置该索引是否有唯一约束
*
* @param unique
* 是否有唯一约束
*/
public void setUnique(boolean unique) {
this.unique = unique;
}
public List<IndexItem> getColumns() {
return columns;
}
/**
* 获得索引所在的表名
*
* @return 索引所在的表名
*/
public String getTableName() {
return tableName;
}
/**
* 设置索引所在的表名
*
* @param tableName
* 索引所在的表名
*/
public void setTableName(String tableName) {
this.tableName = tableName;
}
public String getTableSchema() {
return tableSchema;
}
public void setTableSchema(String tableSchema) {
this.tableSchema = tableSchema;
}
public String getIndexQualifier() {
return indexQualifier;
}
public void setIndexQualifier(String indexQualifier) {
this.indexQualifier = indexQualifier;
}
public String getFilterCondition() {
return filterCondition;
}
public void setFilterCondition(String filterCondition) {
this.filterCondition = filterCondition;
}
public String getUserDefinition() {
return userDefinition;
}
public void setUserDefinition(String userDefinition) {
this.userDefinition = userDefinition;
}
public static class IndexItem{
public String column;
public boolean asc;
public int seq;
public IndexItem(String column,boolean asc,int seq){
this.column=column;
this.asc=asc;
this.seq=seq;
}
public IndexItem(){
}
@Override
public String toString() {
if(asc){
return column;
}else{
return column+" DESC";
}
}
}
public String toCreateSql(DatabaseDialect profile) {
StringBuilder sb = new StringBuilder("CREATE ");
if(this.unique){
sb.append("UNIQUE ");
}
if(this.type==DatabaseMetaData.tableIndexClustered){
sb.append("CLUSTERED ");
}
if (StringUtils.isNotEmpty(userDefinition)) {
sb.append(userDefinition).append(' ');
}
sb.append("INDEX ");
sb.append(StringUtils.isEmpty(indexName)?generateName():indexName).append(" ON ");
sb.append(getTableWithSchem()).append("(");
Iterator<IndexItem> iter=columns.iterator();
sb.append(iter.next());
for(;iter.hasNext();){
sb.append(',').append(iter.next());
}
sb.append(")");
sb.append(profile.getProperty(DbProperty.INDEX_USING_HASH, ""));
return sb.toString();
}
public String generateName() {
if(StringUtils.isEmpty(this.indexName)){
StringBuilder iNameBuilder = new StringBuilder();
iNameBuilder.append("IDX_").append(StringUtils.truncate(StringUtils.removeChars(tableName, '_'), 14));
iNameBuilder.append(StringUtils.randomString());
this.indexName=iNameBuilder.toString();
}
return indexName;
}
public String getTableWithSchem() {
if(StringUtils.isEmpty(tableSchema)){
return tableName;
}else{
return tableSchema+"."+tableName;
}
}
public boolean isOnSingleColumn(String columnName) {
if(columnName==null)return false;
if(this.columns.size()!=1)return false;
for(IndexItem item: this.columns){
if(columnName.equals(item.column)){
return true;
}
}
return false;
}
}