1. 簡介
源碼地址:https://github.com/wukenaihe/db-meta
有任何bug可以直接發送郵件告知承諾會馬上進行修改,如果需要添加功能或者改進也希望告知,會及時進行改進。希望能夠點贊(\(^o^)/~)
數據庫的元數據庫獲取到現在為止並沒有太好用的開源框架,最有名氣的可能是schemacrawler。不過這個軟件實在是太大太大了,不僅包括元數據的獲取,還包括表、列等信息的顯示。同時它的性能存在巨大弊端,基本上oracle數據庫他就不太能用,會把oracle里面的許多臨時表、垃圾表等都一股腦兒拉出來,非常可怕。它的接口相對比較簡單,getDatabase只有這么一個獲取方法,如果你要獲取一張表,你也得用這個方法。
如果,不用開源框架,你可以選擇用jdbc標准DatabaseMeta接口。使用不方便,需要處理大量的SQLException同時,他也不能獲取觸發器、存儲過程、函數等定義內容。
如果,直接從數據庫里面獲取數據庫元數據,相當復雜。
1.1. 設計目標
- l 簡單,易用
- l 易用擴展
- l 線程安全
- l 高性能
簡單,易用:數據庫之間的元數據相差非常大,且均不遵守SQL標准。所以,我們把元數據進行了抽象。根據SQL標准及常用內容,組成了一個類樹。
易擴展:允許添加別的數據庫實現方式,允許方便的重寫現有方法。
高性能:輕量,能獲取小部分的元數據。
2. 例子
https://github.com/wukenaihe/db-meta-example
Maven
<repositories> <repository> <id>clojars</id> <name>Clojars repository</name> <url>https://clojars.org/repo</url> </repository> </repositories> <dependencies> <dependency> <groupId>org.clojars.xumh</groupId> <artifactId>db-meta</artifactId> <version>0.0.1-Release</version> </dependency> <dependencies>
在中國很有可能下載不下來,可以用開源中國上面的maven。不過去缺少依賴logback,需要自己添加下。
<dependency> <groupId>com.cgs.dbMeta</groupId> <artifactId>db-meta</artifactId> <version>0.0.1-Release</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.0.9</version> </dependency>
http://maven.oschina.net/index.html#nexus-search;quick~db-meta
如何使用maven,大家可以參照:http://maven.oschina.net/help.html
public static void main(String[] args) { MysqlDataSource datasource = new MysqlDataSource(); datasource.setServerName("localhost"); datasource.setPort(3306); datasource.setDatabaseName("dctest"); datasource.setUser("root"); datasource.setPassword("123456"); MetaLoader metaLoader=new MetaLoaderImpl(datasource); Set<String> tableNames=metaLoader.getTableNames(); System.out.print("數據庫中擁有表:"); System.out.println(tableNames); Table table=metaLoader.getTable("des"); PrintUtils.printTable(table); Map<String, Procedure> procedures=metaLoader.getProcedures(); // System.out.println(procedures); }
3. 程序結構
3.1. 元數據結構
- Database:數據庫,擁有多個Schema
- Schema:數據集,這個數據庫與SQL中的數據集略微不同(Oracle:schema,mySql:catalog,SqlServer:catalog.schema)。這是因為,不同數據庫對schema的解釋不同造成的。
- Table:擁有列、主鍵、外鍵、約束、觸發器、索引和權限。
- Column:名稱、注釋、是否為null、類型(java.sql.Types)、類型(數據庫類型名稱)、精度、小數位數、默認值。
- 主鍵:主鍵名,列名(按照定義的順序)
- 外鍵:外鍵引用關系、外鍵刪除規則、外鍵更新規則
- 索引:名稱、唯一性、索引類型(JDBC定義)、頁數、定義和列名
- 約束:名稱、約束類型、約束定義(如”D1 Is not null”)
- 觸發器:名稱、所屬表、定義
- 權限:授予者、被授予者、權限、是否有授予權限
- 存儲過程:名稱、定義(大量信息被壓縮在定義中)
- 函數:名稱、定義(大量信息被壓縮在定義中)
3.2. API
通過等級來進行控制,這樣能夠避免讀取不必要的信息而影響性能。
|
小 |
標准 |
大 |
驅動信息(JDBC) |
Yes | Yes | Yes |
數據庫信息 |
Yes | Yes | Yes |
表 |
Yes | Yes | Yes |
主鍵 |
Yes | Yes | Yes |
約束 |
No |
Yes | Yes |
視圖(視為表) |
No |
No |
Yes |
索引 |
No |
Yes | Yes |
外鍵 |
No |
Yes | Yes |
權限 |
No |
No |
Yes |
觸發器 |
No |
No |
Yes |
列 |
Yse | Yes |
Yes |
MetaLoader接口
方法 |
說明 |
Set<String> getTableNames() |
獲取表名(當前Schema) |
Table getTable(String tableName) |
獲取表(標准級別) |
Table getTable(String tableName,SchemaInfoLevel schemaLevel) |
如上 |
Table getTable(String tableName,SchemaInfo schemaInfo) |
獲取指定Schema下的,特定表 |
Set<SchemaInfo> getSchemaInfos() |
獲取數據下的Schema信息 |
Schema getSchema() |
獲取當前schema |
Schema getSchema(SchemaInfo schemaInfo) |
獲取指定的schema元數據 |
Set<String> getProcedureNames() |
獲取當前schema存儲過程名稱 |
Procedure getProcedure(String procedureName) |
獲取存儲過程 |
Map<String,Procedure> getProcedures() |
獲取當前schema存儲過程集合 |
Set<String> getTriggerNames() |
獲取當前schema觸發器名稱 |
Trigger getTrigger(String triggerName) |
獲取指定的觸發器 |
Map<String, Trigger> getTriggers() |
獲取當前schema觸發器集合 |
Set<String> getFunctionNames() |
獲取當前函數名稱 |
Function getFunction(String name) |
獲取指定的函數 |
Map<String, Function> getFunctions() |
獲取當前schema函數集合 |
Database getDatabase() |
獲取數據庫元數據(標准) |
Database getDatabase(SchemaInfoLevel level) |
獲取指定級別的數據庫元數據 |
4. 設計過程
不同的數據庫,獲取元數據的方式必然不同,從這一點來看,我們需要一個策略模式。策略模式也方便進行擴展。
數據庫決定之后,實際上也已經決定會使用哪個具體實現。所以,在程序中實現方式的建立將交由一個工程,根據數據庫類型自動建立。
我們的數據庫要求是線程安全的,所以要求不具有狀態,每一個方法都具有獲取連接、關閉連接等步驟,所以我們在這里使用了一個模板模式。MetaLoader的方法還是比較復雜的。所以,我們抽象除了一個MetaCrawler接口,進行細化,MetaLoader的方法實現可以通過MetaCrawler的組合實現。
部分實現,我們使用的是DatabaseMeta,任何數據庫都是一樣的。但是部分信息如觸發器、存儲過程、函數等信息,DatabaseMeta是獲取不到的,又要分開實現。所以,毫無疑問,我們這里也使用了模板模式。
JDBC的異常為SQLException異常,拋出的異常都必須接住,會讓代碼結構非常混亂。同時,在close的方法拋出的異常,通常交給開發人員是無法進行任何處理的。所以,我們選擇catch這類無法處理的異常,然后進行日志輸出。
4.1. 線程安全
public Procedure getProcedure(String procedureName) { Connection con = JDBCUtils.getConnection(dataSource); MetaCrawler metaCrawler=null; Procedure p; try{ metaCrawler=factory.newInstance(con); p=metaCrawler.getProcedure(procedureName); return p; }catch(DataAccessException e){ logger.debug(e.getMessage(),e); throw new DatabaseMetaGetMetaException("Get tables error!", e); }finally{ JDBCUtils.closeConnection(con); } }
在方法的開始位置會獲取一個連接,然后通過工廠創建一個實例。在方法的末尾關閉連接。整個過程都是無狀態的,所以是線程安全的。