001: /**
002: * Objective Database Abstraction Layer (ODAL)
003: * Copyright (c) 2004, The ODAL Development Group
004: * All rights reserved.
005: * For definition of the ODAL Development Group please refer to LICENCE.txt file
006: *
007: * Distributable under LGPL license.
008: * See terms of license at gnu.org.
009: */package com.completex.objective.components.persistency.meta.impl;
010:
011: import com.completex.objective.components.persistency.BasicQuery;
012: import com.completex.objective.components.persistency.ColumnType;
013: import com.completex.objective.components.persistency.ForeignKeyEntry;
014: import com.completex.objective.components.persistency.MetaColumn;
015: import com.completex.objective.components.persistency.MetaTable;
016: import com.completex.objective.components.persistency.Query;
017: import com.completex.objective.components.persistency.QueryFactoryBase;
018: import com.completex.objective.components.persistency.meta.MetaModel;
019:
020: import java.sql.Connection;
021: import java.sql.DatabaseMetaData;
022: import java.sql.ResultSet;
023: import java.sql.SQLException;
024: import java.util.ArrayList;
025: import java.util.Collection;
026: import java.util.HashSet;
027: import java.util.Hashtable;
028: import java.util.Iterator;
029: import java.util.List;
030:
031: /**
032: * @author Gennady Krizhevsky
033: */
034: public abstract class AbstractDatabaseModelLoader {
035: protected String filterPattern;
036:
037: protected AbstractDatabaseModelLoader() {
038: }
039:
040: protected AbstractDatabaseModelLoader(String filterPattern) {
041: this .filterPattern = filterPattern;
042: }
043:
044: abstract protected void debug(String message);
045:
046: abstract protected void info(String message);
047:
048: abstract protected ColumnType columnType(String dataType,
049: int columnSize, int decimalDigits, boolean required);
050:
051: protected void populateForeignKeysToModel(MetaModel model,
052: String tableName, Collection foreignKeys) {
053: for (Iterator it = foreignKeys.iterator(); it.hasNext();) {
054: Object[] forKey = (Object[]) it.next();
055: ForeignKeyEntry foreignKeyEntry = new ForeignKeyEntry();
056: String foreignKeyTable = (String) forKey[0];
057: List refs = (List) forKey[1];
058: for (int m = 0; m < refs.size(); m++) {
059: String[] refData = (String[]) refs.get(m);
060: foreignKeyEntry.setLocalColumn(refData[0]);
061: foreignKeyEntry.setForeignColumn(refData[1]);
062: }
063: defineForeignKey(model, foreignKeyTable, tableName,
064: foreignKeyEntry);
065: }
066: }
067:
068: /**
069: * Retrieves a list of foreign key columns for a given table.
070: *
071: * @param dbMeta JDBC metadata.
072: * @param tableName Table from which to retrieve FK information.
073: * @return A list of foreign keys in <code>tableName</code>.
074: * @throws java.sql.SQLException
075: */
076: protected Collection getForeignKeys(MetaModel metaModel,
077: DatabaseMetaData dbMeta, String dbSchema, String tableName)
078: throws SQLException {
079: Hashtable fks = new Hashtable();
080: ResultSet foreignKeys = null;
081: try {
082: /*
083: Retrieves a description of the primary key columns that are referenced by a table's foreign key columns (the primary keys imported by a table). They are ordered by PKTABLE_CAT, PKTABLE_SCHEM, PKTABLE_NAME, and KEY_SEQ.
084:
085: Each primary key column description has the following columns:
086:
087: 1. PKTABLE_CAT String => primary key table catalog being imported (may be null)
088: 2. PKTABLE_SCHEM String => primary key table schema being imported (may be null)
089: 3. PKTABLE_NAME String => primary key table name being imported
090: 4. PKCOLUMN_NAME String => primary key column name being imported
091: 5. FKTABLE_CAT String => foreign key table catalog (may be null)
092: 6. FKTABLE_SCHEM String => foreign key table schema (may be null)
093: 7. FKTABLE_NAME String => foreign key table name
094: 8. FKCOLUMN_NAME String => foreign key column name
095: 9. KEY_SEQ short => sequence number within a foreign key
096: 10. UPDATE_RULE short => What happens to a foreign key when the primary key is updated:
097: * importedNoAction - do not allow update of primary key if it has been imported
098: * importedKeyCascade - change imported key to agree with primary key update
099: * importedKeySetNull - change imported key to NULL if its primary key has been updated
100: * importedKeySetDefault - change imported key to default values if its primary key has been updated
101: * importedKeyRestrict - same as importedKeyNoAction (for ODBC 2.x compatibility)
102: 11. DELETE_RULE short => What happens to the foreign key when primary is deleted.
103: * importedKeyNoAction - do not allow delete of primary key if it has been imported
104: * importedKeyCascade - delete rows that import a deleted key
105: * importedKeySetNull - change imported key to NULL if its primary key has been deleted
106: * importedKeyRestrict - same as importedKeyNoAction (for ODBC 2.x compatibility)
107: * importedKeySetDefault - change imported key to default if its primary key has been deleted
108: 12. FK_NAME String => foreign key name (may be null)
109: 13. PK_NAME String => primary key name (may be null)
110: 14. DEFERRABILITY short => can the evaluation of foreign key constraints be deferred until commit
111: * importedKeyInitiallyDeferred - see SQL92 for definition
112: * importedKeyInitiallyImmediate - see SQL92 for definition
113: * importedKeyNotDeferrable - see SQL92 for definition
114:
115: Parameters:
116: catalog - a catalog name; must match the catalog name as it is stored in the database; "" retrieves those without a catalog; null means that the catalog name should not be used to narrow the search
117: schema - a schema name; must match the schema name as it is stored in the database; "" retrieves those without a schema; null means that the schema name should not be used to narrow the search
118: table - a table name; must match the table name as it is stored in the database
119: Returns:
120: ResultSet - each row is a primary key column description
121: Throws:
122: SQLException - if a database access error occurs
123: See Also:
124: getExportedKeys(java.lang.String, java.lang.String, java.lang.String)
125:
126: */
127: foreignKeys = dbMeta.getImportedKeys(null, dbSchema,
128: tableName);
129: while (foreignKeys.next()) {
130: String refTableName = foreignKeys
131: .getString("PKTABLE_NAME");
132: String fkName = foreignKeys.getString("FK_NAME");
133: // if FK has no name - make it up (use tablename instead)
134: if (fkName == null) {
135: fkName = refTableName;
136: }
137: Object[] fk = (Object[]) fks.get(fkName);
138: List refs;
139: if (fk == null) {
140: fk = new Object[2];
141: fk[0] = refTableName; //referenced table name
142: refs = new ArrayList();
143: fk[1] = refs;
144: fks.put(fkName, fk);
145: } else {
146: refs = (ArrayList) fk[1];
147: }
148: String[] ref = new String[2];
149: ref[0] = foreignKeys.getString("FKCOLUMN_NAME"); //local column
150: ref[1] = foreignKeys.getString("PKCOLUMN_NAME"); //foreign column
151: refs.add(ref);
152: }
153: } finally {
154: if (foreignKeys != null) {
155: foreignKeys.close();
156: }
157: }
158: return fks.values();
159: }
160:
161: protected MetaColumn defineColumn(MetaModel model, MetaTable table,
162: String columnName, int jdbcType, ColumnType columnType,
163: String nullable, String remarks, String defaultValue,
164: int columnSize, int decimalDigits,
165: DatabaseModelLoaderImpl.ColumnInfo columnInfo) {
166: try {
167: debug("defineColumn(" + table.getTableName() + ", "
168: + columnName + ", " + columnType + ", " + nullable
169: + ", " + remarks + ", " + defaultValue
170: + ", autoIncrement=" + columnInfo.autoIncrement
171: + ")");
172: } catch (NullPointerException e) {
173: System.err.println("NullPointerException for table "
174: + table.getTableName() + "; column " + columnName);
175: throw e;
176: }
177:
178: MetaColumn column = table.addColumn(columnName);
179: if (nullable == null || nullable.trim().equalsIgnoreCase("NO")) {
180: column.setRequired(true);
181: } else {
182: column.setRequired(false);
183: }
184: column.setType(columnType);
185: column.setRemarks(remarks);
186: column.setDefaultValue(defaultValue);
187: column.setColumnSize(columnSize);
188: column.setDecimalDigits(decimalDigits);
189: column.setAutoIncrement(columnInfo.autoIncrement);
190: column.setJdbcType(jdbcType);
191: //
192: // Get rid of schema:
193: //
194: String typeName = columnInfo.typeName;
195: if (typeName != null && typeName.indexOf(".") >= 0) {
196: int index = typeName.lastIndexOf(".");
197: typeName = typeName.substring(index + 1);
198: }
199: column.setTypeName(typeName);
200: return column;
201: }
202:
203: protected MetaTable defineTable(MetaModel model, String tableName) {
204: info("defineTable(" + tableName + ")");
205: return model.addTable(tableName);
206: }
207:
208: protected void definePrimaryKey(MetaModel model, String tableName,
209: String pkColumnName) {
210: debug("definePrimaryKey(" + tableName + ", " + pkColumnName
211: + ")");
212: MetaTable table = model.getTable(tableName);
213: if (table != null) {
214: MetaColumn column = table.getColumn(pkColumnName);
215: if (column != null) {
216: column.setPrimaryKey(true);
217: table.addToPrimaryKey(new Integer(column
218: .getColumnIndex()));
219: }
220: }
221: }
222:
223: protected void defineForeignKey(MetaModel model, String fkTable,
224: String tableName, ForeignKeyEntry foreignKeyEntry) {
225: debug("defineForeignKey(" + tableName + "," + foreignKeyEntry
226: + ")");
227: MetaTable table = model.getTable(tableName);
228: if (table != null) {
229: table.addToForeignKey(fkTable, foreignKeyEntry, true);
230: }
231: }
232:
233: protected void closeRs(ResultSet rs) {
234: if (rs != null) {
235: try {
236: rs.close();
237: } catch (SQLException e) {
238: throw new RuntimeException("closeRs", e);
239: }
240: }
241: }
242:
243: protected void closeConnection(Connection connection) {
244: if (connection != null) {
245: try {
246: connection.close();
247: } catch (SQLException e) {
248: throw new RuntimeException("closeConnection", e);
249: }
250: }
251: }
252:
253: protected BasicQuery prepareQuery(QueryFactoryBase factory,
254: HashSet seenNames) {
255: BasicQuery query = factory.newBasicQuery();
256: if (query.getName() == null
257: || query.getName().trim().length() == 0) {
258: throw new IllegalArgumentException(
259: "No name found in query [" + query + "]");
260: }
261: if (seenNames.contains(query.getName())) {
262: throw new IllegalArgumentException("Duplicate query name ["
263: + query.getName() + "]");
264: } else {
265: seenNames.add(query.getName());
266: }
267: if (query.getSql() == null) {
268: if (query instanceof Query) {
269: ((Query) query).andToWhere("0=1");
270: }
271: }
272: return query;
273: }
274:
275: public MetaModel load() throws Exception {
276: return this .load(new MetaModel(filterPattern));
277: }
278:
279: public abstract MetaModel load(MetaModel model) throws Exception;
280:
281: static class ColumnInfo {
282: public String name;
283: public boolean autoIncrement;
284: public int jdbcType;
285: public String typeName;
286:
287: public ColumnInfo(String name, boolean autoIncrement,
288: int jdbcType, String typeName) {
289: this .name = name;
290: this .autoIncrement = autoIncrement;
291: this .jdbcType = jdbcType;
292: this .typeName = typeName;
293: }
294:
295: public String toString() {
296: return super .toString() + "; name = " + name
297: + "; autoIncrement = " + autoIncrement
298: + "; jdbcType = " + jdbcType + "; typeName = "
299: + typeName;
300: }
301: }
302: }
|