View Javadoc
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  
22   package org.dbunit.ant;
23  
24  import java.sql.SQLException;
25  import java.util.ArrayList;
26  import java.util.Iterator;
27  import java.util.List;
28  
29  import org.apache.tools.ant.BuildException;
30  import org.apache.tools.ant.ProjectComponent;
31  import org.apache.tools.ant.types.FilterSet;
32  import org.dbunit.database.AmbiguousTableNameException;
33  import org.dbunit.database.IDatabaseConnection;
34  import org.dbunit.database.QueryDataSet;
35  import org.slf4j.Logger;
36  import org.slf4j.LoggerFactory;
37  
38  /**
39   * This element is a container for Queries. It facilitates reuse
40   * through references. Using Ant 1.6 and greater, references can be
41   * defined in a single build file and <i>import</i>ed into many others.
42   * An example of where this is useful follows:
43   * <p>
44   * In our database
45   * we have INDIVIDUALS which must have an associated NAME_INFO and
46   * at least one IND_ADDRESS. The developer creating a dataset for
47   * his/her tests probably won't know all the details of what relationships are
48   * expected, and if he did, its an error prone and repetitive task
49   * to create the correct SQL for entities in each dataset.
50   * Missing a related table, not only creates invalid data for your tests,
51   * but also is likely to cause DBUnit setUp() failures from foreign key
52   * constraint violation errors.
53   * (example: If a previous test had inserted INDIVIDUALS
54   * and NAME_INFO and my test tries to delete only the INDIVIDUALS, the
55   * NAME_INFO.IND_ID constraint would be violated)
56   * <p>
57   * <p>
58   * Each queryset is internally converted to a <code>QueryDataSet</code> and then
59   * combined using a <code>CompositeDataSet</code>. This means that you can use 
60   * more than one <code>query</code> element for any given table provided they 
61   * are nested within separate <code>queryset</code>s. 
62   * <p>
63   * Usage:
64   *
65   * <pre>
66   * &lt;!-- ======== Define the reusable reference ========== --&gt;
67   *
68   * &lt;queryset id="individuals"&gt;
69   *    &lt;query name="INDIVIDUALS" sql="
70   *      SELECT * FROM INDIVIDUALS WHERE IND_ID IN (@subQuery@)"/&gt;
71   *
72   *    &lt;query name="NAME_INFO" sql="
73   *      SELECT B.* FROM INDIVIDUALS A, NAME_INFO B
74   *      WHERE A.IND_ID IN (@subQuery@)
75   *      AND B.IND_ID = A.IND_ID"/&gt;
76   *
77   *    &lt;query name="IND_ADDRESSES" sql="
78   *      SELECT B.* FROM INDIVIDUALS A, IND_ADDRESSES B
79   *      WHERE A.IND_ID IN (@subQuery@)
80   *      AND B.IND_ID = A.IND_ID"/&gt;
81   * &lt;/queryset&gt;
82   *
83   * &lt;!-- ========= Use the reference ====================== --&gt;
84   *
85   * &lt;dbunit driver="${jdbcDriver}"
86   *     url="${jdbcURL}" userid="${jdbcUser}" password="${jdbcPassword}"&gt;
87   *   &lt;export dest="${dest}"&gt;
88   *   &lt;queryset refid="individuals"&gt;
89   *      &lt;filterset&gt;
90   *        &lt;filter token="subQuery" value="
91   *          SELECT IND_ID FROM INDIVIDUALS WHERE USER_NAME = 'UNKNOWN'"/&gt;
92   *      &lt;/filterset&gt;
93   *   &lt;/queryset&gt;
94   *
95   *   &lt;/export&gt;
96   * &lt;/dbunit&gt;
97   *
98   * </pre>
99   *
100  * @author Lenny Marks lenny@aps.org
101  * @author Last changed by: $Author$
102  * @version $Revision$ $Date$
103  * @since 2.2.0 (Sep. 13 2004)
104  */
105 public class QuerySet extends ProjectComponent
106 {
107 
108     /**
109      * Logger for this class
110      */
111     private static final Logger logger = LoggerFactory.getLogger(QuerySet.class);
112 
113 	private String id;
114 	private String refid;
115 	private List queries = new ArrayList();
116 	private List filterSets = new ArrayList();
117 
118 	private static String ERR_MSG =
119 		"Cannot specify 'id' and 'refid' attributes together in queryset.";
120 
121 	public QuerySet() {
122 		super();
123 	}
124 
125 	public void addQuery(Query query) {
126         logger.debug("addQuery(query={}) - start", query);
127 
128 		queries.add(query);
129 	}
130 
131 	public void addFilterSet(FilterSet filterSet) {
132         logger.debug("addFilterSet(filterSet={}) - start", filterSet);
133 
134 		filterSets.add(filterSet);
135 	}
136 
137 	public String getId() {
138 		return id;
139 	}
140 
141 	public String getRefid() {
142 		return refid;
143 	}
144 
145 	public void setId(String string) {
146         logger.debug("setId(string={}) - start", string);
147 
148 		if(refid != null) throw new BuildException(ERR_MSG);
149 		id = string;
150 	}
151 
152 	public void setRefid(String string) {
153         logger.debug("setRefid(string={}) - start", string);
154 
155 		if(id != null) throw new BuildException(ERR_MSG);
156 		refid = string;
157 	}
158 
159 	public List getQueries() {
160         logger.debug("getQueries() - start");
161 
162 		Iterator i = queries.iterator();
163 		while(i.hasNext()) {
164 			Query query = (Query)i.next();
165 			replaceTokens(query);
166 		}
167 
168 		return queries;
169 
170 	}
171 
172 	private void replaceTokens(Query query) {
173         logger.debug("replaceTokens(query={}) - start", query);
174 
175 		Iterator i = filterSets.iterator();
176 		while(i.hasNext()) {
177 			FilterSet filterSet = (FilterSet)i.next();
178 			query.setSql(filterSet.replaceTokens(query.getSql()));
179 		}
180 	}
181 
182 
183 	public void copyQueriesFrom(QuerySet referenced) {
184         logger.debug("copyQueriesFrom(referenced={}) - start", referenced);
185 
186 		Iterator i = referenced.queries.iterator();
187 		while(i.hasNext()) {
188 			addQuery((Query)i.next());
189 		}
190 	}
191 	
192     public QueryDataSet getQueryDataSet(IDatabaseConnection connection) 
193     throws SQLException, AmbiguousTableNameException 
194     {
195         logger.debug("getQueryDataSet(connection={}) - start", connection);
196         
197         //incorporate queries from referenced query-set
198         String refid = getRefid();
199         if(refid != null) {
200             QuerySet referenced = (QuerySet)getProject().getReference(refid);
201             copyQueriesFrom(referenced);
202         }
203         
204         QueryDataSet partialDataSet = new QueryDataSet(connection);
205         
206         Iterator queriesIter = getQueries().iterator();
207         while(queriesIter.hasNext()) {
208             Query query = (Query)queriesIter.next();
209             partialDataSet.addTable(query.getName(), query.getSql());
210         }
211         
212         return partialDataSet;
213         
214     }
215 
216 }