org.dbunit.database.search.AbstractMetaDataBasedSearchCallback Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xmlc-base Show documentation
Show all versions of xmlc-base Show documentation
old 2002 version of xmlc-xmlc-base
The newest version!
/*
*
* The DbUnit Database Testing Framework
* Copyright (C)2005, DbUnit.org
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
package org.dbunit.database.search;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.SortedSet;
import java.util.TreeSet;
import org.dbunit.database.DatabaseConfig;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.database.IMetadataHandler;
import org.dbunit.dataset.NoSuchTableException;
import org.dbunit.util.QualifiedTableName;
import org.dbunit.util.SQLHelper;
import org.dbunit.util.search.AbstractNodesFilterSearchCallback;
import org.dbunit.util.search.IEdge;
import org.dbunit.util.search.SearchException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Super-class for the ISearchCallback that implements the
* getEdges()
method using the database meta-data.
*
* @author Felipe Leme ([email protected])
* @version $Revision$
* @since Aug 25, 2005
*/
public abstract class AbstractMetaDataBasedSearchCallback extends AbstractNodesFilterSearchCallback {
/**
* Logger for this class
*/
private static final Logger logger = LoggerFactory.getLogger(AbstractMetaDataBasedSearchCallback.class);
private final IDatabaseConnection connection;
/**
* Default constructor.
* @param connection connection where the edges will be calculated from
*/
public AbstractMetaDataBasedSearchCallback(IDatabaseConnection connection) {
this.connection = connection;
}
/**
* Get the connection where the edges will be calculated from.
* @return the connection where the edges will be calculated from
*/
public IDatabaseConnection getConnection() {
return connection;
}
protected static final int IMPORT = 0;
protected static final int EXPORT = 1;
/**
* indexes of the column names on the MetaData result sets.
*/
protected static final int[] TABLENAME_INDEXES = { 3, 7 };
protected static final int[] SCHEMANAME_INDEXES = { 2, 6 };
protected static final int[] PK_INDEXES = { 4, 4 };
protected static final int[] FK_INDEXES = { 8, 8 };
/**
* Get the nodes using the direct foreign key dependency, i.e, if table A has
* a FK for a table B, then getNodesFromImportedKeys(A) will return B.
* @param node table name
* @return tables with direct FK dependency from node
* @throws SearchException
*/
protected SortedSet getNodesFromImportedKeys(Object node)
throws SearchException {
logger.debug("getNodesFromImportedKeys(node={}) - start", node);
return getNodes(IMPORT, node);
}
/**
* Get the nodes using the reverse foreign key dependency, i.e, if table C has
* a FK for a table A, then getNodesFromExportedKeys(A) will return C.
*
* NOTE: this method should be used only as an auxiliary
* method for sub-classes that also use getNodesFromImportedKeys()
* or something similar, otherwise the generated sequence of tables might not
* work when inserted in the database (as some tables might be missing).
*
* @param node table name
* @return tables with reverse FK dependency from node
* @throws SearchException
*/
protected SortedSet getNodesFromExportedKeys(Object node)
throws SearchException {
logger.debug("getNodesFromExportedKeys(node={}) - start", node);
return getNodes(EXPORT, node);
}
/**
* Get the nodes using the both direct and reverse foreign key dependency, i.e,
* if table C has a FK for a table A and table A has a FK for a table B, then
* getNodesFromImportAndExportedKeys(A) will return B and C.
* @param node table name
* @return tables with reverse and direct FK dependency from node
* @throws SearchException
*/
protected SortedSet getNodesFromImportAndExportKeys(Object node)
throws SearchException {
logger.debug("getNodesFromImportAndExportKeys(node={}) - start", node);
SortedSet importedNodes = getNodesFromImportedKeys( node );
SortedSet exportedNodes = getNodesFromExportedKeys( node );
importedNodes.addAll( exportedNodes );
return importedNodes;
}
private SortedSet getNodes(int type, Object node) throws SearchException {
if(logger.isDebugEnabled())
logger.debug("getNodes(type={}, node={}) - start", Integer.toString(type), node);
try {
Connection conn = this.connection.getConnection();
String schema = this.connection.getSchema();
DatabaseMetaData metaData = conn.getMetaData();
SortedSet edges = new TreeSet();
getNodes(type, node, conn, schema, metaData, edges);
return edges;
} catch (SQLException e) {
throw new SearchException(e);
} catch (NoSuchTableException e) {
throw new SearchException(e);
}
}
private void getNodes(int type, Object node, Connection conn,
String schema, DatabaseMetaData metaData, SortedSet edges)
throws SearchException, NoSuchTableException
{
if (logger.isDebugEnabled())
{
logger.debug("getNodes(type={}, node={}, conn={}, schema={}, metaData={}, edges={}) - start",
new Object[] {String.valueOf(type), node, conn, schema, metaData, edges});
logger.debug("Getting edges for node " + node);
}
if (!(node instanceof String)) {
throw new IllegalArgumentException("node '" + node + "' should be a String, not a "
+ node.getClass().getName());
}
String tableName = (String) node;
QualifiedTableName qualifiedTableName = new QualifiedTableName(tableName, schema);
schema = qualifiedTableName.getSchema();
tableName = qualifiedTableName.getTable();
ResultSet rs = null;
try {
IMetadataHandler metadataHandler = (IMetadataHandler)
this.connection.getConfig().getProperty(DatabaseConfig.PROPERTY_METADATA_HANDLER);
// Validate if the table exists
if(!metadataHandler.tableExists(metaData, schema, tableName))
{
throw new NoSuchTableException("The table '"+tableName+"' does not exist in schema '"+schema+"'");
}
switch (type) {
case IMPORT:
rs = metaData.getImportedKeys(null, schema, tableName);
break;
case EXPORT:
rs = metaData.getExportedKeys(null, schema, tableName);
break;
}
DatabaseConfig dbConfig = this.connection.getConfig();
while (rs.next()) {
int index = TABLENAME_INDEXES[type];
int schemaindex = SCHEMANAME_INDEXES[type];
String dependentTableName = rs.getString(index);
String dependentSchemaName = rs.getString(schemaindex);
String pkColumn = rs.getString( PK_INDEXES[type] );
String fkColumn = rs.getString( FK_INDEXES[type] );
// set the schema in front if there is none ("SCHEMA.TABLE") - depending on the "qualified table names" feature
tableName = new QualifiedTableName(tableName, schema).getQualifiedNameIfEnabled(dbConfig);
dependentTableName = new QualifiedTableName(dependentTableName, dependentSchemaName).getQualifiedNameIfEnabled(dbConfig);
IEdge edge = newEdge(rs, type, tableName, dependentTableName, fkColumn, pkColumn );
if ( logger.isDebugEnabled() ) {
logger.debug("Adding edge " + edge);
}
edges.add(edge);
}
}
catch (SQLException e) {
throw new SearchException(e);
}
finally
{
try {
SQLHelper.close(rs);
} catch (SQLException e) {
throw new SearchException(e);
}
}
}
/**
* Creates an edge representing a foreign key relationship between 2 tables.
* @param rs database meta-data result set
* @param type type of relationship (IMPORT or EXPORT)
* @param from name of the table representing the 'from' node
* @param to name of the table representing the 'to' node
* @param fkColumn name of the foreign key column
* @param pkColumn name of the primary key column
* @return edge representing the relationship between the 2 tables, according to
* the type
* @throws SearchException not thrown in this method (but might on sub-classes)
*/
protected static ForeignKeyRelationshipEdge createFKEdge(ResultSet rs, int type,
String from, String to, String fkColumn, String pkColumn)
throws SearchException {
if (logger.isDebugEnabled()) {
logger.debug("createFKEdge(rs={}, type={}, from={}, to={}, fkColumn={}, pkColumn={}) - start",
new Object[] {rs, String.valueOf(type), from, to, fkColumn, pkColumn});
}
return type == IMPORT ?
new ForeignKeyRelationshipEdge( from, to, fkColumn, pkColumn ) :
new ForeignKeyRelationshipEdge( to, from, fkColumn, pkColumn );
}
/**
* This method can be overwritten by the sub-classes if they need to decorate
* the edge (for instance, providing an Edge that contains the primary and
* foreign keys used).
* @param rs database meta-data result set
* @param type type of relationship (IMPORT or EXPORT)
* @param from name of the table representing the 'from' node
* @param to name of the table representing the 'to' node
* @param fkColumn name of the foreign key column
* @param pkColumn name of the primary key column
* @return edge representing the relationship between the 2 tables, according to
* the type
* @throws SearchException not thrown in this method (but might on sub-classes)
*/
protected IEdge newEdge(ResultSet rs, int type, String from, String to, String fkColumn, String pkColumn)
throws SearchException {
if (logger.isDebugEnabled()) {
logger.debug("newEdge(rs={}, type={}, from={}, to={}, fkColumn={}, pkColumn={}) - start",
new Object[] {rs, String.valueOf(type), from, to, fkColumn, pkColumn});
}
return createFKEdge( rs, type, from, to, fkColumn, pkColumn );
}
}