Source code: javatools/db/DbDatabase.java
1 /*
2 Javatools (modified version) - Some useful general classes.
3 Copyright (C) 2002-2003 Chris Bitmead (original) Antonio Petrelli (modified)
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19 Contact me at: brenmcguire@users.sourceforge.net
20 */
21 package javatools.db;
22 import java.util.*;
23 import java.sql.*;
24 import java.io.*;
25 import javatools.util.FileLog;
26
27 /**
28 * A class that represents a particular database. A DbDatabase basically
29 * consists of some connection parameters plus we keep track of the open
30 * connections. The constructor is not public. Use DbManager.getDatabase().
31 *
32 * @author Chris Bitmead (original), Antonio Petrelli (modified)
33 * @created 29 August 2001
34 * @version 0.1.10
35 * @commentedby Antonio Petrelli
36 */
37 public class DbDatabase {
38 /** A map to reference each thread to each connection.
39 */
40 Map threadConnectionMap = new HashMap();
41 /** A map to reference each connection to each thread.
42 */
43 Map connectionThreadMap = new HashMap();
44 /** A map to access easily tables.
45 */
46 Map tables = new HashMap();
47 /** The manager for databases.
48 */
49 DbManager manager;
50 /** The name of this DBMS.
51 */
52 String name;
53 /** The JDBC driver to access this DBMS.
54 */
55 String driver;
56 /** The connection string for this DBMS-database.
57 */
58 String connectString;
59 /** The user name to use in connection.
60 */
61 String userName;
62 /** The password to use in connection.
63 */
64 String password;
65 /** <CODE>true</CODE>: each database update is autocommitted;
66 * <CODE>false</CODE>: commit must be made manually.
67 */
68 boolean autoCommit = true;
69
70 /** Builds a new DbDatabase empty object.
71 */
72 public DbDatabase() {
73 }
74
75 /** Builds a new DbDatabase object.
76 * @param manager The manager of all databases.
77 * @param name The name of DBMS.
78 * @param driver The JDBC driver for this DBMS.
79 * @param connectString The connection string to connect the DBMS to.
80 * @param userName the user name to be used in connection.
81 * @param password The password to be used in connection.
82 */
83 public DbDatabase(DbManager manager, String name, String driver, String connectString, String userName, String password) {
84 this.manager = manager;
85 this.name = name;
86 this.driver = driver;
87 this.connectString = connectString;
88 this.userName = userName;
89 this.password = password;
90 getEmulationsNeeded();
91 }
92
93 /** Sets autocommit property.
94 * @param pAutoCommit <CODE>true</CODE>: each database update is autocommitted;
95 * <CODE>false</CODE>: commit must be made manually.
96 */
97 public void setAutoCommit(boolean pAutoCommit) {
98 autoCommit = pAutoCommit;
99 }
100
101 /**
102 * Set a property for this database.
103 *
104 * @param pname The new property name
105 * @param value The new property value
106 * @exception DbException If something goes wrong.
107 */
108 public void setProperty(String pname, String value) throws DbException {
109 try {
110 if (name != null) {
111 pname = name + "." + pname;
112 }
113 manager.getProps().setProperty(pname, value);
114 } catch (IOException e) {
115 throw new DbException("Can't find db.properties", e);
116 }
117 }
118
119 /**
120 * A database can have a set of properties associated with it. We use <dbname>
121 * .<parametername>
122 *
123 * in a properties file and by default we use "driver", "userId", "password"
124 * and "connect".
125 *
126 * @param pname The name of the property.
127 * @return The property value
128 * @exception DbException If something goes wrong.
129 */
130 public String getProperty(String pname) throws DbException {
131 try {
132 if (name != null) {
133 pname = name + "." + pname;
134 }
135 String rtn = manager.getProps().getProperty(pname);
136 if (rtn == null) {
137 throw new DbException("Can't find DbDatabase property: '" + pname + "' in Props: '" + manager.getProps().getFullName() + "\n" + manager.getProps().toString());
138 }
139 return rtn;
140 } catch (IOException e) {
141 throw new DbException("Can't find db.properties", e);
142 }
143 }
144
145 /**
146 * Return an object representing a particular table in the database.
147 *
148 * @param name The name of the table.
149 * @return The table value
150 * @exception DbException If something goes wrong.
151 */
152 public DbTable getTable(String name) throws DbException {
153 // I guess we really should cache these.
154 try {
155 DbTable rtn = (DbTable) tables.get(name);
156 if (rtn == null) {
157 DbConnection con = getNewConnection();
158 PreparedStatement ps = con.con.prepareStatement("SELECT * FROM " + name + " WHERE 1=0");
159 ResultSet rs = ps.executeQuery();
160 rtn = new DbTable(this);
161 rtn.setResultSet(ps, rs);
162 rtn.setTableName(name);
163 tables.put(name, rtn);
164 ps.close();
165 con.close();
166 }
167 return rtn;
168 } catch (SQLException e) {
169 throw new DbException(e);
170 }
171 }
172
173 /** Returns a joined table.
174 * @param tableLeft The table to be joined on the left.
175 * @param tableRight The table to be joined on the right.
176 * @param joinType The type of joining.
177 * @param joinCondition The join condition.
178 * @throws DbException If something goes wrong.
179 * @return The joined table.
180 */
181 public DbJoinedTable getJoinedTable(DbAbstractTable tableLeft,
182 DbAbstractTable tableRight, int joinType, DbExpr joinCondition)
183 throws DbException {
184 return new DbJoinedTable(this, tableLeft, tableRight, joinType,
185 joinCondition);
186 }
187
188 /** Ahem... uncommented. It is obscure, call Chris Bitmead.
189 * @param name A name?
190 * @return A DbSequence.
191 */
192 public DbSequence getSequence(String name) {
193 return new DbSequence(this, name);
194 }
195
196 /**
197 * Get a DbConnection that will be associated with this Thread.
198 *
199 * @return The threadConnection value
200 * @exception DbException If something goes wrong.
201 */
202 public synchronized DbConnection getThreadConnection() throws DbException {
203 Thread thread = Thread.currentThread();
204 DbConnection rtn = (DbConnection) threadConnectionMap.get(thread);
205 if (rtn == null || rtn.isClosed()) {
206 FileLog.singleton().debug("getThreadConnection", "creating Connection");
207 rtn = getNewConnection();
208 threadConnectionMap.put(thread, rtn);
209 connectionThreadMap.put(rtn, thread);
210 }
211 FileLog.singleton().debug("getThreadConnection", "returning Connection: " + rtn + " for thread: " + Thread.currentThread().getName());
212 return rtn;
213 }
214
215 /**
216 * Get a DbConnection that will be associated with this Thread, but only if
217 * one exists already.
218 *
219 * @return The existingThreadConnection value
220 * @exception DbException If something goes wrong.
221 */
222 public synchronized DbConnection getExistingThreadConnection() throws DbException {
223 Thread thread = Thread.currentThread();
224 DbConnection rtn = (DbConnection) threadConnectionMap.get(thread);
225 return rtn;
226 }
227
228 /**
229 * Return a brand new DbConnection.
230 *
231 * @return The newConnection value
232 * @exception DbException If something goes wrong.
233 */
234 public DbConnection getNewConnection() throws DbException {
235 try {
236 Class.forName(driver);
237 } catch (ClassNotFoundException e) {
238 throw new DbException("SQL Driver class not found", e);
239 }
240 try {
241 Connection conn = DriverManager.getConnection(connectString, userName, password);
242 conn.setAutoCommit(autoCommit);
243 return new DbConnection(this, conn);
244 } catch (SQLException e) {
245 System.out.println(e.getMessage());
246 throw new DbException(e);
247 }
248 }
249
250 /** Checks if a DbDatabase object equals this one.
251 * @param o The object to compare to.
252 * @return <CODE>true</CODE>: the objects are equal;
253 * <CODE>false</CODE>: otherwise.
254 */
255 public boolean equals(Object o) {
256 if (!(o instanceof DbDatabase)) {
257 return false;
258 }
259 DbDatabase db = (DbDatabase) o;
260 return name.equals(db.name);
261 }
262
263 /**
264 * Create a new DbSelector.
265 *
266 * @return A selector to perform SELECT operation.
267 * @exception DbException If something goes wrong.
268 */
269 public DbSelector selector() throws DbException {
270 return new DbSelector(this);
271 }
272
273 /**
274 * Does this thread have a default connection associated with it? (i.e. has
275 * this Thread called getThreadConnection()).
276 *
277 * @return <CODE>true</CODE>: Yes it does;
278 * <CODE>false</CODE>: No it does not.
279 */
280 public boolean hasThreadConnection() {
281 Thread thread = Thread.currentThread();
282 DbConnection con = (DbConnection) threadConnectionMap.get(thread);
283 return con != null;
284 }
285
286 /**
287 * Return an expression representing an SQL true expression. We use the
288 * expression "0 = 0" for this purpose. This is handy when using complex code
289 * to construct a big conditional clause, it can be easier to start with a
290 * true expression and "AND" expressions onto the end.
291 *
292 * @return A true expression.
293 */
294 public DbExpr trueExpr() {
295 return new DbTrueExpr(this);
296 }
297
298 /**
299 * Return an expression representing an SQL false expression. We use the
300 * expression "0 = 1" for this purpose. This is handy when using complex code
301 * to construct a big conditional clause, it can be easier to start with a
302 * false expression and "OR" expressions onto the end.
303 *
304 * @return A false expression.
305 */
306 public DbExpr falseExpr() {
307 return new DbFalseExpr(this);
308 }
309
310 /** Returns a value list.
311 * @param col The collection containing the value list.
312 * @return The value list.
313 */
314 public DbExpr valueList(Collection col) {
315 return new DbValueList(this, col);
316 }
317
318 /** A database to a string? Ok if you want it...
319 * @return A string representing the database.
320 */
321 public String toString() {
322 return "DbDatabase: " + name;
323 }
324
325 public String getUserName() {
326 return userName;
327 }
328
329 public String getPassword() {
330 return password;
331 }
332
333 /** Returns the capability of DBMS to handle foreign keys.
334 * @return <CODE>true</CODE>: the DBMS supports foreign keys;
335 * <CODE>false</CODE>: otherwise.
336 */
337 public boolean getForeignKey() {
338 return foreignKey;
339 }
340
341 /** Returns the capability of DBMS to handle check statements.
342 * @return <CODE>true</CODE>: the DBMS supports check clauses;
343 * <CODE>false</CODE>: otherwise.
344 */
345 public boolean getCheckStatement() {
346 return checkStatement;
347 }
348
349 /** Returns the capability of DBMS to handle ON DELETE CASCADE clauses.
350 * @return <CODE>true</CODE>: the DBMS supports ON DELETE CASCADE clauses;
351 * <CODE>false</CODE>: otherwise.
352 */
353 public boolean getOnDeleteCascade() {
354 return onDeleteCascade;
355 }
356
357 /** Returns the capability of DBMS to handle ON DELETE SET DEFAULT clauses.
358 * @return <CODE>true</CODE>: the DBMS supports ON DELETE SET DEFAULT clauses;
359 * <CODE>false</CODE>: otherwise.
360 */
361 public boolean getOnDeleteSetDefault (){
362 return onDeleteSetDefault;
363 }
364
365 /** Returns the capability of DBMS to handle ON DELETE SET NULL clauses.
366 * @return <CODE>true</CODE>: the DBMS supports ON DELETE SET NULL clauses;
367 * <CODE>false</CODE>: otherwise.
368 */
369 public boolean getOnDeleteSetNull() {
370 return onDeleteSetNull;
371 }
372
373 /** Returns the capability of DBMS to handle ON UPDATE CASCADE clauses.
374 * @return <CODE>true</CODE>: the DBMS supports ON UPDATE CASCADE clauses;
375 * <CODE>false</CODE>: otherwise.
376 */
377 public boolean getOnUpdateCascade() {
378 return onUpdateCascade;
379 }
380
381 /** Returns the capability of DBMS to handle ON UPDATE SET DEFAULT clauses.
382 * @return <CODE>true</CODE>: the DBMS supports ON UPDATE SET DEFAULT clauses;
383 * <CODE>false</CODE>: otherwise.
384 */
385 public boolean getOnUpdateSetDefault() {
386 return onUpdateSetDefault;
387 }
388
389 /** Returns the capability of DBMS to handle ON UPDATE SET NULL clauses.
390 * @return <CODE>true</CODE>: the DBMS supports ON UPDATE SET NULL clauses;
391 * <CODE>false</CODE>: otherwise.
392 */
393 public boolean getOnUpdateSetNull (){
394 return onUpdateSetNull;
395 }
396
397 /**
398 * Notify the DbDatabase that one of its connections has closed.
399 *
400 * @param con Description of Parameter
401 */
402 void notifyClose(DbConnection con) {
403 Object thread = connectionThreadMap.get(con);
404 if (thread != null) {
405 connectionThreadMap.remove(con);
406 threadConnectionMap.remove(thread);
407 }
408 }
409
410 // Variables for needed emulation...
411
412 /** A flag indicating if a DBMS supports foreign keys.
413 * <CODE>true</CODE>: the DBMS supports foreign keys;
414 * <CODE>false</CODE>: otherwise.
415 */
416 protected boolean foreignKey;
417 /** A flag indicating if a DBMS supports check clauses in its constraints.
418 * <CODE>true</CODE>: the DBMS supports check clauses;
419 * <CODE>false</CODE>: otherwise.
420 */
421 protected boolean checkStatement;
422 /** A flag indicating if a DBMS supports ON DELETE CASCADE clauses in its
423 * constraints.
424 * <CODE>true</CODE>: the DBMS supports ON DELETE CASCADE clauses;
425 * <CODE>false</CODE>: otherwise.
426 */
427 protected boolean onDeleteCascade;
428 /** A flag indicating if a DBMS supports ON DELETE SET DEFAULT clauses in its
429 * constraints.
430 * <CODE>true</CODE>: the DBMS supports ON DELETE SET DEFAULT clauses;
431 * <CODE>false</CODE>: otherwise.
432 */
433 protected boolean onDeleteSetDefault;
434 /** A flag indicating if a DBMS supports ON DELETE SET NULL clauses in its
435 * constraints.
436 * <CODE>true</CODE>: the DBMS supports ON DELETE SET NULL clauses;
437 * <CODE>false</CODE>: otherwise.
438 */
439 protected boolean onDeleteSetNull;
440 /** A flag indicating if a DBMS supports ON UPDATE CASCADE clauses in its
441 * constraints.
442 * <CODE>true</CODE>: the DBMS supports ON UPDATE CASCADE clauses;
443 * <CODE>false</CODE>: otherwise.
444 */
445 protected boolean onUpdateCascade;
446 /** A flag indicating if a DBMS supports ON UPDATE SET DEFAULT clauses in its
447 * constraints.
448 * <CODE>true</CODE>: the DBMS supports ON UPDATE SET DEFAULT clauses;
449 * <CODE>false</CODE>: otherwise.
450 */
451 protected boolean onUpdateSetDefault;
452 /** A flag indicating if a DBMS supports ON UPDATE SET NULL clauses in its
453 * constraints.
454 * <CODE>true</CODE>: the DBMS supports ON UPDATE SET NULL clauses;
455 * <CODE>false</CODE>: otherwise.
456 */
457 protected boolean onUpdateSetNull;
458
459 private void getEmulationsNeeded() {
460 foreignKey = false;
461 checkStatement = false;
462 onDeleteCascade = false;
463 onDeleteSetDefault = false;
464 onDeleteSetNull = false;
465 onUpdateCascade = false;
466 onUpdateSetDefault = false;
467 onUpdateSetNull = false;
468 try {
469 foreignKey = string2boolean(getProperty("foreignKey"));
470 if (foreignKey) {
471 checkStatement = string2boolean(getProperty("checkStatement"));
472 onDeleteCascade = string2boolean(getProperty("onDeleteCascade"));
473 onDeleteSetDefault = string2boolean(getProperty("onDeleteSetDefault"));
474 onDeleteSetNull = string2boolean(getProperty("onDeleteSetNull"));
475 onUpdateCascade = string2boolean(getProperty("onUpdateCascade"));
476 onUpdateSetDefault = string2boolean(getProperty("onUpdateSetDefault"));
477 onUpdateSetNull = string2boolean(getProperty("onUpdateSetNull"));
478 }
479 }
480 catch (DbException e) {
481 }
482 }
483
484 private boolean string2boolean (String value) {
485 if (value.equals("true"))
486 return true;
487 else
488 return false;
489 }
490 }