QuerySet.java

  1. /*
  2.  *
  3.  * The DbUnit Database Testing Framework
  4.  * Copyright (C)2002-2004, DbUnit.org
  5.  *
  6.  * This library is free software; you can redistribute it and/or
  7.  * modify it under the terms of the GNU Lesser General Public
  8.  * License as published by the Free Software Foundation; either
  9.  * version 2.1 of the License, or (at your option) any later version.
  10.  *
  11.  * This library is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14.  * Lesser General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU Lesser General Public
  17.  * License along with this library; if not, write to the Free Software
  18.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19.  *
  20.  */

  21. package org.dbunit.ant;

  22. import java.sql.SQLException;
  23. import java.util.ArrayList;
  24. import java.util.Iterator;
  25. import java.util.List;

  26. import org.apache.tools.ant.BuildException;
  27. import org.apache.tools.ant.ProjectComponent;
  28. import org.apache.tools.ant.types.FilterSet;
  29. import org.dbunit.database.AmbiguousTableNameException;
  30. import org.dbunit.database.IDatabaseConnection;
  31. import org.dbunit.database.QueryDataSet;
  32. import org.slf4j.Logger;
  33. import org.slf4j.LoggerFactory;

  34. /**
  35.  * This element is a container for Queries. It facilitates reuse
  36.  * through references. Using Ant 1.6 and greater, references can be
  37.  * defined in a single build file and <i>import</i>ed into many others.
  38.  * An example of where this is useful follows:
  39.  * <p>
  40.  * In our database
  41.  * we have INDIVIDUALS which must have an associated NAME_INFO and
  42.  * at least one IND_ADDRESS. The developer creating a dataset for
  43.  * his/her tests probably won't know all the details of what relationships are
  44.  * expected, and if he did, its an error prone and repetitive task
  45.  * to create the correct SQL for entities in each dataset.
  46.  * Missing a related table, not only creates invalid data for your tests,
  47.  * but also is likely to cause DBUnit setUp() failures from foreign key
  48.  * constraint violation errors.
  49.  * (example: If a previous test had inserted INDIVIDUALS
  50.  * and NAME_INFO and my test tries to delete only the INDIVIDUALS, the
  51.  * NAME_INFO.IND_ID constraint would be violated)
  52.  * <p>
  53.  * <p>
  54.  * Each queryset is internally converted to a <code>QueryDataSet</code> and then
  55.  * combined using a <code>CompositeDataSet</code>. This means that you can use
  56.  * more than one <code>query</code> element for any given table provided they
  57.  * are nested within separate <code>queryset</code>s.
  58.  * <p>
  59.  * Usage:
  60.  *
  61.  * <pre>
  62.  * &lt;!-- ======== Define the reusable reference ========== --&gt;
  63.  *
  64.  * &lt;queryset id="individuals"&gt;
  65.  *    &lt;query name="INDIVIDUALS" sql="
  66.  *      SELECT * FROM INDIVIDUALS WHERE IND_ID IN (@subQuery@)"/&gt;
  67.  *
  68.  *    &lt;query name="NAME_INFO" sql="
  69.  *      SELECT B.* FROM INDIVIDUALS A, NAME_INFO B
  70.  *      WHERE A.IND_ID IN (@subQuery@)
  71.  *      AND B.IND_ID = A.IND_ID"/&gt;
  72.  *
  73.  *    &lt;query name="IND_ADDRESSES" sql="
  74.  *      SELECT B.* FROM INDIVIDUALS A, IND_ADDRESSES B
  75.  *      WHERE A.IND_ID IN (@subQuery@)
  76.  *      AND B.IND_ID = A.IND_ID"/&gt;
  77.  * &lt;/queryset&gt;
  78.  *
  79.  * &lt;!-- ========= Use the reference ====================== --&gt;
  80.  *
  81.  * &lt;dbunit driver="${jdbcDriver}"
  82.  *     url="${jdbcURL}" userid="${jdbcUser}" password="${jdbcPassword}"&gt;
  83.  *   &lt;export dest="${dest}"&gt;
  84.  *   &lt;queryset refid="individuals"&gt;
  85.  *      &lt;filterset&gt;
  86.  *        &lt;filter token="subQuery" value="
  87.  *          SELECT IND_ID FROM INDIVIDUALS WHERE USER_NAME = 'UNKNOWN'"/&gt;
  88.  *      &lt;/filterset&gt;
  89.  *   &lt;/queryset&gt;
  90.  *
  91.  *   &lt;/export&gt;
  92.  * &lt;/dbunit&gt;
  93.  *
  94.  * </pre>
  95.  *
  96.  * @author Lenny Marks lenny@aps.org
  97.  * @author Last changed by: $Author$
  98.  * @version $Revision$ $Date$
  99.  * @since 2.2.0 (Sep. 13 2004)
  100.  */
  101. public class QuerySet extends ProjectComponent
  102. {

  103.     /**
  104.      * Logger for this class
  105.      */
  106.     private static final Logger logger = LoggerFactory.getLogger(QuerySet.class);

  107.     private String id;
  108.     private String refid;
  109.     private List queries = new ArrayList();
  110.     private List filterSets = new ArrayList();

  111.     private static String ERR_MSG =
  112.             "Cannot specify 'id' and 'refid' attributes together in queryset.";

  113.     public QuerySet() {
  114.         super();
  115.     }

  116.     public void addQuery(final Query query) {
  117.         logger.debug("addQuery(query={}) - start", query);

  118.         queries.add(query);
  119.     }

  120.     public void addFilterSet(final FilterSet filterSet) {
  121.         logger.debug("addFilterSet(filterSet={}) - start", filterSet);

  122.         filterSets.add(filterSet);
  123.     }

  124.     public String getId() {
  125.         return id;
  126.     }

  127.     public String getRefid() {
  128.         return refid;
  129.     }

  130.     public void setId(final String string) throws BuildException {
  131.         logger.debug("setId(string={}) - start", string);

  132.         if(refid != null) throw new BuildException(ERR_MSG);
  133.         id = string;
  134.     }

  135.     public void setRefid(final String string) throws BuildException {
  136.         logger.debug("setRefid(string={}) - start", string);

  137.         if(id != null) throw new BuildException(ERR_MSG);
  138.         refid = string;
  139.     }

  140.     public List getQueries() {
  141.         logger.debug("getQueries() - start");

  142.         final Iterator i = queries.iterator();
  143.         while(i.hasNext()) {
  144.             final Query query = (Query)i.next();
  145.             replaceTokens(query);
  146.         }

  147.         return queries;

  148.     }

  149.     private void replaceTokens(final Query query) {
  150.         logger.debug("replaceTokens(query={}) - start", query);

  151.         final Iterator i = filterSets.iterator();
  152.         while(i.hasNext()) {
  153.             final FilterSet filterSet = (FilterSet)i.next();
  154.             query.setSql(filterSet.replaceTokens(query.getSql()));
  155.         }
  156.     }


  157.     public void copyQueriesFrom(final QuerySet referenced) {
  158.         logger.debug("copyQueriesFrom(referenced={}) - start", referenced);

  159.         final Iterator i = referenced.queries.iterator();
  160.         while(i.hasNext()) {
  161.             addQuery((Query)i.next());
  162.         }
  163.     }

  164.     public QueryDataSet getQueryDataSet(final IDatabaseConnection connection)
  165.             throws SQLException, AmbiguousTableNameException
  166.     {
  167.         logger.debug("getQueryDataSet(connection={}) - start", connection);

  168.         //incorporate queries from referenced query-set
  169.         final String refid = getRefid();
  170.         if(refid != null) {
  171.             final QuerySet referenced = (QuerySet)getProject().getReference(refid);
  172.             copyQueriesFrom(referenced);
  173.         }

  174.         final QueryDataSet partialDataSet = new QueryDataSet(connection);

  175.         final Iterator queriesIter = getQueries().iterator();
  176.         while(queriesIter.hasNext()) {
  177.             final Query query = (Query)queriesIter.next();
  178.             partialDataSet.addTable(query.getName(), query.getSql());
  179.         }

  180.         return partialDataSet;

  181.     }

  182. }