Source Code Cross Referenced for DatabaseAdapter.java in  » Database-ORM » TJDO » com » triactive » jdo » store » 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 » TJDO » com.triactive.jdo.store 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Copyright 2004 (C) TJDO.
0003:         * All rights reserved.
0004:         *
0005:         * This software is distributed under the terms of the TJDO License version 1.0.
0006:         * See the terms of the TJDO License in the documentation provided with this software.
0007:         *
0008:         * $Id: DatabaseAdapter.java,v 1.30 2004/03/30 06:12:14 jackknifebarber Exp $
0009:         */
0010:
0011:        package com.triactive.jdo.store;
0012:
0013:        import com.triactive.jdo.model.FieldMetaData;
0014:        import java.lang.reflect.InvocationTargetException;
0015:        import java.sql.Connection;
0016:        import java.sql.DatabaseMetaData;
0017:        import java.sql.ResultSet;
0018:        import java.sql.SQLException;
0019:        import java.util.ArrayList;
0020:        import java.util.HashMap;
0021:        import java.util.HashSet;
0022:        import java.util.Set;
0023:        import java.util.StringTokenizer;
0024:        import javax.jdo.JDODataStoreException;
0025:        import javax.jdo.JDOException;
0026:        import javax.jdo.JDOFatalDataStoreException;
0027:        import javax.jdo.JDOFatalInternalException;
0028:        import javax.jdo.JDOUnsupportedOptionException;
0029:        import javax.jdo.JDOUserException;
0030:        import javax.sql.DataSource;
0031:        import org.apache.log4j.Category;
0032:
0033:        /**
0034:         * Provides methods for adapting SQL language elements to a specific vendor's
0035:         * database.  A database adapter is primarily used to map generic JDBC data
0036:         * types and SQL identifiers to specific types/identifiers suitable for the
0037:         * database in use.
0038:         *
0039:         * <p>Each database adapter corresponds to a particular combination of database,
0040:         * database version, driver, and driver version, as provided by the driver's
0041:         * own metadata.  Database adapters cannot be constructed directly, but must be
0042:         * obtained using the {@link #getInstance} method.
0043:         *
0044:         * @author <a href="mailto:mmartin5@austin.rr.com">Mike Martin</a>
0045:         * @author <a href="mailto:cwalk@triactive.com">Christopher Walk</a>
0046:         * @version $Revision: 1.30 $
0047:         *
0048:         * @see java.sql.DatabaseMetaData
0049:         */
0050:        public class DatabaseAdapter {
0051:            private static final Category LOG = Category
0052:                    .getInstance(DatabaseAdapter.class);
0053:
0054:            /** The name of the underlying database. */
0055:            protected String databaseProductName;
0056:
0057:            /** The version number of the underlying database as a string. */
0058:            protected String databaseProductVersion;
0059:
0060:            /** The major version number of the underlying database. */
0061:            protected int databaseMajorVersion;
0062:
0063:            /** The minor version number of the underlying database. */
0064:            protected int databaseMinorVersion;
0065:
0066:            /** The maximum length to be used for a table name. */
0067:            protected int maxTableNameLength;
0068:
0069:            /** The maximum length to be used for a table constraint name. */
0070:            protected int maxConstraintNameLength;
0071:
0072:            /** The maximum length to be used for an index name. */
0073:            protected int maxIndexNameLength;
0074:
0075:            /** The maximum length to be used for a column name. */
0076:            protected int maxColumnNameLength;
0077:
0078:            /** <tt>true</tt> if the database stores all identifiers in lower-case. */
0079:            protected boolean storesLowerCaseIdentifiers;
0080:
0081:            /** <tt>true</tt> if the database stores all identifiers in upper-case. */
0082:            protected boolean storesUpperCaseIdentifiers;
0083:
0084:            /** The String used to quote SQL identifiers. */
0085:            protected String identifierQuoteString;
0086:
0087:            /** The set of SQL keywords for this DBMS, in upper-case. */
0088:            protected final HashSet keywords = new HashSet();
0089:
0090:            protected final HashMap typesByTypeNumber = new HashMap();
0091:            protected final HashMap typeMappings = new HashMap();
0092:
0093:            /**
0094:             * The cache of constructed database adapter objects.
0095:             */
0096:            private static HashMap adaptersByID = new HashMap();
0097:
0098:            /**
0099:             * Returns a <tt>DatabaseAdapter</tt> object appropriate for the database
0100:             * currently underlying the given {@link Connection}.  Multiple calls to
0101:             * this method with connections having the same database/driver/version will
0102:             * return the same <tt>DatabaseAdapter</tt> object.
0103:             *
0104:             * @param conn  An open database connection.
0105:             *
0106:             * @return  a database adapter object for the database underlying the
0107:             *          given connection.
0108:             *
0109:             * @exception SQLException
0110:             *      If a database error occurs.
0111:             */
0112:            public static synchronized DatabaseAdapter getInstance(
0113:                    Connection conn) throws SQLException {
0114:                DatabaseMetaData metadata = conn.getMetaData();
0115:
0116:                String id = metadata.getDatabaseProductName() + ", "
0117:                        + metadata.getDatabaseProductVersion() + ", "
0118:                        + metadata.getDriverName() + ", "
0119:                        + metadata.getDriverVersion();
0120:
0121:                DatabaseAdapter adapter = (DatabaseAdapter) adaptersByID
0122:                        .get(id);
0123:
0124:                if (adapter == null) {
0125:                    if (isCloudscape(metadata))
0126:                        adapter = new CloudscapeAdapter(metadata);
0127:                    else if (isDB2J(metadata))
0128:                        adapter = new DB2JAdapter(metadata);
0129:                    else if (isDB2(metadata))
0130:                        adapter = new DB2Adapter(metadata);
0131:                    else if (isFirebird(metadata))
0132:                        adapter = new FirebirdAdapter(metadata);
0133:                    else if (isHSQLDB(metadata))
0134:                        adapter = new HSQLDBAdapter(metadata);
0135:                    else if (isMSSQLServer(metadata))
0136:                        adapter = new MSSQLServerAdapter(metadata);
0137:                    else if (isMySQL(metadata))
0138:                        adapter = new MySQLAdapter(metadata);
0139:                    else if (isOracle(metadata)) {
0140:                        /* NOTE: Reflection is used here as a temporary means of
0141:                         *       eliminating the dependency between this class
0142:                         *       and the OracleAdapter.  OracleAdapater indirectly
0143:                         *       utilizes classes that are shipped with the
0144:                         *       Oracle drivers which may not be available to
0145:                         *       all users.
0146:                         */
0147:                        try {
0148:                            Class classDefinition = Class
0149:                                    .forName("com.triactive.jdo.store.OracleAdapter");
0150:
0151:                            adapter = (DatabaseAdapter) newInstance(
0152:                                    classDefinition,
0153:                                    new Class[] { DatabaseMetaData.class },
0154:                                    new Object[] { metadata });
0155:                        } catch (Exception e) {
0156:                            throw new JDODataStoreException(
0157:                                    "The Oracle adapter was not found.", e);
0158:                        }
0159:                    } else if (isPointBase(metadata))
0160:                        adapter = new PointBaseAdapter(metadata);
0161:                    else if (isPostgreSQL(metadata))
0162:                        adapter = new PostgreSQLAdapter(metadata);
0163:                    else if (isSAPDB(metadata))
0164:                        adapter = new SAPDBAdapter(metadata);
0165:                    else
0166:                        adapter = new DatabaseAdapter(metadata);
0167:
0168:                    adaptersByID.put(id, adapter);
0169:
0170:                    LOG.info("Adapter initialized: " + adapter);
0171:                }
0172:
0173:                return adapter;
0174:            }
0175:
0176:            private static boolean productNameContains(
0177:                    DatabaseMetaData metadata, String name) {
0178:                try {
0179:                    return metadata.getDatabaseProductName().toLowerCase()
0180:                            .indexOf(name.toLowerCase()) >= 0;
0181:                } catch (SQLException e) {
0182:                    throw new JDODataStoreException(
0183:                            "Error accessing database metadata", e);
0184:                }
0185:            }
0186:
0187:            private static boolean isCloudscape(DatabaseMetaData metadata) {
0188:                return productNameContains(metadata, "cloudscape");
0189:            }
0190:
0191:            private static boolean isDB2(DatabaseMetaData metadata) {
0192:                return productNameContains(metadata, "db2")
0193:                        && !productNameContains(metadata, "db2j");
0194:            }
0195:
0196:            private static boolean isDB2J(DatabaseMetaData metadata) {
0197:                return productNameContains(metadata, "db2j");
0198:            }
0199:
0200:            private static boolean isFirebird(DatabaseMetaData metadata) {
0201:                return productNameContains(metadata, "firebird");
0202:            }
0203:
0204:            private static boolean isHSQLDB(DatabaseMetaData metadata) {
0205:                return productNameContains(metadata, "hsql database engine")
0206:                        || productNameContains(metadata, "hsqldb");
0207:            }
0208:
0209:            private static boolean isMSSQLServer(DatabaseMetaData metadata) {
0210:                return productNameContains(metadata, "sql server");
0211:            }
0212:
0213:            private static boolean isMySQL(DatabaseMetaData metadata) {
0214:                return productNameContains(metadata, "mysql");
0215:            }
0216:
0217:            private static boolean isOracle(DatabaseMetaData metadata) {
0218:                return productNameContains(metadata, "oracle");
0219:            }
0220:
0221:            private static boolean isPointBase(DatabaseMetaData metadata) {
0222:                return productNameContains(metadata, "pointbase");
0223:            }
0224:
0225:            private static boolean isPostgreSQL(DatabaseMetaData metadata) {
0226:                return productNameContains(metadata, "postgresql");
0227:            }
0228:
0229:            private static boolean isSAPDB(DatabaseMetaData metadata) {
0230:                return productNameContains(metadata, "sapdb")
0231:                        || productNameContains(metadata, "sap db");
0232:            }
0233:
0234:            /**
0235:             * Constructs a database adapter based on the given JDBC metadata.
0236:             *
0237:             * @param   metadata    the database metadata.
0238:             */
0239:
0240:            protected DatabaseAdapter(DatabaseMetaData metadata) {
0241:                keywords
0242:                        .addAll(parseKeywordList(SQL92Constants.RESERVED_WORDS));
0243:                keywords
0244:                        .addAll(parseKeywordList(SQL92Constants.NONRESERVED_WORDS));
0245:
0246:                try {
0247:                    keywords
0248:                            .addAll(parseKeywordList(metadata.getSQLKeywords()));
0249:
0250:                    databaseProductName = metadata.getDatabaseProductName();
0251:                    databaseProductVersion = metadata
0252:                            .getDatabaseProductVersion();
0253:
0254:                    try {
0255:                        Class mdc = metadata.getClass();
0256:
0257:                        databaseMajorVersion = ((Integer) mdc.getMethod(
0258:                                "getDatabaseMajorVersion", null).invoke(
0259:                                metadata, null)).intValue();
0260:                        databaseMinorVersion = ((Integer) mdc.getMethod(
0261:                                "getDatabaseMinorVersion", null).invoke(
0262:                                metadata, null)).intValue();
0263:                    } catch (Throwable t) {
0264:                        /*
0265:                         * The driver doesn't support JDBC 3.  Try to parse major and
0266:                         * minor version numbers out of the product version string.
0267:                         * We do this by stripping out everything but digits and periods
0268:                         * and hoping we get something that looks like <major>.<minor>.
0269:                         */
0270:                        StringBuffer stripped = new StringBuffer();
0271:
0272:                        for (int i = 0; i < databaseProductVersion.length(); ++i) {
0273:                            char c = databaseProductVersion.charAt(i);
0274:
0275:                            if (Character.isDigit(c) || c == '.')
0276:                                stripped.append(c);
0277:                        }
0278:
0279:                        StringTokenizer parts = new StringTokenizer(stripped
0280:                                .toString(), ".");
0281:
0282:                        if (parts.hasMoreTokens())
0283:                            databaseMajorVersion = Integer.parseInt(parts
0284:                                    .nextToken());
0285:                        if (parts.hasMoreTokens())
0286:                            databaseMinorVersion = Integer.parseInt(parts
0287:                                    .nextToken());
0288:                    }
0289:
0290:                    maxTableNameLength = metadata.getMaxTableNameLength();
0291:                    /*
0292:                     * The metadata object may return 0 for getMaxTableNameLength to
0293:                     * indicate that there is no table name length.  If that is the case,
0294:                     * we will use SQL92Constants.MAX_IDENTIFIER_LENGTH for the
0295:                     * maximum length.
0296:                     */
0297:                    if (maxTableNameLength == 0) {
0298:                        maxTableNameLength = SQL92Constants.MAX_IDENTIFIER_LENGTH;
0299:                    }
0300:
0301:                    // use maxTableNameLength for maxConstraintNameLength and maxIndexNameLength
0302:                    maxConstraintNameLength = maxTableNameLength;
0303:                    maxIndexNameLength = maxTableNameLength;
0304:
0305:                    maxColumnNameLength = metadata.getMaxColumnNameLength();
0306:                    /*
0307:                     * The metadata object may return 0 for getMaxColumnNameLength to
0308:                     * indicate that there is no column name length.  If that is the case,
0309:                     * we will use SQL92Constants.MAX_IDENTIFIER_LENGTH for the
0310:                     * maximum length.
0311:                     */
0312:                    if (maxColumnNameLength == 0) {
0313:                        maxColumnNameLength = SQL92Constants.MAX_IDENTIFIER_LENGTH;
0314:                    }
0315:
0316:                    storesLowerCaseIdentifiers = metadata
0317:                            .storesLowerCaseIdentifiers();
0318:                    storesUpperCaseIdentifiers = metadata
0319:                            .storesUpperCaseIdentifiers();
0320:                    identifierQuoteString = metadata.getIdentifierQuoteString();
0321:
0322:                    /*
0323:                     * If this is null or an empty String, default to double-quote.
0324:                     */
0325:                    identifierQuoteString = ((null == identifierQuoteString) || (identifierQuoteString
0326:                            .trim().length() < 1)) ? "\""
0327:                            : identifierQuoteString;
0328:
0329:                    /*
0330:                     * Create TypeInfo objects for all of the data types and index them
0331:                     * in a HashMap by their JDBC type number.
0332:                     */
0333:                    createTypeInfo(metadata);
0334:
0335:                } catch (SQLException e) {
0336:                    throw new JDODataStoreException(
0337:                            "Error accessing database metadata", e);
0338:                }
0339:
0340:                typeMappings.put(boolean.class, BooleanMapping.class);
0341:                typeMappings.put(byte.class, ByteMapping.class);
0342:                typeMappings.put(byte[].class, ByteArrayMapping.class);
0343:                typeMappings.put(char.class, CharacterMapping.class);
0344:                typeMappings.put(short.class, ShortMapping.class);
0345:                typeMappings.put(int.class, IntegerMapping.class);
0346:                typeMappings.put(long.class, LongMapping.class);
0347:                typeMappings.put(float.class, FloatMapping.class);
0348:                typeMappings.put(double.class, DoubleMapping.class);
0349:                typeMappings.put(Boolean.class, BooleanMapping.class);
0350:                typeMappings.put(Byte.class, ByteMapping.class);
0351:                typeMappings.put(Character.class, CharacterMapping.class);
0352:                typeMappings.put(Short.class, ShortMapping.class);
0353:                typeMappings.put(Integer.class, IntegerMapping.class);
0354:                typeMappings.put(Long.class, LongMapping.class);
0355:                typeMappings.put(Float.class, FloatMapping.class);
0356:                typeMappings.put(Double.class, DoubleMapping.class);
0357:                typeMappings.put(String.class, StringMapping.class);
0358:                typeMappings.put(java.math.BigDecimal.class,
0359:                        BigDecimalMapping.class);
0360:                typeMappings.put(java.math.BigInteger.class,
0361:                        BigIntegerMapping.class);
0362:                typeMappings.put(java.util.Date.class, DateMapping.class);
0363:                typeMappings.put(java.util.Locale.class,
0364:                        UnsupportedMapping.class);
0365:                typeMappings.put(java.sql.Date.class, SqlDateMapping.class);
0366:                typeMappings.put(java.sql.Timestamp.class,
0367:                        SqlTimestampMapping.class);
0368:                typeMappings.put(OID.class, OIDMapping.class);
0369:
0370:                typeMappings.put(java.util.ArrayList.class,
0371:                        UnsupportedMapping.class);
0372:                typeMappings.put(java.util.Collection.class, SetMapping.class);
0373:                typeMappings.put(java.util.HashMap.class, MapMapping.class);
0374:                typeMappings.put(java.util.HashSet.class, SetMapping.class);
0375:                typeMappings.put(java.util.Hashtable.class, MapMapping.class);
0376:                typeMappings.put(java.util.LinkedList.class,
0377:                        UnsupportedMapping.class);
0378:                typeMappings
0379:                        .put(java.util.List.class, UnsupportedMapping.class);
0380:                typeMappings.put(java.util.Map.class, MapMapping.class);
0381:                typeMappings.put(java.util.Set.class, SetMapping.class);
0382:                typeMappings.put(java.util.TreeMap.class, MapMapping.class);
0383:                typeMappings.put(java.util.TreeSet.class, SetMapping.class);
0384:                typeMappings.put(java.util.Vector.class,
0385:                        UnsupportedMapping.class);
0386:            }
0387:
0388:            public String getVendorID() {
0389:                return null;
0390:            }
0391:
0392:            public int getMaxTableNameLength() {
0393:                return maxTableNameLength;
0394:            }
0395:
0396:            public int getMaxConstraintNameLength() {
0397:                return maxConstraintNameLength;
0398:            }
0399:
0400:            public int getMaxIndexNameLength() {
0401:                return maxIndexNameLength;
0402:            }
0403:
0404:            public int getMaxColumnNameLength() {
0405:                return maxColumnNameLength;
0406:            }
0407:
0408:            public boolean storesLowerCaseIdentifiers() {
0409:                return storesLowerCaseIdentifiers;
0410:            }
0411:
0412:            public boolean storesUpperCaseIdentifiers() {
0413:                return storesUpperCaseIdentifiers;
0414:            }
0415:
0416:            /**
0417:             * Returns a SQLState object for the specified SQLException, if one is
0418:             * present and valid.
0419:             *
0420:             * @param se
0421:             *      A caught SQL exception.
0422:             *
0423:             * @return
0424:             *      A SQLState object, or <code>null</code> if <var>se</var> does not
0425:             *      contain a valid 5-character SQLSTATE.
0426:             */
0427:
0428:            public SQLState getSQLState(SQLException se) {
0429:                String state = se.getSQLState();
0430:
0431:                if (state == null)
0432:                    return null;
0433:
0434:                try {
0435:                    return new SQLState(state);
0436:                } catch (IllegalArgumentException e) {
0437:                    return null;
0438:                }
0439:            }
0440:
0441:            /**
0442:             * Create the appropriate <code>JDODataStoreException</code> or
0443:             * <code>JDOFatalDataStoreException</code> for the given
0444:             * <code>SQLException</code> based on whether the action causing
0445:             * the exception has aborted the current transaction.
0446:             * <p>
0447:             * For historical reasons, the design of this method is flawed.
0448:             * To conform correctly to the spec, if a JDOFatalDataStoreException is
0449:             * returned then there should be some coordination with the appropriate
0450:             * PersistenceManager and its Transaction to allow them to reflect the fact
0451:             * that a transaction is no longer active.
0452:             * At the least, that means that this method would have to be passed a
0453:             * reference to a PersistenceManager.
0454:             * <p>
0455:             * An outstanding question remains how we can reliably determine via JDBC
0456:             * whether or not a failed statement has aborted the current database
0457:             * transaction.
0458:             * Bottom line, this area is ripe for refactoring.
0459:             * <p>
0460:             * The current implementation in this class always returns a new
0461:             * JDODataStoreException and never a JDOFatalDataStoreException.
0462:             *
0463:             * @param message  The message to include in the JDODataStoreException.
0464:             * @param e        The SQLException to create a JDODataStoreException for.
0465:             *
0466:             * @return  A <code>JDODataStoreException</code> or
0467:             *          <code>JDOFatalDataStoreException</code> that wraps the
0468:             *          given <code>SQLException</code>.  A fatal exception is used
0469:             *          to indicate that the active transaction has been aborted.
0470:             */
0471:
0472:            public JDOException newDataStoreException(String message,
0473:                    SQLException e) {
0474:                return new JDODataStoreException(message, e);
0475:            }
0476:
0477:            /**
0478:             * Creates TypeInfo objects for all of the data types and indexes them
0479:             * in the typesByTypeNumber map by their JDBC data type number.
0480:             */
0481:
0482:            protected void createTypeInfo(DatabaseMetaData metadata)
0483:                    throws SQLException {
0484:                ResultSet rs = metadata.getTypeInfo();
0485:
0486:                try {
0487:                    while (rs.next()) {
0488:                        TypeInfo ti = newTypeInfo(rs);
0489:
0490:                        if (ti != null) {
0491:                            Integer key = new Integer(ti.dataType);
0492:
0493:                            if (typesByTypeNumber.get(key) == null)
0494:                                typesByTypeNumber.put(key, ti);
0495:                        }
0496:                    }
0497:                } finally {
0498:                    rs.close();
0499:                }
0500:            }
0501:
0502:            /**
0503:             * A factory for TypeInfo objects.  This method should always be used
0504:             * instead of directly constructing TypeInfo objects in order to give the
0505:             * DatabaseAdapter an opportunity to modify and/or correct the metadata
0506:             * obtained from the JDBC driver.
0507:             *
0508:             * The type information object is constructed from the current row of the
0509:             * given result set.  The {@link ResultSet} object passed must have been
0510:             * obtained from a call to DatabaseMetaData.getTypeInfo().
0511:             *
0512:             * <p>The constructor only retrieves the values from the current row; the
0513:             * caller is required to advance to the next row with {@link ResultSet#next}.
0514:             *
0515:             * @param rs    The result set returned from DatabaseMetaData.getTypeInfo().
0516:             *
0517:             * @return
0518:             *      A TypeInfo object constructed from the current result set row, or
0519:             *      <code>null</code> if the type indicated by this row should be
0520:             *      excluded from use.
0521:             */
0522:
0523:            protected TypeInfo newTypeInfo(ResultSet rs) {
0524:                return new TypeInfo(rs);
0525:            }
0526:
0527:            /**
0528:             * A factory for ColumnInfo objects.  This method should always be used
0529:             * instead of directly constructing ColumnInfo objects in order to give the
0530:             * DatabaseAdapter an opportunity to modify and/or correct the metadata
0531:             * obtained from the JDBC driver.
0532:             *
0533:             * The column information object is constructed from the current row of the
0534:             * given result set.  The {@link ResultSet} object passed must have been
0535:             * obtained from a call to DatabaseMetaData.getColumns().
0536:             *
0537:             * <p>The constructor only retrieves the values from the current row; the
0538:             * caller is required to advance to the next row with {@link ResultSet#next}.
0539:             *
0540:             * @param rs    The result set returned from DatabaseMetaData.getColumns().
0541:             */
0542:
0543:            public ColumnInfo newColumnInfo(ResultSet rs) {
0544:                return new ColumnInfo(rs);
0545:            }
0546:
0547:            /**
0548:             * A factory for ForeignKeyInfo objects.  This method should always be used
0549:             * instead of directly constructing ForeignKeyInfo objects in order to give
0550:             * the DatabaseAdapter an opportunity to modify and/or correct the metadata
0551:             * obtained from the JDBC driver.
0552:             *
0553:             * The column information object is constructed from the current row of the
0554:             * given result set.  The {@link ResultSet} object passed must have been
0555:             * obtained from a call to DatabaseMetaData.getImportedKeys() or
0556:             * DatabaseMetaData.getExportedKeys().
0557:             *
0558:             * <p>The constructor only retrieves the values from the current row; the
0559:             * caller is required to advance to the next row with {@link ResultSet#next}.
0560:             *
0561:             * @param rs The result set returned from DatabaseMetaData.get??portedKeys().
0562:             */
0563:
0564:            public ForeignKeyInfo newForeignKeyInfo(ResultSet rs) {
0565:                return new ForeignKeyInfo(rs);
0566:            }
0567:
0568:            protected Set parseKeywordList(String list) {
0569:                StringTokenizer tokens = new StringTokenizer(list, ",");
0570:                HashSet words = new HashSet();
0571:
0572:                while (tokens.hasMoreTokens())
0573:                    words.add(tokens.nextToken().trim().toUpperCase());
0574:
0575:                return words;
0576:            }
0577:
0578:            /**
0579:             * Tests if a given string is a SQL key word.
0580:             * <p>
0581:             * The list of key words tested against is defined to contain all SQL/92
0582:             * key words, plus any additional key words reported by the JDBC driver
0583:             * for this adapter via <code>DatabaseMetaData.getSQLKeywords()</code>.
0584:             * <p>
0585:             * In general, use of a SQL key word as an identifier should be avoided.
0586:             * SQL/92 key words are divided into reserved and non-reserved words.
0587:             * If a reserved word is used as an identifier it must be quoted with double
0588:             * quotes.
0589:             * Strictly speaking, the same is not true of non-reserved words.
0590:             * However, as C.J. Date writes in <u>A Guide To The SQL Standard</u>:
0591:             * <blockquote>
0592:             * The rule by which it is determined within the standard that one key word
0593:             * needs to be reserved while another need not is not clear to this writer.
0594:             * In practice, it is probably wise to treat all key words as reserved.
0595:             * </blockquote>
0596:             *
0597:             * @param word  The word to test.
0598:             *
0599:             * @return  <code>true</code> if <var>word</var> is a SQL key word for this
0600:             *          DBMS.  The comparison is case-insensitive.
0601:             *
0602:             * @see SQL92Constants
0603:             */
0604:
0605:            public boolean isSQLKeyword(String word) {
0606:                return keywords.contains(word.toUpperCase());
0607:            }
0608:
0609:            /**
0610:             * Returns type information for the database type that best implements the
0611:             * given JDBC type.
0612:             *
0613:             * @param   dataType    JDBC type number of the data type.
0614:             *
0615:             * @return  type information for the best matching type.
0616:             */
0617:
0618:            public TypeInfo getTypeInfo(int dataType)
0619:                    throws UnsupportedDataTypeException {
0620:                TypeInfo ti = (TypeInfo) typesByTypeNumber.get(new Integer(
0621:                        dataType));
0622:
0623:                if (ti == null)
0624:                    throw new UnsupportedDataTypeException("JDBC type = "
0625:                            + TypeInfo.getJDBCTypeName(dataType));
0626:
0627:                return ti;
0628:            }
0629:
0630:            /**
0631:             * Returns type information for the first one of the given candidate JDBC
0632:             * data types supported by this database.
0633:             *
0634:             * @param   candidateDataTypes
0635:             *              array of JDBC type numbers of the data types to be checked
0636:             *              in order of preference.
0637:             *
0638:             * @return  type information for the first supported type.
0639:             */
0640:
0641:            public TypeInfo getTypeInfo(int[] candidateDataTypes)
0642:                    throws UnsupportedDataTypeException {
0643:                for (int i = 0; i < candidateDataTypes.length; ++i) {
0644:                    TypeInfo ti = (TypeInfo) typesByTypeNumber.get(new Integer(
0645:                            candidateDataTypes[i]));
0646:
0647:                    if (ti != null)
0648:                        return ti;
0649:                }
0650:
0651:                throw new UnsupportedDataTypeException(
0652:                        "No JDBC types specified are supported");
0653:            }
0654:
0655:            /**
0656:             * Returns the precision value to be used when creating string columns of
0657:             * "unlimited" length.  Usually, if this value is needed it is provided in
0658:             * the database metadata ({@link TypeInfo#precision}).  However, for some
0659:             * types in some databases the value must be computed specially.
0660:             *
0661:             * @param typeInfo  the typeInfo object for which the precision value is
0662:             *                  needed.
0663:             *
0664:             * @return  the precision value to be used when creating the column, or -1
0665:             *          if no value should be used.
0666:             */
0667:
0668:            public int getUnlimitedLengthPrecisionValue(TypeInfo typeInfo) {
0669:                if (typeInfo.createParams != null
0670:                        && typeInfo.createParams.length() > 0)
0671:                    return typeInfo.precision;
0672:                else
0673:                    return -1;
0674:            }
0675:
0676:            public boolean isEmbeddedType(Class c) {
0677:                return !OIDMapping.class.isAssignableFrom(getMappingClass(c));
0678:            }
0679:
0680:            public Mapping getMapping(Class c) {
0681:                Class mc = getMappingClass(c);
0682:
0683:                try {
0684:                    return (Mapping) newInstance(mc, new Class[] {
0685:                            DatabaseAdapter.class, Class.class }, new Object[] {
0686:                            this , c });
0687:                } catch (NoSuchMethodException e) {
0688:                    String name = mc.getName();
0689:                    name = name.substring(name.lastIndexOf('.') + 1);
0690:
0691:                    throw new JDOFatalInternalException("Missing constructor "
0692:                            + mc.getName() + "(DatabaseAdapter, Class)");
0693:                }
0694:            }
0695:
0696:            public ColumnMapping getMapping(Column col) {
0697:                Class mc = getMappingClass(col.getType());
0698:
0699:                try {
0700:                    return (ColumnMapping) newInstance(mc,
0701:                            new Class[] { Column.class }, new Object[] { col });
0702:                } catch (NoSuchMethodException e) {
0703:                    String name = mc.getName();
0704:                    name = name.substring(name.lastIndexOf('.') + 1);
0705:
0706:                    throw new JDOUserException(
0707:                            name
0708:                                    + " can only be used with a persistence-capable field");
0709:                }
0710:            }
0711:
0712:            public Mapping getMapping(ClassBaseTable table,
0713:                    int relativeFieldNumber) {
0714:                FieldMetaData fmd = table.getClassMetaData().getFieldRelative(
0715:                        relativeFieldNumber);
0716:                Class mc = getMappingClass(fmd.getType());
0717:
0718:                try {
0719:                    return (Mapping) newInstance(mc, new Class[] {
0720:                            ClassBaseTable.class, int.class }, new Object[] {
0721:                            table, new Integer(relativeFieldNumber) });
0722:                } catch (NoSuchMethodException e) {
0723:                    throw new JDOFatalInternalException("Missing constructor "
0724:                            + mc.getName() + "(ClassBaseTable, int)");
0725:                }
0726:            }
0727:
0728:            protected Class getMappingClass(Class c) {
0729:                Class mappingClass = (Class) typeMappings.get(c);
0730:
0731:                if (mappingClass == UnsupportedMapping.class)
0732:                    throw new JDOUnsupportedOptionException("Fields of type "
0733:                            + c.getName() + " not (yet) supported");
0734:
0735:                if (mappingClass == null)
0736:                    mappingClass = PersistenceCapableMapping.class;
0737:
0738:                return mappingClass;
0739:            }
0740:
0741:            private static Object newInstance(Class clazz,
0742:                    Class[] ctorArgTypes, Object[] ctorArgs)
0743:                    throws NoSuchMethodException {
0744:                try {
0745:                    return clazz.getConstructor(ctorArgTypes).newInstance(
0746:                            ctorArgs);
0747:                } catch (IllegalAccessException e) {
0748:                    throw new JDOFatalInternalException(
0749:                            "Can't access constructor for mapping object", e);
0750:                } catch (InstantiationException e) {
0751:                    throw new JDOFatalInternalException(
0752:                            "Can't instantiate mapping object", e);
0753:                } catch (InvocationTargetException e) {
0754:                    Throwable t = e.getTargetException();
0755:
0756:                    if (t instanceof  Error)
0757:                        throw (Error) t;
0758:                    else if (t instanceof  RuntimeException)
0759:                        throw (RuntimeException) t;
0760:                    else
0761:                        throw new JDOFatalInternalException("Constructor for "
0762:                                + clazz.getName() + " failed", e);
0763:                }
0764:            }
0765:
0766:            public Connection getConnection(DataSource ds, String userName,
0767:                    String password, int isolationLevel) throws SQLException {
0768:                Connection conn;
0769:
0770:                if (userName == null)
0771:                    conn = ds.getConnection();
0772:                else
0773:                    conn = ds.getConnection(userName, password);
0774:
0775:                boolean succeeded = false;
0776:
0777:                try {
0778:                    if (isolationLevel == Connection.TRANSACTION_NONE)
0779:                        conn.setAutoCommit(true);
0780:                    else {
0781:                        conn.setAutoCommit(false);
0782:                        conn.setTransactionIsolation(isolationLevel);
0783:                    }
0784:
0785:                    succeeded = true;
0786:                } finally {
0787:                    if (!succeeded)
0788:                        conn.close();
0789:                }
0790:
0791:                return conn;
0792:            }
0793:
0794:            public void closeConnection(Connection conn) throws SQLException {
0795:                conn.close();
0796:            }
0797:
0798:            public String getSchemaName(Connection conn) throws SQLException {
0799:                throw new UnsupportedOperationException(
0800:                        "Don't know how to determine the current schema for this type of DBMS: "
0801:                                + databaseProductName + ' '
0802:                                + databaseProductVersion);
0803:            }
0804:
0805:            public String getIdentifierQuoteString() {
0806:                return identifierQuoteString;
0807:            }
0808:
0809:            public boolean createIndexesBeforeForeignKeys() {
0810:                return false;
0811:            }
0812:
0813:            public boolean includeOrderByColumnsInSelect() {
0814:                return true;
0815:            }
0816:
0817:            public boolean supportsAlterTableDropConstraint() {
0818:                return true;
0819:            }
0820:
0821:            public boolean supportsDeferredConstraints() {
0822:                return true;
0823:            }
0824:
0825:            /**
0826:             * Indicates whether or not two boolean expressions can be directly compared
0827:             * to each other using the = or &lt;&gt; operator.
0828:             * Some DBMS's allow you to say WHERE (X = 12) = (Y = 34), others don't.
0829:             * <p>
0830:             * The default value returned by the implementation in this class is
0831:             * <code>true</code>.
0832:             *
0833:             * @return
0834:             *      <code>true</code> if boolean expressions can be compared,
0835:             *      <code>false</code> otherwise.
0836:             */
0837:
0838:            public boolean supportsBooleanComparison() {
0839:                return true;
0840:            }
0841:
0842:            public boolean supportsNullsInCandidateKeys() {
0843:                return true;
0844:            }
0845:
0846:            public QueryStatement newQueryStatement(Table table) {
0847:                return new QueryStatement(table);
0848:            }
0849:
0850:            public QueryStatement newQueryStatement(Table table,
0851:                    SQLIdentifier rangeVar) {
0852:                return new QueryStatement(table, rangeVar);
0853:            }
0854:
0855:            /**
0856:             * Returns a new TableExpression object appropriate for this DBMS.
0857:             * This should be an instance of one of the three built-in styles of table
0858:             * expression:
0859:             * <ul>
0860:             *   <li>TableExprAsJoins</li>
0861:             *   <li>TableExprAsSubjoins</li>
0862:             *   <li>TableExprAsSubquery</li>
0863:             * </ul>
0864:             * TableExprAsSubjoins is the default, which arguably produces the most
0865:             * readable SQL but doesn't work on all DBMS's.  TableExprAsSubjoins
0866:             * should work anywhere, but may be less efficient.
0867:             *
0868:             * @param qs        The query statement in which the table expression will
0869:             *                  be included.
0870:             * @param table     The main table in the expression.
0871:             * @param rangeVar  The SQL alias, or "range variable", to assign to the
0872:             *                  expression or to the main table.
0873:             */
0874:
0875:            public TableExpression newTableExpression(QueryStatement qs,
0876:                    Table table, SQLIdentifier rangeVar) {
0877:                return new TableExprAsSubjoins(qs, table, rangeVar);
0878:            }
0879:
0880:            /**
0881:             * Returns the appropriate SQL to create the given table having the given
0882:             * columns.  No column constraints or key definitions should be included.
0883:             * It should return something like:
0884:             * <p>
0885:             * <blockquote><pre>
0886:             * CREATE TABLE FOO ( BAR VARCHAR(30), BAZ INTEGER )
0887:             * </pre></blockquote>
0888:             *
0889:             * @param table     The table to create.
0890:             * @param columns   The columns of the table.
0891:             *
0892:             * @return  The text of the SQL statement.
0893:             */
0894:
0895:            public String getCreateTableStatement(BaseTable table,
0896:                    Column[] columns) {
0897:                StringBuffer createStmt = new StringBuffer();
0898:
0899:                createStmt.append("CREATE TABLE ").append(table.getName())
0900:                        .append("\n(\n");
0901:                for (int i = 0; i < columns.length; ++i) {
0902:                    if (i > 0)
0903:                        createStmt.append(",\n");
0904:
0905:                    createStmt.append("    ").append(
0906:                            columns[i].getSQLDefinition());
0907:                }
0908:
0909:                createStmt.append("\n)");
0910:
0911:                return createStmt.toString();
0912:            }
0913:
0914:            /**
0915:             * Returns the appropriate SQL to add a primary key to its table.
0916:             * It should return something like:
0917:             * <p>
0918:             * <blockquote><pre>
0919:             * ALTER TABLE FOO ADD CONSTRAINT FOO_PK PRIMARY KEY (BAR)
0920:             * </pre></blockquote>
0921:             *
0922:             * @param pkName    The name of the primary key to add.
0923:             * @param pk        An object describing the primary key.
0924:             *
0925:             * @return  The text of the SQL statement.
0926:             */
0927:
0928:            public String getAddPrimaryKeyStatement(SQLIdentifier pkName,
0929:                    PrimaryKey pk) {
0930:                return "ALTER TABLE " + pk.getTable().getName()
0931:                        + " ADD CONSTRAINT " + pkName + ' ' + pk;
0932:            }
0933:
0934:            /**
0935:             * Returns the appropriate SQL to add a candidate key to its table.
0936:             * It should return something like:
0937:             * <p>
0938:             * <blockquote><pre>
0939:             * ALTER TABLE FOO ADD CONSTRAINT FOO_CK UNIQUE (BAZ)
0940:             * </pre></blockquote>
0941:             *
0942:             * @param ckName    The name of the candidate key to add.
0943:             * @param ck        An object describing the candidate key.
0944:             *
0945:             * @return  The text of the SQL statement.
0946:             */
0947:
0948:            public String getAddCandidateKeyStatement(SQLIdentifier ckName,
0949:                    CandidateKey ck) {
0950:                return "ALTER TABLE " + ck.getTable().getName()
0951:                        + " ADD CONSTRAINT " + ckName + ' ' + ck;
0952:            }
0953:
0954:            /**
0955:             * Returns the appropriate SQL to add a foreign key to its table.
0956:             * It should return something like:
0957:             * <p>
0958:             * <blockquote><pre>
0959:             * ALTER TABLE FOO ADD CONSTRAINT FOO_FK1 FOREIGN KEY (BAR, BAZ) REFERENCES ABC (COL1, COL2)
0960:             * </pre></blockquote>
0961:             *
0962:             * @param fkName    The name of the foreign key to add.
0963:             * @param fk        An object describing the foreign key.
0964:             *
0965:             * @return  The text of the SQL statement.
0966:             */
0967:
0968:            public String getAddForeignKeyStatement(SQLIdentifier fkName,
0969:                    ForeignKey fk) {
0970:                return "ALTER TABLE " + fk.getTable().getName()
0971:                        + " ADD CONSTRAINT " + fkName + ' ' + fk;
0972:            }
0973:
0974:            /**
0975:             * Returns the appropriate SQL to add an index to its table.
0976:             * It should return something like:
0977:             * <p>
0978:             * <blockquote><pre>
0979:             * CREATE INDEX FOO_N1 ON FOO (BAR,BAZ)
0980:             * CREATE UNIQUE INDEX FOO_U1 ON FOO (BAR,BAZ)
0981:             * </pre></blockquote>
0982:             *
0983:             * @param idxName   The name of the index to add.
0984:             * @param idx       An object describing the index.
0985:             *
0986:             * @return  The text of the SQL statement.
0987:             */
0988:
0989:            public String getCreateIndexStatement(SQLIdentifier idxName,
0990:                    Index idx) {
0991:                return "CREATE " + (idx.getUnique() ? "UNIQUE " : "")
0992:                        + "INDEX " + idxName + " ON "
0993:                        + idx.getTable().getName() + ' ' + idx;
0994:            }
0995:
0996:            /**
0997:             * Returns the appropriate SQL to drop the given table.
0998:             * It should return something like:
0999:             * <p>
1000:             * <blockquote><pre>
1001:             * DROP TABLE FOO CASCADE
1002:             * </pre></blockquote>
1003:             *
1004:             * @param table     The table to drop.
1005:             *
1006:             * @return  The text of the SQL statement.
1007:             */
1008:
1009:            public String getDropTableStatement(BaseTable table) {
1010:                return "DROP TABLE " + table.getName() + " CASCADE";
1011:            }
1012:
1013:            /**
1014:             * Returns the appropriate SQL to drop the given view.
1015:             * It should return something like:
1016:             * <p>
1017:             * <blockquote><pre>
1018:             * DROP VIEW FOO
1019:             * </pre></blockquote>
1020:             *
1021:             * @param view      The view to drop.
1022:             *
1023:             * @return  The text of the SQL statement.
1024:             */
1025:
1026:            public String getDropViewStatement(View view) {
1027:                return "DROP VIEW " + view.getName();
1028:            }
1029:
1030:            /**
1031:             * Returns the appropriate SQL expression for the JDOQL String.length()
1032:             * method.
1033:             * It should return something like:
1034:             * <p>
1035:             * <blockquote><pre>
1036:             * CHAR_LENGTH(str)
1037:             * </pre></blockquote>
1038:             *
1039:             * @param str   The argument to the length() method.
1040:             *
1041:             * @return  The text of the SQL expression.
1042:             */
1043:
1044:            public NumericExpression lengthMethod(CharacterExpression str) {
1045:                ArrayList args = new ArrayList();
1046:                args.add(str);
1047:
1048:                return new NumericExpression("CHAR_LENGTH", args);
1049:            }
1050:
1051:            /**
1052:             * Returns the appropriate SQL expression for the JDOQL
1053:             * String.substring(str,begin) method.
1054:             * It should return something like:
1055:             * <p>
1056:             * <blockquote><pre>
1057:             * SUBSTRING(str FROM begin)
1058:             * </pre></blockquote>
1059:             * Note that the value of <var>begin</var> is base 0 (Java-style), while most
1060:             * SQL string functions use base 1.
1061:             *
1062:             * @param str   The first argument to the substring() method.
1063:             * @param begin The second argument to the substring() method.
1064:             *
1065:             * @return  The text of the SQL expression.
1066:             */
1067:
1068:            public CharacterExpression substringMethod(CharacterExpression str,
1069:                    NumericExpression begin) {
1070:                return new SubstringExpression(str, begin);
1071:            }
1072:
1073:            /**
1074:             * Returns the appropriate SQL expression for the JDOQL
1075:             * String.substring(str,begin,end) method.
1076:             * It should return something like:
1077:             * <p>
1078:             * <blockquote><pre>
1079:             * SUBSTRING(str FROM begin FOR len)
1080:             * </pre></blockquote>
1081:             * Note that the value of <var>begin</var> is base 0 (Java-style), while most
1082:             * SQL string functions use base 1.
1083:             * Note also that an end position is given, while most SQL substring
1084:             * functions take a length.
1085:             *
1086:             * @param str   The first argument to the substring() method.
1087:             * @param begin The second argument to the substring() method.
1088:             * @param end   The third argument to the substring() method.
1089:             *
1090:             * @return  The text of the SQL expression.
1091:             */
1092:
1093:            public CharacterExpression substringMethod(CharacterExpression str,
1094:                    NumericExpression begin, NumericExpression end) {
1095:                return new SubstringExpression(str, begin, end);
1096:            }
1097:
1098:            public String toString() {
1099:                String className = getClass().getName();
1100:                String name = className
1101:                        .substring(className.lastIndexOf('.') + 1);
1102:
1103:                return name + ", " + databaseProductName + " version "
1104:                        + databaseProductVersion + " major "
1105:                        + databaseMajorVersion + " minor "
1106:                        + databaseMinorVersion;
1107:            }
1108:
1109:            private static class UnsupportedMapping {
1110:                private UnsupportedMapping() {
1111:                }
1112:            }
1113:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.