[瘋狂Java]JDBC:資料庫元資料分析
1. 資料庫元資料——有時候並不僅僅需要分析和業務邏輯相關的表,也需要分析當前資料庫的有關資訊:
1) 比如分析當前資料庫中有多少張表、當前共建立了多少外來鍵、多少索引、某個表的結果如何等等;
2) 這些都屬於描述資料庫中資料的資料,稱為元資料;
3) JDBC提供了對資料庫元資料分析的API——DatabaseMetaData類物件(通過Connection獲得,物件方法,獲取本次連線的資料庫的元資料物件):
DatabaseMetaData Connection.getMetaData();
!!接著只要呼叫DatabaseMetaData的各種方法就可以查詢資料庫的元資料了;
!!接下來要介紹的方法都是DatabaseMetaData的物件方法;
2. 獲取資料庫的一些廠商資訊:
1) 基本以get作為字首,最常用的是獲取當前資料庫的品牌、驅動、版本等資訊;
2) 獲取品牌資訊:
i. String getDatabaseProductName(); // 返回品牌名稱,MySQL返回的就是"MySQL"
ii. String getDatabaseProductVersion(); // 返回品牌版本好,MySQL返回的直接就是幾點幾點幾的版本號
3) 獲取驅動資訊:驅動資訊裡肯定包含有connector字串,因為驅動的本質就是資料庫聯結器
i. String getDriverName(); // 返回驅動器名稱,MySQL返回的是"MySQL Connector Java"
ii. String getDriverVersion(); // 返回驅動器版本號,MySQL返回的是"mysql-connector-java-版本號"
3. 瞭解資料庫支援的功能:
1) 基本以supports作為字首,返回是否支援某項功能,返回值肯定是boolean;
2) 常用的兩個方法:
i. boolean supportsCorrelatedSubqueries(); // 是否支援關聯子查詢
ii. boolean supportsBatchUpdates(); // 是否支援批處理
!!MySQL這兩個功能都支援
4. 查詢資料字典中的資訊:
1) 很多方法會基於資料字典(元資料)進行查詢,而資料字典也是資料庫中的物件(表、索引、檢視等等),因此底層也是通過SQL語句實現的,因此這類查詢會以ResultSet來返回結果,如果查詢資訊不可用就返回null;
2) 這類查詢的方法都以get作為字首;
3) 首先需要了解的就是資料庫支援的表的型別:
i. 方法是:ResultSet getTableTypes();
ii. 標準SQL規定了如下幾種表的型別:TABLE(使用者表)、VIEW(使用者檢視)、SYSTEM TABLE(系統表,屬於資料字典)、SYSTEM VIEW(系統檢視,屬於資料字典)、LOCAL TEMPORARY(本地臨時資料);
4) 查詢資料庫中表的資訊:
i. 方法是:ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String types[]);
ii. catalog:表所屬的範疇,即查詢的表所屬的資料庫(比如上面例子中的select_test)名稱,如果傳""表示該表沒有所屬的資料庫,如果傳null就表示沒有這項篩選條件;
iii. schemaPattern:表所屬的模式,可以使用SQL的萬用字元%和_,同樣""表示沒有模式,null表示不使用該項篩選條件
iv. types:表的型別,即getTableTypes返回的資料庫所支援的表的型別,這裡是一個String陣列的集合
v. 呼叫後,會根據引數的篩選資訊,把所有符合要求的表都篩選出來然後返回每一張表的相關資訊;
5) 查詢資料庫中的儲存過程:
i. 方法是: ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern);
ii. 前兩個引數的意義還是老樣子,procedureNamePattern就是儲存過程的名稱,可以使用SQL萬用字元%和_來過濾;
iii. 將返回所有符合篩選條件的儲存過程的相關資訊;
6) 查詢主鍵:
i. 方法:ResultSet getPrimaryKeys(String catalog, String schema, String table);
ii. 主鍵肯定有所屬的表,這裡所屬的表的表名table必須是精確的,不能有萬用字元;
iii. 只要引數名裡有pattern就表示這是一個匹配模式,可以使用SQL萬用字元,如果沒有pattern則表示必須完全匹配;
iv. 會返回所有符合要求的主鍵的資訊
!!這裡使用的萬用字元都是SQL的萬用字元而不是Java的正則表示式!!
7) 查詢列的資訊:
i. 方法:ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern);
ii. tableNamePattern:確定列所屬的表的名字,可以用SQL萬用字元;
iii. columnNamePattern:列的名字,可以使用萬用字元
8) 查詢外來鍵:
ResultSet getCrossReference(
String parentCatalog, String parentSchema, String parentTable,
String foreignCatalog, String foreignSchema, String foreignTable
);
i. Catalog、Schema的含義還是老樣子;
ii. table就是表名,這裡必須是精確的;
iii. parent是被參照的表,foreign是主動去參照的表;
5. 示例:
public class Test {
private String driver;
private String url;
private String user;
private String pass;
public void initParam() throws FileNotFoundException, IOException {
Properties props = new Properties();
props.load(new FileInputStream("mysql.ini"));
driver = props.getProperty("driver");
url = props.getProperty("url");
user = props.getProperty("user");
pass = props.getProperty("pass");
}
private void showResult(ResultSet rs) throws Exception {
ResultSetMetaData rsmd = rs.getMetaData();
for (int i = 0; i < rsmd.getColumnCount(); i++) {
System.out.print('[' + rsmd.getColumnName(i + 1) + ']' + '\t');
}
System.out.println();
while (rs.next()) {
for (int i = 0; i < rsmd.getColumnCount(); i++) {
System.out.print(rs.getString(i + 1) + '\t');
}
System.out.println();
}
System.out.println();
rs.close();
}
public void init() throws Exception {
initParam();
Class.forName(driver);
try (Connection conn = DriverManager.getConnection(url, user, pass)) {
DatabaseMetaData dbmd = conn.getMetaData();
System.out.println(dbmd.supportsCorrelatedSubqueries());
System.out.println(dbmd.supportsBatchUpdates());
System.out.println(dbmd.getDatabaseProductName());
System.out.println(dbmd.getDatabaseProductVersion());
System.out.println(dbmd.getDriverName());
System.out.println(dbmd.getDriverVersion());
showResult(dbmd.getTableTypes()); // MySQL支援的表的型別
showResult(dbmd.getTables(null, null, "%", new String[]{"TABLE"})); // 列出要查詢的表
showResult(dbmd.getPrimaryKeys(null, null, "student_table")); // 列出指定表
showResult(dbmd.getProcedures(null, null, "%")); // 列出要查詢的儲存過程
showResult(dbmd.getCrossReference(null, null, "teacher_table", null, null, "student_table")); // 列出要查詢的外來鍵
showResult(dbmd.getColumns(null, null, "student_table", "%")); // 列出要查詢的表的所有列資訊(表結構)
}
}
public static void main(String[] args) throws Exception {
new Test().init();
}
}
6. 直接在MySQL命令列中分析資料字典:這裡只講MySQL的系統表,只在MySQL下存在
1) MySQL直接提供了一個information_schema資料庫,即資料字典,來儲存資料庫元資料,使用者可以直接查詢該資料庫來獲取描述資料庫的資訊;
2) information_schema中的資料是不允許使用者修改的,這些系統表就相當於檢視,只能檢視,這是為了避免修改帶來的毀滅性災難;
3) 常用的系統表:
i. tables:存放該資料庫例項中所有的表的資訊;
ii. views:所有的檢視的資訊;
iii. triggers:所有的處觸發器的資訊;
iv. rountines:所有的儲存過程和函式的資訊;
v. statistics:所有的索引資訊;
vi. table_constraints:所有的表約束資訊;
vii. key_column_usage:列上的鍵資訊;
viii. columns:所有的列資訊;
ix. schemata:資料庫對應的資訊(資料庫版本等);
7. 選擇合適的分析方法:
1) DatabaseMetaData一般用於應用程式層級,因為其可以輕鬆實現跨資料庫,但是無法更進一步瞭解資料庫更底層的細節;
2) 直接分析資料庫中的資料字典顯然會增加和底層的耦合性,一般用於純資料庫層面的靜態分析;