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); } }
在方法的开始位置会获取一个连接,然后通过工厂创建一个实例。在方法的末尾关闭连接。整个过程都是无状态的,所以是线程安全的。