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 * <!-- ======== Define the reusable reference ========== -->
67 *
68 * <queryset id="individuals">
69 * <query name="INDIVIDUALS" sql="
70 * SELECT * FROM INDIVIDUALS WHERE IND_ID IN (@subQuery@)"/>
71 *
72 * <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"/>
76 *
77 * <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"/>
81 * </queryset>
82 *
83 * <!-- ========= Use the reference ====================== -->
84 *
85 * <dbunit driver="${jdbcDriver}"
86 * url="${jdbcURL}" userid="${jdbcUser}" password="${jdbcPassword}">
87 * <export dest="${dest}">
88 * <queryset refid="individuals">
89 * <filterset>
90 * <filter token="subQuery" value="
91 * SELECT IND_ID FROM INDIVIDUALS WHERE USER_NAME = 'UNKNOWN'"/>
92 * </filterset>
93 * </queryset>
94 *
95 * </export>
96 * </dbunit>
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(final Query query) {
126 logger.debug("addQuery(query={}) - start", query);
127
128 queries.add(query);
129 }
130
131 public void addFilterSet(final 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(final String string) throws BuildException {
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(final String string) throws BuildException {
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 final Iterator i = queries.iterator();
163 while(i.hasNext()) {
164 final Query query = (Query)i.next();
165 replaceTokens(query);
166 }
167
168 return queries;
169
170 }
171
172 private void replaceTokens(final Query query) {
173 logger.debug("replaceTokens(query={}) - start", query);
174
175 final Iterator i = filterSets.iterator();
176 while(i.hasNext()) {
177 final FilterSet filterSet = (FilterSet)i.next();
178 query.setSql(filterSet.replaceTokens(query.getSql()));
179 }
180 }
181
182
183 public void copyQueriesFrom(final QuerySet referenced) {
184 logger.debug("copyQueriesFrom(referenced={}) - start", referenced);
185
186 final Iterator i = referenced.queries.iterator();
187 while(i.hasNext()) {
188 addQuery((Query)i.next());
189 }
190 }
191
192 public QueryDataSet getQueryDataSet(final IDatabaseConnection connection)
193 throws SQLException, AmbiguousTableNameException
194 {
195 logger.debug("getQueryDataSet(connection={}) - start", connection);
196
197 //incorporate queries from referenced query-set
198 final String refid = getRefid();
199 if(refid != null) {
200 final QuerySet referenced = (QuerySet)getProject().getReference(refid);
201 copyQueriesFrom(referenced);
202 }
203
204 final QueryDataSet partialDataSet = new QueryDataSet(connection);
205
206 final Iterator queriesIter = getQueries().iterator();
207 while(queriesIter.hasNext()) {
208 final Query query = (Query)queriesIter.next();
209 partialDataSet.addTable(query.getName(), query.getSql());
210 }
211
212 return partialDataSet;
213
214 }
215
216 }