KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > persist > TableMapping


1 // $Id: TableMapping.java 12 2007-08-29 05:23:13Z jcamaia $
2

3 package net.sf.persist;
4
5 import java.lang.reflect.Method JavaDoc;
6 import java.sql.DatabaseMetaData JavaDoc;
7 import java.sql.ResultSet JavaDoc;
8 import java.sql.SQLException JavaDoc;
9 import java.util.ArrayList JavaDoc;
10 import java.util.Collection JavaDoc;
11 import java.util.HashSet JavaDoc;
12 import java.util.LinkedHashMap JavaDoc;
13 import java.util.List JavaDoc;
14 import java.util.Locale JavaDoc;
15 import java.util.Map JavaDoc;
16 import java.util.Set JavaDoc;
17
18 /**
19  * Holds mapping data from a given class and a table
20  */

21 public final class TableMapping extends Mapping {
22
23     private final Class JavaDoc objectClass;
24
25     private final net.sf.persist.annotations.Table tableAnnotation;
26     private final String JavaDoc tableName;
27
28     private final String JavaDoc[] fields; // list of fields which have getters and setters
29
private final Map JavaDoc<String JavaDoc, net.sf.persist.annotations.Column> annotationsMap; // maps field names to annotations
30
private final Map JavaDoc<String JavaDoc, Method JavaDoc> gettersMap; // maps field names to getters
31
private final Map JavaDoc<String JavaDoc, Method JavaDoc> settersMap; // maps field names to setters
32

33     private final boolean supportsGetGeneratedKeys;
34     private final boolean supportsBatchUpdates;
35
36     private final Map JavaDoc<String JavaDoc, String JavaDoc> columnsMap = new LinkedHashMap JavaDoc(); // maps table columns to property names
37
private final String JavaDoc[] columns;
38     private final String JavaDoc[] primaryKeys;
39     private final String JavaDoc[] notPrimaryKeys;
40     private final String JavaDoc[] autoGeneratedColumns;
41     private final String JavaDoc[] notAutoGeneratedColumns;
42
43     private final String JavaDoc selectSql;
44     private final String JavaDoc selectAllSql;
45     private final String JavaDoc insertSql;
46     private final String JavaDoc updateSql;
47     private final String JavaDoc deleteSql;
48
49     public TableMapping(final DatabaseMetaData JavaDoc metaData, final Class JavaDoc objectClass, final NameGuesser nameGuesser)
50             throws SQLException JavaDoc {
51
52         ResultSet JavaDoc resultSet = null;
53
54         // object class
55
this.objectClass = objectClass;
56
57         // database support for auto increment keys
58
supportsGetGeneratedKeys = metaData.supportsGetGeneratedKeys();
59
60         // database support for batch updates
61
supportsBatchUpdates = metaData.supportsBatchUpdates();
62
63         // database name
64
final String JavaDoc databaseProductName = metaData.getDatabaseProductName();
65
66         // table annotation
67
tableAnnotation = (net.sf.persist.annotations.Table) objectClass
68                 .getAnnotation(net.sf.persist.annotations.Table.class);
69
70         // schema pattern
71
String JavaDoc schemaPattern = null;
72         if (databaseProductName.equalsIgnoreCase("Oracle")) {
73             schemaPattern = "%"; // oracle expects a pattern such as "%" to work
74
}
75
76         // table name and annotation
77
tableName = getTableName(metaData, schemaPattern, objectClass, nameGuesser);
78
79         // all column names and types (from db)
80

81         final List JavaDoc<String JavaDoc> columnsList = new ArrayList JavaDoc();
82         resultSet = metaData.getColumns(null, schemaPattern, tableName, "%");
83         while (resultSet.next()) {
84             final String JavaDoc columnName = resultSet.getString(4);
85             columnsList.add(columnName);
86         }
87         columns = toArray(columnsList);
88
89         // all primary keys (from db)
90

91         final List JavaDoc<String JavaDoc> primaryKeysList = new ArrayList JavaDoc();
92         resultSet = metaData.getPrimaryKeys(null, schemaPattern, tableName);
93         while (resultSet.next()) {
94             final String JavaDoc columnName = resultSet.getString(4);
95             primaryKeysList.add(columnName);
96         }
97         primaryKeys = toArray(primaryKeysList);
98
99         // not primary keys
100

101         final List JavaDoc<String JavaDoc> notPrimaryKeysList = new ArrayList JavaDoc();
102         for (String JavaDoc columnName : columns) {
103             if (!primaryKeysList.contains(columnName)) {
104                 notPrimaryKeysList.add(columnName);
105             }
106         }
107         notPrimaryKeys = toArray(notPrimaryKeysList);
108
109         // map field names to annotations, getters and setters
110

111         final Map JavaDoc[] fieldsMaps = getFieldsMaps(objectClass);
112         annotationsMap = fieldsMaps[0];
113         gettersMap = fieldsMaps[1];
114         settersMap = fieldsMaps[2];
115         fields = toArray(gettersMap.keySet());
116
117         // map column names to field names; create list of auto-increment columns
118
// columnsMap use keys in lower case
119

120         // the actual autoGeneratedColumns list should have columns in the database order
121
final Set JavaDoc<String JavaDoc> autoGeneratedColumnsTemp = new HashSet JavaDoc();
122         for (String JavaDoc fieldName : fields) {
123             final String JavaDoc columnName = getColumnName(objectClass, nameGuesser, annotationsMap, columnsList, tableName,
124                     fieldName);
125             columnsMap.put(columnName.toLowerCase(Locale.ENGLISH), fieldName);
126             final net.sf.persist.annotations.Column annotation = annotationsMap.get(fieldName);
127             if (annotation != null && annotation.autoGenerated()) {
128                 autoGeneratedColumnsTemp.add(columnName);
129             }
130         }
131
132         // auto-increment and not-auto-increment columns, in the database order
133

134         final List JavaDoc<String JavaDoc> notAutoGeneratedColumnsList = new ArrayList JavaDoc();
135         final List JavaDoc<String JavaDoc> autoGeneratedColumnsList = new ArrayList JavaDoc();
136         for (String JavaDoc columnName : columns) {
137             if (autoGeneratedColumnsTemp.contains(columnName)) {
138                 autoGeneratedColumnsList.add(columnName);
139             } else {
140                 notAutoGeneratedColumnsList.add(columnName);
141             }
142         }
143         notAutoGeneratedColumns = toArray(notAutoGeneratedColumnsList);
144         autoGeneratedColumns = toArray(autoGeneratedColumnsList);
145
146         // assemble sql blocks to be used by crud sql statements
147

148         final String JavaDoc allColumns = join(columns, "", ",");
149         final String JavaDoc noAutoColumns = join(notAutoGeneratedColumns, "", ",");
150         final String JavaDoc allPlaceholders = multiply("?", columns.length, ",");
151         final String JavaDoc noAutoPlaceholders = multiply("?", notAutoGeneratedColumns.length, ",");
152         final String JavaDoc where = join(primaryKeys, "=?", ",");
153         final String JavaDoc updateSet = join(notPrimaryKeys, "=?", ",");
154
155         // assemble crud sql statements
156

157         selectSql = "select " + allColumns + " from " + tableName + " where " + where;
158         selectAllSql = "select " + allColumns + " from " + tableName;
159
160         if (autoGeneratedColumns.length == 0) {
161             insertSql = "insert into " + tableName + "(" + allColumns + ")values(" + allPlaceholders + ")";
162         } else {
163             insertSql = "insert into " + tableName + "(" + noAutoColumns + ")values(" + noAutoPlaceholders + ")";
164         }
165
166         updateSql = "update " + tableName + " set " + updateSet + " where " + where;
167         deleteSql = "delete from " + tableName + " where " + where;
168
169     }
170
171     // ---------- getters and setters ----------
172

173     public boolean supportsGetGeneratedKeys() {
174         return supportsGetGeneratedKeys;
175     }
176
177     public boolean supportsBatchUpdates() {
178         return supportsBatchUpdates;
179     }
180
181     public Class JavaDoc getObjectClass() {
182         return objectClass;
183     }
184
185     public String JavaDoc getTableName() {
186         return tableName;
187     }
188
189     public net.sf.persist.annotations.Table getTableAnnotation() {
190         return tableAnnotation;
191     }
192
193     public String JavaDoc[] getColumns() {
194         return columns;
195     }
196
197     public Map JavaDoc<String JavaDoc, String JavaDoc> getColumnsMap() {
198         return columnsMap;
199     }
200
201     public String JavaDoc[] getPrimaryKeys() {
202         return primaryKeys;
203     }
204
205     public String JavaDoc[] getNotPrimaryKeys() {
206         return notPrimaryKeys;
207     }
208
209     public String JavaDoc[] getAutoGeneratedColumns() {
210         return autoGeneratedColumns;
211     }
212
213     public String JavaDoc[] getNotAutoGeneratedColumns() {
214         return notAutoGeneratedColumns;
215     }
216
217     public String JavaDoc[] getFields() {
218         return fields;
219     }
220
221     public Map JavaDoc<String JavaDoc, net.sf.persist.annotations.Column> getAnnotationsMap() {
222         return annotationsMap;
223     }
224
225     public Map JavaDoc<String JavaDoc, Method JavaDoc> getGettersMap() {
226         return gettersMap;
227     }
228
229     public Map JavaDoc<String JavaDoc, Method JavaDoc> getSettersMap() {
230         return settersMap;
231     }
232
233     public Method JavaDoc getGetterForColumn(final String JavaDoc columnName) {
234         final String JavaDoc fieldName = columnsMap.get(columnName.toLowerCase(Locale.ENGLISH));
235         return gettersMap.get(fieldName);
236     }
237
238     public Method JavaDoc getSetterForColumn(final String JavaDoc columnName) {
239         final String JavaDoc fieldName = columnsMap.get(columnName.toLowerCase(Locale.ENGLISH));
240         return settersMap.get(fieldName);
241     }
242
243     public String JavaDoc getSelectSql() {
244         return selectSql;
245     }
246
247     public String JavaDoc getSelectAllSql() {
248         return selectAllSql;
249     }
250
251     public String JavaDoc getInsertSql() {
252         return insertSql;
253     }
254
255     public String JavaDoc getUpdateSql() {
256         return updateSql;
257     }
258
259     public String JavaDoc getDeleteSql() {
260         return deleteSql;
261     }
262
263     // ---------- utility methods ----------
264

265     private static String JavaDoc getTableName(final DatabaseMetaData JavaDoc metaData, final String JavaDoc schema, final Class JavaDoc objectClass,
266             final NameGuesser nameGuesser) throws SQLException JavaDoc {
267
268         String JavaDoc name = null;
269
270         final net.sf.persist.annotations.Table tableAnnotation = (net.sf.persist.annotations.Table) objectClass
271                 .getAnnotation(net.sf.persist.annotations.Table.class);
272
273         if (tableAnnotation != null && !tableAnnotation.name().equals("")) {
274             // if there's a Table annotation, use it
275
name = checkTableName(metaData, schema, tableAnnotation.name());
276
277             // test if the specified table name actually exists
278
if (name == null) {
279                 throw new RuntimeSQLException("Class [" + objectClass.getName() + "] specifies table ["
280                         + tableAnnotation.name() + "] that does not exist in the database");
281             }
282         } else {
283             // if no annotation, try guessed table names
284
final String JavaDoc className = objectClass.getSimpleName().substring(0, 1).toLowerCase()
285                     + objectClass.getSimpleName().substring(1);
286             final Set JavaDoc<String JavaDoc> guessedNames = nameGuesser.guessColumn(className);
287             for (String JavaDoc guessedTableName : guessedNames) {
288                 name = checkTableName(metaData, schema, guessedTableName);
289                 if (name != null) {
290                     break;
291                 }
292             }
293             if (name == null) {
294                 throw new RuntimeSQLException("Class [" + objectClass.getName()
295                         + "] does not specify a table name through a Table annotation and no guessed table names "
296                         + guessedNames + " exist in the database");
297             }
298         }
299
300         return name;
301     }
302
303     /**
304      * Check if the given name corresponds to a table in the database and
305      * returns the corresponding name with the capitalization returned by the
306      * database metadata
307      */

308     private static String JavaDoc checkTableName(final DatabaseMetaData JavaDoc metaData, final String JavaDoc schema, final String JavaDoc tableName)
309             throws SQLException JavaDoc {
310
311         ResultSet JavaDoc resultSet;
312         String JavaDoc ret = null;
313
314         // try name in upper case -- should work in most databases
315
resultSet = metaData.getTables(null, schema, tableName.toUpperCase(Locale.ENGLISH), null);
316         if (resultSet.next()) {
317             ret = tableName.toUpperCase(Locale.ENGLISH);
318         }
319         resultSet.close();
320
321         if (ret == null) {
322             // try name in lower case
323
resultSet = metaData.getTables(null, schema, tableName.toLowerCase(Locale.ENGLISH), null);
324             if (resultSet.next()) {
325                 ret = tableName.toLowerCase(Locale.ENGLISH);
326             }
327             resultSet.close();
328         }
329
330         if (ret == null) {
331             // last resort: compare with all table names in the schema
332
// (may be very expensive in databases such as oracle)
333
// this may end up being used in databases that allow case sensitive names (such as postgresql)
334
resultSet = metaData.getTables(null, schema, "%", null);
335             while (resultSet.next()) {
336                 final String JavaDoc dbTableName = resultSet.getString(3);
337                 if (tableName.equalsIgnoreCase(dbTableName)) {
338                     ret = dbTableName;
339                     break;
340                 }
341             }
342             resultSet.close();
343         }
344
345         return ret;
346     }
347
348     private static String JavaDoc getColumnName(final Class JavaDoc objectClass, final NameGuesser nameGuesser,
349             final Map JavaDoc<String JavaDoc, net.sf.persist.annotations.Column> annotationsMap, final List JavaDoc<String JavaDoc> columnsList,
350             final String JavaDoc tableName, final String JavaDoc fieldName) throws SQLException JavaDoc {
351
352         String JavaDoc columnName = null;
353
354         final net.sf.persist.annotations.Column annotation = annotationsMap.get(fieldName);
355         if (annotation != null && !annotation.name().equals("")) {
356             // if there's an annotation, use it
357
columnName = getIgnoreCase(columnsList, annotation.name());
358
359             // check if the specified column actually exists in the table
360
if (columnName == null) {
361                 throw new RuntimeSQLException("Field [" + fieldName + "] from class [" + objectClass.getName()
362                         + "] specifies column [" + annotation.name() + "] on table ["
363                         + tableName.toLowerCase(Locale.ENGLISH) + "] that does not exist in the database");
364             }
365         } else {
366             // if no annotation, try guessed column names
367
final Set JavaDoc<String JavaDoc> guessedNames = nameGuesser.guessColumn(fieldName);
368             for (String JavaDoc guessedColumnName : guessedNames) {
369                 columnName = getIgnoreCase(columnsList, guessedColumnName);
370                 if (columnName != null) {
371                     break;
372                 }
373             }
374             if (columnName == null) {
375                 throw new RuntimeSQLException("Field [" + fieldName + "] from class [" + objectClass.getName()
376                         + "] does not specify a column name through a Column annotation and no guessed column names "
377                         + guessedNames + " exist in the database. If this field is not supposed to be associated "
378                         + "with the database, please annotate it with @NoColumn");
379             }
380         }
381
382         return columnName;
383     }
384
385     // ---------- helpers ----------
386

387     /**
388      * Returns the first entry from the provided collection that matches the
389      * provided string ignoring case during the comparison.
390      */

391     private static String JavaDoc getIgnoreCase(final Collection JavaDoc<String JavaDoc> collection, final String JavaDoc str) {
392         String JavaDoc ret = null;
393         for (String JavaDoc s : collection) {
394             if (s.equalsIgnoreCase(str)) {
395                 ret = s;
396                 break;
397             }
398         }
399         return ret;
400     }
401
402     private static String JavaDoc[] toArray(final List JavaDoc<String JavaDoc> list) {
403         String JavaDoc[] array = new String JavaDoc[list.size()];
404         for (int i = 0; i < list.size(); i++) {
405             array[i] = list.get(i);
406         }
407         return array;
408     }
409
410     private static String JavaDoc[] toArray(final Set JavaDoc<String JavaDoc> set) {
411         final String JavaDoc[] array = new String JavaDoc[set.size()];
412         int i = 0;
413         for (String JavaDoc s : set) {
414             array[i] = s;
415             i++;
416         }
417         return array;
418     }
419
420     private static String JavaDoc join(final String JavaDoc[] list, final String JavaDoc suffix, final String JavaDoc separator) {
421         final StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
422         for (String JavaDoc obj : list) {
423             buf.append(obj.toString()).append(suffix).append(separator);
424         }
425         if (buf.length() > 0 && separator.length() > 0) {
426             buf.delete(buf.length() - separator.length(), buf.length());
427         }
428         return buf.toString();
429     }
430
431     private static String JavaDoc multiply(final String JavaDoc str, final int times, final String JavaDoc separator) {
432         final StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
433         for (int i = 0; i < times; i++) {
434             buf.append(str).append(separator);
435         }
436         if (separator.length() > 0) {
437             buf.delete(buf.length() - separator.length(), buf.length());
438         }
439         return buf.toString();
440     }
441
442 }
443
Popular Tags