package org.jcommons.db.load.meta;
import java.sql.*;
import java.util.*;
import javax.sql.DataSource;
import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.lang.StringUtils;
import org.jcommons.db.column.MetaColumn;
import org.jcommons.db.column.MetaColumnUtils;
import org.jcommons.db.exception.TableNotFoundException;
import org.jcommons.lang.string.NamedString;
/**
* Utility class to retrieve the meta data for a given table.
*
* @author Thorsten Goeckeler
*/
public final class MetaTable
{
private static final String SELECT = "select * from ${table} where 1=0";
/** hide sole constructor */
private MetaTable() {
}
/**
* Retrieve the column meta data for all columns of a given table
*
* @param dataSource the data source to use, never null
* @param tableName the table name to query on, never null
* @return the list of meta data on all columns, can be empty but never null
* @throws TableNotFoundException if database cannot be accessed or privileges are missing
*/
public static List<MetaColumn> getMetaData(final DataSource dataSource, final String tableName)
throws TableNotFoundException
{
Map<String, String> parameter = new HashMap<String, String>();
parameter.put("table", StringUtils.upperCase(tableName));
String sql = NamedString.message(SELECT).with("table", tableName).toString();
List<MetaColumn> columns = null;
try {
List<String> primaryKeys = primaryKeys(dataSource, tableName);
QueryRunner query = new QueryRunner(dataSource);
columns = query.query(sql, new MetaColumnHandler(primaryKeys));
} catch (SQLException ex) {
throw new TableNotFoundException(tableName, ex);
}
return columns;
}
/**
* List all columns that form the primary key of the given table.
*
* @param dataSource the database connection to use
* @param tableName the table for which we need the primary keys
* @return the list of primary keys, can be empty but never null
* @throws SQLException if the database cannot be accessed or the driver does not support this feature
*/
private static List<String> primaryKeys(final DataSource dataSource, final String tableName)
throws SQLException
{
List<String> keys = new LinkedList<String>();
Connection connection = null;
try {
connection = dataSource.getConnection();
DatabaseMetaData meta = connection.getMetaData();
ResultSet primaries = meta.getPrimaryKeys(null, null, StringUtils.upperCase(tableName));
while (primaries.next()) {
keys.add(primaries.getString("COLUMN_NAME"));
}
} finally {
DbUtils.closeQuietly(connection);
}
return keys;
}
/**
* Determines all tables that the given table depends upon.
*
* @param dataSource the database connection to use
* @param tableName the table for which we want to know which tables this one depends upon
* @return the list of table names that this table references, can be empty but never <code>null</code>
* @throws SQLException if the database cannot be accessed or the driver does not support this feature
*/
public static Set<String> dependsOn(final DataSource dataSource, final String tableName)
throws SQLException
{
Set<String> tables = new HashSet<String>();
Connection connection = null;
try {
connection = dataSource.getConnection();
DatabaseMetaData meta = connection.getMetaData();
ResultSet foreigns = meta.getImportedKeys(null, null, StringUtils.upperCase(tableName));
while (foreigns.next()) {
tables.add(foreigns.getString("PKTABLE_NAME"));
}
} finally {
DbUtils.closeQuietly(connection);
}
return tables;
}
/**
* Determines all tables that the given table depends upon and requires data for.
*
* @param dataSource the database connection to use
* @param tableName the table for which we want to know which tables this one depends upon
* @return the list of table names that this table references on mandatory keys, never <code>null</code>
* @throws SQLException if the database cannot be accessed or the driver does not support this feature
*/
public static Set<String> dependsMandatoryOn(final DataSource dataSource, final String tableName)
throws SQLException
{
List<MetaColumn> columns = getMetaData(dataSource, tableName);
Set<String> tables = new HashSet<String>();
Connection connection = null;
try {
connection = dataSource.getConnection();
DatabaseMetaData meta = connection.getMetaData();
ResultSet foreigns = meta.getImportedKeys(null, null, StringUtils.upperCase(tableName));
while (foreigns.next()) {
String foreignKey = foreigns.getString("FKCOLUMN_NAME");
MetaColumn foreignColumn = MetaColumnUtils.findByColumnName(foreignKey, columns);
if (foreignColumn != null && foreignColumn.isNotNullable()) {
tables.add(foreigns.getString("PKTABLE_NAME"));
}
}
} finally {
DbUtils.closeQuietly(connection);
}
return tables;
}
}