[meta]完善模块功能

This commit is contained in:
datagear 2020-03-12 23:30:33 +08:00
parent b8e10f7f87
commit f85e1e7792
24 changed files with 2048 additions and 1 deletions

52
datagear-meta/pom.xml Normal file
View File

@ -0,0 +1,52 @@
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.datagear</groupId>
<artifactId>datagear</artifactId>
<version>1.7.0</version>
</parent>
<artifactId>datagear-meta</artifactId>
<name>datagear-meta</name>
<dependencies>
<dependency>
<groupId>org.datagear</groupId>
<artifactId>datagear-util</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.datagear</groupId>
<artifactId>datagear-connection</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>${maven-antrun-plugin.version}</version>
<executions>
<!-- 拷贝LICENSE文件 -->
<execution>
<id>copyLICENSE</id>
<phase>prepare-package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<copy file="../LICENSE" todir="${project.build.outputDirectory}" />
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,68 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.meta;
import java.io.Serializable;
import java.util.Arrays;
/**
* 表键
*
* @author datagear@163.com
*
*/
public abstract class AbstractKey implements Serializable
{
private static final long serialVersionUID = 1L;
/** 列名 */
private String[] columnNames;
/** 键名 */
private String keyName;
public AbstractKey()
{
super();
}
public AbstractKey(String[] columnNames)
{
super();
this.columnNames = columnNames;
}
public String[] getColumnNames()
{
return columnNames;
}
public void setColumnNames(String[] columnNames)
{
this.columnNames = columnNames;
}
public boolean hasKeyName()
{
return (this.keyName != null && !this.keyName.isEmpty());
}
public String getKeyName()
{
return keyName;
}
public void setKeyName(String keyName)
{
this.keyName = keyName;
}
@Override
public String toString()
{
return getClass().getSimpleName() + " [columnNames=" + Arrays.toString(columnNames) + ", keyName=" + keyName
+ "]";
}
}

View File

@ -0,0 +1,75 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.meta;
import java.io.Serializable;
/**
* 抽象表元信息
*
* @author datagear@163.com
*
*/
public class AbstractTable implements Serializable
{
private static final long serialVersionUID = 1L;
/** 名称 */
private String name;
/** 类型 */
private TableType type;
/** 描述 */
private String comment;
public AbstractTable()
{
super();
}
public AbstractTable(String name, TableType type)
{
super();
this.name = name;
this.type = type;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public TableType getType()
{
return type;
}
public void setType(TableType type)
{
this.type = type;
}
public String getComment()
{
return comment;
}
public void setComment(String comment)
{
this.comment = comment;
}
@Override
public String toString()
{
return getClass().getSimpleName() + " [name=" + name + ", type=" + type + ", comment=" + comment + "]";
}
}

View File

@ -0,0 +1,190 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.meta;
import java.io.Serializable;
/**
*
*
* @author datagear@163.com
*
*/
public class Column implements Serializable
{
private static final long serialVersionUID = 1L;
/** 名称 */
private String name;
/** JDBC类型对应java.sql.Types中的值 */
private int type;
/** 数据源依赖的类型名称,参考"TYPE_NAME"说明 */
private String typeName;
/** 列大小,字符串长度或者数值总长度 */
private int size = 0;
/** 小数部分的位数如果不是小数值为0 */
private int decimalDigits = 0;
/** 是否允许为null */
private boolean nullable = false;
/** 描述 */
private String comment;
/** 默认值 */
private String defaultValue = null;
/** 是否自增长 */
private boolean autoincrement = false;
/** 可搜索类型 */
private SearchableType searchableType;
public Column()
{
super();
}
public Column(String name, int type)
{
super();
this.name = name;
this.type = type;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public int getType()
{
return type;
}
public void setType(int type)
{
this.type = type;
}
public boolean hasTypeName()
{
return (this.typeName != null && !this.typeName.isEmpty());
}
public String getTypeName()
{
return typeName;
}
public void setTypeName(String typeName)
{
this.typeName = typeName;
}
public int getSize()
{
return size;
}
public void setSize(int size)
{
this.size = size;
}
public int getDecimalDigits()
{
return decimalDigits;
}
public void setDecimalDigits(int decimalDigits)
{
this.decimalDigits = decimalDigits;
}
public boolean isNullable()
{
return nullable;
}
public void setNullable(boolean nullable)
{
this.nullable = nullable;
}
public boolean hasComment()
{
return (this.comment != null && !this.comment.isEmpty());
}
public String getComment()
{
return comment;
}
public void setComment(String comment)
{
this.comment = comment;
}
public boolean hasDefaultValue()
{
return (this.defaultValue != null && !this.defaultValue.isEmpty());
}
public String getDefaultValue()
{
return defaultValue;
}
public void setDefaultValue(String defaultValue)
{
this.defaultValue = defaultValue;
}
public boolean isAutoincrement()
{
return autoincrement;
}
public void setAutoincrement(boolean autoincrement)
{
this.autoincrement = autoincrement;
}
public boolean hasSearchableType()
{
return (this.searchableType != null);
}
public SearchableType getSearchableType()
{
return searchableType;
}
public void setSearchableType(SearchableType searchableType)
{
this.searchableType = searchableType;
}
@Override
public String toString()
{
return getClass().getSimpleName() + " [name=" + name + ", type=" + type + ", typeName=" + typeName + ", size="
+ size
+ ", decimalDigits=" + decimalDigits + ", nullable=" + nullable + ", comment=" + comment
+ ", defaultValue=" + defaultValue + ", autoincrement=" + autoincrement + ", searchableType="
+ searchableType + "]";
}
}

View File

@ -0,0 +1,64 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.meta;
import java.util.Arrays;
/**
* 导入外键
*
* @author datagear@163.com
*
*/
public class ImportKey extends AbstractKey
{
private static final long serialVersionUID = 1L;
/** 主表名 */
private String primaryTableName;
/** 主表键列名 */
private String[] primaryColumnNames;
public ImportKey()
{
super();
}
public ImportKey(String[] columnNames, String primaryTableName, String[] primaryColumnNames)
{
super(columnNames);
this.primaryTableName = primaryTableName;
this.primaryColumnNames = primaryColumnNames;
}
public String getPrimaryTableName()
{
return primaryTableName;
}
public void setPrimaryTableName(String primaryTableName)
{
this.primaryTableName = primaryTableName;
}
public String[] getPrimaryColumnNames()
{
return primaryColumnNames;
}
public void setPrimaryColumnNames(String[] primaryColumnNames)
{
this.primaryColumnNames = primaryColumnNames;
}
@Override
public String toString()
{
return getClass().getSimpleName() + " [columnNames=" + Arrays.toString(getColumnNames())
+ ", primaryTableName=" + primaryTableName + ", primaryColumnNames="
+ Arrays.toString(primaryColumnNames) + "]";
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.meta;
/**
* 主键
*
* @author datagear@163.com
*
*/
public class PrimaryKey extends AbstractKey
{
private static final long serialVersionUID = 1L;
public PrimaryKey()
{
super();
}
public PrimaryKey(String[] columnNames)
{
super(columnNames);
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.meta;
import java.sql.DatabaseMetaData;
/**
* 可搜索类型WHERE条件类型
* <p>
* 参考{@linkplain DatabaseMetaData#getTypeInfo()}结果集{@code SEARCHABLE}列说明
* </p>
*
* @author datagear@163.com
*
*/
public enum SearchableType
{
/** 不可用于WHERE */
NO,
/** 仅可用于WHERE中的LIKE */
ONLY_LIKE,
/** 仅不可用于WHERE中的LIKE */
EXPCEPT_LIKE,
/** 可用于WHERE中的任何情况 */
ALL
}

View File

@ -0,0 +1,26 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.meta;
/**
* 简单表元信息
*
* @author datagear@163.com
*
*/
public class SimpleTable extends AbstractTable
{
private static final long serialVersionUID = 1L;
public SimpleTable()
{
super();
}
public SimpleTable(String name, TableType type)
{
super(name, type);
}
}

View File

@ -0,0 +1,104 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.meta;
import java.util.Arrays;
/**
*
*
* @author datagear@163.com
*
*/
public class Table extends AbstractTable
{
private static final long serialVersionUID = 1L;
/** 列集 */
private Column[] columns;
/** 主键 */
private PrimaryKey primaryKey;
/** 唯一键 */
private UniqueKey[] uniqueKeys;
/** 导入外键 */
private ImportKey[] importKeys;
public Table()
{
super();
}
public Table(String name, TableType type, Column[] columns)
{
super(name, type);
this.columns = columns;
}
public Column[] getColumns()
{
return columns;
}
public void setColumns(Column[] columns)
{
this.columns = columns;
}
public boolean hasPrimaryKey()
{
return (this.primaryKey != null);
}
public PrimaryKey getPrimaryKey()
{
return primaryKey;
}
public void setPrimaryKey(PrimaryKey primaryKey)
{
this.primaryKey = primaryKey;
}
public boolean hasUniqueKey()
{
return (this.uniqueKeys != null && this.uniqueKeys.length > 0);
}
public UniqueKey[] getUniqueKeys()
{
return uniqueKeys;
}
public void setUniqueKeys(UniqueKey[] uniqueKeys)
{
this.uniqueKeys = uniqueKeys;
}
public boolean hasImportKey()
{
return (this.importKeys != null && this.importKeys.length > 0);
}
public ImportKey[] getImportKeys()
{
return importKeys;
}
public void setImportKeys(ImportKey[] importKeys)
{
this.importKeys = importKeys;
}
@Override
public String toString()
{
return getClass().getSimpleName() + " [name=" + getName() + ", type=" + getType() + ", comment=" + getComment()
+ ", columns=" + Arrays.toString(columns) + ", primaryKey=" + primaryKey + ", uniqueKeys="
+ Arrays.toString(uniqueKeys) + ", importKeys=" + Arrays.toString(importKeys) + "]";
}
}

View File

@ -0,0 +1,20 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.meta;
/**
* 表类型
*
* @author datagear@163.com
*
*/
public enum TableType
{
/** 实体表 */
TABLE,
/** 视图 */
VIEW
}

View File

@ -0,0 +1,26 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.meta;
/**
* 唯一键
*
* @author datagear@163.com
*
*/
public class UniqueKey extends AbstractKey
{
private static final long serialVersionUID = 1L;
public UniqueKey()
{
super();
}
public UniqueKey(String[] columnNames)
{
super(columnNames);
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.meta.resolver;
import java.sql.Connection;
import org.datagear.connection.ConnectionSensor;
/**
* 抽象数据库连接敏感的{@linkplain DevotedDBMetaResolver}
* <p>
* 此类可以作为特定数据库{@linkplain DevotedDBMetaResolver}实现类的父类
* </p>
*
* @author datagear@163.com
*
*/
public abstract class AbstractConnectionDevotedDBMetaResolver extends AbstractDevotedDBMetaResolver
{
private ConnectionSensor connectionSensor;
public AbstractConnectionDevotedDBMetaResolver()
{
super();
}
public AbstractConnectionDevotedDBMetaResolver(ConnectionSensor connectionSensor)
{
super();
this.connectionSensor = connectionSensor;
}
public ConnectionSensor getConnectionSensor()
{
return connectionSensor;
}
public void setConnectionSensor(ConnectionSensor connectionSensor)
{
this.connectionSensor = connectionSensor;
}
@Override
public boolean supports(Connection cn)
{
return this.connectionSensor.supports(cn);
}
}

View File

@ -0,0 +1,714 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.meta.resolver;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.SQLNonTransientException;
import java.util.ArrayList;
import java.util.List;
import org.datagear.meta.Column;
import org.datagear.meta.ImportKey;
import org.datagear.meta.PrimaryKey;
import org.datagear.meta.SimpleTable;
import org.datagear.meta.Table;
import org.datagear.meta.TableType;
import org.datagear.meta.UniqueKey;
import org.datagear.util.JDBCCompatiblity;
import org.datagear.util.JdbcUtil;
import org.datagear.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 抽象{@linkplain DevotedDBMetaResolver}
*
* @author datagear@163.com
*
*/
public abstract class AbstractDevotedDBMetaResolver implements DevotedDBMetaResolver
{
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractDevotedDBMetaResolver.class);
public static final String TABLE_TYPE_TABLE = "TABLE";
public static final String TABLE_TYPE_VIEW = "VIEW";
protected static final String[] TABLE_TYPES = { TABLE_TYPE_TABLE, TABLE_TYPE_VIEW };
protected static final String[] EMPTY_STRING_ARRAY = new String[0];
public AbstractDevotedDBMetaResolver()
{
super();
}
@Override
public List<SimpleTable> getSimpleTables(Connection cn) throws DBMetaResolverException
{
DatabaseMetaData metaData = getDatabaseMetaData(cn);
String schema = getSchema(cn, metaData);
return getSimpleTables(cn, metaData, schema, null);
}
@Override
public SimpleTable getRandomSimpleTable(Connection cn) throws DBMetaResolverException
{
DatabaseMetaData metaData = getDatabaseMetaData(cn);
String schema = getSchema(cn, metaData);
return getRandomSimpleTable(cn, metaData, schema);
}
@Override
public Table getTable(Connection cn, String tableName) throws DBMetaResolverException
{
DatabaseMetaData metaData = getDatabaseMetaData(cn);
String schema = getSchema(cn, metaData);
return getTable(cn, metaData, schema, tableName);
}
@Override
public Column getRandomColumn(Connection cn, String tableName) throws DBMetaResolverException
{
DatabaseMetaData metaData = getDatabaseMetaData(cn);
String schema = getSchema(cn, metaData);
Column[] columns = getColumns(cn, metaData, schema, tableName, 1);
return (columns == null || columns.length < 1 ? null : columns[0]);
}
@Override
public Column[] getColumns(Connection cn, ResultSetMetaData resultSetMetaData) throws DBMetaResolverException
{
try
{
int columnCount = resultSetMetaData.getColumnCount();
Column[] columnInfos = new Column[columnCount];
for (int i = 1; i <= columnCount; i++)
{
Column column = new Column();
String columnName = resultSetMetaData.getColumnLabel(i);
if (columnName == null || columnName.isEmpty())
columnName = resultSetMetaData.getColumnName(i);
column.setName(columnName);
column.setType(resultSetMetaData.getColumnType(i));
column.setTypeName(resultSetMetaData.getColumnTypeName(i));
column.setSize(resultSetMetaData.getPrecision(i));
column.setDecimalDigits(resultSetMetaData.getScale(i));
column.setNullable(DatabaseMetaData.columnNoNulls == resultSetMetaData.isNullable(i));
column.setAutoincrement(resultSetMetaData.isAutoIncrement(i));
columnInfos[i - 1] = column;
}
return columnInfos;
}
catch(SQLException e)
{
throw new DBMetaResolverException(e);
}
}
protected Table getTable(Connection cn, DatabaseMetaData metaData, String schema, String tableName)
throws DBMetaResolverException
{
List<SimpleTable> simpleTables = getSimpleTables(cn, metaData, schema, tableName);
if (simpleTables == null || simpleTables.isEmpty())
throw new TableNotFoundException(tableName);
SimpleTable simpleTable = simpleTables.get(0);
Table table = new Table();
table.setName(simpleTable.getName());
table.setType(simpleTable.getType());
table.setComment(simpleTable.getComment());
table.setColumns(getColumns(cn, metaData, schema, tableName, null));
table.setPrimaryKey(getPrimaryKey(cn, metaData, schema, tableName));
table.setUniqueKeys(getUniqueKeys(cn, metaData, schema, tableName));
return table;
}
/**
*
* @param cn
* @param metaData
* @param schema
* @param tableName
* @return 返回{@code null}表示无主键
* @throws DBMetaResolverException
*/
protected PrimaryKey getPrimaryKey(Connection cn, DatabaseMetaData metaData, String schema, String tableName)
throws DBMetaResolverException
{
PrimaryKey primaryKey = null;
ResultSet rs = null;
try
{
rs = getPrimaryKeyResulSet(cn, metaData, schema, tableName);
List<String> columnNames = new ArrayList<String>();
String keyName = null;
while (rs.next())
{
String columnName = rs.getString("COLUMN_NAME");
if (StringUtil.isEmpty(keyName))
keyName = rs.getString("PK_NAME");
addName(columnNames, columnName);
}
if (!columnNames.isEmpty())
{
primaryKey = new PrimaryKey(columnNames.toArray(new String[columnNames.size()]));
primaryKey.setKeyName(keyName);
}
return primaryKey;
}
catch(SQLNonTransientException e)
{
@JDBCCompatiblity("当tableName是视图时某些驱动比如Oracle可能会抛出SQLSyntaxErrorException异常")
SQLNonTransientException e1 = e;
LOGGER.warn("return null primary key object for exception", e1);
return null;
}
catch(SQLException e)
{
throw new DBMetaResolverException(e);
}
finally
{
JdbcUtil.closeResultSet(rs);
}
}
/**
*
* @param cn
* @param metaData
* @param schema
* @param tableName
* @param count {@code null}获取全部否则获取指定数目
* @return
* @throws DBMetaResolverException
*/
protected Column[] getColumns(Connection cn, DatabaseMetaData metaData, String schema, String tableName,
Integer count)
throws DBMetaResolverException
{
ResultSet rs = null;
try
{
rs = getColumnResulSet(cn, metaData, schema, tableName);
List<Column> columns = new ArrayList<Column>();
while (rs.next())
{
Column column = readColumn(rs);
addColumn(columns, column);
if (count != null && columns.size() == count)
break;
}
columns = postProcessColumns(columns);
return columns.toArray(new Column[columns.size()]);
}
catch(SQLException e)
{
throw new DBMetaResolverException(e);
}
finally
{
JdbcUtil.closeResultSet(rs);
}
}
/**
* @param cn
* @param metaData
* @param schema
* @param tableName
* @return 返回{@code null}表示无唯一键
* @throws DBMetaResolverException
*/
protected UniqueKey[] getUniqueKeys(Connection cn, DatabaseMetaData metaData, String schema,
String tableName) throws DBMetaResolverException
{
UniqueKey[] uniqueKeys = null;
ResultSet rs = null;
List<String> keyNames = new ArrayList<String>();
List<List<String>> keyColumnNamess = new ArrayList<List<String>>();
try
{
rs = getUniqueKeyResulSet(cn, metaData, schema, tableName);
while (rs.next())
{
String keyName = rs.getString("INDEX_NAME");
if (keyName == null)
keyName = "";
String columnName = rs.getString("COLUMN_NAME");
int myIndex = keyNames.indexOf(keyName);
List<String> keyColumnNames = null;
if (myIndex < 0)
{
keyNames.add(keyName);
keyColumnNames = new ArrayList<String>();
keyColumnNamess.add(keyColumnNames);
}
else
keyColumnNames = keyColumnNamess.get(myIndex);
addName(keyColumnNames, columnName);
}
}
catch(SQLNonTransientException e)
{
@JDBCCompatiblity("当tableName是视图时某些驱动比如Oracle可能会抛出SQLSyntaxErrorException异常")
SQLNonTransientException e1 = e;
LOGGER.warn("return null primary key object for exception", e1);
return null;
}
catch(SQLException e)
{
throw new DBMetaResolverException(e);
}
finally
{
JdbcUtil.closeResultSet(rs);
}
if (!keyNames.isEmpty())
{
uniqueKeys = new UniqueKey[keyNames.size()];
for (int i = 0; i < uniqueKeys.length; i++)
{
List<String> keyColumnNames = keyColumnNamess.get(i);
uniqueKeys[i] = new UniqueKey(keyColumnNames.toArray(new String[keyColumnNames.size()]));
uniqueKeys[i].setKeyName(keyNames.get(i));
}
}
return uniqueKeys;
}
protected ImportKey[] getImportedKeys(Connection cn, DatabaseMetaData metaData, String schema,
String tableName) throws DBMetaResolverException
{
ImportKey[] importKeys = null;
ResultSet rs = null;
List<String> keyNames = new ArrayList<String>();
List<List<String>> columnNamess = new ArrayList<List<String>>();
List<String> primaryTableNames = new ArrayList<String>();
List<List<String>> primaryColumnNamess = new ArrayList<List<String>>();
try
{
rs = getImportKeyResulSet(cn, metaData, schema, tableName);
while (rs.next())
{
String keyName = rs.getString("FK_NAME");
if (keyName == null)
keyName = "";
String columnName = rs.getString("FKCOLUMN_NAME");
String primaryColumnName = rs.getString("PKCOLUMN_NAME");
int myIndex = keyNames.indexOf(keyName);
List<String> columnNames = null;
List<String> primaryColumnNames = null;
if (myIndex < 0)
{
keyNames.add(keyName);
primaryTableNames.add(rs.getString("PKTABLE_NAME"));
columnNames = new ArrayList<String>();
columnNamess.add(columnNames);
primaryColumnNames = new ArrayList<String>();
primaryColumnNamess.add(primaryColumnNames);
}
else
{
columnNames = columnNamess.get(myIndex);
primaryColumnNames = primaryColumnNamess.get(myIndex);
}
addName(columnNames, columnName);
addName(primaryColumnNames, primaryColumnName);
}
}
catch(SQLNonTransientException e)
{
@JDBCCompatiblity("当tableName是视图时某些驱动比如Oracle可能会抛出SQLSyntaxErrorException异常")
SQLNonTransientException e1 = e;
LOGGER.warn("return null import key object for exception", e1);
return null;
}
catch(SQLException e)
{
throw new DBMetaResolverException(e);
}
finally
{
JdbcUtil.closeResultSet(rs);
}
if (!keyNames.isEmpty())
{
importKeys = new ImportKey[keyNames.size()];
for (int i = 0; i < importKeys.length; i++)
{
ImportKey importKey = new ImportKey();
List<String> columnNames = columnNamess.get(i);
List<String> primaryColumnNames = primaryColumnNamess.get(i);
importKey.setColumnNames(columnNames.toArray(new String[columnNames.size()]));
importKey.setPrimaryTableName(primaryTableNames.get(i));
importKey.setPrimaryColumnNames(primaryColumnNames.toArray(new String[primaryColumnNames.size()]));
importKey.setKeyName(keyNames.get(i));
importKeys[i] = importKey;
}
}
return importKeys;
}
@JDBCCompatiblity("避免某些驱动程序的结果集出现非法或者重复项")
protected boolean addName(List<String> names, String name)
{
if (StringUtil.isEmpty(name) || names.indexOf(name) > -1)
return false;
names.add(name);
return true;
}
@JDBCCompatiblity("避免某些驱动程序的结果集出现非法或者重复项")
protected boolean addColumn(List<Column> columns, Column column)
{
if (column == null || containsColumn(columns, column.getName()))
return false;
columns.add(column);
return true;
}
protected boolean containsColumn(List<Column> columns, String name)
{
for (Column column : columns)
{
if (column.getName().equals(name))
return true;
}
return false;
}
/**
*
* @param rs
* @return 返回{@code null}表示未读取到
*/
protected Column readColumn(ResultSet rs)
{
try
{
String name = rs.getString("COLUMN_NAME");
if (StringUtil.isEmpty(name))
{
LOGGER.warn("Invalid column row : name={}", name);
return null;
}
Column column = new Column(name, rs.getInt("DATA_TYPE"));
column.setTypeName(rs.getString("TYPE_NAME"));
column.setSize(rs.getInt("COLUMN_SIZE"));
column.setDecimalDigits(rs.getInt("DECIMAL_DIGITS"));
column.setNullable(DatabaseMetaData.columnNoNulls == rs.getInt("NULLABLE"));
column.setComment(rs.getString("REMARKS"));
column.setDefaultValue(rs.getString("COLUMN_DEF"));
column.setAutoincrement("yes".equalsIgnoreCase(rs.getString("IS_AUTOINCREMENT")));
return column;
}
catch(SQLException e)
{
return null;
}
}
private List<Column> postProcessColumns(List<Column> columns)
{
return columns;
}
protected ResultSet getPrimaryKeyResulSet(Connection cn, DatabaseMetaData databaseMetaData, String schema,
String tableName) throws SQLException
{
return databaseMetaData.getPrimaryKeys(cn.getCatalog(), schema, tableName);
}
protected ResultSet getColumnResulSet(Connection cn, DatabaseMetaData databaseMetaData, String schema,
String tableName) throws SQLException
{
return databaseMetaData.getColumns(cn.getCatalog(), schema, tableName, "%");
}
protected ResultSet getUniqueKeyResulSet(Connection cn, DatabaseMetaData databaseMetaData, String schema,
String tableName) throws SQLException
{
return databaseMetaData.getIndexInfo(cn.getCatalog(), getSchema(cn, databaseMetaData), tableName, true, false);
}
protected ResultSet getImportKeyResulSet(Connection cn, DatabaseMetaData databaseMetaData, String schema,
String tableName) throws SQLException
{
return databaseMetaData.getImportedKeys(cn.getCatalog(), schema, tableName);
}
/**
* @param cn
* @param metaData
* @param schema
* @param tableNamePattern 允许为{@code null}
* @return
* @throws DBMetaResolverException
*/
protected List<SimpleTable> getSimpleTables(Connection cn, DatabaseMetaData metaData, String schema,
String tableNamePattern)
throws DBMetaResolverException
{
ResultSet rs = null;
try
{
rs = getTableResulSet(cn, metaData, schema, tableNamePattern, TABLE_TYPES);
List<SimpleTable> simpleTables = new ArrayList<SimpleTable>();
while (rs.next())
{
SimpleTable simpleTable = readSimpleTable(rs);
if (simpleTable != null)
simpleTables.add(simpleTable);
}
simpleTables = postProcessSimpleTables(simpleTables);
return simpleTables;
}
catch(SQLException e)
{
throw new DBMetaResolverException(e);
}
finally
{
JdbcUtil.closeResultSet(rs);
}
}
protected SimpleTable getRandomSimpleTable(Connection cn, DatabaseMetaData metaData, String schema)
throws DBMetaResolverException
{
SimpleTable simpleTable = null;
ResultSet rs = null;
try
{
rs = getTableResulSet(cn, metaData, schema, null, TABLE_TYPES);
while (rs.next())
{
simpleTable = readSimpleTable(rs);
if (simpleTable != null)
return simpleTable;
}
return null;
}
catch(SQLException e)
{
throw new DBMetaResolverException(e);
}
finally
{
JdbcUtil.closeResultSet(rs);
}
}
/**
* @param rs
* @return 返回{@code null}表示未读取到
*/
protected SimpleTable readSimpleTable(ResultSet rs)
{
try
{
String name = rs.getString("TABLE_NAME");
TableType type = toTableType(rs.getString("TABLE_TYPE"));
if (StringUtil.isEmpty(name) || StringUtil.isEmpty(type))
{
LOGGER.warn("Invalid table row : name={}, type={}", name, type);
return null;
}
SimpleTable simpleTable = new SimpleTable(name, type);
simpleTable.setComment(rs.getString("REMARKS"));
return simpleTable;
}
catch(SQLException e)
{
return null;
}
}
protected TableType toTableType(String type)
{
if (TABLE_TYPE_TABLE.equalsIgnoreCase(type))
return TableType.TABLE;
else if (TABLE_TYPE_VIEW.equalsIgnoreCase(type))
return TableType.VIEW;
else
return null;
}
protected List<SimpleTable> postProcessSimpleTables(List<SimpleTable> simpleTables)
{
return simpleTables;
}
protected ResultSet getTableResulSet(Connection cn, DatabaseMetaData databaseMetaData, String schema,
String tableNamePattern, String[] tableTypes) throws SQLException
{
if (tableNamePattern == null || tableNamePattern.isEmpty())
tableNamePattern = "%";
return databaseMetaData.getTables(cn.getCatalog(), schema, tableNamePattern, tableTypes);
}
protected DatabaseMetaData getDatabaseMetaData(Connection cn) throws DBMetaResolverException
{
try
{
return cn.getMetaData();
}
catch (SQLException e)
{
throw new DBMetaResolverException(e);
}
}
protected String getSchema(Connection cn, DatabaseMetaData databaseMetaData) throws DBMetaResolverException
{
// 在JDBC4.0JDK1.6中需要将其设置为null才符合DatabaseMetaData.getTables(...)等接口的参数要求
String schema = null;
// JDBC4.1JDK1.7才有Connection.getSchema()接口为了兼容性本应用采用了JDBC4.0JDK1.6
// 这里为了能支持JDBC4.1的的驱动先采用反射方式获取
if (GET_SCHEMA_METHOD_JDBC41 != null)
{
try
{
schema = (String) GET_SCHEMA_METHOD_JDBC41.invoke(cn, new Object[0]);
}
catch (Exception e)
{
}
}
// 测试使用连接用户名是否可行
if (schema == null)
{
ResultSet rs = null;
try
{
String dbUserName = databaseMetaData.getUserName();
rs = getTableResulSet(cn, databaseMetaData, dbUserName, null, TABLE_TYPES);
if (rs.next())
schema = dbUserName;
}
catch (SQLException e)
{
}
finally
{
JdbcUtil.closeResultSet(rs);
}
}
return schema;
}
protected static Method GET_SCHEMA_METHOD_JDBC41 = null;
static
{
Method[] methods = Connection.class.getMethods();
for (Method method : methods)
{
if ("getSchema".equals(method.getName()))
{
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes == null || parameterTypes.length == 0)
{
GET_SCHEMA_METHOD_JDBC41 = method;
break;
}
}
}
}
}

View File

@ -0,0 +1,70 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.meta.resolver;
import java.sql.Connection;
import java.sql.ResultSetMetaData;
import java.util.List;
import org.datagear.meta.Column;
import org.datagear.meta.SimpleTable;
import org.datagear.meta.Table;
/**
* 数据库元信息解析类
*
* @author datagear@163.com
*
*/
public interface DBMetaResolver
{
/**
* 获取所有{@linkplain SimpleTable}
*
* @param cn
* @return
* @throws DBMetaResolverException
*/
List<SimpleTable> getSimpleTables(Connection cn) throws DBMetaResolverException;
/**
* 随机获取一个{@linkplain SimpleTable}
*
* @param cn
* @return 可能返回{@code null}
* @throws DBMetaResolverException
*/
SimpleTable getRandomSimpleTable(Connection cn) throws DBMetaResolverException;
/**
* 获取指定名称的{@linkplain Table}
*
* @param cn
* @param tableName
* @return
* @throws DBMetaResolverException
*/
Table getTable(Connection cn, String tableName) throws DBMetaResolverException;
/**
* 获取指定表的一个随机{@linkplain Column}
*
* @param cn
* @param tableName
* @return 返回{@code null}表示没找到任何字段
* @throws DBMetaResolverException
*/
Column getRandomColumn(Connection cn, String tableName) throws DBMetaResolverException;
/**
* 获取指定{@linkplain ResultSetMetaData}{@linkplain Column}
*
* @param cn
* @param resultSetMetaData
* @return
* @throws DBMetaResolverException
*/
Column[] getColumns(Connection cn, ResultSetMetaData resultSetMetaData) throws DBMetaResolverException;
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.meta.resolver;
/**
* 数据库元信息解析异常
*
* @author datagear@163.com
*
*/
public class DBMetaResolverException extends RuntimeException
{
private static final long serialVersionUID = 1L;
public DBMetaResolverException()
{
super();
}
public DBMetaResolverException(String message)
{
super(message);
}
public DBMetaResolverException(Throwable cause)
{
super(cause);
}
public DBMetaResolverException(String message, Throwable cause)
{
super(message, cause);
}
}

View File

@ -0,0 +1,20 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.meta.resolver;
import org.datagear.connection.ConnectionSensor;
/**
* 专职{@linkplain DBMetaResolver}
* <p>
* 此类继承自{@linkplain DBMetaResolver}的所有方法仅在{@linkplain #supports(java.sql.Connection)}返回{@code true}时可用
* </p>
*
* @author datagear@163.com
*
*/
public interface DevotedDBMetaResolver extends DBMetaResolver, ConnectionSensor
{
}

View File

@ -0,0 +1,126 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.meta.resolver;
import java.sql.Connection;
import java.sql.ResultSetMetaData;
import java.util.List;
import org.datagear.connection.ConnectionOption;
import org.datagear.meta.Column;
import org.datagear.meta.SimpleTable;
import org.datagear.meta.Table;
/**
* 通用{@linkplain DBMetaResolver}
* <p>
* 它从其包含的{@linkplain DevotedDBMetaResolver}
* {@linkplain #getDevotedDBMetaResolvers()}越靠前越优先使用查找能够处理给定{@link Connection}
* 的那一个并使用其API
* </p>
* <p>
* 如果没有查找到能处理给定{@link Connection}{@linkplain DevotedDBMetaResolver}此类将抛出
* {@linkplain UnsupportedDBMetaResolverException}异常
* </p>
*
* @author datagear@163.com
*
*/
public class GenericDBMetaResolver implements DBMetaResolver
{
private List<DevotedDBMetaResolver> devotedDBMetaResolvers = null;
public GenericDBMetaResolver()
{
super();
}
public GenericDBMetaResolver(List<DevotedDBMetaResolver> devotedDBMetaResolvers)
{
super();
this.devotedDBMetaResolvers = devotedDBMetaResolvers;
}
public List<DevotedDBMetaResolver> getDevotedDBMetaResolvers()
{
return devotedDBMetaResolvers;
}
public void setDevotedDBMetaResolvers(List<DevotedDBMetaResolver> devotedDBMetaResolvers)
{
this.devotedDBMetaResolvers = devotedDBMetaResolvers;
}
@Override
public List<SimpleTable> getSimpleTables(Connection cn) throws DBMetaResolverException
{
DevotedDBMetaResolver resolver = doGetDevotedDBMetaResolverNotNull(cn);
return resolver.getSimpleTables(cn);
}
@Override
public SimpleTable getRandomSimpleTable(Connection cn) throws DBMetaResolverException
{
DevotedDBMetaResolver resolver = doGetDevotedDBMetaResolverNotNull(cn);
return resolver.getRandomSimpleTable(cn);
}
@Override
public Table getTable(Connection cn, String tableName) throws DBMetaResolverException
{
DevotedDBMetaResolver resolver = doGetDevotedDBMetaResolverNotNull(cn);
return resolver.getTable(cn, tableName);
}
@Override
public Column getRandomColumn(Connection cn, String tableName) throws DBMetaResolverException
{
DevotedDBMetaResolver resolver = doGetDevotedDBMetaResolverNotNull(cn);
return resolver.getRandomColumn(cn, tableName);
}
@Override
public Column[] getColumns(Connection cn, ResultSetMetaData resultSetMetaData) throws DBMetaResolverException
{
DevotedDBMetaResolver resolver = doGetDevotedDBMetaResolverNotNull(cn);
return resolver.getColumns(cn, resultSetMetaData);
}
/**
* 获取支持指定{@linkplain Connection}{@linkplain DevotedDBMetaResolver}
*
* @param cn
* @return
* @throws UnsupportedDBMetaResolverException
*/
protected DevotedDBMetaResolver doGetDevotedDBMetaResolverNotNull(Connection cn)
throws UnsupportedDBMetaResolverException
{
DevotedDBMetaResolver resolver = doGetDevotedDBMetaResolver(cn);
if (resolver == null)
throw new UnsupportedDBMetaResolverException(ConnectionOption.valueOf(cn));
return resolver;
}
/**
* @param cn
* @return 返回{@code null}表示没有
*/
protected DevotedDBMetaResolver doGetDevotedDBMetaResolver(Connection cn)
{
if (this.devotedDBMetaResolvers == null)
return null;
for (DevotedDBMetaResolver resolver : this.devotedDBMetaResolvers)
{
if (resolver.supports(cn))
return resolver;
}
return null;
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.meta.resolver;
/**
* 表未找到异常
*
* @author datagear@163.com
*
*/
public class TableNotFoundException extends DBMetaResolverException
{
private static final long serialVersionUID = 1L;
private String tableName;
public TableNotFoundException()
{
super();
}
public TableNotFoundException(String tableName)
{
super("Table [" + tableName + "] not found");
this.tableName = tableName;
}
public TableNotFoundException(String tableName, Throwable cause)
{
this(tableName);
this.tableName = tableName;
}
public String getTableName()
{
return tableName;
}
protected void setTableName(String tableName)
{
this.tableName = tableName;
}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.meta.resolver;
import org.datagear.connection.ConnectionOption;
/**
* 不支持数据库信息解析异常
* <p>
* {@linkplain GenericDBMetaResolver}在未找到支持{@linkplain DevotedDBMetaResolver}时抛出此异常
* </p>
*
* @author datagear@163.com
*
*/
public class UnsupportedDBMetaResolverException extends DBMetaResolverException
{
private static final long serialVersionUID = 1L;
private ConnectionOption connectionOption;
public UnsupportedDBMetaResolverException(ConnectionOption connectionOption)
{
super("Resolving database info for [" + connectionOption + "] is not supported");
this.connectionOption = connectionOption;
}
public ConnectionOption getConnectionOption()
{
return connectionOption;
}
protected void setConnectionOption(ConnectionOption connectionOption)
{
this.connectionOption = connectionOption;
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.meta.resolver;
import java.sql.Connection;
/**
* 通配{@linkplain DevotedDBMetaResolver}
*
* @author datagear@163.com
*
*/
public class WildcardDevotedDBMetaResolver extends AbstractDevotedDBMetaResolver
{
public WildcardDevotedDBMetaResolver()
{
super();
}
@Override
public boolean supports(Connection cn)
{
return true;
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.meta.resolver.support;
import java.util.List;
import org.datagear.connection.ConnectionSensor;
import org.datagear.connection.URLConnectionSensor;
import org.datagear.connection.support.MySqlURLSensor;
import org.datagear.meta.SimpleTable;
import org.datagear.meta.resolver.AbstractConnectionDevotedDBMetaResolver;
import org.datagear.meta.resolver.DevotedDBMetaResolver;
import org.datagear.util.StringUtil;
/**
* MySQL {@linkplain DevotedDBMetaResolver}
*
* @author datagear@163.com
*
*/
public class MySqlDevotedDBMetaResolver extends AbstractConnectionDevotedDBMetaResolver
{
public MySqlDevotedDBMetaResolver()
{
super(new URLConnectionSensor(new MySqlURLSensor()));
}
@Override
public void setConnectionSensor(ConnectionSensor connectionSensor)
{
throw new UnsupportedOperationException();
}
@Override
protected List<SimpleTable> postProcessSimpleTables(List<SimpleTable> simpleTables)
{
if (simpleTables != null)
{
for (SimpleTable st : simpleTables)
resolveTableComment(st);
}
return simpleTables;
}
protected void resolveTableComment(SimpleTable st)
{
String comment = st.getComment();
if (StringUtil.isEmpty(comment))
return;
int colonIdx = comment.indexOf(';');
if (colonIdx > -1)
{
comment = comment.substring(0, colonIdx);
st.setComment(comment);
}
else
st.setComment("");
}
}

View File

@ -0,0 +1,95 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.meta;
import static org.hamcrest.beans.HasPropertyWithValue.hasProperty;
import static org.hamcrest.collection.ArrayMatching.hasItemInArray;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.hamcrest.core.IsIterableContaining.hasItem;
import static org.junit.Assert.assertThat;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;
import org.datagear.meta.resolver.DevotedDBMetaResolver;
import org.datagear.meta.resolver.GenericDBMetaResolver;
import org.datagear.meta.resolver.WildcardDevotedDBMetaResolver;
import org.datagear.meta.resolver.support.MySqlDevotedDBMetaResolver;
import org.datagear.util.JdbcUtil;
import org.datagear.util.test.DBTestSupport;
import org.junit.Test;
/**
* {@linkplain GenericDBMetaResolver}单元测试类
*
* @author datagear@163.com
*
*/
public class GenericDBMetaResolverTest extends DBTestSupport
{
private GenericDBMetaResolver genericDBMetaResolver;
public GenericDBMetaResolverTest()
{
super();
List<DevotedDBMetaResolver> devotedDBMetaResolvers = new ArrayList<DevotedDBMetaResolver>();
devotedDBMetaResolvers.add(new MySqlDevotedDBMetaResolver());
devotedDBMetaResolvers.add(new WildcardDevotedDBMetaResolver());
this.genericDBMetaResolver = new GenericDBMetaResolver(devotedDBMetaResolvers);
}
@Test
public void getSimpleTablesTest() throws Exception
{
Connection cn = null;
List<SimpleTable> simpleTables = null;
try
{
cn = getConnection();
simpleTables = this.genericDBMetaResolver.getSimpleTables(cn);
}
finally
{
JdbcUtil.closeConnection(cn);
}
assertThat(simpleTables, hasItem(hasProperty("name", equalTo("T_ACCOUNT"))));
assertThat(simpleTables, hasItem(hasProperty("name", equalTo("T_ADDRESS"))));
}
@Test
public void getTableTest() throws Exception
{
Connection cn = null;
Table table0 = null;
Table table1 = null;
try
{
cn = getConnection();
table0 = this.genericDBMetaResolver.getTable(cn, "T_ACCOUNT");
table1 = this.genericDBMetaResolver.getTable(cn, "T_ADDRESS");
}
finally
{
JdbcUtil.closeConnection(cn);
}
assertThat(table0, hasProperty("name", equalTo("T_ACCOUNT")));
assertThat(table0, hasProperty("columns", hasItemInArray(hasProperty("name", equalTo("ID")))));
assertThat(table0, hasProperty("primaryKey", hasProperty("columnNames", hasItemInArray(equalTo("ID")))));
assertThat(table1, hasProperty("name", equalTo("T_ADDRESS")));
assertThat(table1, hasProperty("columns", hasItemInArray(hasProperty("name", equalTo("ACCOUNT_ID")))));
}
}

View File

@ -0,0 +1,69 @@
package org.datagear.util.test;
import java.io.File;
import java.io.FileReader;
import java.io.Reader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Properties;
/**
* 数据库测试支持类
*
* @author datagear@163.com
*
*/
public abstract class DBTestSupport
{
private static final Properties JDBC_PROPERTIES = new Properties();
static
{
File jdbcConfigFile = new File("test/config/jdbc.properties");
if (!jdbcConfigFile.exists())
jdbcConfigFile = new File("../test/config/jdbc.properties");
try
{
Reader reader = new FileReader(jdbcConfigFile);
JDBC_PROPERTIES.load(reader);
reader.close();
}
catch (Exception e)
{
if (e instanceof RuntimeException)
throw (RuntimeException) e;
else
throw new RuntimeException(e);
}
}
protected Connection getConnection() throws Exception
{
return DriverManager.getConnection(JDBC_PROPERTIES.getProperty("jdbc.url"),
JDBC_PROPERTIES.getProperty("jdbc.user"), JDBC_PROPERTIES.getProperty("jdbc.password"));
}
protected Connection getConnection(Properties properties) throws Exception
{
properties.setProperty("user", JDBC_PROPERTIES.getProperty("jdbc.user"));
properties.setProperty("password", JDBC_PROPERTIES.getProperty("jdbc.password"));
return DriverManager.getConnection(JDBC_PROPERTIES.getProperty("jdbc.url"), properties);
}
protected void println()
{
System.out.println();
}
protected void println(Object o)
{
System.out.println((o == null ? "null" : o.toString()));
}
protected void print(Object o)
{
System.out.print((o == null ? "null" : o.toString()));
}
}

10
pom.xml
View File

@ -15,6 +15,7 @@
<maven.compile.source>1.8</maven.compile.source>
<maven.compile.target>1.8</maven.compile.target>
<junit.version>4.11</junit.version>
<hamcrest.version>2.2</hamcrest.version>
<spring.version>4.3.26.RELEASE</spring.version>
<spring.security.version>3.2.10.RELEASE</spring.security.version>
<DBCP.version>1.4</DBCP.version><!-- 更高版本的不兼容JDBC 4.0 -->
@ -41,7 +42,8 @@
<module>datagear-web</module>
<module>datagear-webapp</module>
<module>datagear-webappembd</module>
</modules>
<module>datagear-meta</module>
</modules>
<dependencies>
<dependency>
@ -49,6 +51,12 @@
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<version>${hamcrest.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>