Source Code Cross Referenced for SDriver.java in  » Database-ORM » SimpleORM » simpleorm » core » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Database ORM » SimpleORM » simpleorm.core 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        package simpleorm.core;
002:
003:        import simpleorm.properties.*;
004:        import java.sql.*;
005:
006:        /*
007:         * Copyright (c) 2002 Southern Cross Software Queensland (SCSQ).  All rights
008:         * reserved.  See COPYRIGHT.txt included in this distribution.
009:         */
010:
011:        /** This is the generic database driver that contains minimal
012:         database dependent code.  Specific SimpleORM drivers extend this
013:         class and specialize its methods as required.  The driver type is
014:         infered from the jdbc connection's meta data.<p>
015:        
016:         ## Profiling suggests that memoising these generaters could produce a
017:         5-10% improvement in bulk updates.<p>
018:        
019:         There is now one driver instance per connection so one can say
020:         SConnection.getDriver().setMyFavouriteParam(...)<p>
021:        
022:         SQL 92 standard data types, I think:-
023:         boolean, Character(n), character varying(n), date, float(p), real, double precission, smallint, int | integer, 
024:         decimal(p,s), numeric(p,s), time, interval, timestamp with timezone, 
025:         */
026:        public class SDriver implements  SConstants {
027:
028:            /** Note that these are only prototypes.  
029:             * New SDriver instances are created for each connection.
030:             * See static below.*/
031:            static SArrayList drivers = new SArrayList();
032:
033:            static {
034:                SDriver[] ds = new SDriver[] { new SDriverHSQL(),
035:                        new SDriverPostgres(), new SDriverMySQL(),
036:                        new SDriverOracle(), new SDriverDaffodil(),
037:                        new SDriverMSSQL(), new SDriverDB2_400(),
038:                        new SDriverInformix(), new SDriverInterbase(),
039:                        new SDriverFirebird(), new SDriverSapDB(),
040:                        new SDriverSybase(), new SDriverDerby() };
041:                for (int dx = 0; dx < ds.length; dx++)
042:                    ((SDriver) ds[dx]).registerDriver();
043:            }
044:
045:            /**
046:             Add driver to the list of possible drivers that can be found by
047:             SConnection.attach.  The key is returned by driverName.
048:             */
049:            public void registerDriver() {
050:                drivers.add(this );
051:            }
052:
053:            /** The driver name to be compared to getMetaData().getDriverName() */
054:            protected String driverName() {
055:                return "Generic Driver";
056:            }
057:
058:            /** The maximum size for table names and foreign key constraint names. */
059:            public int maxIdentNameLength() {
060:                return 30;
061:            }
062:
063:            /** Wraps identifiers in "s to avoid reserved word issues. 
064:             * (Override this for dbs that use different chars, eg [xxx] for MS-SQL.
065:             * We do quote these days to avoid the endless problems with reserved words.
066:             */
067:            public String quoteIdentifier(String ident) {
068:                if (ident.length() > maxIdentNameLength())
069:                    throw new SException.Error("Identifier '" + ident
070:                            + "' is longer than " + maxIdentNameLength()
071:                            + " chars as permitted by " + driverName());
072:                return "\"" + ident + "\"";
073:            }
074:
075:            public String quoteColumn(String ident) {
076:                return quoteIdentifier(ident);
077:            }
078:
079:            public String quoteTable(String ident) {
080:                return quoteIdentifier(ident);
081:            }
082:
083:            public String quoteConstraint(String ident) {
084:                return quoteIdentifier(ident);
085:            }
086:
087:            /** True if exclusive locks are properly supported.  Otherwise
088:             optimistic locks are always used. <p>
089:             */
090:            public boolean supportsLocking() {
091:                return true;
092:            }
093:
094:            /** Chooses default driver based on the connection's meta data.<p>
095:             
096:             Note that if you have trouble with the driver defaulting mechanism, then
097:             specify the driver explicitly as the third parameter to SConnection.attach.<p>
098:             
099:             Note also that a new driver instance is created each time.
100:             */
101:            static SDriver newSDriver(java.sql.Connection con, String driverName) {
102:                String databaseName = null;
103:                try {
104:                    driverName = driverName != null ? driverName : con
105:                            .getMetaData().getDriverName();
106:                    databaseName = con.getMetaData().getDatabaseProductName();
107:                } catch (Exception ex) {
108:                    throw new SException.JDBC(ex);
109:                }
110:                for (int dx = 0; dx < drivers.size(); dx++) {
111:                    SDriver driver = (SDriver) drivers.get(dx);
112:                    if (driver.driverName().equals(driverName))
113:                        try {
114:                            return (SDriver) driver.getClass().newInstance();
115:                        } catch (Exception ex) {
116:                            throw new SException.Error(ex);
117:                        }
118:                }
119:
120:                SLog.slog.warn("Unknown Database '" + databaseName
121:                        + "' driver '" + driverName
122:                        + "'. Using generic SDriver.");
123:                return new SDriver();
124:            }
125:
126:            /** These alow you to create a new SFieldMeta object at runtime
127:             and then update the table to include it.  Eg. for end user
128:             customizations.
129:             
130:             ## Ideally this could be further automated so that the SRecordMeta
131:             and JDBC meta data for a table could be compared and the table
132:             automatically altered.
133:             */
134:            public String alterTableAddColumnSQL(SFieldMeta field) {
135:                StringBuffer sql = new StringBuffer();
136:
137:                sql.append("\nALTER TABLE ");
138:                sql
139:                        .append(quoteTable(field.sRecordMeta
140:                                .getString(STABLE_NAME)));
141:                sql.append(" ADD COLUMN ");
142:                sql.append(quoteColumn(wholeColumnSQL(field)));
143:                sql.append(clauseSeparator("    "));
144:
145:                return sql.toString();
146:            }
147:
148:            public String alterTableDropColumnSQL(SFieldMeta field) {
149:                StringBuffer sql = new StringBuffer();
150:
151:                sql.append("\nALTER TABLE ");
152:                sql
153:                        .append(quoteTable(field.sRecordMeta
154:                                .getString(STABLE_NAME)));
155:                sql.append(" DROP COLUMN ");
156:                sql.append(quoteColumn(field.getString(SCOLUMN_NAME)));
157:                sql.append(clauseSeparator("    "));
158:
159:                return sql.toString();
160:            }
161:
162:            /** Returns a <code>CREATE TABLE</code> for this table.  Delegated
163:             from SRecord. This is split up into many sub-methods so that
164:             they can be selectively specialized by other drivers.*/
165:            public String createTableSQL(SRecordMeta meta) {
166:                StringBuffer sql = new StringBuffer(1000);
167:                String tname = meta.getString(STABLE_NAME);
168:                sql.append("\nCREATE TABLE " + quoteTable(tname) + "(");
169:                for (int fx = 0; fx < meta.sFieldMetas.size(); fx++) {
170:                    //SLog.temp("SFld " + meta.fields.get(fx));
171:                    SFieldMeta fld = (SFieldMeta) meta.sFieldMetas.get(fx);
172:                    Object cq = fld.getProperty(SCOLUMN_QUERY);
173:                    if (!(fld instanceof  SFieldReference) && cq == null) {
174:                        sql.append(clauseSeparator("    "));
175:                        sql.append(wholeColumnSQL(fld));
176:                        sql.append(", "); // Assume always a Primary Key clause
177:                    }
178:                }
179:                sql.append(clauseSeparator("    "));
180:                sql.append(primaryKeySQL(meta));
181:                sql.append(indexKeySQL(meta));
182:                sql.append(foreignKeysSQL(meta));
183:                sql.append(postTablePreParenSQL(meta));
184:                String xTable = (String) meta.getProperty(SEXTRA_TABLE_DDL);
185:                if (xTable != null)
186:                    sql.append(xTable);
187:                sql.append(")");
188:                sql.append(postTablePostParenSQL(meta));
189:                sql.append(clauseSeparator("    "));
190:                return sql.toString();
191:            }
192:
193:            /** Normally newline and indent to separate clauses of large SQL
194:             statement */
195:            protected String clauseSeparator(String indent) {
196:                return "\n" + indent;
197:            }
198:
199:            /** Returns <code>MY_COL VARCHAR(13) NOT NULL</code>.
200:             @see #SMANDATORY
201:             */
202:            protected String wholeColumnSQL(SFieldMeta fld) {
203:                StringBuffer sql = new StringBuffer(60);
204:                String columnName = fld.getString(SCOLUMN_NAME);
205:                int len = columnName.length();
206:                sql.append(quoteColumn(columnName)
207:                        + "            ".substring(len > 10 ? 10 : len - 1)
208:                        + columnTypeSQL(fld));
209:                addNull(sql, fld);
210:                sql.append(postColumnSQL(fld));
211:                String xCols = (String) fld.getProperty(SEXTRA_COLUMN_DDL);
212:                if (xCols != null)
213:                    sql.append(xCols);
214:                return sql.toString();
215:            }
216:
217:            protected void addNull(StringBuffer sql, SFieldMeta fld) {
218:                if (fld.isPrimaryKey || fld.getBoolean(SMANDATORY))
219:                    sql.append(" NOT NULL");
220:                else
221:                    sql.append(" NULL"); // ### NULL is redundant and troublesome, remove.
222:                // ### Sybase anywhere and ALLOW_NULLS_BY_DEFAULT option.  This
223:                // is a hack, need to distinguish NULL from empty.  See SMANDATORY.
224:            }
225:
226:            protected String columnTypeSQL(SFieldMeta field) {
227:                String res = (String) field.getProperty(SDATA_TYPE);
228:                if (res.equals("VARCHAR") || res.equals("CHAR"))
229:                    res = res + "(" + field.getProperty(SBYTE_SIZE) + ")";
230:                return res;
231:            }
232:
233:            /** After NOT NULL but before the ",", ie column specific annotations. */
234:            protected String postColumnSQL(SFieldMeta field) {
235:                return "";
236:            }
237:
238:            /** Return <code>PRIMARY KEY(KCOL, KCOL)</code> appended to end. */
239:            protected String primaryKeySQL(SRecordMeta meta) {
240:                StringBuffer pkey = new StringBuffer("PRIMARY KEY (");
241:                boolean firstpk = true;
242:                for (int fx = 0; fx < meta.sFieldMetas.size(); fx++) {
243:                    SFieldMeta fld = (SFieldMeta) meta.sFieldMetas.get(fx);
244:                    if (!(fld instanceof  SFieldReference)) {
245:                        if (fld.isPrimaryKey) {
246:                            if (!firstpk)
247:                                pkey.append(", ");
248:                            firstpk = false;
249:                            pkey
250:                                    .append(quoteColumn(fld
251:                                            .getString(SCOLUMN_NAME)));
252:                        }
253:                    }
254:                }
255:                pkey.append(")");
256:                return pkey.toString();
257:            }
258:
259:            /** Needed for MySQL to create indexes on foreign keys */
260:            protected String indexKeySQL(SRecordMeta meta) {
261:                return "";
262:            }
263:
264:            /** Returns <code>FOREIGN KEY (FKCOL, FKCOL) REFERENCES FTABLE (KCOL,
265:             KCOL)</code> appended to end. */
266:            protected String foreignKeysSQL(SRecordMeta meta) {
267:                return mapForeignKeys(meta, true);
268:            }
269:
270:            protected String mapForeignKeys(SRecordMeta meta, boolean foreignKey) {
271:                StringBuffer fkey = new StringBuffer("");
272:                for (int fx = 0; fx < meta.sFieldMetas.size(); fx++) {
273:                    SFieldMeta fld = (SFieldMeta) meta.sFieldMetas.get(fx);
274:                    //SLog.slog.debug("fld " + fld);
275:                    if (fld instanceof  SFieldReference) {
276:
277:                        SFieldReference fldRef = (SFieldReference) fld;
278:                        //SLog.slog.debug("  fldRef " + fldRef);
279:
280:                        if (!fldRef.getBoolean(SNO_FOREIGN_KEY)
281:                                && (fld.sFieldReference == null || fld
282:                                        .getBoolean(SINNER_FOREIGN_KEY))) {
283:
284:                            // Obtain foreign and referenced fields.
285:                            StringBuffer sbFkey = new StringBuffer(40);
286:                            StringBuffer sbRefed = new StringBuffer(40);
287:
288:                            // Recur through identifying foreign keys
289:                            aReferenceSQL(fldRef, fldRef, sbFkey, sbRefed);
290:
291:                            if (foreignKey)
292:                                makeForeignKeySQL(meta, fx, fldRef, sbFkey,
293:                                        sbRefed, fkey);
294:                            else
295:                                makeForeignKeyIndexSQL(meta, fx, fldRef,
296:                                        sbFkey, sbRefed, fkey);
297:                        }
298:                    }
299:                }
300:                return fkey.toString();
301:            }
302:
303:            private void makeForeignKeySQL(SRecordMeta meta, int fx,
304:                    SFieldReference fldRef, StringBuffer sbFkey,
305:                    StringBuffer sbRefed, StringBuffer fkey) {
306:                /// The FOREIGN KEY clause and constraint name
307:                fkey.append(",\n    CONSTRAINT ");
308:                String tname = meta.getString(STABLE_NAME);
309:                String fxStr = fx + "";
310:                if (tname.length() > maxIdentNameLength() - fxStr.length())
311:                    throw new SException.Error("Table name '" + tname
312:                            + "' is longer than " + maxIdentNameLength()
313:                            + " - " + (fxStr.length() + 1)
314:                            + " chars as permitted by " + driverName()
315:                            + " to allow for _nn constraint name.");
316:
317:                String fkeyName = tname + "_" + fxStr; // Ensure unique
318:                String fname = fldRef.getString(SFIELD_NAME);
319:                int spare = maxIdentNameLength() - tname.length()
320:                        - (fxStr.length() + 1);
321:                if (spare > 1)
322:                    fkeyName += "_"; // >1 so no trailing _
323:                if (spare > fname.length())
324:                    fkeyName += fname;
325:                else if (spare > 0)
326:                    fkeyName += fname.substring(0, spare - 1);
327:                fkey.append(quoteConstraint(fkeyName));
328:
329:                fkey.append("\n      FOREIGN KEY (");
330:
331:                fkey.append(sbFkey.toString()); // toString required for 1.3.1
332:                fkey.append(")\n      REFERENCES ");
333:
334:                fkey.append(quoteTable(fldRef.referencedRecord
335:                        .getString(STABLE_NAME)));
336:                fkey.append(" (");
337:                fkey.append(sbRefed.toString());
338:                fkey.append(")");
339:
340:                String xddl = fldRef.getString(SEXTRA_FKEY_DDL);
341:                if (xddl != null)
342:                    fkey.append(xddl);
343:            }
344:
345:            /*
346:             * For MySQL.  Index needs to be created as part of Create Index statement.
347:             */
348:            protected void makeForeignKeyIndexSQL(SRecordMeta meta, int fx,
349:                    SFieldReference fldRef, StringBuffer sbFkey,
350:                    StringBuffer sbRefed, StringBuffer fkey) {
351:            }
352:
353:            protected void aReferenceSQL(SFieldReference topRef,
354:                    SFieldReference fldRef, StringBuffer sbFkey,
355:                    StringBuffer sbRefed) {
356:                for (int ff = 0; ff < fldRef.foreignKeyFields.length; ff++) {
357:                    SFieldMeta ref = fldRef.foreignKeyFields[ff];
358:                    //SLog.slog.debug("    top ref " + topRef + " fldRef " + fldRef + " ref " +  ref);
359:                    if (!(ref instanceof  SFieldReference)) {
360:                        if (sbFkey.length() > 0) {
361:                            sbFkey.append(", ");
362:                            sbRefed.append(", ");
363:                        }
364:                        sbFkey.append(quoteColumn(ref.getString(SCOLUMN_NAME)));
365:
366:                        SFieldMeta refed = findRecordRefedField(
367:                                topRef.referencedRecord, ref);
368:                        //SLog.slog.debug("Refed(" + fldRef + fldRef.referencedRecord + ref + "): " + refed);
369:                        sbRefed.append(quoteColumn(refed
370:                                .getString(SCOLUMN_NAME)));
371:                    } else {
372:                        aReferenceSQL(topRef, (SFieldReference) ref, sbFkey,
373:                                sbRefed);
374:                    }
375:                }
376:
377:            }
378:
379:            private SFieldMeta findRecordRefedField(SRecordMeta rec,
380:                    SFieldMeta ref) {
381:                for (;;) {
382:                    SFieldMeta ref2 = ref.referencedKeyField;
383:                    // ## May be a funny case with deep recursive keys.
384:                    if (ref2 == null)
385:                        throw new SException.InternalError("Record " + ref
386:                                + " has null referencedKeyField.");
387:                    ref = ref2;
388:                    if (ref.sRecordMeta == rec)
389:                        return ref;
390:                }
391:            }
392:
393:            /** Any other text to be added before the final ")"*/
394:            protected String postTablePreParenSQL(SRecordMeta meta) {
395:                return "";
396:            }
397:
398:            /** Any other text to be added after the final ")".  No ";". */
399:            protected String postTablePostParenSQL(SRecordMeta meta) {
400:                return "";
401:            }
402:
403:            /** Returns the SQL statement for a SELECT in a structured way.
404:             Used by findOrInsert.  <code>select</code> and
405:             <code>where</code> are arrays of <code>SFieldMeta</code>s.  Returns
406:             SQL statement as a string.<p>
407:             
408:             This now quotes table and column names so that they become
409:             case independent.<p>
410:             
411:             sps is links to the SPreparedStatement object.  It can have arbitrary properties set to
412:             provide fine control over the query.  Examples include limits.
413:             */
414:            protected String selectSQL(SFieldMeta[] select, SRecordMeta from,
415:                    SFieldMeta[] where, String orderBy, boolean forUpdate,
416:                    boolean unrepeatableRead, SPreparedStatement sps) {
417:
418:                StringBuffer ret = new StringBuffer(100);
419:                for (int wx = 0; wx < where.length; wx++) {
420:                    if (wx > 0)
421:                        ret.append(" AND ");
422:                    SFieldMeta wfld = (SFieldMeta) where[wx];
423:                    ret.append(quoteColumn(wfld.getString(SCOLUMN_NAME))
424:                            + " = ? ");
425:                }
426:                return selectSQL(select, from, ret.toString(), orderBy,
427:                        forUpdate, unrepeatableRead, sps);
428:            }
429:
430:            protected String selectSQL(SFieldMeta[] select, SRecordMeta from,
431:                    String where, String orderBy, boolean forUpdate,
432:                    boolean unrepeatableRead, SPreparedStatement sps) {
433:
434:                SRecordMeta[] joinTables = sps == null ? null : sps
435:                        .getJoinTables();
436:                boolean distinct = sps == null ? false : sps.getDistinct();
437:
438:                StringBuffer ret = new StringBuffer(200);
439:                ret.append("SELECT ");
440:
441:                // If joining, query may return multiple rows for same record. These need to
442:                // be weeded out.
443:                // ### AJB I do not see how this can happen.  I think this is noise and should
444:                // be removed.
445:                if (distinct) {
446:                    ret.append("DISTINCT ");
447:                }
448:
449:                String tableName = from.getString(STABLE_NAME);
450:                boolean doJoin = joinTables != null && joinTables.length > 0;
451:
452:                for (int sx = 0; sx < select.length; sx++) {
453:                    //System.out.println("selectSQL " + from + select[sx]);
454:                    if (sx > 0)
455:                        ret.append(", ");
456:                    SFieldMeta sfld = (SFieldMeta) select[sx];
457:                    String colName = sfld.getString(SCOLUMN_QUERY);
458:                    if (colName == null) {
459:                        colName = sfld.getString(SCOLUMN_NAME);
460:                        if (colName == null)
461:                            throw new SException.InternalError(
462:                                    "Null Column Name " + sfld);
463:                        colName = quoteColumn(colName);
464:                    }
465:                    if (doJoin) {
466:                        ret.append(quoteTable(tableName));
467:                        ret.append('.');
468:                    }
469:
470:                    ret.append(colName);
471:                }
472:
473:                ret.append(fromSQL(from, joinTables));
474:                ret.append(postFromSQL(forUpdate, unrepeatableRead));
475:
476:                if (where != null)
477:                    ret.append(" WHERE " + where);
478:                if (orderBy != null)
479:                    ret.append(" ORDER BY " + orderBy);
480:                ret.append(forUpdateSQL(forUpdate));
481:                return ret.toString();
482:            }
483:
484:            /** Returns update clause, may not be valid in certain lock modes etc. 
485:             Right at the end of the query. <p>
486:             
487:             Oracle, Postgresql, and new in MS SQL 2005 support data versioning
488:             or snapshots.  This means that repeatable read is achieved by
489:             caching the previous value read instead of using read locks.  This
490:             approach makes it critical to add FOR UPDATE where appropriate or
491:             there is effectively no locking. <p>
492:             
493:             Indeed, in Oracle, you are guaranteed that several SELECTS will
494:             return the same value, but a subsequent SELECT FOR UPDATE in the
495:             same transaction may return a different value.<p>
496:             
497:             */
498:            protected String forUpdateSQL(boolean forUpdate) {
499:                if (supportsLocking() && forUpdate)
500:                    return " FOR UPDATE";
501:                else
502:                    return "";
503:            }
504:
505:            /** Returns the FROM Table, Table... clause */
506:            protected String fromSQL(SRecordMeta from, SRecordMeta[] joinTables) {
507:                String tableName = from.getString(STABLE_NAME);
508:                String res = " FROM " + quoteTable(tableName) + " ";
509:                // Do not add + tableName.charAt(0) + " ";
510:                // Breaks Oracle(!) -- which then insists that ONLY the alias be used.
511:
512:                if (joinTables != null) {
513:                    for (int ii = 0; ii < joinTables.length; ii++)
514:                        res = res
515:                                + ", "
516:                                + quoteTable(joinTables[ii]
517:                                        .getString(STABLE_NAME));
518:                }
519:                return res;
520:            }
521:
522:            /** For MSSQL. Just after all the tables in the From clause.*/
523:            protected String postFromSQL(boolean forUpdate,
524:                    boolean unrepeatableRead) {
525:                return "";
526:            }
527:
528:            /** Returns the SQL statement for an UPDATE in a structured way.
529:             Used by flush().  <code>updates</code> and
530:             <code>where</code> are SArrayLists of SFieldMetas.  Returns SQL
531:             statement as a string. */
532:            protected String updateSQL(SArrayList updates, SRecordMeta from,
533:                    SArrayList where, SRecordInstance instance,
534:                    Object[] keyMetaValues) {
535:                StringBuffer ret = new StringBuffer(200);
536:                ret.append("UPDATE ");
537:                ret.append(quoteTable(from.getString(STABLE_NAME)) + " SET ");
538:                for (int sx = 0; sx < updates.size(); sx++) {
539:                    if (sx > 0)
540:                        ret.append(", ");
541:                    SFieldMeta sfld = (SFieldMeta) updates.get(sx);
542:                    ret.append(quoteTable(sfld.getString(SCOLUMN_NAME))
543:                            + " = ?");
544:                }
545:                whereSQL(ret, where, instance, keyMetaValues);
546:                return ret.toString();
547:            }
548:
549:            /** Returns the SQL statement for an INSERT in a structured way.
550:             Used by flush().  <code>updates</code> and
551:             <code>where</code> are SArrayLists of SFieldMetas.  Returns SQL
552:             statement as a string. */
553:            protected String insertSQL(SArrayList updates, SRecordMeta from) {
554:                StringBuffer ret = new StringBuffer(200);
555:                ret.append("INSERT INTO ");
556:                ret.append(quoteTable(from.getString(STABLE_NAME)) + " (");
557:                for (int sx = 0; sx < updates.size(); sx++) {
558:                    if (sx > 0)
559:                        ret.append(", ");
560:                    SFieldMeta sfld = (SFieldMeta) updates.get(sx);
561:                    ret.append(quoteColumn(sfld.getString(SCOLUMN_NAME)));
562:                }
563:                ret.append(") VALUES (");
564:                for (int sx = 0; sx < updates.size(); sx++) {
565:                    if (sx > 0)
566:                        ret.append(", ");
567:                    ret.append("?");
568:                }
569:                ret.append(")");
570:                return ret.toString();
571:            }
572:
573:            /** Returns the SQL statement for an DELETE in a structured way.
574:             Used by flush(). <code>where</code> are SArrayLists of
575:             SFieldMetas.  Returns SQL statement as a string. */
576:            protected String deleteSQL(SRecordMeta from, SArrayList where,
577:                    SRecordInstance instance, Object[] keyMetaValues) {
578:                StringBuffer ret = new StringBuffer(200);
579:                ret.append("DELETE FROM ");
580:                ret.append(quoteTable(from.getString(STABLE_NAME)));
581:                whereSQL(ret, where, instance, keyMetaValues);
582:                return ret.toString();
583:            }
584:
585:            /** Produces the WHERE clause of UPDATE and DELETE statements.
586:             Needs to know the instance values so that it can use the IS NULL
587:             test (for optimisitic locking).*/
588:            protected void whereSQL(StringBuffer ret, SArrayList where,
589:                    SRecordInstance instance, Object[] keyMetaValues) {
590:                ret.append(" WHERE ");
591:                for (int wx = 0; wx < where.size(); wx++) {
592:                    if (wx > 0)
593:                        ret.append(" AND ");
594:                    SFieldMeta wfld = (SFieldMeta) where.get(wx);
595:                    Object value = keyMetaValues[wx];
596:                    // not instance.fieldValues[wfld.fieldIndex]; for optimistic locking
597:                    if (value != null)
598:                        ret.append(quoteColumn(wfld.getString(SCOLUMN_NAME))
599:                                + " = ? ");
600:                    else
601:                        ret.append(quoteColumn(wfld.getString(SCOLUMN_NAME))
602:                                + " IS NULL ");
603:                }
604:            }
605:
606:            /** Called just after executeQuery to Skip over records until offset
607:             * has been reached.  Drivers may optimize this in various ways,
608:             * eg. LIMIT keywords where supported by the database, or by using
609:             * the JDBC  */
610:            protected void initResultSet(SResultSet srs) {
611:                ResultSet rs = srs.getDBResultSet();
612:                SPreparedStatement sps = srs.getSPreparedStatement();
613:                try {
614:                    //if (sps.getLimit() > 0) 
615:                    //rs.setMaxRows((int)sps.getLimit()); only on statement.
616:
617:                    if (canResultSetAbsolute() && sps.getOffset() != 0) {
618:                        rs.setFetchDirection(ResultSet.FETCH_UNKNOWN);
619:                        rs.absolute((int) sps.getOffset()); // 1 is first row, 0 is before first row.
620:                    } else {
621:                        while (srs.getNrRetrieved() < sps.getOffset()
622:                                && srs.hasNext())
623:                            srs.getRecord();
624:                        srs.nrRetrieved = 0;
625:                    }
626:                } catch (SQLException ex) {
627:                    throw new SException.JDBC(ex);
628:                }
629:            }
630:
631:            /** true if resultset.absolure(nn) works properly to implement limit/offset. */
632:            protected boolean canResultSetAbsolute() {
633:                return false;
634:            }
635:
636:            /** Generates a new key using SELECT MAX+1.  This will (hopefully)
637:             be specialized for each database driver to be correct. Note that
638:             there is a global counter kept so it will actually work OK if
639:             all the updates are from one JVM.  Amazing that there is still
640:             no standard way to do this in SQL.<p>
641:             
642:             ## (There is scope to optimize this at some point so that one JDBC
643:             call can both generate the sequence number and insert a new
644:             record.  But that means that the new record's key is not
645:             available until insert time which causes problems for foreign
646:             keys.  Alternatively one can get batches of 10 (say) sequences
647:             at a time and then use an internal counter, but this will leave
648:             big holes in the sequence.  Defer this to version 1.)
649:             */
650:            protected long generateKeySelectMax(SRecordMeta rec,
651:                    SFieldMeta keyFld) {
652:                String columnName = keyFld.getString(SCOLUMN_NAME);
653:                String tableName = rec.getString(STABLE_NAME);
654:                String qry = "SELECT MAX(" + quoteColumn(columnName)
655:                        + ") FROM " + quoteColumn(tableName);
656:
657:                Object next = SConnection.rawQueryJDBC(qry);
658:
659:                long db = next == null ? 1 : SJSharp.object2Long(next) + 1;
660:                return keyFld.nextGeneratedValue(db);
661:            }
662:
663:            protected long generateKeySequence(SRecordMeta rec,
664:                    SFieldMeta keyFld) {
665:                throw new SException.Error(
666:                        "Database does not support SEQUENCES");
667:            }
668:
669:            public boolean supportsKeySequences() {
670:                return false;
671:            }
672:
673:            protected String createSequenceDDL(String name) {
674:                throw new SException.Error("Sequences not supported");
675:            }
676:
677:            protected String dropSequenceDDL(String name) {
678:                throw new SException.Error("Sequences not supported");
679:            }
680:
681:            /** Utility routine for dropping tables called by SConnection.
682:             Driver specific versions should only hide table non existant
683:             errors (and not warn about them).*/
684:            public void dropTableNoError(String table) {
685:                Connection con = SConnection.getBegunDBConnection();
686:                try {
687:                    PreparedStatement ps = con.prepareStatement("DROP TABLE "
688:                            + table);
689:                    ps.executeUpdate();
690:                } catch (SQLException ex) {
691:                    SLog.slog.warn("DROPPING " + table + ": " + ex);
692:                }
693:            }
694:
695:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.