All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dbunit.database.search.AbstractMetaDataBasedSearchCallback Maven / Gradle / Ivy

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 ); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy