前言
我们在开发过程中,有时需要读取表结构信息。但是如何处理不同数据库的兼容性问题呐?我们可以从数据库连接的元数据中获取我们需要的信息。
属性说明
- getTables
TABLE_CAT String => 表类别(可为 null)
TABLE_SCHEM String => 表模式(可为 null)
TABLE_NAME String => 表名称
TABLE_TYPE String => 表类型。典型的类型是 "TABLE"、"VIEW"、"SYSTEM TABLE"、"GLOBAL TEMPORARY"、"LOCAL TEMPORARY"、"ALIAS" 和 "SYNONYM"。
REMARKS String => 表的解释性注释
TYPE_CAT String => 类型的类别(可为 null)
TYPE_SCHEM String => 类型模式(可为 null)
TYPE_NAME String => 类型名称(可为 null)
SELF_REFERENCING_COL_NAME String => 有类型表的指定 "identifier" 列的名称(可为null)
REF_GENERATION String => 指定在 SELF_REFERENCING_COL_NAME 中创建值的方式。这些值为 "SYSTEM"、"USER" 和 "DERIVED"。(可能为null)
- getPrimaryKeys
TABLE_CAT String => 表类别(可为 null)
TABLE_SCHEM String => 表模式(可为 null)
TABLE_NAME String => 表名称
COLUMN_NAME String => 列名称
KEY_SEQ short => 主键中的序列号
PK_NAME String => 主键的名称(可为 null)
- getIndexInfo
NON_UNIQUE int => 非唯一索引
INDEX_QUALIFIER String => 索引目录(可为 null)
INDEX_NAME String => 索引的名称
TYPE short => 索引类型
ORDINAL_POSITION short => 在索引列顺序号
COLUMN_NAME String => 列名
ASC_OR_DESC String => 列排序顺序:升序还是降序
CARDINALITY int => 基数
- getColumns
TABLE_CAT String => 表类别(可为 null)
TABLE_SCHEM String => 表模式(可为 null)
TABLE_NAME String => 表名称
COLUMN_NAME String => 列名称
DATA_TYPE int => 来自 java.sql.Types 的 SQL 类型
TYPE_NAME String => 数据源依赖的类型名称,对于 UDT,该类型名称是完全限定的
COLUMN_SIZE int => 列的大小。对于 char 或 date 类型,列的大小是最大字符数,对于 numeric 和 decimal 类型,列的大小就是精度。
BUFFER_LENGTH 未被使用。
DECIMAL_DIGITS int => 小数部分的位数
NUM_PREC_RADIX int => 基数(通常为 10 或 2)
NULLABLE int => 是否允许使用 NULL。
columnNoNulls - 可能不允许使用 NULL 值
columnNullable - 明确允许使用 NULL 值
columnNullableUnknown - 不知道是否可使用 null
REMARKS String => 描述列的注释(可为 null)
COLUMN_DEF String => 默认值(可为 null)
SQL_DATA_TYPE int => 未使用
SQL_DATETIME_SUB int => 未使用
CHAR_OCTET_LENGTH int => 对于 char 类型,该长度是列中的最大字节数
ORDINAL_POSITION int => 表中的列的索引(从 1 开始)
IS_NULLABLE String => "NO" 表示明确不允许列使用 NULL 值,"YES" 表示可能允许列使用 NULL 值。空字符串表示没人知道是否允许使用 null 值。
SCOPE_CATLOG String => 表的类别,它是引用属性的作用域(如果 DATA_TYPE 不是 REF,则为null)
SCOPE_SCHEMA String => 表的模式,它是引用属性的作用域(如果 DATA_TYPE 不是 REF,则为null)
SCOPE_TABLE String => 表名称,它是引用属性的作用域(如果 DATA_TYPE 不是 REF,则为 null)
SOURCE_DATA_TYPE short => 不同类型或用户生成 Ref 类型、来自 java.sql.Types 的 SQL 类型的源类型(如果 DATA_TYPE 不是 DISTINCT 或用户生成的 REF,则为null)
工具方法
DbUtils
public class DbUtils {
private static final Logger LOG = LoggerFactory.getLogger(DbUtils.class);
private DbUtils() {
}
private static final String MYSQL = "MYSQL";
private static final String ORACLE = "ORACLE";
private static final String HANA = "HANA";
private static final String SQLSERVER = "SQLSERVER";
private static final String H2 = "H2";
private static final String POSTGRESQL = "POSTGRESQL";
private static final String ZENITH = "ZENITH";
private static final String SCHEMA = "";
public static List<Table> getTables(DbConfig config) {
List<Table> list = new ArrayList<>();
try (Connection connection = getConnection(config)) {
DatabaseMetaData metaData = connection.getMetaData();
if (metaData == null) {
return list;
}
String[] types = {"TABLE"};
ResultSet tables = metaData.getTables(null, SCHEMA, "%", types);
while (tables.next()) {
String tableName = tables.getString("TABLE_NAME");
String tableRemarks = tables.getString("REMARKS");
Table table = new Table(tableName, tableRemarks);
ResultSet primaryKeys = metaData.getPrimaryKeys(null, SCHEMA, tableName);
while (primaryKeys.next()) {
String primaryKey = primaryKeys.getString("COLUMN_NAME");
table.setPrimaryKey(primaryKey);
}
if (LOG.isInfoEnabled()) {
String a = String.format("tableName: %-30s primaryKey: %-30s remarks: %-1s", table.getName(), table.getPrimaryKey(), table.getRemarks());
LOG.info(a);
LOG.info("======================================================================================================================");
String b = String.format("%-30s | %-12s | %-1s", "index_name", "unique", "columns");
LOG.info(b);
LOG.info("----------------------------------------------------------------------------------------------------------------------");
}
// 获取索引
table.setIndexList(getIndex(metaData, table));
if (LOG.isInfoEnabled()) {
LOG.info("======================================================================================================================");
String c = String.format("%-30s | %-12s | %-6s | %-6s | %-8s | %-13s | %-18s | %-1s", "name", "type", "size", "digits", "nullable", "autoIncrement", "defaultValue", "remarks");
LOG.info(c);
LOG.info("----------------------------------------------------------------------------------------------------------------------");
}
// 获取列
table.setColumnList(getColumn(metaData, table));
LOG.info("#################################################################################################################################");
list.add(table);
}
return list;
} catch (Exception e) {
return list;
}
}
private static List<Index> getIndex(DatabaseMetaData metaData, Table table) {
String tableName = table.getName();
List<Index> list = new ArrayList<>();
if (metaData == null) {
return list;
}
Map<String, List<String>> indexMap = new HashMap<>(16);
Map<String, Index> map = new HashMap<>(16);
try {
ResultSet indexInfo = metaData.getIndexInfo(null, SCHEMA, tableName, false, false);
while (indexInfo.next()) {
String indexName = indexInfo.getString("INDEX_NAME");
String columnName = indexInfo.getString("COLUMN_NAME");
boolean unique = !indexInfo.getBoolean("NON_UNIQUE");
short ordinalPosition = indexInfo.getShort("ORDINAL_POSITION");
if (!map.containsKey(indexName)) {
map.put(indexName, new Index().setName(indexName).setUnique(unique));
}
// 暂存索引列
List<String> indexList = indexMap.computeIfAbsent(indexName, k -> new ArrayList<>());
indexList.add(ordinalPosition - 1, columnName);
}
map.forEach((indexName, index) -> {
List<String> indexList = indexMap.get(indexName);
index = index.setFields(StringUtils.join(indexList, ","));
if (LOG.isInfoEnabled()) {
String a = String.format("%-30s | %-12s | %-1s", index.getName(), index.isUnique(), index.getFields());
LOG.info(a);
}
list.add(index);
});
return list;
} catch (Exception e) {
return list;
}
}
private static List<Column> getColumn(DatabaseMetaData metaData, Table table) {
String tableName = table.getName();
List<Column> list = new ArrayList<>();
if (metaData == null) {
return list;
}
try {
ResultSet columns = metaData.getColumns(null, "%", tableName, "%");
while (columns.next()) {
String columnName = columns.getString("COLUMN_NAME");
String columnType = columns.getString("TYPE_NAME");
int dataSize = columns.getInt("COLUMN_SIZE");
int digits = columns.getInt("DECIMAL_DIGITS");
boolean nullable = columns.getBoolean("NULLABLE");
boolean autoIncrement = columns.getBoolean("IS_AUTOINCREMENT");
String defaultValue = columns.getString("COLUMN_DEF");
if (defaultValue == null) {
defaultValue = "";
}
String remarks = columns.getString("REMARKS");
if (LOG.isInfoEnabled()) {
String a = String.format("%-30s | %-12s | %-6s | %-6s | %-8s | %-13s | %-18s | %-1s", columnName, columnType, dataSize, digits, nullable, autoIncrement, defaultValue, remarks);
LOG.info(a);
}
list.add(new Column(columnName, columnType, dataSize, digits, nullable, autoIncrement, defaultValue, remarks));
}
return list;
} catch (Exception e) {
return list;
}
}
private static Connection getConnection(DbConfig config) throws SQLException, ClassNotFoundException {
Class.forName(config.getDriver());
String dbType = getDbType(config.getUrl());
Properties properties = new Properties();
properties.setProperty("user", config.getUsername());
properties.setProperty("password", config.getPassword());
switch (dbType) {
case MYSQL:
properties.setProperty("remarks", "true");
properties.setProperty("useInformationSchema", "true");
break;
case ORACLE:
default:
properties.setProperty("remarks", "true");
break;
}
return DriverManager.getConnection(config.getUrl(), properties);
}
private static String getDbType(String url) {
if (url.startsWith("jdbc:h2")) {
return H2;
} else if (url.startsWith("jdbc:oracle")) {
return ORACLE;
} else if (url.startsWith("jdbc:mysql")) {
return MYSQL;
} else if (url.startsWith("jdbc:sqlserver")) {
return SQLSERVER;
} else if (url.startsWith("jdbc:sap")) {
return HANA;
} else if (url.startsWith("jdbc:postgresql")) {
return POSTGRESQL;
} else if (url.startsWith("jdbc:zenith")) {
return ZENITH;
}
return "UNKNOWN";
}
}
实体
DbConfig 数据库连接信息
public class DbConfig {
public DbConfig() {
}
public DbConfig(String driver, String url, String username, String password) {
this.driver = driver;
this.url = url;
this.username = username;
this.password = password;
}
private String driver;
private String url;
private String username;
private String password;
public String getDriver() {
return driver;
}
public DbConfig setDriver(String driver) {
this.driver = driver;
return this;
}
public String getUrl() {
return url;
}
public DbConfig setUrl(String url) {
this.url = url;
return this;
}
public String getUsername() {
return username;
}
public DbConfig setUsername(String username) {
this.username = username;
return this;
}
public String getPassword() {
return password;
}
public DbConfig setPassword(String password) {
this.password = password;
return this;
}
}
Table 表对象
public class Table {
/**
* 表名称
*/
private String name;
/**
* 注释
*/
private String remarks;
/**
* 主键字段
*/
private String primaryKey;
private List<Index> indexList;
private List<Column> columnList;
public Table() {
}
public Table(String name, String remarks) {
this.name = name;
this.remarks = remarks;
}
public Table(String name, String remarks, String primaryKey) {
this.name = name;
this.remarks = remarks;
this.primaryKey = primaryKey;
}
public String getName() {
return name;
}
public Table setName(String name) {
this.name = name;
return this;
}
public String getRemarks() {
return remarks;
}
public Table setRemarks(String remarks) {
this.remarks = remarks;
return this;
}
public String getPrimaryKey() {
return primaryKey;
}
public Table setPrimaryKey(String primaryKey) {
this.primaryKey = primaryKey;
return this;
}
public List<Index> getIndexList() {
return indexList;
}
public Table setIndexList(List<Index> indexList) {
this.indexList = indexList;
return this;
}
public List<Column> getColumnList() {
return columnList;
}
public Table setColumnList(List<Column> columnList) {
this.columnList = columnList;
return this;
}
@Override
public String toString() {
return "Table{" +
"name='" + name + '\'' +
", remarks='" + remarks + '\'' +
", primaryKey='" + primaryKey + '\'' +
", indexList=" + indexList +
", columnList=" + columnList +
'}';
}
Index 表索引
public class Index {
/**
* 索引名称
*/
private String name;
/**
* 是否唯一索引
*/
private boolean unique;
/**
* 索引列 逗号分隔
*/
private String fields;
public String getName() {
return name;
}
public Index setName(String name) {
this.name = name;
return this;
}
public boolean isUnique() {
return unique;
}
public Index setUnique(boolean unique) {
this.unique = unique;
return this;
}
public String getFields() {
return fields;
}
public Index setFields(String fields) {
this.fields = fields;
return this;
}
@Override
public String toString() {
return "Index{" +
", name='" + name + '\'' +
", unique=" + unique +
", fields='" + fields + '\'' +
'}';
}
}
Column 表格列
public class Column {
/**
* 字段名
*/
private String name;
/**
* 字段类型
*/
private String type;
/**
* 字段长度
*/
private int size;
/**
* 小数
*/
private int digits;
/**
* 可否为空
*/
private boolean nullable;
/**
* 是否自增
*/
private boolean autoIncrement;
/**
* 默认值
*/
private String defaultValue;
/**
* 备注
*/
private String remarks;
public Column() {
}
public Column(String name, String type, int size, int digits, boolean nullable, boolean autoIncrement, String defaultValue, String remarks) {
this.name = name;
this.type = type;
this.size = size;
this.digits = digits;
this.nullable = nullable;
this.autoIncrement = autoIncrement;
this.defaultValue = defaultValue;
this.remarks = remarks;
}
public String getName() {
return name;
}
public Column setName(String name) {
this.name = name;
return this;
}
public String getType() {
return type;
}
public Column setType(String type) {
this.type = type;
return this;
}
public int getSize() {
return size;
}
public Column setSize(int size) {
this.size = size;
return this;
}
public int getDigits() {
return digits;
}
public Column setDigits(int digits) {
this.digits = digits;
return this;
}
public boolean isNullable() {
return nullable;
}
public Column setNullable(boolean nullable) {
this.nullable = nullable;
return this;
}
public boolean isAutoIncrement() {
return autoIncrement;
}
public Column setAutoIncrement(boolean autoIncrement) {
this.autoIncrement = autoIncrement;
return this;
}
public String getDefaultValue() {
return defaultValue;
}
public Column setDefaultValue(String defaultValue) {
this.defaultValue = defaultValue;
return this;
}
public String getRemarks() {
return remarks;
}
public Column setRemarks(String remarks) {
this.remarks = remarks;
return this;
}
@Override
public String toString() {
return "Column{" +
"name='" + name + '\'' +
", type='" + type + '\'' +
", size=" + size +
", digits=" + digits +
", nullable=" + nullable +
", autoIncrement=" + autoIncrement +
", defaultValue='" + defaultValue + '\'' +
", remarks='" + remarks + '\'' +
'}';
}
}
网友评论