package org.springframework.roo.addon.dbre.model;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import org.springframework.roo.addon.dbre.model.dialect.Dialect;
import org.springframework.roo.model.JavaPackage;
import org.springframework.roo.support.util.Assert;
import org.springframework.roo.support.util.StringUtils;
/**
* Creates a {@link Database database} model from a live database using JDBC.
*
* @author Alan Stewart
* @since 1.1
*/
public class DatabaseIntrospector {
private Connection connection;
private DatabaseMetaData databaseMetaData;
private String catalogName;
private Schema schema;
private JavaPackage destinationPackage;
private boolean view;
private Set<String> includeTables = null;
private Set<String> excludeTables = null;
private String tableName;
private String columnName;
public DatabaseIntrospector(Connection connection, Schema schema, JavaPackage destinationPackage, boolean view, Set<String> includeTables, Set<String> excludeTables) throws SQLException {
this(connection);
catalogName = this.connection.getCatalog();
databaseMetaData = this.connection.getMetaData();
Assert.notNull(databaseMetaData, "Database metadata is null");
this.schema = schema;
this.destinationPackage = destinationPackage;
this.view = view;
this.includeTables = includeTables;
this.excludeTables = excludeTables;
}
public DatabaseIntrospector(Connection connection) throws SQLException {
Assert.notNull(connection, "Connection must not be null");
this.connection = connection;
}
public Connection getConnection() {
return connection;
}
public String getCatalogName() {
return catalogName;
}
public void setCatalogName(String catalogName) {
this.catalogName = catalogName;
}
public Schema getSchema() {
return schema;
}
public String getSchemaName() {
return schema != null ? schema.getName() : null;
}
public void setSchema(Schema schema) {
this.schema = schema;
}
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public String getColumnName() {
return columnName;
}
public void setColumnName(String columnName) {
this.columnName = columnName;
}
public Set<Schema> getSchemas() throws SQLException {
Set<Schema> schemas = new LinkedHashSet<Schema>();
ResultSet rs = databaseMetaData.getSchemas();
try {
while (rs.next()) {
schemas.add(new Schema(rs.getString("TABLE_SCHEM")));
}
} finally {
rs.close();
}
return schemas;
}
public Database createDatabase() throws SQLException {
String name = StringUtils.hasText(schema.getName()) ? schema.getName() : catalogName;
return new Database(name, getTables(), destinationPackage);
}
private Set<Table> getTables() throws SQLException {
Set<Table> tables = new LinkedHashSet<Table>();
String[] types = view ? new String[] { TableType.TABLE.name(), TableType.VIEW.name() } : new String[] { TableType.TABLE.name() };
ResultSet rs = databaseMetaData.getTables(getCatalog(), getSchemaPattern(), getTableNamePattern(), types);
try {
while (rs.next()) {
tableName = rs.getString("TABLE_NAME");
catalogName = rs.getString("TABLE_CAT");
schema = new Schema(rs.getString("TABLE_SCHEM"));
// Check for certain tables such as Oracle recycle bin tables, and ignore
if (ignoreTables()) {
continue;
}
if (hasIncludedTable(tableName) && !hasExcludedTable(tableName)) {
Table table = new Table();
table.setName(tableName);
table.setCatalog(catalogName);
table.setSchema(schema);
table.setDescription(rs.getString("REMARKS"));
readColumns(table);
readForeignKeys(table, false);
readForeignKeys(table, true);
readIndices(table);
for (String columnName : readPrimaryKeyNames()) {
Column column = table.findColumn(columnName);
if (column != null) {
column.setPrimaryKey(true);
}
}
tables.add(table);
}
}
} finally {
rs.close();
}
return tables;
}
//这里就是过滤收回站的表
private boolean ignoreTables() {
boolean ignore = false;
try {
if ("Oracle".equalsIgnoreCase(databaseMetaData.getDatabaseProductName()) && tableName.startsWith("BIN$")) {
ignore = true;
}
} catch (SQLException ignored) {}
return ignore;
}
private void readColumns(Table table) throws SQLException {
ResultSet rs = databaseMetaData.getColumns(catalogName, getSchemaName(), tableName, getColumnNamePattern());
try {
while (rs.next()) {
Column column = new Column(rs.getString("COLUMN_NAME"), rs.getInt("DATA_TYPE"), rs.getString("TYPE_NAME"), rs.getInt("COLUMN_SIZE"), rs.getInt("DECIMAL_DIGITS"));
column.setDescription(rs.getString("REMARKS"));
column.setDefaultValue(rs.getString("COLUMN_DEF"));
column.setRequired("NO".equalsIgnoreCase(rs.getString("IS_NULLABLE")));
table.addColumn(column);
}
} finally {
rs.close();
}
}
private void readForeignKeys(Table table, boolean exported) throws SQLException {
Map<String, ForeignKey> foreignKeys = new LinkedHashMap<String, ForeignKey>();
ResultSet rs;
if (exported) {
rs = databaseMetaData.getExportedKeys(catalogName, getSchemaName(), tableName);
} else {
rs = databaseMetaData.getImportedKeys(catalogName, getSchemaName(), tableName);
}
try {
while (rs.next()) {
String name = rs.getString("FK_NAME");
String foreignTableName = rs.getString(exported ? "FKTABLE_NAME" : "PKTABLE_NAME");
String key = name + "_" + foreignTableName;
if (!hasExcludedTable(foreignTableName)) {
ForeignKey foreignKey = new ForeignKey(name, foreignTableName);
foreignKey.setOnUpdate(getCascadeAction(rs.getShort("UPDATE_RULE")));
foreignKey.setOnDelete(getCascadeAction(rs.getShort("DELETE_RULE")));
foreignKey.setExported(exported);
String localColumnName = rs.getString(exported ? "PKCOLUMN_NAME" : "FKCOLUMN_NAME");
String foreignColumnName = rs.getString(exported ? "FKCOLUMN_NAME" : "PKCOLUMN_NAME");
Reference reference = new Reference(localColumnName, foreignColumnName);
if (foreignKeys.containsKey(key)) {
foreignKeys.get(key).addReference(reference);
} else {
foreignKey.addReference(reference);
foreignKeys.put(key, foreignKey);
}
}
}
} finally {
rs.close();
}
for (ForeignKey foreignKey : foreignKeys.values()) {
if (exported) {
table.addExportedKey(foreignKey);
} else {
table.addImportedKey(foreignKey);
}
}
}
private CascadeAction getCascadeAction(Short actionValue) {
CascadeAction cascadeAction;
switch (actionValue.intValue()) {
case DatabaseMetaData.importedKeyCascade:
cascadeAction = CascadeAction.CASCADE;
break;
case DatabaseMetaData.importedKeySetNull:
cascadeAction = CascadeAction.SET_NULL;
break;
case DatabaseMetaData.importedKeySetDefault:
cascadeAction = CascadeAction.SET_DEFAULT;
break;
case DatabaseMetaData.importedKeyRestrict:
cascadeAction = CascadeAction.RESTRICT;
break;
case DatabaseMetaData.importedKeyNoAction:
cascadeAction = CascadeAction.NONE;
break;
default:
cascadeAction = CascadeAction.NONE;
}
return cascadeAction;
}
private boolean hasIncludedTable(String tableName) {
if (includeTables == null || includeTables.isEmpty()) {
return true;
}
return hasTable(includeTables, tableName);
}
private boolean hasExcludedTable(String tableName) {
if (excludeTables == null || excludeTables.isEmpty()) {
return false;
}
return hasTable(excludeTables, tableName);
}
private boolean hasTable(Set<String> tables, String tableName) {
for (String table : tables) {
String regex = table.replaceAll("\\*", ".*").replaceAll("\\?", ".?");
Pattern pattern = Pattern.compile(regex);
if (pattern.matcher(tableName).matches()) {
return true;
}
}
return false;
}
private void readIndices(Table table) throws SQLException {
Set<Index> indices = new LinkedHashSet<Index>();
ResultSet rs;
try {
// Catching SQLException here due to Oracle throwing exception when attempting to retrieve indices for deleted tables that exist in Oracle's recycle bin
rs = databaseMetaData.getIndexInfo(catalogName, getSchemaName(), tableName, false, false);
} catch (SQLException e) {
return;
}
if (rs != null) {
try {
while (rs.next()) {
Short type = rs.getShort("TYPE");
if (type == DatabaseMetaData.tableIndexStatistic) {
continue;
}
String indexName = rs.getString("INDEX_NAME");
Index index = findIndex(indexName, indices);
if (index == null) {
index = new Index(indexName);
} else {
indices.remove(index);
}
index.setUnique(!rs.getBoolean("NON_UNIQUE"));
IndexColumn indexColumn = new IndexColumn(rs.getString("COLUMN_NAME"));
index.addColumn(indexColumn);
indices.add(index);
}
} finally {
rs.close();
}
}
for (Index index : indices) {
table.addIndex(index);
}
}
private Index findIndex(String name, Set<Index> indices) {
for (Index index : indices) {
if (index.getName().equalsIgnoreCase(name)) {
return index;
}
}
return null;
}
private Set<String> readPrimaryKeyNames() throws SQLException {
Set<String> columnNames = new LinkedHashSet<String>();
ResultSet rs = databaseMetaData.getPrimaryKeys(catalogName, getSchemaName(), tableName);
try {
while (rs.next()) {
columnNames.add(rs.getString("COLUMN_NAME"));
}
} finally {
rs.close();
}
return columnNames;
}
private String getCatalog() throws SQLException {
if (databaseMetaData.storesLowerCaseIdentifiers()) {
return StringUtils.toLowerCase(catalogName);
} else if (databaseMetaData.storesUpperCaseIdentifiers()) {
return StringUtils.toUpperCase(catalogName);
} else {
return catalogName;
}
}
private String getSchemaPattern() throws SQLException {
if (databaseMetaData.storesLowerCaseIdentifiers()) {
return StringUtils.toLowerCase(getSchemaName());
} else if (databaseMetaData.storesUpperCaseIdentifiers()) {
return StringUtils.toUpperCase(getSchemaName());
} else {
return getSchemaName();
}
}
private String getTableNamePattern() throws SQLException {
if (databaseMetaData.storesLowerCaseIdentifiers()) {
return StringUtils.toLowerCase(tableName);
} else if (databaseMetaData.storesUpperCaseIdentifiers()) {
return StringUtils.toUpperCase(tableName);
} else {
return tableName;
}
}
private String getColumnNamePattern() throws SQLException {
if (databaseMetaData.storesLowerCaseIdentifiers()) {
return StringUtils.toLowerCase(columnName);
} else if (databaseMetaData.storesUpperCaseIdentifiers()) {
return StringUtils.toUpperCase(columnName);
} else {
return columnName;
}
}
@SuppressWarnings("unused") private Dialect getDialect() {
try {
String productName = databaseMetaData.getDatabaseProductName();
return (Dialect) Class.forName("org.springframework.roo.addon.dbre.model.dialect." + productName + "Dialect").newInstance();
} catch (Exception e) {
return null;
}
}
}