1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 package pl.kernelpanic.dbmonster.schema;
48
49 import java.sql.Connection;
50 import java.sql.DatabaseMetaData;
51 import java.sql.PreparedStatement;
52 import java.sql.ResultSet;
53 import java.util.ArrayList;
54 import java.util.HashMap;
55 import java.util.Iterator;
56 import java.util.List;
57 import java.util.Map;
58
59 import org.apache.commons.collections.iterators.FilterIterator;
60 import org.apache.commons.logging.Log;
61
62 import pl.kernelpanic.dbmonster.DBMonster;
63 import pl.kernelpanic.dbmonster.DBMonsterContext;
64 import pl.kernelpanic.dbmonster.ProgressMonitor;
65 import pl.kernelpanic.dbmonster.connection.ConnectionProvider;
66 import pl.kernelpanic.dbmonster.connection.Transaction;
67
68 /***
69 * The table.
70 *
71 * @author Piotr Maj <piotr.maj@kernelpanic.pl>
72 *
73 * @version $Id: Table.html,v 1.1 2007/06/21 08:38:14 sbahloul Exp $
74 */
75 public class Table implements Comparable {
76
77 /***
78 * The name of the table.
79 */
80 private String name = null;
81
82 /***
83 * The schema which owns the table.
84 */
85 private Schema schema = null;
86
87 /***
88 * A primary key of the table.
89 */
90 private Key key = null;
91
92 /***
93 * A map of columns.
94 */
95 private List columns = new ArrayList();
96
97 /***
98 * DBMonsterContext.
99 */
100 private DBMonsterContext context = null;
101
102 /***
103 * Progress monitor.
104 */
105 private ProgressMonitor progressMonitor = null;
106
107 /***
108 * The number of rows to generate.
109 */
110 private int rows = 0;
111
112 /***
113 * Statement.
114 */
115 private String query = null;
116
117 /***
118 * The number of retries.
119 */
120 private long maxTries = 1000L;
121
122 /***
123 * Is progress monitor set?
124 */
125 private boolean monitorSet = false;
126
127 /***
128 * Column types hash.
129 */
130 private Map columnTypes = new HashMap();
131
132 /***
133 * Returns the name of the table.
134 *
135 * @return the name of the table.
136 */
137 public final String getName() {
138 return name;
139 }
140
141 /***
142 * Sets the name of the table.
143 *
144 * @param s the name of the table.
145 */
146 public final void setName(final String s) {
147 name = s;
148 }
149
150 /***
151 * Returns schema.
152 *
153 * @return the schema
154 */
155 public final Schema getSchema() {
156 return schema;
157 }
158
159 /***
160 * Sets the schema - it cannot be null.
161 *
162 * @param s a schema
163 */
164 public final void setSchema(final Schema s) {
165 schema = s;
166 }
167
168 /***
169 * Status of table generation.
170 */
171 private boolean generated = false;
172
173 private ConnectionProvider connectionProvider;
174
175 /***
176 * Adds a column to the schema.
177 *
178 * @param column the column to add
179 *
180 * @throws SchemaException on schema error.
181 */
182 public final void addColumn(final Column column) throws SchemaException {
183 if (column == null) {
184 throw new SchemaException("Column is null!");
185 }
186 String columnName = column.getName();
187 if (containsColumn(columnName)) {
188 throw new SchemaException("Column <" + columnName + "> already exists.");
189 }
190 column.setTable(this);
191 columns.add(column);
192 }
193
194 /***
195 * Sets the key.
196 *
197 * @param k key
198 */
199 public final void setKey(final Key k) {
200 key = k;
201 if (key != null) {
202 key.setTable(this);
203 }
204 }
205
206 /***
207 * Returns a key.
208 *
209 * @return a key
210 */
211 public final Key getKey() {
212 return key;
213 }
214
215 /***
216 * Initializes the table.
217 *
218 * @param ctx dbmonster context.
219 *
220 * @throws Exception if initialization fails
221 *
222 */
223 public final void initialize(final DBMonsterContext ctx) throws Exception {
224 context = ctx;
225
226 Log log = (Log) context.getProperty(DBMonster.LOGGER_KEY);
227
228
229 connectionProvider = (ConnectionProvider) context.getProperty(
230 DBMonster.CONNECTION_PROVIDER_KEY);
231 Connection conn = null;
232 try {
233 if (log.isDebugEnabled()) {
234 log.debug("Getting column types.");
235 }
236
237 conn = connectionProvider.getConnection();
238 DatabaseMetaData metaData = conn.getMetaData();
239 String schemaName = (String) ctx.getProperty("dbmonster.jdbc.schema");
240 ResultSet rs = metaData.getColumns(null, schemaName, name, null);
241
242 String columnName = null;
243 int columnType = 0;
244 while (rs.next()) {
245 columnName = rs.getString("COLUMN_NAME");
246 columnType = rs.getInt("DATA_TYPE");
247 columnTypes.put(columnName, new Integer(columnType));
248 if (log.isDebugEnabled()) {
249 log.debug("Column: " + columnName + ", SQL type: " + columnType);
250 }
251 }
252 } catch (Exception e) {
253 throw new Exception("Couldn't get column types from database. Buggy JDBC driver?");
254 } finally {
255 if (conn != null) {
256 conn.close();
257 }
258 }
259 String lng = (String) context.getProperty("dbmonster.max-tries");
260 try {
261 maxTries = Long.parseLong(lng);
262 } catch (Exception e) {
263 if (log.isDebugEnabled()) {
264 log.debug("Unable to parse dbmonster.max-tries."
265 + " Using default value of 1000.");
266 }
267 }
268
269 progressMonitor =
270 (ProgressMonitor) context.getProperty(
271 DBMonster.PROGRESS_MONITOR_KEY);
272 if (log.isDebugEnabled()) {
273 log.debug("Initializing table <" + name + ">.");
274 }
275
276 StringBuffer sql = new StringBuffer("INSERT INTO ");
277 sql.append(name);
278 sql.append(" (");
279
280 int count = 0;
281 if (key != null && !key.getDatabaseDefault()) {
282 key.initialize(ctx);
283 List cols = key.getGenerator().getColumns();
284 for (int i = 0; i < cols.size(); i++) {
285 Column column = (Column) cols.get(i);
286 if (i != 0) {
287 sql.append(", ");
288 }
289 sql.append(column.getName());
290 ++count;
291 }
292 }
293
294 Iterator it = columnIterator();
295 if (it.hasNext()) {
296 if (key != null && !key.getDatabaseDefault()) {
297 sql.append(", ");
298 }
299 }
300
301 boolean first = true;
302 while (it.hasNext()) {
303 Column column = (Column) it.next();
304 column.initialize(ctx);
305 if (!first) {
306 sql.append(", ");
307 }
308 sql.append(column.getName());
309 first = false;
310 ++count;
311 }
312
313 sql.append(") VALUES (");
314 while (--count >= 0) {
315 sql.append("?");
316 if (count != 0) {
317 sql.append(", ");
318 }
319 }
320 sql.append(")");
321
322 query = sql.toString();
323
324 if (log.isDebugEnabled()) {
325 log.debug("Query: " + query);
326 log.debug("Initialization of table <" + name + "> finished.");
327 }
328 }
329
330 /***
331 * Generates the table.
332 *
333 * @throws Exception if table generation fails
334 */
335 public final void generate() throws Exception {
336 Log log = (Log) context.getProperty(DBMonster.LOGGER_KEY);
337
338 if (!generated) {
339
340 if (log.isInfoEnabled()) {
341 log.info("Generating table <" + name + ">.");
342 }
343
344 int counter = 0;
345 List keyColumns = null;
346 Transaction tx = null;
347 try {
348 tx = new Transaction(connectionProvider);
349 tx.begin();
350 PreparedStatement pstmt = tx.prepareStatement(query);
351 int currentTry = 0;
352 while (counter < rows) {
353 if (Thread.interrupted()) {
354 throw new InterruptedException();
355 }
356 pstmt.clearParameters();
357 if (key != null && !key.getDatabaseDefault()) {
358 if (log.isDebugEnabled()) {
359 log.debug("Generating key: " + key);
360 }
361 keyColumns = key.generate();
362 }
363 Iterator it = columnIterator();
364 while (it.hasNext()) {
365 ((Column) it.next()).generate();
366 }
367
368 if (!monitorSet) {
369 if (progressMonitor != null) {
370 progressMonitor.setTableName(name);
371 }
372 if (progressMonitor != null) {
373 progressMonitor.setRowsCount(rows);
374 }
375 monitorSet = true;
376 }
377
378 int paramCount = 1;
379 if (key != null && !key.getDatabaseDefault()) {
380 it = keyColumns.iterator();
381 while (it.hasNext()) {
382 Column column = (Column) it.next();
383 if (log.isDebugEnabled()) {
384 log.debug("Key column parameter " + paramCount
385 + " is " + column.getValue() + ".");
386 }
387 Integer columnType = ((Integer) columnTypes.get(column.getName()));
388 if (columnType != null) {
389 pstmt.setObject(paramCount++, column.getValue(), columnType.intValue());
390 } else {
391 pstmt.setObject(paramCount++, column.getValue());
392 }
393 }
394 }
395
396 it = columnIterator();
397 while (it.hasNext()) {
398 Column column = (Column) it.next();
399 if (log.isDebugEnabled()) {
400 log.debug("Column parameter " + paramCount
401 + " is " + column.getValue() + ".");
402 }
403 Integer columnType = ((Integer) columnTypes.get(column.getName()));
404 if (columnType != null) {
405 pstmt.setObject(paramCount++, column.getValue(), columnType.intValue());
406 } else {
407 pstmt.setObject(paramCount++, column.getValue());
408 }
409 }
410 try {
411 tx.execute();
412 counter++;
413 if (progressMonitor != null) {
414 progressMonitor.rowComplete();
415 }
416 } catch (Exception e) {
417 if (log.isDebugEnabled()) {
418 log.debug("Execution failed for reason: "
419 + e.getMessage());
420 }
421 currentTry++;
422 if (currentTry > maxTries) {
423 throw new Exception("Max tries exceeded! Quiting.");
424 }
425 }
426 resetColumns();
427 }
428
429 tx.commit();
430 } catch (Exception e) {
431 tx.abort();
432 throw e;
433 } finally {
434 tx.close();
435 }
436 generated = true;
437 if (progressMonitor != null) {
438 progressMonitor.tableComplete();
439 }
440 if (log.isInfoEnabled()) {
441 log.info("Generation of table <" + name + "> finished.");
442 }
443 }
444 }
445
446 /***
447 * Resets key and all columns.
448 */
449 public final void resetColumns() {
450 Iterator it = columnIterator();
451 while (it.hasNext()) {
452 ((Column) it.next()).reset();
453 }
454 }
455
456 /***
457 * Resets the entire table and mark it as if it was not generated.
458 */
459 public final void reset() {
460 resetColumns();
461 for (int i = 0; i < columns.size(); i++) {
462 Column c = (Column) columns.get(i);
463 c.getGenerator().reset();
464 }
465 generated = false;
466 monitorSet = false;
467 }
468
469 /***
470 * Returns the number of rows we want to generate for this table.
471 *
472 * @return number of rows
473 */
474 public final int getRows() {
475 return rows;
476 }
477
478 /***
479 * Sets the number of rows.
480 *
481 * @param i number of rows
482 */
483 public final void setRows(final int i) {
484 rows = i;
485 }
486
487 /***
488 * Checks if the table is already generated.
489 *
490 * @return <code>true</code> if the table is already generated.
491 */
492 public final boolean isGenerated() {
493 return generated;
494 }
495
496 /***
497 * Returns a column iterator.
498 *
499 * @return iterator over columns
500 */
501 public final Iterator columnIterator() {
502 return new FilterIterator(columns.iterator(), new ColumnPredicate());
503 }
504
505 /***
506 * Returns list of columns.
507 *
508 * @return list of columns
509 */
510 public final List getColumns() {
511 return columns;
512 }
513
514 /***
515 * @see java.lang.Comparable#compareTo(java.lang.Object)
516 */
517 public final int compareTo(final Object o) {
518 Table t = (Table) o;
519 return getName().compareTo(t.getName());
520 }
521
522 /***
523 * Removes column from table.
524 *
525 * @param column column to remove
526 */
527 public final void removeColumn(final Column column) {
528 for (int i = 0; i < columns.size(); i++) {
529 Column c = (Column) columns.get(i);
530 if (c == column) {
531 columns.remove(i);
532 break;
533 }
534 }
535 }
536
537 public boolean containsColumn(String name) {
538 for (int i = 0; i < columns.size(); i++) {
539 Column column = (Column) columns.get(i);
540 if (name.equals(column.getName())) {
541 return true;
542 }
543 }
544 return false;
545 }
546
547 public Column findColumn(String name) {
548 Column c = null;
549 for (int i = 0; i < columns.size(); i++) {
550 c = (Column) columns.get(i);
551 if (c.getName().equals(name)) {
552 return c;
553 }
554 c = null;
555 }
556 return c;
557 }
558 }